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@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 47MODULE(MODULE_CLASS_SECMODEL, extensions, NULL); 48 49static int dovfsusermount; 50static int curtain; 51static int user_set_cpu_affinity; 52 53#ifdef PT_SETDBREGS 54int user_set_dbregs; 55#endif 56 57static kauth_listener_t l_system, l_process, l_network; 58 59static secmodel_t extensions_sm; 60 61static void secmodel_extensions_init(void); 62static void secmodel_extensions_start(void); 63static void secmodel_extensions_stop(void); 64 65static void sysctl_security_extensions_setup(struct sysctllog **); 66static int sysctl_extensions_user_handler(SYSCTLFN_PROTO); 67static int sysctl_extensions_curtain_handler(SYSCTLFN_PROTO); 68static bool is_securelevel_above(int); 69 70static int secmodel_extensions_system_cb(kauth_cred_t, kauth_action_t, 71 void *, void *, void *, void *, void *); 72static int secmodel_extensions_process_cb(kauth_cred_t, kauth_action_t, 73 void *, void *, void *, void *, void *); 74static int secmodel_extensions_network_cb(kauth_cred_t, kauth_action_t, 75 void *, void *, void *, void *, void *); 76 77SYSCTL_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 179static int 180sysctl_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 */ 214static int 215sysctl_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 */ 248static bool 249is_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 262static void 263secmodel_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 273static void 274secmodel_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 285static void 286secmodel_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 294static int 295extensions_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 334static int 335secmodel_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 412static int 413secmodel_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 473static int 474secmodel_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