Module Name: src Committed By: rmind Date: Tue May 26 00:39:14 UTC 2009
Modified Files: src/sys/kern: sys_mqueue.c Log Message: - Slightly rework the way permissions are checked. Neither mq_receive() not mq_send() should fail due to permissions. Noted by Stathis Kamperis! - Check for empty message queue name (POSIX does not allow this for regular files, and it's weird), check for DTYPE_MQUEUE, fix permission check in mq_unlink(), clean up. To generate a diff of this commit: cvs rdiff -u -r1.17 -r1.18 src/sys/kern/sys_mqueue.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/kern/sys_mqueue.c diff -u src/sys/kern/sys_mqueue.c:1.17 src/sys/kern/sys_mqueue.c:1.18 --- src/sys/kern/sys_mqueue.c:1.17 Sat May 16 23:58:09 2009 +++ src/sys/kern/sys_mqueue.c Tue May 26 00:39:14 2009 @@ -1,4 +1,4 @@ -/* $NetBSD: sys_mqueue.c,v 1.17 2009/05/16 23:58:09 rmind Exp $ */ +/* $NetBSD: sys_mqueue.c,v 1.18 2009/05/26 00:39:14 rmind Exp $ */ /* * Copyright (c) 2007, 2008 Mindaugas Rasiukevicius <rmind at NetBSD org> @@ -42,7 +42,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sys_mqueue.c,v 1.17 2009/05/16 23:58:09 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sys_mqueue.c,v 1.18 2009/05/26 00:39:14 rmind Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -87,8 +87,6 @@ static int mq_stat_fop(file_t *, struct stat *); static int mq_close_fop(file_t *); -#define FNOVAL -1 - static const struct fileops mqops = { .fo_read = fbadop_read, .fo_write = fbadop_write, @@ -167,57 +165,28 @@ } /* - * Check access against message queue. - */ -static inline int -mqueue_access(struct lwp *l, struct mqueue *mq, int access) -{ - mode_t acc_mode = 0; - - KASSERT(mutex_owned(&mq->mq_mtx)); - KASSERT(access != FNOVAL); - - /* Note the difference between VREAD/VWRITE and FREAD/FWRITE */ - if (access & FREAD) - acc_mode |= VREAD; - if (access & FWRITE) - acc_mode |= VWRITE; - - return vaccess(VNON, mq->mq_mode, mq->mq_euid, mq->mq_egid, - acc_mode, l->l_cred); -} - -/* - * Get the mqueue from the descriptor. - * => locks the message queue, if found - * => increments the reference on file entry + * mqueue_get: get the mqueue from the descriptor. + * => locks the message queue, if found. + * => holds a reference on the file descriptor. */ static int -mqueue_get(struct lwp *l, mqd_t mqd, int access, file_t **fpr) +mqueue_get(mqd_t mqd, file_t **fpr) { - file_t *fp; struct mqueue *mq; + file_t *fp; - /* Get the file and descriptor */ fp = fd_getfile((int)mqd); - if (fp == NULL) + if (__predict_false(fp == NULL)) { return EBADF; - - /* Increment the reference of file entry, and lock the mqueue */ - mq = fp->f_data; - *fpr = fp; - mutex_enter(&mq->mq_mtx); - if (access == FNOVAL) { - KASSERT(mutex_owned(&mq->mq_mtx)); - return 0; } - - /* Check the access mode and permission */ - if ((fp->f_flag & access) != access || mqueue_access(l, mq, access)) { - mutex_exit(&mq->mq_mtx); + if (__predict_false(fp->f_type != DTYPE_MQUEUE)) { fd_putfile((int)mqd); - return EPERM; + return EBADF; } + mq = fp->f_data; + mutex_enter(&mq->mq_mtx); + + *fpr = fp; return 0; } @@ -364,6 +333,12 @@ return EMFILE; } + /* Empty name is invalid */ + if (name[0] == '\0') { + kmem_free(name, MQ_NAMELEN); + return EINVAL; + } + /* Check for mqueue attributes */ if (SCARG(uap, attr)) { error = copyin(SCARG(uap, attr), &attr, @@ -400,7 +375,9 @@ strlcpy(mq_new->mq_name, name, MQ_NAMELEN); memcpy(&mq_new->mq_attrib, &attr, sizeof(struct mq_attr)); - mq_new->mq_attrib.mq_flags = oflag; + + CTASSERT((O_MASK & (MQ_UNLINK | MQ_RECEIVE)) == 0); + mq_new->mq_attrib.mq_flags = (O_MASK & oflag); /* Store mode and effective UID with GID */ mq_new->mq_mode = ((SCARG(uap, mode) & @@ -425,6 +402,8 @@ mutex_enter(&mqlist_mtx); mq = mqueue_lookup(name); if (mq) { + mode_t acc_mode; + KASSERT(mutex_owned(&mq->mq_mtx)); /* Check if mqueue is not marked as unlinking */ @@ -437,8 +416,20 @@ error = EEXIST; goto exit; } - /* Check the permission */ - if (mqueue_access(l, mq, fp->f_flag)) { + + /* + * Check the permissions. Note the difference between + * VREAD/VWRITE and FREAD/FWRITE. + */ + acc_mode = 0; + if (fp->f_flag & FREAD) { + acc_mode |= VREAD; + } + if (fp->f_flag & FWRITE) { + acc_mode |= VWRITE; + } + if (vaccess(VNON, mq->mq_mode, mq->mq_euid, mq->mq_egid, + acc_mode, l->l_cred)) { error = EACCES; goto exit; } @@ -509,7 +500,7 @@ int error; /* Get the message queue */ - error = mqueue_get(l, mqdes, FREAD, &fp); + error = mqueue_get(mqdes, &fp); if (error) return error; mq = fp->f_data; @@ -670,7 +661,7 @@ msg->msg_prio = msg_prio; /* Get the mqueue */ - error = mqueue_get(l, mqdes, FWRITE, &fp); + error = mqueue_get(mqdes, &fp); if (error) { mqueue_freemsg(msg, size); return error; @@ -814,7 +805,7 @@ return error; } - error = mqueue_get(l, SCARG(uap, mqdes), FNOVAL, &fp); + error = mqueue_get(SCARG(uap, mqdes), &fp); if (error) return error; mq = fp->f_data; @@ -853,7 +844,7 @@ int error; /* Get the message queue */ - error = mqueue_get(l, SCARG(uap, mqdes), FNOVAL, &fp); + error = mqueue_get(SCARG(uap, mqdes), &fp); if (error) return error; mq = fp->f_data; @@ -884,7 +875,7 @@ nonblock = (attr.mq_flags & O_NONBLOCK); /* Get the message queue */ - error = mqueue_get(l, SCARG(uap, mqdes), FNOVAL, &fp); + error = mqueue_get(SCARG(uap, mqdes), &fp); if (error) return error; mq = fp->f_data; @@ -942,7 +933,8 @@ } /* Check the permissions */ - if (mqueue_access(l, mq, FWRITE)) { + if (kauth_cred_geteuid(l->l_cred) != mq->mq_euid && + kauth_authorize_generic(l->l_cred, KAUTH_GENERIC_ISSUSER, NULL)) { mutex_exit(&mq->mq_mtx); error = EACCES; goto error;