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