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