From 4c9fcb77c36b64ee6cca39f1373eca4c48f4fd90 Mon Sep 17 00:00:00 2001 From: elad Date: Mon, 5 Oct 2009 04:20:13 +0000 Subject: [PATCH] - Add usermount_common_policy() that implements some common (everything but access control) user mounting policies: enforced MNT_NOSUID and MNT_NODEV, no MNT_EXPORT, MNT_EXEC propagation. This can be useful for secmodels that are interested in simply adding finer grained user mount support. - Add a mount subsystem listener for KAUTH_REQ_SYSTEM_MOUNT_GET. --- sys/kern/vfs_init.c | 60 +++++++++++++- sys/secmodel/suser/secmodel_suser.c | 119 +++++++++++++--------------- sys/sys/mount.h | 4 +- 3 files changed, 115 insertions(+), 68 deletions(-) diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c index 11d96681e384..cffe6a341c6e 100644 --- a/sys/kern/vfs_init.c +++ b/sys/kern/vfs_init.c @@ -1,4 +1,4 @@ -/* $NetBSD: vfs_init.c,v 1.44 2009/05/03 21:25:44 elad Exp $ */ +/* $NetBSD: vfs_init.c,v 1.45 2009/10/05 04:20:13 elad Exp $ */ /*- * Copyright (c) 1998, 2000, 2008 The NetBSD Foundation, Inc. @@ -67,7 +67,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: vfs_init.c,v 1.44 2009/05/03 21:25:44 elad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: vfs_init.c,v 1.45 2009/10/05 04:20:13 elad Exp $"); #include #include @@ -83,6 +83,7 @@ __KERNEL_RCSID(0, "$NetBSD: vfs_init.c,v 1.44 2009/05/03 21:25:44 elad Exp $"); #include #include #include +#include /* * Sigh, such primitive tools are these... @@ -119,6 +120,8 @@ const struct vnodeopv_desc * const vfs_special_vnodeopv_descs[] = { struct vfs_list_head vfs_list = /* vfs list */ LIST_HEAD_INITIALIZER(vfs_list); +static kauth_listener_t mount_listener; + /* * This code doesn't work if the defn is **vnodop_defns with cc. * The problem is because of the compiler sometimes putting in an @@ -331,6 +334,56 @@ vfs_op_check(void) } #endif /* DEBUG */ +/* + * Common routine to check if an unprivileged mount is allowed. + * + * We export just this part (i.e., without the access control) so that if a + * secmodel wants to implement finer grained user mounts it can do so without + * copying too much code. More elaborate policies (i.e., specific users allowed + * to also create devices and/or introduce set-id binaries, or export + * file-systems) will require a different implementation. + * + * This routine is intended to be called from listener context, and as such + * does not take credentials as an argument. + */ +int +usermount_common_policy(struct mount *mp, u_long flags) +{ + + /* No exporting if unprivileged. */ + if (flags & MNT_EXPORTED) + return EPERM; + + /* Must have 'nosuid' and 'nodev'. */ + if ((flags & MNT_NODEV) == 0 || (flags & MNT_NOSUID) == 0) + return EPERM; + + /* Retain 'noexec'. */ + if ((mp->mnt_flag & MNT_NOEXEC) && (flags & MNT_NOEXEC) == 0) + return EPERM; + + return 0; +} + +static int +mount_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie, + void *arg0, void *arg1, void *arg2, void *arg3) +{ + int result; + enum kauth_system_req req; + + result = KAUTH_RESULT_DEFER; + req = (enum kauth_system_req)arg0; + + if ((action != KAUTH_SYSTEM_MOUNT) || + (req != KAUTH_REQ_SYSTEM_MOUNT_GET)) + return result; + + result = KAUTH_RESULT_ALLOW; + + return result; +} + /* * Initialize the vnode structures and initialize each file system type. */ @@ -382,6 +435,9 @@ vfsinit(void) */ vfs_hooks_init(); + mount_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, + mount_listener_cb, NULL); + /* * Establish each file system which was statically * included in the kernel. diff --git a/sys/secmodel/suser/secmodel_suser.c b/sys/secmodel/suser/secmodel_suser.c index 544791951d0f..4658d4cb6e14 100644 --- a/sys/secmodel/suser/secmodel_suser.c +++ b/sys/secmodel/suser/secmodel_suser.c @@ -1,4 +1,4 @@ -/* $NetBSD: secmodel_suser.c,v 1.26 2009/10/03 03:59:39 elad Exp $ */ +/* $NetBSD: secmodel_suser.c,v 1.27 2009/10/05 04:20:13 elad Exp $ */ /*- * Copyright (c) 2006 Elad Efrat * All rights reserved. @@ -38,7 +38,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: secmodel_suser.c,v 1.26 2009/10/03 03:59:39 elad Exp $"); +__KERNEL_RCSID(0, "$NetBSD: secmodel_suser.c,v 1.27 2009/10/05 04:20:13 elad Exp $"); #include #include @@ -152,7 +152,6 @@ void secmodel_suser_init(void) { secmodel_suser_curtain = 0; - dovfsusermount = 0; } void @@ -303,82 +302,71 @@ secmodel_suser_system_cb(kauth_cred_t cred, kauth_action_t action, break; - case KAUTH_SYSTEM_FS_RESERVEDSPACE: - if (isroot) - result = KAUTH_RESULT_ALLOW; - break; - case KAUTH_SYSTEM_MOUNT: switch (req) { - case KAUTH_REQ_SYSTEM_MOUNT_GET: + case KAUTH_REQ_SYSTEM_MOUNT_NEW: { + struct mount *mp = ((struct vnode *)arg1)->v_mount; + u_long flags = (u_long)arg2; + + if (isroot) { + result = KAUTH_RESULT_ALLOW; + break; + } + + if (!dovfsusermount) + break; + + if (usermount_common_policy(mp, flags) != 0) + break; + 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: { + struct mount *mp = arg1; - case KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT: - if (isroot) + 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; } + if (!dovfsusermount) + break; + + /* Must own the mount. */ + if (mp->mnt_stat.f_owner != kauth_cred_geteuid(cred)) + break; + + 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; } + case KAUTH_REQ_SYSTEM_MOUNT_UPDATE: { + struct mount *mp = arg1; + u_long flags = (u_long)arg2; + + if (isroot) { + result = KAUTH_RESULT_ALLOW; + break; + } + + if (!dovfsusermount) + break; + + /* Must own the mount. */ + if (mp->mnt_stat.f_owner != kauth_cred_geteuid(cred)) + break; + + if (usermount_common_policy(mp, flags) != 0) + break; + + result = KAUTH_RESULT_ALLOW; + break; + } default: break; @@ -444,6 +432,7 @@ secmodel_suser_system_cb(kauth_cred_t cred, kauth_action_t action, case KAUTH_SYSTEM_MKNOD: case KAUTH_SYSTEM_SETIDCORE: case KAUTH_SYSTEM_MODULE: + case KAUTH_SYSTEM_FS_RESERVEDSPACE: if (isroot) result = KAUTH_RESULT_ALLOW; break; diff --git a/sys/sys/mount.h b/sys/sys/mount.h index 7f2e42c5d778..e96f67ceb9f9 100644 --- a/sys/sys/mount.h +++ b/sys/sys/mount.h @@ -1,4 +1,4 @@ -/* $NetBSD: mount.h,v 1.191 2009/07/18 16:31:43 reinoud Exp $ */ +/* $NetBSD: mount.h,v 1.192 2009/10/05 04:20:13 elad Exp $ */ /* * Copyright (c) 1989, 1991, 1993 @@ -433,6 +433,8 @@ void mount_finispecific(struct mount *); void * mount_getspecific(struct mount *, specificdata_key_t); void mount_setspecific(struct mount *, specificdata_key_t, void *); +int usermount_common_policy(struct mount *, u_long); + LIST_HEAD(vfs_list_head, vfsops); extern struct vfs_list_head vfs_list;