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