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