xref: /netbsd-src/sys/secmodel/extensions/secmodel_extensions.c (revision 53b02e147d4ed531c0d2a5ca9b3e8026ba3e99b5)
1 /* $NetBSD: secmodel_extensions.c,v 1.12 2020/03/16 21:20:12 pgoyette Exp $ */
2 /*-
3  * Copyright (c) 2011 Elad Efrat <elad@NetBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: secmodel_extensions.c,v 1.12 2020/03/16 21:20:12 pgoyette Exp $");
31 
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/kauth.h>
35 
36 #include <sys/mount.h>
37 #include <sys/vnode.h>
38 #include <sys/socketvar.h>
39 #include <sys/sysctl.h>
40 #include <sys/proc.h>
41 #include <sys/ptrace.h>
42 #include <sys/module.h>
43 
44 #include <secmodel/secmodel.h>
45 #include <secmodel/extensions/extensions.h>
46 
47 MODULE(MODULE_CLASS_SECMODEL, extensions, NULL);
48 
49 static int dovfsusermount;
50 static int curtain;
51 static int user_set_cpu_affinity;
52 
53 #ifdef PT_SETDBREGS
54 int user_set_dbregs;
55 #endif
56 
57 static kauth_listener_t l_system, l_process, l_network;
58 
59 static secmodel_t extensions_sm;
60 
61 static void secmodel_extensions_init(void);
62 static void secmodel_extensions_start(void);
63 static void secmodel_extensions_stop(void);
64 
65 static void sysctl_security_extensions_setup(struct sysctllog **);
66 static int  sysctl_extensions_user_handler(SYSCTLFN_PROTO);
67 static int  sysctl_extensions_curtain_handler(SYSCTLFN_PROTO);
68 static bool is_securelevel_above(int);
69 
70 static int secmodel_extensions_system_cb(kauth_cred_t, kauth_action_t,
71     void *, void *, void *, void *, void *);
72 static int secmodel_extensions_process_cb(kauth_cred_t, kauth_action_t,
73     void *, void *, void *, void *, void *);
74 static int secmodel_extensions_network_cb(kauth_cred_t, kauth_action_t,
75     void *, void *, void *, void *, void *);
76 
77 SYSCTL_SETUP(sysctl_security_extensions_setup,
78     "security extensions sysctl")
79 {
80 	const struct sysctlnode *rnode, *rnode2;
81 
82 	sysctl_createv(clog, 0, NULL, &rnode,
83 		       CTLFLAG_PERMANENT,
84 		       CTLTYPE_NODE, "models", NULL,
85 		       NULL, 0, NULL, 0,
86 		       CTL_SECURITY, CTL_CREATE, CTL_EOL);
87 
88 	/* Compatibility: security.models.bsd44 */
89 	rnode2 = rnode;
90 	sysctl_createv(clog, 0, &rnode2, &rnode2,
91 		       CTLFLAG_PERMANENT,
92 		       CTLTYPE_NODE, "bsd44", NULL,
93 		       NULL, 0, NULL, 0,
94 		       CTL_CREATE, CTL_EOL);
95 
96         /* Compatibility: security.models.bsd44.curtain */
97 	sysctl_createv(clog, 0, &rnode2, NULL,
98 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
99 		       CTLTYPE_INT, "curtain",
100 		       SYSCTL_DESCR("Curtain information about objects to "\
101 		       		    "users not owning them."),
102 		       sysctl_extensions_curtain_handler, 0, &curtain, 0,
103 		       CTL_CREATE, CTL_EOL);
104 
105 	sysctl_createv(clog, 0, &rnode, &rnode,
106 		       CTLFLAG_PERMANENT,
107 		       CTLTYPE_NODE, "extensions", NULL,
108 		       NULL, 0, NULL, 0,
109 		       CTL_CREATE, CTL_EOL);
110 
111 	sysctl_createv(clog, 0, &rnode, NULL,
112 		       CTLFLAG_PERMANENT,
113 		       CTLTYPE_STRING, "name", NULL,
114 		       NULL, 0, __UNCONST(SECMODEL_EXTENSIONS_NAME), 0,
115 		       CTL_CREATE, CTL_EOL);
116 
117 	sysctl_createv(clog, 0, &rnode, NULL,
118 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
119 		       CTLTYPE_INT, "usermount",
120 		       SYSCTL_DESCR("Whether unprivileged users may mount "
121 				    "filesystems"),
122 		       sysctl_extensions_user_handler, 0, &dovfsusermount, 0,
123 		       CTL_CREATE, CTL_EOL);
124 
125 	sysctl_createv(clog, 0, &rnode, NULL,
126 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
127 		       CTLTYPE_INT, "curtain",
128 		       SYSCTL_DESCR("Curtain information about objects to "\
129 		       		    "users not owning them."),
130 		       sysctl_extensions_curtain_handler, 0, &curtain, 0,
131 		       CTL_CREATE, CTL_EOL);
132 
133 	sysctl_createv(clog, 0, &rnode, NULL,
134 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
135 		       CTLTYPE_INT, "user_set_cpu_affinity",
136 		       SYSCTL_DESCR("Whether unprivileged users may control "\
137 		       		    "CPU affinity."),
138 		       sysctl_extensions_user_handler, 0,
139 		       &user_set_cpu_affinity, 0,
140 		       CTL_CREATE, CTL_EOL);
141 
142 #ifdef PT_SETDBREGS
143 	sysctl_createv(clog, 0, &rnode, NULL,
144 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
145 		       CTLTYPE_INT, "user_set_dbregs",
146 		       SYSCTL_DESCR("Whether unprivileged users may set "\
147 		       		    "CPU Debug Registers."),
148 		       sysctl_extensions_user_handler, 0,
149 		       &user_set_dbregs, 0,
150 		       CTL_CREATE, CTL_EOL);
151 #endif
152 
153 	/* Compatibility: vfs.generic.usermount */
154 	sysctl_createv(clog, 0, NULL, NULL,
155 		       CTLFLAG_PERMANENT,
156 		       CTLTYPE_NODE, "generic",
157 		       SYSCTL_DESCR("Non-specific vfs related information"),
158 		       NULL, 0, NULL, 0,
159 		       CTL_VFS, VFS_GENERIC, CTL_EOL);
160 
161 	sysctl_createv(clog, 0, NULL, NULL,
162 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
163 		       CTLTYPE_INT, "usermount",
164 		       SYSCTL_DESCR("Whether unprivileged users may mount "
165 				    "filesystems"),
166 		       sysctl_extensions_user_handler, 0, &dovfsusermount, 0,
167 		       CTL_VFS, VFS_GENERIC, VFS_USERMOUNT, CTL_EOL);
168 
169 	/* Compatibility: security.curtain */
170 	sysctl_createv(clog, 0, NULL, NULL,
171 		       CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
172 		       CTLTYPE_INT, "curtain",
173 		       SYSCTL_DESCR("Curtain information about objects to "\
174 		       		    "users not owning them."),
175 		       sysctl_extensions_curtain_handler, 0, &curtain, 0,
176 		       CTL_SECURITY, CTL_CREATE, CTL_EOL);
177 }
178 
179 static int
180 sysctl_extensions_curtain_handler(SYSCTLFN_ARGS)
181 {
182 	struct sysctlnode node;
183 	int val, error;
184 
185 	val = *(int *)rnode->sysctl_data;
186 
187 	node = *rnode;
188 	node.sysctl_data = &val;
189 
190 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
191 	if (error || newp == NULL)
192 		return error;
193 
194 	/* shortcut */
195 	if (val == *(int *)rnode->sysctl_data)
196 		return 0;
197 
198 	/* curtain cannot be disabled when securelevel is above 0 */
199 	if (val == 0 && is_securelevel_above(0)) {
200 		return EPERM;
201 	}
202 
203 	*(int *)rnode->sysctl_data = val;
204 	return 0;
205 }
206 
207 /*
208  * Generic sysctl extensions handler for user mount and set CPU affinity
209  * rights. Checks the following conditions:
210  * - setting value to 0 is always permitted (decrease user rights)
211  * - setting value != 0 is not permitted when securelevel is above 0 (increase
212  *   user rights).
213  */
214 static int
215 sysctl_extensions_user_handler(SYSCTLFN_ARGS)
216 {
217 	struct sysctlnode node;
218 	int val, error;
219 
220 	val = *(int *)rnode->sysctl_data;
221 
222 	node = *rnode;
223 	node.sysctl_data = &val;
224 
225 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
226 	if (error || newp == NULL)
227 		return error;
228 
229 	/* shortcut */
230 	if (val == *(int *)rnode->sysctl_data)
231 		return 0;
232 
233 	/* we cannot grant more rights to users when securelevel is above 0 */
234 	if (val != 0 && is_securelevel_above(0)) {
235 		return EPERM;
236 	}
237 
238 	*(int *)rnode->sysctl_data = val;
239 	return 0;
240 }
241 
242 /*
243  * Query secmodel_securelevel(9) to know whether securelevel is strictly
244  * above 'level' or not.
245  * Returns true if it is, false otherwise (when securelevel is absent or
246  * securelevel is at or below 'level').
247  */
248 static bool
249 is_securelevel_above(int level)
250 {
251 	bool above;
252 	int error;
253 
254 	error = secmodel_eval("org.netbsd.secmodel.securelevel",
255 	    "is-securelevel-above", KAUTH_ARG(level), &above);
256 	if (error == 0 && above)
257 		return true;
258 	else
259 		return false;
260 }
261 
262 static void
263 secmodel_extensions_init(void)
264 {
265 
266 	curtain = 0;
267 	user_set_cpu_affinity = 0;
268 #ifdef PT_SETDBREGS
269 	user_set_dbregs = 0;
270 #endif
271 }
272 
273 static void
274 secmodel_extensions_start(void)
275 {
276 
277 	l_system = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
278 	    secmodel_extensions_system_cb, NULL);
279 	l_process = kauth_listen_scope(KAUTH_SCOPE_PROCESS,
280 	    secmodel_extensions_process_cb, NULL);
281 	l_network = kauth_listen_scope(KAUTH_SCOPE_NETWORK,
282 	    secmodel_extensions_network_cb, NULL);
283 }
284 
285 static void
286 secmodel_extensions_stop(void)
287 {
288 
289 	kauth_unlisten_scope(l_system);
290 	kauth_unlisten_scope(l_process);
291 	kauth_unlisten_scope(l_network);
292 }
293 
294 static int
295 extensions_modcmd(modcmd_t cmd, void *arg)
296 {
297 	int error = 0;
298 
299 	switch (cmd) {
300 	case MODULE_CMD_INIT:
301 		error = secmodel_register(&extensions_sm,
302 		    SECMODEL_EXTENSIONS_ID, SECMODEL_EXTENSIONS_NAME,
303 		    NULL, NULL, NULL);
304 		if (error != 0)
305 			printf("extensions_modcmd::init: secmodel_register "
306 			    "returned %d\n", error);
307 
308 		secmodel_extensions_init();
309 		secmodel_extensions_start();
310 		break;
311 
312 	case MODULE_CMD_FINI:
313 		secmodel_extensions_stop();
314 
315 		error = secmodel_deregister(extensions_sm);
316 		if (error != 0)
317 			printf("extensions_modcmd::fini: secmodel_deregister "
318 			    "returned %d\n", error);
319 
320 		break;
321 
322 	case MODULE_CMD_AUTOUNLOAD:
323 		error = EPERM;
324 		break;
325 
326 	default:
327 		error = ENOTTY;
328 		break;
329 	}
330 
331 	return (error);
332 }
333 
334 static int
335 secmodel_extensions_system_cb(kauth_cred_t cred, kauth_action_t action,
336     void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
337 {
338 	vnode_t *vp;
339 	struct vattr va;
340 	struct mount *mp;
341 	u_long flags;
342 	int result;
343 	enum kauth_system_req req;
344 	int error;
345 
346 	req = (enum kauth_system_req)(uintptr_t)arg0;
347 	result = KAUTH_RESULT_DEFER;
348 
349 	switch (action) {
350 	case KAUTH_SYSTEM_MOUNT:
351 		if (dovfsusermount == 0)
352 			break;
353 		switch (req) {
354 		case KAUTH_REQ_SYSTEM_MOUNT_NEW:
355 			vp = (vnode_t *)arg1;
356 			mp = vp->v_mount;
357 			flags = (u_long)arg2;
358 
359 			/*
360 			 * Ensure that the user owns the directory onto which
361 			 * the mount is attempted.
362 			 */
363 			vn_lock(vp, LK_SHARED | LK_RETRY);
364 			error = VOP_GETATTR(vp, &va, cred);
365 			VOP_UNLOCK(vp);
366 			if (error)
367 				break;
368 
369 			if (va.va_uid != kauth_cred_geteuid(cred))
370 				break;
371 
372 			error = usermount_common_policy(mp, flags);
373 			if (error)
374 				break;
375 
376 			result = KAUTH_RESULT_ALLOW;
377 
378 			break;
379 
380 		case KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT:
381 			mp = arg1;
382 
383 			/* Must own the mount. */
384 			if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred))
385 				result = KAUTH_RESULT_ALLOW;
386 
387 			break;
388 
389 		case KAUTH_REQ_SYSTEM_MOUNT_UPDATE:
390 			mp = arg1;
391 			flags = (u_long)arg2;
392 
393 			/* Must own the mount. */
394 			if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred) &&
395 				usermount_common_policy(mp, flags) == 0)
396 				result = KAUTH_RESULT_ALLOW;
397 
398 			break;
399 
400 		default:
401 			break;
402 		}
403 		break;
404 
405 	default:
406 		break;
407 	}
408 
409 	return (result);
410 }
411 
412 static int
413 secmodel_extensions_process_cb(kauth_cred_t cred, kauth_action_t action,
414     void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
415 {
416 	int result;
417 	enum kauth_process_req req;
418 
419 	result = KAUTH_RESULT_DEFER;
420 	req = (enum kauth_process_req)(uintptr_t)arg1;
421 
422 	switch (action) {
423 	case KAUTH_PROCESS_CANSEE:
424 		switch (req) {
425 		case KAUTH_REQ_PROCESS_CANSEE_ARGS:
426 		case KAUTH_REQ_PROCESS_CANSEE_ENTRY:
427 		case KAUTH_REQ_PROCESS_CANSEE_OPENFILES:
428 		case KAUTH_REQ_PROCESS_CANSEE_EPROC:
429 			if (curtain != 0) {
430 				struct proc *p = arg0;
431 
432 				/*
433 				 * Only process' owner and root can see
434 				 * through curtain
435 				 */
436 				if (!kauth_cred_uidmatch(cred, p->p_cred)) {
437 					int error;
438 					bool isroot = false;
439 
440 					error = secmodel_eval(
441 					    "org.netbsd.secmodel.suser",
442 					    "is-root", cred, &isroot);
443 					if (error == 0 && !isroot)
444 						result = KAUTH_RESULT_DENY;
445 				}
446 			}
447 
448 			break;
449 
450 		case KAUTH_REQ_PROCESS_CANSEE_KPTR:
451 		default:
452 			break;
453 		}
454 
455 		break;
456 
457 	case KAUTH_PROCESS_SCHEDULER_SETAFFINITY:
458 		if (user_set_cpu_affinity != 0) {
459 			struct proc *p = arg0;
460 
461 			if (kauth_cred_uidmatch(cred, p->p_cred))
462 				result = KAUTH_RESULT_ALLOW;
463 		}
464 		break;
465 
466 	default:
467 		break;
468 	}
469 
470 	return (result);
471 }
472 
473 static int
474 secmodel_extensions_network_cb(kauth_cred_t cred, kauth_action_t action,
475     void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
476 {
477 	int result;
478 	enum kauth_network_req req;
479 
480 	result = KAUTH_RESULT_DEFER;
481 	req = (enum kauth_network_req)(uintptr_t)arg0;
482 
483 	if (action != KAUTH_NETWORK_SOCKET ||
484 	    req != KAUTH_REQ_NETWORK_SOCKET_CANSEE)
485 		return result;
486 
487 	if (curtain != 0) {
488 		struct socket *so = (struct socket *)arg1;
489 
490 		if (__predict_false(so == NULL || so->so_cred == NULL))
491 			return KAUTH_RESULT_DENY;
492 
493 		if (!kauth_cred_uidmatch(cred, so->so_cred)) {
494 			int error;
495 			bool isroot = false;
496 
497 			error = secmodel_eval("org.netbsd.secmodel.suser",
498 			    "is-root", cred, &isroot);
499 			if (error == 0 && !isroot)
500 				result = KAUTH_RESULT_DENY;
501 		}
502 	}
503 
504 	return (result);
505 }
506