Subject: Re: mount(2) on kauth(9)
To: None <tech-kern@NetBSD.org>
From: Elad Efrat <elad@NetBSD.org>
List: tech-kern
Date: 01/02/2007 00:47:06
This is a multi-part message in MIME format.
--------------020608020207080308040701
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

attached is a diff that makes mount(2) use kauth(9).

the only difference between this and the original diff is that the
flags are no longer passed as pointer -- apart from requiring certain
order if we introduce logging listeners, this also breaks the kauth(9)
restrictive policy, as a malicious listener can modify the flags.

the MNT_NOSUID/MNT_NODEV/MNT_NOEXEC enforcement is done inside the
secmodel, simply preventing non-root users from doing what earlier
would be silently enforced. (this is a compromise we'll have to make.)

if there are no objections, I'll commit this.

-e.

--------------020608020207080308040701
Content-Type: text/plain;
 name="mount.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="mount.diff"

Index: sys/kauth.h
===================================================================
RCS file: /usr/cvs/src/sys/sys/kauth.h,v
retrieving revision 1.29
diff -u -p -r1.29 kauth.h
--- sys/kauth.h	26 Dec 2006 10:43:44 -0000	1.29
+++ sys/kauth.h	29 Dec 2006 10:44:15 -0000
@@ -87,6 +87,7 @@ enum {
 	KAUTH_SYSTEM_FILEHANDLE,
 	KAUTH_SYSTEM_LKM,
 	KAUTH_SYSTEM_MKNOD,
+	KAUTH_SYSTEM_MOUNT,
 	KAUTH_SYSTEM_REBOOT,
 	KAUTH_SYSTEM_SETIDCORE,
 	KAUTH_SYSTEM_SWAPCTL,
@@ -101,6 +102,10 @@ enum kauth_system_req {
 	KAUTH_REQ_SYSTEM_CHROOT_CHROOT=1,
 	KAUTH_REQ_SYSTEM_CHROOT_FCHROOT,
 	KAUTH_REQ_SYSTEM_DEBUG_IPKDB,
+	KAUTH_REQ_SYSTEM_MOUNT_GET,
+	KAUTH_REQ_SYSTEM_MOUNT_NEW,
+	KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT,
+	KAUTH_REQ_SYSTEM_MOUNT_UPDATE,
 	KAUTH_REQ_SYSTEM_SYSCTL_ADD,
 	KAUTH_REQ_SYSTEM_SYSCTL_DELETE,
 	KAUTH_REQ_SYSTEM_SYSCTL_DESC,
Index: kern/vfs_syscalls.c
===================================================================
RCS file: /usr/cvs/src/sys/kern/vfs_syscalls.c,v
retrieving revision 1.289
diff -u -p -r1.289 vfs_syscalls.c
--- kern/vfs_syscalls.c	31 Dec 2006 10:05:52 -0000	1.289
+++ kern/vfs_syscalls.c	31 Dec 2006 12:10:01 -0000
@@ -170,39 +170,12 @@ mount_update(struct lwp *l, struct vnode
 		error = EOPNOTSUPP;	/* Needs translation */
 		goto out;
 	}
-	/*
-	 * In "highly secure" mode, don't let the caller do anything
-	 * but downgrade a filesystem from read-write to read-only.
-	 */
-	if (securelevel >= 2 &&
-	    flags !=
-	    (mp->mnt_flag | MNT_RDONLY | MNT_RELOAD | MNT_FORCE | MNT_UPDATE)) {
-		error = EPERM;
-		goto out;
-	}
-	/*
-	 * Only root, or the user that did the original mount is
-	 * permitted to update it.
-	 */
- 	if (mp->mnt_stat.f_owner != kauth_cred_geteuid(l->l_cred) &&
-	    (error = kauth_authorize_generic(l->l_cred,
-	    KAUTH_GENERIC_ISSUSER, NULL)) != 0) {
+
+	error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
+	    KAUTH_REQ_SYSTEM_MOUNT_UPDATE, mp, KAUTH_ARG(flags), data);
+	if (error)
 		goto out;
-	}
-	/*
-	 * Do not allow NFS export by non-root users. For non-root
-	 * users, silently enforce MNT_NOSUID and MNT_NODEV, and
-	 * MNT_NOEXEC if mount point is already MNT_NOEXEC.
-	 */
-	if (kauth_cred_geteuid(l->l_cred) != 0) {
-		if (flags & MNT_EXPORTED) {
-			error = EPERM;
-			goto out;
-		}
-		flags |= MNT_NOSUID | MNT_NODEV;
-		if (saved_flags & MNT_NOEXEC)
-			flags |= MNT_NOEXEC;
-	}
+
 	if (vfs_busy(mp, LK_NOWAIT, 0)) {
 		error = EPERM;
 		goto out;
@@ -273,9 +246,9 @@ mount_domount(struct lwp *l, struct vnod
 	char fstypename[MFSNAMELEN];
 	int error;
 
-	/* XXX secmodel stuff. */
-	if (securelevel >= 2) {
-		error = EPERM;
+	error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
+	    KAUTH_REQ_SYSTEM_MOUNT_NEW, vp, KAUTH_ARG(flags), data);
+	if (error) {
 		vput(vp);
 		goto out;
 	}
@@ -306,13 +279,6 @@ mount_domount(struct lwp *l, struct vnod
 	}
 
 	/*
-	 * For non-root users, silently enforce MNT_NOSUID and MNT_NODEV.
-	 */
-	if (kauth_cred_geteuid(l->l_cred) != 0) {
-		flags |= MNT_NOSUID | MNT_NODEV;
-	}
-
-	/*
 	 * Copy file-system type from userspace.
 	 */
 	error = copyinstr(fstype, fstypename, MFSNAMELEN, NULL);
@@ -439,6 +405,12 @@ mount_getargs(struct lwp *l, struct vnod
 
 	mp = vp->v_mount;
 
+	/* XXX: probably some notion of "can see" here if we want isolation. */ 
+	error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
+	    KAUTH_REQ_SYSTEM_MOUNT_GET, mp, data, NULL);
+	if (error)
+		goto out;
+
 	if ((vp->v_flag & VROOT) == 0) {
 		error = EINVAL;
 		goto out;
@@ -473,12 +445,6 @@ sys_mount(struct lwp *l, void *v, regist
 	struct nameidata nd;
 	int error;
 
-	/* XXX secmodel stuff. */
-	if (dovfsusermount == 0 && (SCARG(uap, flags) & MNT_GETARGS) == 0 &&
-	    (error = kauth_authorize_generic(l->l_cred, KAUTH_GENERIC_ISSUSER,
-	    &l->l_acflag)))
-		return (error);
-
 	/*
 	 * Get vnode to be covered
 	 */
Index: secmodel/bsd44/secmodel_bsd44_securelevel.c
===================================================================
RCS file: /usr/cvs/src/sys/secmodel/bsd44/secmodel_bsd44_securelevel.c,v
retrieving revision 1.21
diff -u -p -r1.21 secmodel_bsd44_securelevel.c
--- secmodel/bsd44/secmodel_bsd44_securelevel.c	31 Dec 2006 10:38:18 -0000	1.21
+++ secmodel/bsd44/secmodel_bsd44_securelevel.c	31 Dec 2006 11:33:51 -0000
@@ -170,6 +170,37 @@ secmodel_bsd44_securelevel_system_cb(kau
 			result = KAUTH_RESULT_ALLOW;
 		break;
 
+	case KAUTH_SYSTEM_MOUNT:
+		switch (req) {
+		case KAUTH_REQ_SYSTEM_MOUNT_NEW:
+			if (securelevel > 1)
+				break;
+
+			result = KAUTH_RESULT_ALLOW;
+			break;
+
+		case KAUTH_REQ_SYSTEM_MOUNT_UPDATE:
+			if (securelevel > 1) {
+				struct mount *mp = arg1;
+				u_long flags = (u_long)arg2;
+
+				/* Can only degrade from read/write to read-only. */
+				if (flags != (mp->mnt_flag | MNT_RDONLY | MNT_RELOAD |
+				    MNT_FORCE | MNT_UPDATE))
+					break;
+			}
+
+			result = KAUTH_RESULT_ALLOW;
+
+			break;
+
+		default:
+			result = KAUTH_RESULT_DEFER;
+			break;
+		}
+
+		break;
+
 	case KAUTH_SYSTEM_SYSCTL:
 		switch (req) {
 		case KAUTH_REQ_SYSTEM_SYSCTL_ADD:
Index: secmodel/bsd44/secmodel_bsd44_suser.c
===================================================================
RCS file: /usr/cvs/src/sys/secmodel/bsd44/secmodel_bsd44_suser.c,v
retrieving revision 1.23
diff -u -p -r1.23 secmodel_bsd44_suser.c
--- secmodel/bsd44/secmodel_bsd44_suser.c	27 Dec 2006 10:02:46 -0000	1.23
+++ secmodel/bsd44/secmodel_bsd44_suser.c	31 Dec 2006 12:09:31 -0000
@@ -60,6 +60,8 @@ __KERNEL_RCSID(0, "$NetBSD: secmodel_bsd
 
 #include <secmodel/bsd44/suser.h>
 
+extern int dovfsusermount;
+
 void
 secmodel_bsd44_suser_start(void)
 {
@@ -138,6 +140,83 @@ secmodel_bsd44_suser_system_cb(kauth_cre
 	req = (enum kauth_system_req)arg0;
 
 	switch (action) {
+	case KAUTH_SYSTEM_MOUNT:
+		switch (req) {
+		case KAUTH_REQ_SYSTEM_MOUNT_GET:
+			result = KAUTH_RESULT_ALLOW;
+			break;
+
+		case KAUTH_REQ_SYSTEM_MOUNT_NEW:
+			if (isroot)
+				result = KAUTH_RESULT_ALLOW;
+			else if (dovfsusermount) {
+				struct vnode *vp = arg1;
+				u_long flags = (u_long)arg2;
+
+				if (!(flags & MNT_NODEV) ||
+				    !(flags & MNT_NOSUID))
+					break;
+
+				if ((vp->v_mount->mnt_flag & MNT_NOEXEC) &&
+				    !(flags & MNT_NOEXEC))
+					break;
+
+				result = KAUTH_RESULT_ALLOW;
+			}
+
+			break;
+
+		case KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT:
+			if (isroot)
+				result = KAUTH_RESULT_ALLOW;
+			else {
+				struct mount *mp = arg1;
+
+				if (mp->mnt_stat.f_owner ==
+				    kauth_cred_geteuid(cred))
+					result = KAUTH_RESULT_ALLOW;
+			}
+
+			break;
+
+		case KAUTH_REQ_SYSTEM_MOUNT_UPDATE:
+			if (isroot)
+				result = KAUTH_RESULT_ALLOW;
+			else if (dovfsusermount) {
+				struct mount *mp = arg1;
+				u_long flags = (u_long)arg2;
+
+				/* No exporting for non-root. */
+				if (flags & MNT_EXPORTED)
+					break;
+
+				if (!(flags & MNT_NODEV) ||
+				    !(flags & MNT_NOSUID))
+					break;
+
+				/*
+				 * Only super-user, or user that did the mount,
+				 * can update.
+				 */
+				if (mp->mnt_stat.f_owner !=
+				    kauth_cred_geteuid(cred))
+					break;
+
+				/* Retain 'noexec'. */
+				if ((mp->mnt_flag & MNT_NOEXEC) &&
+				    !(flags & MNT_NOEXEC))
+					break;
+
+				result = KAUTH_RESULT_ALLOW;
+			}
+
+			break;
+
+		default:
+			result = KAUTH_RESULT_DEFER;
+			break;
+		}
+
 	case KAUTH_SYSTEM_TIME:
 		switch (req) {
 		case KAUTH_REQ_SYSTEM_TIME_ADJTIME:

--------------020608020207080308040701--