Home | History | Annotate | Line # | Download | only in extensions
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 (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.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 
     46 MODULE(MODULE_CLASS_SECMODEL, extensions, NULL);
     47 
     48 static int dovfsusermount;
     49 static int curtain;
     50 static int user_set_cpu_affinity;
     51 
     52 static kauth_listener_t l_system, l_process, l_network;
     53 
     54 static secmodel_t extensions_sm;
     55 static struct sysctllog *extensions_sysctl_log;
     56 
     57 static void secmodel_extensions_init(void);
     58 static void secmodel_extensions_start(void);
     59 static void secmodel_extensions_stop(void);
     60 
     61 static void sysctl_security_extensions_setup(struct sysctllog **);
     62 static int  sysctl_extensions_user_handler(SYSCTLFN_PROTO);
     63 static int  sysctl_extensions_curtain_handler(SYSCTLFN_PROTO);
     64 static bool is_securelevel_above(int);
     65 
     66 static int secmodel_extensions_system_cb(kauth_cred_t, kauth_action_t,
     67     void *, void *, void *, void *, void *);
     68 static int secmodel_extensions_process_cb(kauth_cred_t, kauth_action_t,
     69     void *, void *, void *, void *, void *);
     70 static int secmodel_extensions_network_cb(kauth_cred_t, kauth_action_t,
     71     void *, void *, void *, void *, void *);
     72 
     73 static void
     74 sysctl_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 
    165 static int
    166 sysctl_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  */
    200 static int
    201 sysctl_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  */
    234 static bool
    235 is_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 
    248 static void
    249 secmodel_extensions_init(void)
    250 {
    251 
    252 	curtain = 0;
    253 	user_set_cpu_affinity = 0;
    254 }
    255 
    256 static void
    257 secmodel_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 
    268 static void
    269 secmodel_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 
    277 static int
    278 extensions_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 
    319 static int
    320 secmodel_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 
    391 static int
    392 secmodel_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 
    450 static int
    451 secmodel_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