secmodel_keylock.c revision 1.3
1/* $NetBSD: secmodel_keylock.c,v 1.3 2009/10/03 20:48:42 elad Exp $ */
2/*-
3 * Copyright (c) 2009 Marc Balmer <marc@msys.ch>
4 * Copyright (c) 2006 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/*
31 * This file contains kauth(9) listeners needed to implement an experimental
32 * keylock based security scheme.
33 *
34 * The position of the keylock is a system-global indication on what
35 * operations are allowed or not. It affects all users, including root.
36 *
37 * Rules:
38 *
39 * - If the number of possible keylock positions is 0, assume there is no
40 *   keylock present, do not dissallow any action, i.e. do nothing
41 *
42 * - If the number of possible keylock positions is greater than 0, but the
43 *   current lock position is 0, assume tampering with the lock and forbid
44 *   all actions.
45 *
46 * - If the lock is in the lowest position, assume the system is locked and
47 *   forbid most actions.
48 *
49 * - If the lock is in the highest position, assume the system to be open and
50 *   forbid nothing.
51 *
52 * - If the security.models.keylock.order sysctl is set to a value != 0,
53 *   reverse this order.
54 */
55
56#include <sys/cdefs.h>
57__KERNEL_RCSID(0, "$NetBSD: secmodel_keylock.c,v 1.3 2009/10/03 20:48:42 elad Exp $");
58
59#include <sys/types.h>
60#include <sys/param.h>
61#include <sys/kauth.h>
62
63#include <sys/conf.h>
64#include <sys/mount.h>
65#include <sys/sysctl.h>
66#include <sys/vnode.h>
67#include <sys/timevar.h>
68
69#include <dev/keylock.h>
70
71#include <miscfs/specfs/specdev.h>
72
73#include <secmodel/keylock/keylock.h>
74
75static kauth_listener_t l_system, l_process, l_network, l_machdep, l_device;
76
77SYSCTL_SETUP(sysctl_security_keylock_setup,
78    "sysctl security keylock setup")
79{
80	const struct sysctlnode *rnode;
81
82	sysctl_createv(clog, 0, NULL, &rnode,
83		       CTLFLAG_PERMANENT,
84		       CTLTYPE_NODE, "security", NULL,
85		       NULL, 0, NULL, 0,
86		       CTL_SECURITY, CTL_EOL);
87
88	sysctl_createv(clog, 0, &rnode, &rnode,
89		       CTLFLAG_PERMANENT,
90		       CTLTYPE_NODE, "models", NULL,
91		       NULL, 0, NULL, 0,
92		       CTL_CREATE, CTL_EOL);
93
94	sysctl_createv(clog, 0, &rnode, &rnode,
95		       CTLFLAG_PERMANENT,
96		       CTLTYPE_NODE, "keylock",
97		       SYSCTL_DESCR("Keylock security model"),
98		       NULL, 0, NULL, 0,
99		       CTL_CREATE, CTL_EOL);
100
101	sysctl_createv(clog, 0, &rnode, NULL,
102		       CTLFLAG_PERMANENT,
103		       CTLTYPE_STRING, "name", NULL,
104		       NULL, 0, __UNCONST("Keylock"), 0,
105		       CTL_CREATE, CTL_EOL);
106}
107
108void
109secmodel_keylock_init(void)
110{
111}
112
113void
114secmodel_keylock_start(void)
115{
116	l_system = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
117	    secmodel_keylock_system_cb, NULL);
118	l_process = kauth_listen_scope(KAUTH_SCOPE_PROCESS,
119	    secmodel_keylock_process_cb, NULL);
120	l_network = kauth_listen_scope(KAUTH_SCOPE_NETWORK,
121	    secmodel_keylock_network_cb, NULL);
122	l_machdep = kauth_listen_scope(KAUTH_SCOPE_MACHDEP,
123	    secmodel_keylock_machdep_cb, NULL);
124	l_device = kauth_listen_scope(KAUTH_SCOPE_DEVICE,
125	    secmodel_keylock_device_cb, NULL);
126}
127
128void
129secmodel_keylock_stop(void)
130{
131	kauth_unlisten_scope(l_system);
132	kauth_unlisten_scope(l_process);
133	kauth_unlisten_scope(l_network);
134	kauth_unlisten_scope(l_machdep);
135	kauth_unlisten_scope(l_device);
136}
137
138/*
139 * kauth(9) listener
140 *
141 * Security model: Multi-position keylock
142 * Scope: System
143 * Responsibility: Keylock
144 */
145int
146secmodel_keylock_system_cb(kauth_cred_t cred,
147    kauth_action_t action, void *cookie, void *arg0, void *arg1,
148    void *arg2, void *arg3)
149{
150	int result;
151	enum kauth_system_req req;
152	int kstate;
153
154	kstate = keylock_state();
155	if (kstate == KEYLOCK_ABSENT)
156		return KAUTH_RESULT_DEFER;
157	else if (kstate == KEYLOCK_TAMPER)
158		return KAUTH_RESULT_DENY;
159
160	result = KAUTH_RESULT_DEFER;
161	req = (enum kauth_system_req)arg0;
162
163	switch (action) {
164	case KAUTH_SYSTEM_CHSYSFLAGS:
165		if (kstate == KEYLOCK_CLOSE)
166			result = KAUTH_RESULT_DENY;
167		break;
168
169	case KAUTH_SYSTEM_TIME:
170		switch (req) {
171		case KAUTH_REQ_SYSTEM_TIME_RTCOFFSET:
172			if (kstate == KEYLOCK_CLOSE)
173				result = KAUTH_RESULT_DENY;
174			break;
175
176		case KAUTH_REQ_SYSTEM_TIME_SYSTEM: {
177			struct timespec *ts = arg1;
178			struct timespec *delta = arg2;
179
180			if (keylock_position() > 1 && time_wraps(ts, delta))
181				result = KAUTH_RESULT_DENY;
182			break;
183		}
184		default:
185			break;
186		}
187		break;
188
189	case KAUTH_SYSTEM_MODULE:
190		if (kstate == KEYLOCK_CLOSE)
191			result = KAUTH_RESULT_DENY;
192		break;
193
194	case KAUTH_SYSTEM_MOUNT:
195		switch (req) {
196		case KAUTH_REQ_SYSTEM_MOUNT_NEW:
197			if (kstate == KEYLOCK_CLOSE)
198				result = KAUTH_RESULT_DENY;
199
200			break;
201
202		case KAUTH_REQ_SYSTEM_MOUNT_UPDATE:
203			if (kstate == KEYLOCK_CLOSE) {
204				struct mount *mp = arg1;
205				u_long flags = (u_long)arg2;
206
207				/*
208				 * Can only degrade from read/write to
209				 * read-only.
210				 */
211				if (flags != (mp->mnt_flag | MNT_RDONLY |
212				    MNT_RELOAD | MNT_FORCE | MNT_UPDATE))
213					result = KAUTH_RESULT_DENY;
214			}
215			break;
216		default:
217			break;
218		}
219
220		break;
221
222	case KAUTH_SYSTEM_SYSCTL:
223		switch (req) {
224		case KAUTH_REQ_SYSTEM_SYSCTL_ADD:
225		case KAUTH_REQ_SYSTEM_SYSCTL_DELETE:
226		case KAUTH_REQ_SYSTEM_SYSCTL_DESC:
227			if (kstate == KEYLOCK_CLOSE)
228				result = KAUTH_RESULT_DENY;
229			break;
230		default:
231			break;
232		}
233		break;
234
235	case KAUTH_SYSTEM_SETIDCORE:
236		if (kstate == KEYLOCK_CLOSE)
237			result = KAUTH_RESULT_DENY;
238		break;
239
240	case KAUTH_SYSTEM_DEBUG:
241		switch (req) {
242		case KAUTH_REQ_SYSTEM_DEBUG_IPKDB:
243			if (kstate == KEYLOCK_CLOSE)
244				result = KAUTH_RESULT_DENY;
245			break;
246		default:
247			break;
248		}
249		break;
250	}
251
252	return result;
253}
254
255/*
256 * kauth(9) listener
257 *
258 * Security model: Multi-position keylock
259 * Scope: Process
260 * Responsibility: Keylock
261 */
262int
263secmodel_keylock_process_cb(kauth_cred_t cred,
264    kauth_action_t action, void *cookie, void *arg0,
265    void *arg1, void *arg2, void *arg3)
266{
267	struct proc *p;
268	int result, kstate;
269
270	kstate = keylock_state();
271	if (kstate == KEYLOCK_ABSENT)
272		return KAUTH_RESULT_DEFER;
273	else if (kstate == KEYLOCK_TAMPER)
274		return KAUTH_RESULT_DENY;
275
276	result = KAUTH_RESULT_DEFER;
277	p = arg0;
278
279	switch (action) {
280	case KAUTH_PROCESS_PROCFS: {
281		enum kauth_process_req req;
282
283		req = (enum kauth_process_req)arg2;
284		switch (req) {
285		case KAUTH_REQ_PROCESS_PROCFS_READ:
286			break;
287
288		case KAUTH_REQ_PROCESS_PROCFS_RW:
289		case KAUTH_REQ_PROCESS_PROCFS_WRITE:
290			if ((p == initproc) && (kstate != KEYLOCK_OPEN))
291				result = KAUTH_RESULT_DENY;
292
293			break;
294		default:
295			break;
296		}
297
298		break;
299		}
300
301	case KAUTH_PROCESS_PTRACE:
302		if ((p == initproc) && (kstate != KEYLOCK_OPEN))
303			result = KAUTH_RESULT_DENY;
304
305		break;
306
307	case KAUTH_PROCESS_CORENAME:
308		if (kstate == KEYLOCK_CLOSE)
309			result = KAUTH_RESULT_DENY;
310		break;
311	}
312	return result;
313}
314
315/*
316 * kauth(9) listener
317 *
318 * Security model: Multi-position keylock
319 * Scope: Network
320 * Responsibility: Keylock
321 */
322int
323secmodel_keylock_network_cb(kauth_cred_t cred,
324    kauth_action_t action, void *cookie, void *arg0,
325    void *arg1, void *arg2, void *arg3)
326{
327	int result, kstate;
328	enum kauth_network_req req;
329
330	kstate = keylock_state();
331	if (kstate == KEYLOCK_ABSENT)
332		return KAUTH_RESULT_DEFER;
333	else if (kstate == KEYLOCK_TAMPER)
334		return KAUTH_RESULT_DENY;
335
336	result = KAUTH_RESULT_DEFER;
337	req = (enum kauth_network_req)arg0;
338
339	switch (action) {
340	case KAUTH_NETWORK_FIREWALL:
341		switch (req) {
342		case KAUTH_REQ_NETWORK_FIREWALL_FW:
343		case KAUTH_REQ_NETWORK_FIREWALL_NAT:
344			if (kstate == KEYLOCK_CLOSE)
345				result = KAUTH_RESULT_DENY;
346			break;
347
348		default:
349			break;
350		}
351		break;
352
353	case KAUTH_NETWORK_FORWSRCRT:
354		if (kstate != KEYLOCK_OPEN)
355			result = KAUTH_RESULT_DENY;
356		break;
357	}
358
359	return result;
360}
361
362/*
363 * kauth(9) listener
364 *
365 * Security model: Multi-position keylock
366 * Scope: Machdep
367 * Responsibility: Keylock
368 */
369int
370secmodel_keylock_machdep_cb(kauth_cred_t cred,
371    kauth_action_t action, void *cookie, void *arg0,
372    void *arg1, void *arg2, void *arg3)
373{
374        int result, kstate;
375
376	kstate = keylock_state();
377	if (kstate == KEYLOCK_ABSENT)
378		return KAUTH_RESULT_DEFER;
379	else if (kstate == KEYLOCK_TAMPER)
380		return KAUTH_RESULT_DENY;
381
382        result = KAUTH_RESULT_DEFER;
383
384        switch (action) {
385	case KAUTH_MACHDEP_IOPERM_SET:
386	case KAUTH_MACHDEP_IOPL:
387		if (kstate != KEYLOCK_OPEN)
388			result = KAUTH_RESULT_DENY;
389		break;
390
391	case KAUTH_MACHDEP_UNMANAGEDMEM:
392		if (kstate != KEYLOCK_OPEN)
393			result = KAUTH_RESULT_DENY;
394		break;
395	}
396
397	return result;
398}
399
400/*
401 * kauth(9) listener
402 *
403 * Security model: Multi-position keylock
404 * Scope: Device
405 * Responsibility: Keylock
406 */
407int
408secmodel_keylock_device_cb(kauth_cred_t cred,
409    kauth_action_t action, void *cookie, void *arg0,
410    void *arg1, void *arg2, void *arg3)
411{
412	int result, kstate;
413
414	kstate = keylock_state();
415	if (kstate == KEYLOCK_ABSENT)
416		return KAUTH_RESULT_DEFER;
417	else if (kstate == KEYLOCK_TAMPER)
418		return KAUTH_RESULT_DENY;
419
420	result = KAUTH_RESULT_DEFER;
421
422	switch (action) {
423	case KAUTH_DEVICE_RAWIO_SPEC: {
424		struct vnode *vp, *bvp;
425		enum kauth_device_req req;
426		dev_t dev;
427		int d_type;
428
429		req = (enum kauth_device_req)arg0;
430		vp = arg1;
431
432		KASSERT(vp != NULL);
433
434		dev = vp->v_rdev;
435		d_type = D_OTHER;
436		bvp = NULL;
437
438		/* Handle /dev/mem and /dev/kmem. */
439		if ((vp->v_type == VCHR) && iskmemdev(dev)) {
440			switch (req) {
441			case KAUTH_REQ_DEVICE_RAWIO_SPEC_READ:
442				break;
443
444			case KAUTH_REQ_DEVICE_RAWIO_SPEC_WRITE:
445			case KAUTH_REQ_DEVICE_RAWIO_SPEC_RW:
446				if (kstate != KEYLOCK_OPEN)
447					result = KAUTH_RESULT_DENY;
448				break;
449			default:
450				break;
451			}
452			break;
453		}
454
455		switch (req) {
456		case KAUTH_REQ_DEVICE_RAWIO_SPEC_READ:
457			break;
458
459		case KAUTH_REQ_DEVICE_RAWIO_SPEC_WRITE:
460		case KAUTH_REQ_DEVICE_RAWIO_SPEC_RW:
461			switch (vp->v_type) {
462			case VCHR: {
463				const struct cdevsw *cdev;
464
465				cdev = cdevsw_lookup(dev);
466				if (cdev != NULL) {
467					dev_t blkdev;
468
469					blkdev = devsw_chr2blk(dev);
470					if (blkdev != NODEV) {
471						vfinddev(blkdev, VBLK, &bvp);
472						if (bvp != NULL)
473							d_type = (cdev->d_flag
474							    & D_TYPEMASK);
475					}
476				}
477
478				break;
479				}
480			case VBLK: {
481				const struct bdevsw *bdev;
482
483				bdev = bdevsw_lookup(dev);
484				if (bdev != NULL)
485					d_type = (bdev->d_flag & D_TYPEMASK);
486
487				bvp = vp;
488
489				break;
490				}
491			default:
492				break;
493			}
494
495			if (d_type != D_DISK)
496				break;
497
498			/*
499			 * XXX: This is bogus. We should be failing the request
500			 * XXX: not only if this specific slice is mounted, but
501			 * XXX: if it's on a disk with any other mounted slice.
502			 */
503			if (vfs_mountedon(bvp) && (kstate != KEYLOCK_OPEN))
504				break;
505
506			if (kstate == KEYLOCK_CLOSE)
507				result = KAUTH_RESULT_DENY;
508
509			break;
510		default:
511			break;
512		}
513		break;
514		}
515
516	case KAUTH_DEVICE_RAWIO_PASSTHRU:
517		if (kstate != KEYLOCK_OPEN) {
518			u_long bits;
519
520			bits = (u_long)arg0;
521
522			KASSERT(bits != 0);
523			KASSERT((bits & ~KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_ALL)
524			    == 0);
525
526			if (bits & ~KAUTH_REQ_DEVICE_RAWIO_PASSTHRU_READCONF)
527				result = KAUTH_RESULT_DENY;
528		}
529		break;
530
531	case KAUTH_DEVICE_GPIO_PINSET:
532		if (kstate != KEYLOCK_OPEN)
533			result = KAUTH_RESULT_DENY;
534		break;
535	default:
536		break;
537	}
538	return result;
539}
540