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@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
secmodel_extensions_vfs_start(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
secmodel_extensions_vfs_stop(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
secmodel_extensions_vfs_sysctl(struct sysctllog ** clog,const struct sysctlnode * rnode)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
secmodel_extensions_system_cb(kauth_cred_t cred,kauth_action_t action,void * cookie,void * arg0,void * arg1,void * arg2,void * arg3)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
secmodel_extensions_vnode_cb(kauth_cred_t cred,kauth_action_t action,void * cookie,void * arg0,void * arg1,void * arg2,void * arg3)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