secmodel_extensions.c revision 1.14
1/* $NetBSD: secmodel_extensions.c,v 1.14 2022/03/28 19:08:43 rillig 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.14 2022/03/28 19:08:43 rillig 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 47MODULE(MODULE_CLASS_SECMODEL, extensions, NULL); 48 49static int dovfsusermount; 50static int curtain; 51static int user_set_cpu_affinity; 52static int hardlink_check_uid; 53static int hardlink_check_gid; 54 55#ifdef PT_SETDBREGS 56int user_set_dbregs; 57#endif 58 59static kauth_listener_t l_system, l_process, l_network, l_vnode; 60 61static secmodel_t extensions_sm; 62 63static void secmodel_extensions_init(void); 64static void secmodel_extensions_start(void); 65static void secmodel_extensions_stop(void); 66 67static void sysctl_security_extensions_setup(struct sysctllog **); 68static int sysctl_extensions_user_handler(SYSCTLFN_PROTO); 69static int sysctl_extensions_curtain_handler(SYSCTLFN_PROTO); 70static bool is_securelevel_above(int); 71 72static int secmodel_extensions_system_cb(kauth_cred_t, kauth_action_t, 73 void *, void *, void *, void *, void *); 74static int secmodel_extensions_process_cb(kauth_cred_t, kauth_action_t, 75 void *, void *, void *, void *, void *); 76static int secmodel_extensions_network_cb(kauth_cred_t, kauth_action_t, 77 void *, void *, void *, void *, void *); 78static int secmodel_extensions_vnode_cb(kauth_cred_t, kauth_action_t, 79 void *, void *, void *, void *, void *); 80 81SYSCTL_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 that 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 202static int 203sysctl_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 */ 237static int 238sysctl_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 */ 271static bool 272is_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 285static void 286secmodel_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 296static void 297secmodel_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 310static void 311secmodel_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 320static int 321extensions_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 360static int 361secmodel_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 438static int 439secmodel_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 499static int 500secmodel_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 533static int 534secmodel_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; 554checkroot: 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