secmodel_extensions.c revision 1.13 1 /* $NetBSD: secmodel_extensions.c,v 1.13 2022/03/27 16:28:35 christos Exp $ */
2 /*-
3 * Copyright (c) 2011 Elad Efrat <elad (at) 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.13 2022/03/27 16:28:35 christos 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 static int hardlink_check_uid;
53 static int hardlink_check_gid;
54
55 #ifdef PT_SETDBREGS
56 int user_set_dbregs;
57 #endif
58
59 static kauth_listener_t l_system, l_process, l_network, l_vnode;
60
61 static secmodel_t extensions_sm;
62
63 static void secmodel_extensions_init(void);
64 static void secmodel_extensions_start(void);
65 static void secmodel_extensions_stop(void);
66
67 static void sysctl_security_extensions_setup(struct sysctllog **);
68 static int sysctl_extensions_user_handler(SYSCTLFN_PROTO);
69 static int sysctl_extensions_curtain_handler(SYSCTLFN_PROTO);
70 static bool is_securelevel_above(int);
71
72 static int secmodel_extensions_system_cb(kauth_cred_t, kauth_action_t,
73 void *, void *, void *, void *, void *);
74 static int secmodel_extensions_process_cb(kauth_cred_t, kauth_action_t,
75 void *, void *, void *, void *, void *);
76 static int secmodel_extensions_network_cb(kauth_cred_t, kauth_action_t,
77 void *, void *, void *, void *, void *);
78 static int secmodel_extensions_vnode_cb(kauth_cred_t, kauth_action_t,
79 void *, void *, void *, void *, void *);
80
81 SYSCTL_SETUP(sysctl_security_extensions_setup,
82 "security extensions sysctl")
83 {
84 const struct sysctlnode *rnode, *rnode2;
85
86 sysctl_createv(clog, 0, NULL, &rnode,
87 CTLFLAG_PERMANENT,
88 CTLTYPE_NODE, "models", NULL,
89 NULL, 0, NULL, 0,
90 CTL_SECURITY, CTL_CREATE, CTL_EOL);
91
92 /* Compatibility: security.models.bsd44 */
93 rnode2 = rnode;
94 sysctl_createv(clog, 0, &rnode2, &rnode2,
95 CTLFLAG_PERMANENT,
96 CTLTYPE_NODE, "bsd44", NULL,
97 NULL, 0, NULL, 0,
98 CTL_CREATE, CTL_EOL);
99
100 /* Compatibility: security.models.bsd44.curtain */
101 sysctl_createv(clog, 0, &rnode2, NULL,
102 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
103 CTLTYPE_INT, "curtain",
104 SYSCTL_DESCR("Curtain information about objects to "\
105 "users not owning them."),
106 sysctl_extensions_curtain_handler, 0, &curtain, 0,
107 CTL_CREATE, CTL_EOL);
108
109 sysctl_createv(clog, 0, &rnode, &rnode,
110 CTLFLAG_PERMANENT,
111 CTLTYPE_NODE, "extensions", NULL,
112 NULL, 0, NULL, 0,
113 CTL_CREATE, CTL_EOL);
114
115 sysctl_createv(clog, 0, &rnode, NULL,
116 CTLFLAG_PERMANENT,
117 CTLTYPE_STRING, "name", NULL,
118 NULL, 0, __UNCONST(SECMODEL_EXTENSIONS_NAME), 0,
119 CTL_CREATE, CTL_EOL);
120
121 sysctl_createv(clog, 0, &rnode, NULL,
122 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
123 CTLTYPE_INT, "usermount",
124 SYSCTL_DESCR("Whether unprivileged users may mount "
125 "filesystems"),
126 sysctl_extensions_user_handler, 0, &dovfsusermount, 0,
127 CTL_CREATE, CTL_EOL);
128
129 sysctl_createv(clog, 0, &rnode, NULL,
130 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
131 CTLTYPE_INT, "curtain",
132 SYSCTL_DESCR("Curtain information about objects to "\
133 "users not owning them."),
134 sysctl_extensions_curtain_handler, 0, &curtain, 0,
135 CTL_CREATE, CTL_EOL);
136
137 sysctl_createv(clog, 0, &rnode, NULL,
138 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
139 CTLTYPE_INT, "user_set_cpu_affinity",
140 SYSCTL_DESCR("Whether unprivileged users may control "\
141 "CPU affinity."),
142 sysctl_extensions_user_handler, 0,
143 &user_set_cpu_affinity, 0,
144 CTL_CREATE, CTL_EOL);
145
146 #ifdef PT_SETDBREGS
147 sysctl_createv(clog, 0, &rnode, NULL,
148 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
149 CTLTYPE_INT, "user_set_dbregs",
150 SYSCTL_DESCR("Whether unprivileged users may set "\
151 "CPU Debug Registers."),
152 sysctl_extensions_user_handler, 0,
153 &user_set_dbregs, 0,
154 CTL_CREATE, CTL_EOL);
155 #endif
156
157 sysctl_createv(clog, 0, &rnode, NULL,
158 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
159 CTLTYPE_INT, "hardlink_check_uid",
160 SYSCTL_DESCR("Whether unprivileged users can hardlink "\
161 "to files they don't own"),
162 sysctl_extensions_user_handler, 0,
163 &hardlink_check_uid, 0,
164 CTL_CREATE, CTL_EOL);
165
166 sysctl_createv(clog, 0, &rnode, NULL,
167 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
168 CTLTYPE_INT, "hardlink_check_gid",
169 SYSCTL_DESCR("Whether unprivileged users can hardlink "\
170 "to files they that they are not in their " \
171 "group membership"),
172 sysctl_extensions_user_handler, 0,
173 &hardlink_check_gid, 0,
174 CTL_CREATE, CTL_EOL);
175
176 /* Compatibility: vfs.generic.usermount */
177 sysctl_createv(clog, 0, NULL, NULL,
178 CTLFLAG_PERMANENT,
179 CTLTYPE_NODE, "generic",
180 SYSCTL_DESCR("Non-specific vfs related information"),
181 NULL, 0, NULL, 0,
182 CTL_VFS, VFS_GENERIC, CTL_EOL);
183
184 sysctl_createv(clog, 0, NULL, NULL,
185 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
186 CTLTYPE_INT, "usermount",
187 SYSCTL_DESCR("Whether unprivileged users may mount "
188 "filesystems"),
189 sysctl_extensions_user_handler, 0, &dovfsusermount, 0,
190 CTL_VFS, VFS_GENERIC, VFS_USERMOUNT, CTL_EOL);
191
192 /* Compatibility: security.curtain */
193 sysctl_createv(clog, 0, NULL, NULL,
194 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
195 CTLTYPE_INT, "curtain",
196 SYSCTL_DESCR("Curtain information about objects to "\
197 "users not owning them."),
198 sysctl_extensions_curtain_handler, 0, &curtain, 0,
199 CTL_SECURITY, CTL_CREATE, CTL_EOL);
200 }
201
202 static int
203 sysctl_extensions_curtain_handler(SYSCTLFN_ARGS)
204 {
205 struct sysctlnode node;
206 int val, error;
207
208 val = *(int *)rnode->sysctl_data;
209
210 node = *rnode;
211 node.sysctl_data = &val;
212
213 error = sysctl_lookup(SYSCTLFN_CALL(&node));
214 if (error || newp == NULL)
215 return error;
216
217 /* shortcut */
218 if (val == *(int *)rnode->sysctl_data)
219 return 0;
220
221 /* curtain cannot be disabled when securelevel is above 0 */
222 if (val == 0 && is_securelevel_above(0)) {
223 return EPERM;
224 }
225
226 *(int *)rnode->sysctl_data = val;
227 return 0;
228 }
229
230 /*
231 * Generic sysctl extensions handler for user mount and set CPU affinity
232 * rights. Checks the following conditions:
233 * - setting value to 0 is always permitted (decrease user rights)
234 * - setting value != 0 is not permitted when securelevel is above 0 (increase
235 * user rights).
236 */
237 static int
238 sysctl_extensions_user_handler(SYSCTLFN_ARGS)
239 {
240 struct sysctlnode node;
241 int val, error;
242
243 val = *(int *)rnode->sysctl_data;
244
245 node = *rnode;
246 node.sysctl_data = &val;
247
248 error = sysctl_lookup(SYSCTLFN_CALL(&node));
249 if (error || newp == NULL)
250 return error;
251
252 /* shortcut */
253 if (val == *(int *)rnode->sysctl_data)
254 return 0;
255
256 /* we cannot grant more rights to users when securelevel is above 0 */
257 if (val != 0 && is_securelevel_above(0)) {
258 return EPERM;
259 }
260
261 *(int *)rnode->sysctl_data = val;
262 return 0;
263 }
264
265 /*
266 * Query secmodel_securelevel(9) to know whether securelevel is strictly
267 * above 'level' or not.
268 * Returns true if it is, false otherwise (when securelevel is absent or
269 * securelevel is at or below 'level').
270 */
271 static bool
272 is_securelevel_above(int level)
273 {
274 bool above;
275 int error;
276
277 error = secmodel_eval("org.netbsd.secmodel.securelevel",
278 "is-securelevel-above", KAUTH_ARG(level), &above);
279 if (error == 0 && above)
280 return true;
281 else
282 return false;
283 }
284
285 static void
286 secmodel_extensions_init(void)
287 {
288
289 curtain = 0;
290 user_set_cpu_affinity = 0;
291 #ifdef PT_SETDBREGS
292 user_set_dbregs = 0;
293 #endif
294 }
295
296 static void
297 secmodel_extensions_start(void)
298 {
299
300 l_system = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
301 secmodel_extensions_system_cb, NULL);
302 l_process = kauth_listen_scope(KAUTH_SCOPE_PROCESS,
303 secmodel_extensions_process_cb, NULL);
304 l_network = kauth_listen_scope(KAUTH_SCOPE_NETWORK,
305 secmodel_extensions_network_cb, NULL);
306 l_vnode = kauth_listen_scope(KAUTH_SCOPE_VNODE,
307 secmodel_extensions_vnode_cb, NULL);
308 }
309
310 static void
311 secmodel_extensions_stop(void)
312 {
313
314 kauth_unlisten_scope(l_system);
315 kauth_unlisten_scope(l_process);
316 kauth_unlisten_scope(l_network);
317 kauth_unlisten_scope(l_vnode);
318 }
319
320 static int
321 extensions_modcmd(modcmd_t cmd, void *arg)
322 {
323 int error = 0;
324
325 switch (cmd) {
326 case MODULE_CMD_INIT:
327 error = secmodel_register(&extensions_sm,
328 SECMODEL_EXTENSIONS_ID, SECMODEL_EXTENSIONS_NAME,
329 NULL, NULL, NULL);
330 if (error != 0)
331 printf("extensions_modcmd::init: secmodel_register "
332 "returned %d\n", error);
333
334 secmodel_extensions_init();
335 secmodel_extensions_start();
336 break;
337
338 case MODULE_CMD_FINI:
339 secmodel_extensions_stop();
340
341 error = secmodel_deregister(extensions_sm);
342 if (error != 0)
343 printf("extensions_modcmd::fini: secmodel_deregister "
344 "returned %d\n", error);
345
346 break;
347
348 case MODULE_CMD_AUTOUNLOAD:
349 error = EPERM;
350 break;
351
352 default:
353 error = ENOTTY;
354 break;
355 }
356
357 return (error);
358 }
359
360 static int
361 secmodel_extensions_system_cb(kauth_cred_t cred, kauth_action_t action,
362 void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
363 {
364 vnode_t *vp;
365 struct vattr va;
366 struct mount *mp;
367 u_long flags;
368 int result;
369 enum kauth_system_req req;
370 int error;
371
372 req = (enum kauth_system_req)(uintptr_t)arg0;
373 result = KAUTH_RESULT_DEFER;
374
375 switch (action) {
376 case KAUTH_SYSTEM_MOUNT:
377 if (dovfsusermount == 0)
378 break;
379 switch (req) {
380 case KAUTH_REQ_SYSTEM_MOUNT_NEW:
381 vp = (vnode_t *)arg1;
382 mp = vp->v_mount;
383 flags = (u_long)arg2;
384
385 /*
386 * Ensure that the user owns the directory onto which
387 * the mount is attempted.
388 */
389 vn_lock(vp, LK_SHARED | LK_RETRY);
390 error = VOP_GETATTR(vp, &va, cred);
391 VOP_UNLOCK(vp);
392 if (error)
393 break;
394
395 if (va.va_uid != kauth_cred_geteuid(cred))
396 break;
397
398 error = usermount_common_policy(mp, flags);
399 if (error)
400 break;
401
402 result = KAUTH_RESULT_ALLOW;
403
404 break;
405
406 case KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT:
407 mp = arg1;
408
409 /* Must own the mount. */
410 if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred))
411 result = KAUTH_RESULT_ALLOW;
412
413 break;
414
415 case KAUTH_REQ_SYSTEM_MOUNT_UPDATE:
416 mp = arg1;
417 flags = (u_long)arg2;
418
419 /* Must own the mount. */
420 if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred) &&
421 usermount_common_policy(mp, flags) == 0)
422 result = KAUTH_RESULT_ALLOW;
423
424 break;
425
426 default:
427 break;
428 }
429 break;
430
431 default:
432 break;
433 }
434
435 return (result);
436 }
437
438 static int
439 secmodel_extensions_process_cb(kauth_cred_t cred, kauth_action_t action,
440 void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
441 {
442 int result;
443 enum kauth_process_req req;
444
445 result = KAUTH_RESULT_DEFER;
446 req = (enum kauth_process_req)(uintptr_t)arg1;
447
448 switch (action) {
449 case KAUTH_PROCESS_CANSEE:
450 switch (req) {
451 case KAUTH_REQ_PROCESS_CANSEE_ARGS:
452 case KAUTH_REQ_PROCESS_CANSEE_ENTRY:
453 case KAUTH_REQ_PROCESS_CANSEE_OPENFILES:
454 case KAUTH_REQ_PROCESS_CANSEE_EPROC:
455 if (curtain != 0) {
456 struct proc *p = arg0;
457
458 /*
459 * Only process' owner and root can see
460 * through curtain
461 */
462 if (!kauth_cred_uidmatch(cred, p->p_cred)) {
463 int error;
464 bool isroot = false;
465
466 error = secmodel_eval(
467 "org.netbsd.secmodel.suser",
468 "is-root", cred, &isroot);
469 if (error == 0 && !isroot)
470 result = KAUTH_RESULT_DENY;
471 }
472 }
473
474 break;
475
476 case KAUTH_REQ_PROCESS_CANSEE_KPTR:
477 default:
478 break;
479 }
480
481 break;
482
483 case KAUTH_PROCESS_SCHEDULER_SETAFFINITY:
484 if (user_set_cpu_affinity != 0) {
485 struct proc *p = arg0;
486
487 if (kauth_cred_uidmatch(cred, p->p_cred))
488 result = KAUTH_RESULT_ALLOW;
489 }
490 break;
491
492 default:
493 break;
494 }
495
496 return (result);
497 }
498
499 static int
500 secmodel_extensions_network_cb(kauth_cred_t cred, kauth_action_t action,
501 void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
502 {
503 int result;
504 enum kauth_network_req req;
505
506 result = KAUTH_RESULT_DEFER;
507 req = (enum kauth_network_req)(uintptr_t)arg0;
508
509 if (action != KAUTH_NETWORK_SOCKET ||
510 req != KAUTH_REQ_NETWORK_SOCKET_CANSEE)
511 return result;
512
513 if (curtain != 0) {
514 struct socket *so = (struct socket *)arg1;
515
516 if (__predict_false(so == NULL || so->so_cred == NULL))
517 return KAUTH_RESULT_DENY;
518
519 if (!kauth_cred_uidmatch(cred, so->so_cred)) {
520 int error;
521 bool isroot = false;
522
523 error = secmodel_eval("org.netbsd.secmodel.suser",
524 "is-root", cred, &isroot);
525 if (error == 0 && !isroot)
526 result = KAUTH_RESULT_DENY;
527 }
528 }
529
530 return (result);
531 }
532
533 static int
534 secmodel_extensions_vnode_cb(kauth_cred_t cred, kauth_action_t action,
535 void *cookie, void *arg0, void *arg1, void *arg2, void *arg3)
536 {
537 int error, isroot;
538 struct vattr va;
539
540 if ((action & KAUTH_VNODE_ADD_LINK) == 0)
541 return KAUTH_RESULT_DEFER;
542
543 error = VOP_GETATTR((vnode_t *)arg0, &va, cred);
544 if (error)
545 goto checkroot;
546
547 if (hardlink_check_uid && kauth_cred_geteuid(cred) != va.va_uid)
548 goto checkroot;
549
550 if (hardlink_check_gid && kauth_cred_groupmember(cred, va.va_gid) != 0)
551 goto checkroot;
552
553 return KAUTH_RESULT_DEFER;
554 checkroot:
555 error = secmodel_eval("org.netbsd.secmodel.suser", "is-root",
556 cred, &isroot);
557 if (error || !isroot)
558 return KAUTH_RESULT_DENY;
559
560 return KAUTH_RESULT_DEFER;
561 }
562