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