1 | /* $NetBSD: secmodel_securelevel.c,v 1.30 2014/02/25 18:30:13 pooka Exp $ */ |
2 | /*- |
3 | * Copyright (c) 2006 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 | /* |
30 | * This file contains kauth(9) listeners needed to implement the traditional |
31 | * NetBSD securelevel. |
32 | * |
33 | * The securelevel is a system-global indication on what operations are |
34 | * allowed or not. It affects all users, including root. |
35 | */ |
36 | |
37 | #include <sys/cdefs.h> |
38 | __KERNEL_RCSID(0, "$NetBSD: secmodel_securelevel.c,v 1.30 2014/02/25 18:30:13 pooka Exp $" ); |
39 | |
40 | #ifdef _KERNEL_OPT |
41 | #include "opt_insecure.h" |
42 | #endif /* _KERNEL_OPT */ |
43 | |
44 | #include <sys/types.h> |
45 | #include <sys/param.h> |
46 | #include <sys/kauth.h> |
47 | |
48 | #include <sys/conf.h> |
49 | #include <sys/mount.h> |
50 | #include <sys/sysctl.h> |
51 | #include <sys/vnode.h> |
52 | #include <sys/module.h> |
53 | #include <sys/timevar.h> |
54 | |
55 | #include <miscfs/specfs/specdev.h> |
56 | |
57 | #include <secmodel/secmodel.h> |
58 | #include <secmodel/securelevel/securelevel.h> |
59 | |
60 | MODULE(MODULE_CLASS_SECMODEL, securelevel, NULL); |
61 | |
62 | static int securelevel; |
63 | |
64 | static kauth_listener_t l_system, l_process, l_network, l_machdep, l_device, |
65 | l_vnode; |
66 | |
67 | static secmodel_t securelevel_sm; |
68 | static struct sysctllog *securelevel_sysctl_log; |
69 | |
70 | /* |
71 | * Sysctl helper routine for securelevel. Ensures that the value only rises |
72 | * unless the caller is init. |
73 | */ |
74 | int |
75 | secmodel_securelevel_sysctl(SYSCTLFN_ARGS) |
76 | { |
77 | int newsecurelevel, error; |
78 | struct sysctlnode node; |
79 | |
80 | newsecurelevel = securelevel; |
81 | node = *rnode; |
82 | node.sysctl_data = &newsecurelevel; |
83 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
84 | if (error || newp == NULL) |
85 | return (error); |
86 | |
87 | if ((newsecurelevel < securelevel) && (l->l_proc != initproc)) |
88 | return (EPERM); |
89 | |
90 | securelevel = newsecurelevel; |
91 | |
92 | return (error); |
93 | } |
94 | |
95 | void |
96 | sysctl_security_securelevel_setup(struct sysctllog **clog) |
97 | { |
98 | const struct sysctlnode *rnode, *rnode2; |
99 | |
100 | sysctl_createv(clog, 0, NULL, &rnode, |
101 | CTLFLAG_PERMANENT, |
102 | CTLTYPE_NODE, "models" , NULL, |
103 | NULL, 0, NULL, 0, |
104 | CTL_SECURITY, CTL_CREATE, CTL_EOL); |
105 | |
106 | /* Compatibility: security.models.bsd44 */ |
107 | rnode2 = rnode; |
108 | sysctl_createv(clog, 0, &rnode2, &rnode2, |
109 | CTLFLAG_PERMANENT, |
110 | CTLTYPE_NODE, "bsd44" , NULL, |
111 | NULL, 0, NULL, 0, |
112 | CTL_CREATE, CTL_EOL); |
113 | |
114 | /* Compatibility: security.models.bsd44.securelevel */ |
115 | sysctl_createv(clog, 0, &rnode2, NULL, |
116 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
117 | CTLTYPE_INT, "securelevel" , |
118 | SYSCTL_DESCR("System security level" ), |
119 | secmodel_securelevel_sysctl, 0, NULL, 0, |
120 | CTL_CREATE, CTL_EOL); |
121 | |
122 | sysctl_createv(clog, 0, &rnode, &rnode, |
123 | CTLFLAG_PERMANENT, |
124 | CTLTYPE_NODE, "securelevel" , NULL, |
125 | NULL, 0, NULL, 0, |
126 | CTL_CREATE, CTL_EOL); |
127 | |
128 | sysctl_createv(clog, 0, &rnode, NULL, |
129 | CTLFLAG_PERMANENT, |
130 | CTLTYPE_STRING, "name" , NULL, |
131 | NULL, 0, __UNCONST(SECMODEL_SECURELEVEL_NAME), 0, |
132 | CTL_CREATE, CTL_EOL); |
133 | |
134 | sysctl_createv(clog, 0, &rnode, NULL, |
135 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
136 | CTLTYPE_INT, "securelevel" , |
137 | SYSCTL_DESCR("System security level" ), |
138 | secmodel_securelevel_sysctl, 0, NULL, 0, |
139 | CTL_CREATE, CTL_EOL); |
140 | |
141 | /* Compatibility: kern.securelevel */ |
142 | |
143 | sysctl_createv(clog, 0, NULL, NULL, |
144 | CTLFLAG_PERMANENT|CTLFLAG_READWRITE, |
145 | CTLTYPE_INT, "securelevel" , |
146 | SYSCTL_DESCR("System security level" ), |
147 | secmodel_securelevel_sysctl, 0, NULL, 0, |
148 | CTL_KERN, KERN_SECURELVL, CTL_EOL); |
149 | } |
150 | |
151 | void |
152 | secmodel_securelevel_init(void) |
153 | { |
154 | #ifdef INSECURE |
155 | securelevel = -1; |
156 | #else |
157 | securelevel = 0; |
158 | #endif /* INSECURE */ |
159 | } |
160 | |
161 | void |
162 | secmodel_securelevel_start(void) |
163 | { |
164 | l_system = kauth_listen_scope(KAUTH_SCOPE_SYSTEM, |
165 | secmodel_securelevel_system_cb, NULL); |
166 | l_process = kauth_listen_scope(KAUTH_SCOPE_PROCESS, |
167 | secmodel_securelevel_process_cb, NULL); |
168 | l_network = kauth_listen_scope(KAUTH_SCOPE_NETWORK, |
169 | secmodel_securelevel_network_cb, NULL); |
170 | l_machdep = kauth_listen_scope(KAUTH_SCOPE_MACHDEP, |
171 | secmodel_securelevel_machdep_cb, NULL); |
172 | l_device = kauth_listen_scope(KAUTH_SCOPE_DEVICE, |
173 | secmodel_securelevel_device_cb, NULL); |
174 | l_vnode = kauth_listen_scope(KAUTH_SCOPE_VNODE, |
175 | secmodel_securelevel_vnode_cb, NULL); |
176 | } |
177 | |
178 | void |
179 | secmodel_securelevel_stop(void) |
180 | { |
181 | kauth_unlisten_scope(l_system); |
182 | kauth_unlisten_scope(l_process); |
183 | kauth_unlisten_scope(l_network); |
184 | kauth_unlisten_scope(l_machdep); |
185 | kauth_unlisten_scope(l_device); |
186 | kauth_unlisten_scope(l_vnode); |
187 | } |
188 | |
189 | static int |
190 | securelevel_eval(const char *what, void *arg, void *ret) |
191 | { |
192 | int error = 0; |
193 | |
194 | if (strcasecmp(what, "is-securelevel-above" ) == 0) { |
195 | int level = (int)(uintptr_t)arg; |
196 | bool *bp = ret; |
197 | |
198 | *bp = (securelevel > level); |
199 | } else { |
200 | error = ENOENT; |
201 | } |
202 | |
203 | return error; |
204 | } |
205 | |
206 | static int |
207 | securelevel_modcmd(modcmd_t cmd, void *arg) |
208 | { |
209 | int error = 0; |
210 | |
211 | switch (cmd) { |
212 | case MODULE_CMD_INIT: |
213 | secmodel_securelevel_init(); |
214 | error = secmodel_register(&securelevel_sm, |
215 | SECMODEL_SECURELEVEL_ID, SECMODEL_SECURELEVEL_NAME, |
216 | NULL, securelevel_eval, NULL); |
217 | if (error != 0) |
218 | printf("securelevel_modcmd::init: secmodel_register " |
219 | "returned %d\n" , error); |
220 | |
221 | secmodel_securelevel_start(); |
222 | sysctl_security_securelevel_setup(&securelevel_sysctl_log); |
223 | break; |
224 | |
225 | case MODULE_CMD_FINI: |
226 | sysctl_teardown(&securelevel_sysctl_log); |
227 | secmodel_securelevel_stop(); |
228 | |
229 | error = secmodel_deregister(securelevel_sm); |
230 | if (error != 0) |
231 | printf("securelevel_modcmd::fini: secmodel_deregister " |
232 | "returned %d\n" , error); |
233 | |
234 | break; |
235 | |
236 | case MODULE_CMD_AUTOUNLOAD: |
237 | error = EPERM; |
238 | break; |
239 | |
240 | default: |
241 | error = ENOTTY; |
242 | break; |
243 | } |
244 | |
245 | return (error); |
246 | } |
247 | |
248 | /* |
249 | * kauth(9) listener |
250 | * |
251 | * Security model: Traditional NetBSD |
252 | * Scope: System |
253 | * Responsibility: Securelevel |
254 | */ |
255 | int |
256 | secmodel_securelevel_system_cb(kauth_cred_t cred, kauth_action_t action, |
257 | void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) |
258 | { |
259 | int result; |
260 | enum kauth_system_req req; |
261 | |
262 | result = KAUTH_RESULT_DEFER; |
263 | req = (enum kauth_system_req)arg0; |
264 | |
265 | switch (action) { |
266 | case KAUTH_SYSTEM_CHSYSFLAGS: |
267 | /* Deprecated. */ |
268 | if (securelevel > 0) |
269 | result = KAUTH_RESULT_DENY; |
270 | break; |
271 | |
272 | case KAUTH_SYSTEM_TIME: |
273 | switch (req) { |
274 | case KAUTH_REQ_SYSTEM_TIME_RTCOFFSET: |
275 | if (securelevel > 0) |
276 | result = KAUTH_RESULT_DENY; |
277 | break; |
278 | |
279 | case KAUTH_REQ_SYSTEM_TIME_SYSTEM: { |
280 | struct timespec *ts = arg1; |
281 | struct timespec *delta = arg2; |
282 | |
283 | if (securelevel > 1 && time_wraps(ts, delta)) |
284 | result = KAUTH_RESULT_DENY; |
285 | |
286 | break; |
287 | } |
288 | |
289 | default: |
290 | break; |
291 | } |
292 | break; |
293 | |
294 | case KAUTH_SYSTEM_MAP_VA_ZERO: |
295 | if (securelevel > 0) |
296 | result = KAUTH_RESULT_DENY; |
297 | break; |
298 | |
299 | case KAUTH_SYSTEM_MODULE: |
300 | if (securelevel > 0) |
301 | result = KAUTH_RESULT_DENY; |
302 | break; |
303 | |
304 | case KAUTH_SYSTEM_MOUNT: |
305 | switch (req) { |
306 | case KAUTH_REQ_SYSTEM_MOUNT_NEW: |
307 | if (securelevel > 1) |
308 | result = KAUTH_RESULT_DENY; |
309 | |
310 | break; |
311 | |
312 | case KAUTH_REQ_SYSTEM_MOUNT_UPDATE: |
313 | if (securelevel > 1) { |
314 | struct mount *mp = arg1; |
315 | u_long flags = (u_long)arg2; |
316 | |
317 | /* Can only degrade from read/write to read-only. */ |
318 | if (flags != (mp->mnt_flag | MNT_RDONLY | MNT_RELOAD | |
319 | MNT_FORCE | MNT_UPDATE)) |
320 | result = KAUTH_RESULT_DENY; |
321 | } |
322 | |
323 | break; |
324 | |
325 | default: |
326 | break; |
327 | } |
328 | |
329 | break; |
330 | |
331 | case KAUTH_SYSTEM_SYSCTL: |
332 | switch (req) { |
333 | case KAUTH_REQ_SYSTEM_SYSCTL_ADD: |
334 | case KAUTH_REQ_SYSTEM_SYSCTL_DELETE: |
335 | case KAUTH_REQ_SYSTEM_SYSCTL_DESC: |
336 | if (securelevel > 0) |
337 | result = KAUTH_RESULT_DENY; |
338 | break; |
339 | |
340 | default: |
341 | break; |
342 | } |
343 | break; |
344 | |
345 | case KAUTH_SYSTEM_SETIDCORE: |
346 | if (securelevel > 0) |
347 | result = KAUTH_RESULT_DENY; |
348 | break; |
349 | |
350 | case KAUTH_SYSTEM_DEBUG: |
351 | switch (req) { |
352 | case KAUTH_REQ_SYSTEM_DEBUG_IPKDB: |
353 | if (securelevel > 0) |
354 | result = KAUTH_RESULT_DENY; |
355 | break; |
356 | |
357 | default: |
358 | break; |
359 | } |
360 | break; |
361 | |
362 | default: |
363 | break; |
364 | } |
365 | |
366 | return (result); |
367 | } |
368 | |
369 | /* |
370 | * kauth(9) listener |
371 | * |
372 | * Security model: Traditional NetBSD |
373 | * Scope: Process |
374 | * Responsibility: Securelevel |
375 | */ |
376 | int |
377 | secmodel_securelevel_process_cb(kauth_cred_t cred, kauth_action_t action, |
378 | void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) |
379 | { |
380 | struct proc *p; |
381 | int result; |
382 | |
383 | result = KAUTH_RESULT_DEFER; |
384 | p = arg0; |
385 | |
386 | switch (action) { |
387 | case KAUTH_PROCESS_PROCFS: { |
388 | enum kauth_process_req req; |
389 | |
390 | req = (enum kauth_process_req)arg2; |
391 | switch (req) { |
392 | case KAUTH_REQ_PROCESS_PROCFS_READ: |
393 | break; |
394 | |
395 | case KAUTH_REQ_PROCESS_PROCFS_RW: |
396 | case KAUTH_REQ_PROCESS_PROCFS_WRITE: |
397 | if ((p == initproc) && (securelevel > -1)) |
398 | result = KAUTH_RESULT_DENY; |
399 | |
400 | break; |
401 | |
402 | default: |
403 | break; |
404 | } |
405 | |
406 | break; |
407 | } |
408 | |
409 | case KAUTH_PROCESS_PTRACE: |
410 | if ((p == initproc) && (securelevel > -1)) |
411 | result = KAUTH_RESULT_DENY; |
412 | |
413 | break; |
414 | |
415 | case KAUTH_PROCESS_CORENAME: |
416 | if (securelevel > 1) |
417 | result = KAUTH_RESULT_DENY; |
418 | break; |
419 | |
420 | default: |
421 | break; |
422 | } |
423 | |
424 | return (result); |
425 | } |
426 | |
427 | /* |
428 | * kauth(9) listener |
429 | * |
430 | * Security model: Traditional NetBSD |
431 | * Scope: Network |
432 | * Responsibility: Securelevel |
433 | */ |
434 | int |
435 | secmodel_securelevel_network_cb(kauth_cred_t cred, kauth_action_t action, |
436 | void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) |
437 | { |
438 | int result; |
439 | enum kauth_network_req req; |
440 | |
441 | result = KAUTH_RESULT_DEFER; |
442 | req = (enum kauth_network_req)arg0; |
443 | |
444 | switch (action) { |
445 | case KAUTH_NETWORK_FIREWALL: |
446 | switch (req) { |
447 | case KAUTH_REQ_NETWORK_FIREWALL_FW: |
448 | case KAUTH_REQ_NETWORK_FIREWALL_NAT: |
449 | if (securelevel > 1) |
450 | result = KAUTH_RESULT_DENY; |
451 | break; |
452 | |
453 | default: |
454 | break; |
455 | } |
456 | break; |
457 | |
458 | case KAUTH_NETWORK_FORWSRCRT: |
459 | if (securelevel > 0) |
460 | result = KAUTH_RESULT_DENY; |
461 | break; |
462 | |
463 | default: |
464 | break; |
465 | } |
466 | |
467 | return (result); |
468 | } |
469 | |
470 | /* |
471 | * kauth(9) listener |
472 | * |
473 | * Security model: Traditional NetBSD |
474 | * Scope: Machdep |
475 | * Responsibility: Securelevel |
476 | */ |
477 | int |
478 | secmodel_securelevel_machdep_cb(kauth_cred_t cred, kauth_action_t action, |
479 | void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) |
480 | { |
481 | int result; |
482 | |
483 | result = KAUTH_RESULT_DEFER; |
484 | |
485 | switch (action) { |
486 | case KAUTH_MACHDEP_IOPERM_SET: |
487 | case KAUTH_MACHDEP_IOPL: |
488 | if (securelevel > 0) |
489 | result = KAUTH_RESULT_DENY; |
490 | break; |
491 | |
492 | case KAUTH_MACHDEP_UNMANAGEDMEM: |
493 | if (securelevel > 0) |
494 | result = KAUTH_RESULT_DENY; |
495 | break; |
496 | |
497 | case KAUTH_MACHDEP_CPU_UCODE_APPLY: |
498 | if (securelevel > 1) |
499 | result = KAUTH_RESULT_DENY; |
500 | break; |
501 | |
502 | default: |
503 | break; |
504 | } |
505 | |
506 | return (result); |
507 | } |
508 | |
509 | /* |
510 | * kauth(9) listener |
511 | * |
512 | * Security model: Traditional NetBSD |
513 | * Scope: Device |
514 | * Responsibility: Securelevel |
515 | */ |
516 | int |
517 | secmodel_securelevel_device_cb(kauth_cred_t cred, kauth_action_t action, |
518 | void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) |
519 | { |
520 | int result; |
521 | |
522 | result = KAUTH_RESULT_DEFER; |
523 | |
524 | switch (action) { |
525 | case KAUTH_DEVICE_RAWIO_SPEC: { |
526 | struct vnode *vp; |
527 | enum kauth_device_req req; |
528 | |
529 | req = (enum kauth_device_req)arg0; |
530 | vp = arg1; |
531 | |
532 | KASSERT(vp != NULL); |
533 | |
534 | /* Handle /dev/mem and /dev/kmem. */ |
535 | if (iskmemvp(vp)) { |
536 | switch (req) { |
537 | case KAUTH_REQ_DEVICE_RAWIO_SPEC_READ: |
538 | break; |
539 | |
540 | case KAUTH_REQ_DEVICE_RAWIO_SPEC_WRITE: |
541 | case KAUTH_REQ_DEVICE_RAWIO_SPEC_RW: |
542 | if (securelevel > 0) |
543 | result = KAUTH_RESULT_DENY; |
544 | |
545 | break; |
546 | |
547 | default: |
548 | break; |
549 | } |
550 | |
551 | break; |
552 | } |
553 | |
554 | switch (req) { |
555 | case KAUTH_REQ_DEVICE_RAWIO_SPEC_READ: |
556 | break; |
557 | |
558 | case KAUTH_REQ_DEVICE_RAWIO_SPEC_WRITE: |
559 | case KAUTH_REQ_DEVICE_RAWIO_SPEC_RW: { |
560 | int error; |
561 | |
562 | error = rawdev_mounted(vp, NULL); |
563 | |
564 | /* Not a disk. */ |
565 | if (error == EINVAL) |
566 | break; |
567 | |
568 | if (error && securelevel > 0) |
569 | result = KAUTH_RESULT_DENY; |
570 | |
571 | if (securelevel > 1) |
572 | result = KAUTH_RESULT_DENY; |
573 | |
574 | break; |
575 | } |
576 | |
577 | default: |
578 | break; |
579 | } |
580 | |
581 | break; |
582 | } |
583 | |
584 | case KAUTH_DEVICE_RAWIO_PASSTHRU: |
585 | if (securelevel > 0) { |
586 | u_long bits; |
587 | |
588 | bits = (u_long)arg0; |
589 | |
590 | KASSERT(bits != 0); |
591 | KASSERT((bits & ~KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_ALL) == 0); |
592 | |
593 | if (bits & ~KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_READCONF) |
594 | result = KAUTH_RESULT_DENY; |
595 | } |
596 | |
597 | break; |
598 | |
599 | case KAUTH_DEVICE_GPIO_PINSET: |
600 | if (securelevel > 0) |
601 | result = KAUTH_RESULT_DENY; |
602 | break; |
603 | |
604 | case KAUTH_DEVICE_RND_ADDDATA_ESTIMATE: |
605 | if (securelevel > 0) |
606 | result = KAUTH_RESULT_DENY; |
607 | break; |
608 | |
609 | default: |
610 | break; |
611 | } |
612 | |
613 | return (result); |
614 | } |
615 | |
616 | int |
617 | secmodel_securelevel_vnode_cb(kauth_cred_t cred, kauth_action_t action, |
618 | void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) |
619 | { |
620 | int result; |
621 | |
622 | result = KAUTH_RESULT_DEFER; |
623 | |
624 | if ((action & KAUTH_VNODE_WRITE_SYSFLAGS) && |
625 | (action & KAUTH_VNODE_HAS_SYSFLAGS)) { |
626 | if (securelevel > 0) |
627 | result = KAUTH_RESULT_DENY; |
628 | } |
629 | |
630 | return (result); |
631 | } |
632 | |
633 | |