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