Home | History | Annotate | Line # | Download | only in extensions
      1 /*	$NetBSD: secmodel_extensions_vfs.c,v 1.1 2023/04/22 13:54:19 riastradh Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2011 Elad Efrat <elad (at) NetBSD.org>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. The name of the author may not be used to endorse or promote products
     16  *    derived from this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 #include <sys/cdefs.h>
     31 __KERNEL_RCSID(0, "$NetBSD: secmodel_extensions_vfs.c,v 1.1 2023/04/22 13:54:19 riastradh Exp $");
     32 
     33 #include <sys/types.h>
     34 #include <sys/param.h>
     35 
     36 #include <sys/kauth.h>
     37 #include <sys/vnode.h>
     38 
     39 #include <secmodel/secmodel.h>
     40 #include <secmodel/extensions/extensions.h>
     41 #include <secmodel/extensions/extensions_impl.h>
     42 
     43 static int dovfsusermount;
     44 static int hardlink_check_uid;
     45 static int hardlink_check_gid;
     46 
     47 static kauth_listener_t l_system, l_vnode;
     48 
     49 static int secmodel_extensions_system_cb(kauth_cred_t, kauth_action_t,
     50     void *, void *, void *, void *, void *);
     51 static int secmodel_extensions_vnode_cb(kauth_cred_t, kauth_action_t,
     52     void *, void *, void *, void *, void *);
     53 
     54 void
     55 secmodel_extensions_vfs_start(void)
     56 {
     57 
     58 	l_system = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
     59 	    secmodel_extensions_system_cb, NULL);
     60 	l_vnode = kauth_listen_scope(KAUTH_SCOPE_VNODE,
     61 	    secmodel_extensions_vnode_cb, NULL);
     62 }
     63 
     64 void
     65 secmodel_extensions_vfs_stop(void)
     66 {
     67 
     68 	kauth_unlisten_scope(l_system);
     69 	kauth_unlisten_scope(l_vnode);
     70 }
     71 
     72 void
     73 secmodel_extensions_vfs_sysctl(struct sysctllog **clog,
     74     const struct sysctlnode *rnode)
     75 {
     76 
     77 	sysctl_createv(clog, 0, &rnode, NULL,
     78 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
     79 		       CTLTYPE_INT, "usermount",
     80 		       SYSCTL_DESCR("Whether unprivileged users may mount "
     81 				    "filesystems"),
     82 		       sysctl_extensions_user_handler, 0, &dovfsusermount, 0,
     83 		       CTL_CREATE, CTL_EOL);
     84 
     85 	sysctl_createv(clog, 0, &rnode, NULL,
     86 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
     87 		       CTLTYPE_INT, "hardlink_check_uid",
     88 		       SYSCTL_DESCR("Whether unprivileged users can hardlink "\
     89 			    "to files they don't own"),
     90 		       sysctl_extensions_user_handler, 0,
     91 		       &hardlink_check_uid, 0,
     92 		       CTL_CREATE, CTL_EOL);
     93 
     94 	sysctl_createv(clog, 0, &rnode, NULL,
     95 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
     96 		       CTLTYPE_INT, "hardlink_check_gid",
     97 		       SYSCTL_DESCR("Whether unprivileged users can hardlink "\
     98 			    "to files that are not in their " \
     99 			    "group membership"),
    100 		       sysctl_extensions_user_handler, 0,
    101 		       &hardlink_check_gid, 0,
    102 		       CTL_CREATE, CTL_EOL);
    103 
    104 	/* Compatibility: vfs.generic.usermount */
    105 	sysctl_createv(clog, 0, NULL, NULL,
    106 		       CTLFLAG_PERMANENT,
    107 		       CTLTYPE_NODE, "generic",
    108 		       SYSCTL_DESCR("Non-specific vfs related information"),
    109 		       NULL, 0, NULL, 0,
    110 		       CTL_VFS, VFS_GENERIC, CTL_EOL);
    111 
    112 	sysctl_createv(clog, 0, NULL, NULL,
    113 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    114 		       CTLTYPE_INT, "usermount",
    115 		       SYSCTL_DESCR("Whether unprivileged users may mount "
    116 				    "filesystems"),
    117 		       sysctl_extensions_user_handler, 0, &dovfsusermount, 0,
    118 		       CTL_VFS, VFS_GENERIC, VFS_USERMOUNT, CTL_EOL);
    119 }
    120 
    121 static int
    122 secmodel_extensions_system_cb(kauth_cred_t cred, kauth_action_t action,
    123     void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
    124 {
    125 	vnode_t *vp;
    126 	struct vattr va;
    127 	struct mount *mp;
    128 	u_long flags;
    129 	int result;
    130 	enum kauth_system_req req;
    131 	int error;
    132 
    133 	req = (enum kauth_system_req)(uintptr_t)arg0;
    134 	result = KAUTH_RESULT_DEFER;
    135 
    136 	switch (action) {
    137 	case KAUTH_SYSTEM_MOUNT:
    138 		if (dovfsusermount == 0)
    139 			break;
    140 		switch (req) {
    141 		case KAUTH_REQ_SYSTEM_MOUNT_NEW:
    142 			vp = (vnode_t *)arg1;
    143 			mp = vp->v_mount;
    144 			flags = (u_long)arg2;
    145 
    146 			/*
    147 			 * Ensure that the user owns the directory onto which
    148 			 * the mount is attempted.
    149 			 */
    150 			vn_lock(vp, LK_SHARED | LK_RETRY);
    151 			error = VOP_GETATTR(vp, &va, cred);
    152 			VOP_UNLOCK(vp);
    153 			if (error)
    154 				break;
    155 
    156 			if (va.va_uid != kauth_cred_geteuid(cred))
    157 				break;
    158 
    159 			error = usermount_common_policy(mp, flags);
    160 			if (error)
    161 				break;
    162 
    163 			result = KAUTH_RESULT_ALLOW;
    164 
    165 			break;
    166 
    167 		case KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT:
    168 			mp = arg1;
    169 
    170 			/* Must own the mount. */
    171 			if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred))
    172 				result = KAUTH_RESULT_ALLOW;
    173 
    174 			break;
    175 
    176 		case KAUTH_REQ_SYSTEM_MOUNT_UPDATE:
    177 			mp = arg1;
    178 			flags = (u_long)arg2;
    179 
    180 			/* Must own the mount. */
    181 			if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred) &&
    182 				usermount_common_policy(mp, flags) == 0)
    183 				result = KAUTH_RESULT_ALLOW;
    184 
    185 			break;
    186 
    187 		default:
    188 			break;
    189 		}
    190 		break;
    191 
    192 	default:
    193 		break;
    194 	}
    195 
    196 	return (result);
    197 }
    198 
    199 static int
    200 secmodel_extensions_vnode_cb(kauth_cred_t cred, kauth_action_t action,
    201     void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
    202 {
    203 	int error;
    204 	bool isroot;
    205 	struct vattr va;
    206 
    207 	if ((action & KAUTH_VNODE_ADD_LINK) == 0)
    208 		return KAUTH_RESULT_DEFER;
    209 
    210 	error = VOP_GETATTR((vnode_t *)arg0, &va, cred);
    211 	if (error)
    212 		goto checkroot;
    213 
    214 	if (hardlink_check_uid && kauth_cred_geteuid(cred) != va.va_uid)
    215 		goto checkroot;
    216 
    217 	if (hardlink_check_gid && kauth_cred_groupmember(cred, va.va_gid) != 0)
    218 		goto checkroot;
    219 
    220 	return KAUTH_RESULT_DEFER;
    221 checkroot:
    222 	error = secmodel_eval("org.netbsd.secmodel.suser", "is-root",
    223 	    cred, &isroot);
    224 	if (error || !isroot)
    225 		return KAUTH_RESULT_DENY;
    226 
    227 	return KAUTH_RESULT_DEFER;
    228 }
    229 
    230