secmodel_extensions.c revision 1.6
1/* $NetBSD: secmodel_extensions.c,v 1.6 2014/02/25 18:30:13 pooka 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.6 2014/02/25 18:30:13 pooka 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 if (action != KAUTH_SYSTEM_MOUNT || dovfsusermount == 0) 334 return result; 335 336 switch (req) { 337 case KAUTH_REQ_SYSTEM_MOUNT_NEW: 338 vp = (vnode_t *)arg1; 339 mp = vp->v_mount; 340 flags = (u_long)arg2; 341 342 /* 343 * Ensure that the user owns the directory onto which the 344 * mount is attempted. 345 */ 346 vn_lock(vp, LK_SHARED | LK_RETRY); 347 error = VOP_GETATTR(vp, &va, cred); 348 VOP_UNLOCK(vp); 349 if (error) 350 break; 351 352 if (va.va_uid != kauth_cred_geteuid(cred)) 353 break; 354 355 error = usermount_common_policy(mp, flags); 356 if (error) 357 break; 358 359 result = KAUTH_RESULT_ALLOW; 360 361 break; 362 363 case KAUTH_REQ_SYSTEM_MOUNT_UNMOUNT: 364 mp = arg1; 365 366 /* Must own the mount. */ 367 if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred)) 368 result = KAUTH_RESULT_ALLOW; 369 370 break; 371 372 case KAUTH_REQ_SYSTEM_MOUNT_UPDATE: 373 mp = arg1; 374 flags = (u_long)arg2; 375 376 /* Must own the mount. */ 377 if (mp->mnt_stat.f_owner == kauth_cred_geteuid(cred) && 378 usermount_common_policy(mp, flags) == 0) 379 result = KAUTH_RESULT_ALLOW; 380 381 break; 382 383 default: 384 break; 385 } 386 387 return (result); 388} 389 390static int 391secmodel_extensions_process_cb(kauth_cred_t cred, kauth_action_t action, 392 void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) 393{ 394 int result; 395 enum kauth_process_req req; 396 397 result = KAUTH_RESULT_DEFER; 398 req = (enum kauth_process_req)arg1; 399 400 switch (action) { 401 case KAUTH_PROCESS_CANSEE: 402 switch (req) { 403 case KAUTH_REQ_PROCESS_CANSEE_ARGS: 404 case KAUTH_REQ_PROCESS_CANSEE_ENTRY: 405 case KAUTH_REQ_PROCESS_CANSEE_OPENFILES: 406 if (curtain != 0) { 407 struct proc *p = arg0; 408 409 /* 410 * Only process' owner and root can see 411 * through curtain 412 */ 413 if (!kauth_cred_uidmatch(cred, p->p_cred)) { 414 int error; 415 bool isroot = false; 416 417 error = secmodel_eval( 418 "org.netbsd.secmodel.suser", 419 "is-root", cred, &isroot); 420 if (error == 0 && !isroot) 421 result = KAUTH_RESULT_DENY; 422 } 423 } 424 425 break; 426 427 default: 428 break; 429 } 430 431 break; 432 433 case KAUTH_PROCESS_SCHEDULER_SETAFFINITY: 434 if (user_set_cpu_affinity != 0) { 435 struct proc *p = arg0; 436 437 if (kauth_cred_uidmatch(cred, p->p_cred)) 438 result = KAUTH_RESULT_ALLOW; 439 } 440 break; 441 442 default: 443 break; 444 } 445 446 return (result); 447} 448 449static int 450secmodel_extensions_network_cb(kauth_cred_t cred, kauth_action_t action, 451 void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) 452{ 453 int result; 454 enum kauth_network_req req; 455 456 result = KAUTH_RESULT_DEFER; 457 req = (enum kauth_network_req)arg0; 458 459 if (action != KAUTH_NETWORK_SOCKET || 460 req != KAUTH_REQ_NETWORK_SOCKET_CANSEE) 461 return result; 462 463 if (curtain != 0) { 464 struct socket *so = (struct socket *)arg1; 465 466 if (__predict_false(so == NULL || so->so_cred == NULL)) 467 return KAUTH_RESULT_DENY; 468 469 if (!kauth_cred_uidmatch(cred, so->so_cred)) { 470 int error; 471 bool isroot = false; 472 473 error = secmodel_eval("org.netbsd.secmodel.suser", 474 "is-root", cred, &isroot); 475 if (error == 0 && !isroot) 476 result = KAUTH_RESULT_DENY; 477 } 478 } 479 480 return (result); 481} 482