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