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