Index: kern/vfs_syscalls.c =================================================================== RCS file: /home/ncvs/src/sys/kern/vfs_syscalls.c,v retrieving revision 1.132 diff -u -r1.132 vfs_syscalls.c --- vfs_syscalls.c 1999/08/22 16:50:30 1.132 +++ vfs_syscalls.c 1999/09/13 18:15:57 @@ -36,7 +36,7 @@ * SUCH DAMAGE. * * @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94 - * $Id: vfs_syscalls.c,v 1.131 1999/08/22 01:46:57 jdp Exp $ + * $Id: vfs_syscalls.c,v 1.132 1999/08/22 16:50:30 jdp Exp $ */ /* For 4.3 integer FS ID compatibility */ @@ -60,6 +60,7 @@ #include #include #include +#include #include @@ -73,18 +74,141 @@ static void checkdirs __P((struct vnode *olddp)); static int chroot_refuse_vdir_fds __P((struct filedesc *fdp)); static int getutimes __P((const struct timeval *, struct timespec *)); +static int isusermountable __P((char *)); static int setfown __P((struct proc *, struct vnode *, uid_t, gid_t)); static int setfmode __P((struct proc *, struct vnode *, int)); static int setfflags __P((struct proc *, struct vnode *, int)); static int setutimes __P((struct proc *, struct vnode *, const struct timespec *, int)); -static int usermount = 0; /* if 1, non-root can mount fs. */ +static char *usermount = NULL; /* List of user-mountable points */ +static int usermountlen = 0; /* Length of current buffer */ int (*union_dircheckp) __P((struct proc *, struct vnode **, struct file *)); -SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0, ""); +/* + * Support calls + */ + +/* + * initusermount + * + * Sets up a small buffer for vfs.usermount + */ + +static int +initusermount(void) +{ + /* Make sure it hasn't been called already */ + if (usermount != NULL) { + return (0); + } + usermountlen = 32; + usermount = malloc(usermountlen, M_MOUNT, M_NOWAIT); + if (usermount == NULL) { + printf("initusermount(): malloc failed!\n"); + return (ENOMEM); + } + usermount[0] = '\0'; + return (0); +} /* + * sysctl_vfs_usermount + * + * Checks if the securelevel is < 1 before modifying. + * note: this uses a malloc()ed buffer. + */ + +static int +sysctl_vfs_usermount SYSCTL_HANDLER_ARGS +{ + int error; + char *newump; + + /* Allocate a buffer if it hasn't been */ + if (usermount == NULL) + if ((error = initusermount())) + return error; + /* Handle a read request */ + if (!req->newptr) { + return (SYSCTL_OUT(req, usermount, strlen(usermount)+1)); + } + newump = malloc(req->newlen+1, M_MOUNT, M_NOWAIT); + if (newump == NULL) { + printf("sysctl_vfs_usermount(): malloc for newump failed!\n"); + return (ENOMEM); + } + error = sysctl_handle_string(oidp, newump, req->newlen+1, req); + if (error == 0 && strcmp(newump, usermount) != 0) { + /* We only update usermount if securelevel is less than 1. */ + if (securelevel > 0) { + error = ENOMEM; + goto finish; + } + /* Check if we have to remalloc, and do it if needed */ + if (strlen(newump) + 1 > usermountlen) { + usermountlen = strlen(newump) + 1; + free(usermount, M_MOUNT); + usermount = malloc(usermountlen, M_MOUNT, M_NOWAIT); + if (usermount == NULL) { + printf("sysctl_vfs_usermount(): malloc failed!\n"); + error = ENOMEM; + goto finish; + } + } + strncpy(usermount, newump, usermountlen); + error = 0; + } + +finish: + free(newump, M_MOUNT); + return (error); +} + +SYSCTL_PROC(_vfs, OID_AUTO, usermount, CTLTYPE_STRING | CTLFLAG_RW, 0, 0, + sysctl_vfs_usermount, "A", ""); + +/* + * isusermountable + * + * This takes a string, and returns true if the mountpoint exists in + * usermount, false otherwise. + */ + +static int +isusermountable(path) + char *path; +{ + char *cp; + char *ep; + int error; + char result[32]; + + /* Allocate a buffer if it hasn't been */ + if (usermount == NULL) + if ((error = initusermount())) + return error; + /* walk usermount, seperated by ':' */ + cp = usermount; + for (;;) { + /* find the end of this component */ + for (ep = cp; (*ep != 0) && (*ep != ':'); ep++); + /* Do the comparison */ + if ((strlen(path) == ep - cp) && + ((strncmp(cp, path, ep - cp) == 0))) { + return (1); + } + /* Check for end of string */ + if (*ep == 0) + break; + /* Get the beginning of the next component */ + cp = ep + 1; + } + /* Definitely no match */ + return (0); +} + +/* * Virtual File System System Calls */ @@ -121,8 +245,6 @@ struct nameidata nd; char fstypename[MFSNAMELEN]; - if (usermount == 0 && (error = suser(p))) - return (error); /* * Do not allow NFS export by non-root users. */ @@ -132,11 +254,6 @@ return (error); } /* - * Silently enforce MNT_NOSUID and MNT_NODEV for non-root users - */ - if (suser_xxx(p->p_ucred, 0, 0)) - SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; - /* * Get vnode to be covered */ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, @@ -180,14 +297,42 @@ goto update; } /* - * If the user is not root, ensure that they own the directory - * onto which we are attempting to mount. + * XXX Get the attributes for the vnode. If we can't read them, + * then we shouldn't be trying this mount. + * (XXX is for "i don't understand", can someone check I'm right + * or wrong and tell me please?) */ - if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || - (va.va_uid != p->p_ucred->cr_uid && - (error = suser(p)))) { - vput(vp); + if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p))) return (error); + + /* + * Check usermounts. + * If the user isn't root, then: + * - check path is in vfs.usermount . If not, error + * - enforce NOSUID and NODEV + * - if the user has rw privs on the mountpoint, allow the mount. + * - if the user has ro privs on the mountpoint, allow a ro mount. + * - if the user has no privs on the mountpoint, error. + */ + if ((error = suser(p))) { + if (!isusermountable(SCARG(uap, path))) { + vput(vp); + return (error); + } + /* Enforce MNT_NOSUID and MNT_NODEV for non-root users */ + SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; + + /* check vnode access permissions */ + if (!(error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p))) { + /* read/write permission */ + } else if (!(error = VOP_ACCESS(vp, VREAD, p->p_ucred, p))) { + /* enforce read only */ + SCARG(uap, flags) |= MNT_RDONLY; + } else { + /* No permission */ + vput(vp); + return (error); + } } if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) return (error);