secmodel_extensions.c revision 1.16
1/* $NetBSD: secmodel_extensions.c,v 1.16 2023/04/22 13:54:19 riastradh 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.16 2023/04/22 13:54:19 riastradh 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/socketvar.h> 38#include <sys/sysctl.h> 39#include <sys/proc.h> 40#include <sys/ptrace.h> 41#include <sys/module.h> 42 43#include <secmodel/secmodel.h> 44#include <secmodel/extensions/extensions.h> 45#include <secmodel/extensions/extensions_impl.h> 46 47MODULE(MODULE_CLASS_SECMODEL, extensions, NULL); 48 49static int curtain; 50static int user_set_cpu_affinity; 51 52#ifdef PT_SETDBREGS 53int user_set_dbregs; 54#endif 55 56static kauth_listener_t l_process, l_network; 57 58static secmodel_t extensions_sm; 59 60static void secmodel_extensions_init(void); 61static void secmodel_extensions_start(void); 62static void secmodel_extensions_stop(void); 63 64static void sysctl_security_extensions_setup(struct sysctllog **); 65static int sysctl_extensions_curtain_handler(SYSCTLFN_PROTO); 66static bool is_securelevel_above(int); 67 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 73SYSCTL_SETUP(sysctl_security_extensions_setup, 74 "security extensions sysctl") 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, "curtain", 116 SYSCTL_DESCR("Curtain information about objects to "\ 117 "users not owning them."), 118 sysctl_extensions_curtain_handler, 0, &curtain, 0, 119 CTL_CREATE, CTL_EOL); 120 121 sysctl_createv(clog, 0, &rnode, NULL, 122 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 123 CTLTYPE_INT, "user_set_cpu_affinity", 124 SYSCTL_DESCR("Whether unprivileged users may control "\ 125 "CPU affinity."), 126 sysctl_extensions_user_handler, 0, 127 &user_set_cpu_affinity, 0, 128 CTL_CREATE, CTL_EOL); 129 130#ifdef PT_SETDBREGS 131 sysctl_createv(clog, 0, &rnode, NULL, 132 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 133 CTLTYPE_INT, "user_set_dbregs", 134 SYSCTL_DESCR("Whether unprivileged users may set "\ 135 "CPU Debug Registers."), 136 sysctl_extensions_user_handler, 0, 137 &user_set_dbregs, 0, 138 CTL_CREATE, CTL_EOL); 139#endif 140 141 /* Compatibility: security.curtain */ 142 sysctl_createv(clog, 0, NULL, NULL, 143 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 144 CTLTYPE_INT, "curtain", 145 SYSCTL_DESCR("Curtain information about objects to "\ 146 "users not owning them."), 147 sysctl_extensions_curtain_handler, 0, &curtain, 0, 148 CTL_SECURITY, CTL_CREATE, CTL_EOL); 149 150 secmodel_extensions_vfs_sysctl(clog, rnode); 151} 152 153static int 154sysctl_extensions_curtain_handler(SYSCTLFN_ARGS) 155{ 156 struct sysctlnode node; 157 int val, error; 158 159 val = *(int *)rnode->sysctl_data; 160 161 node = *rnode; 162 node.sysctl_data = &val; 163 164 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 165 if (error || newp == NULL) 166 return error; 167 168 /* shortcut */ 169 if (val == *(int *)rnode->sysctl_data) 170 return 0; 171 172 /* curtain cannot be disabled when securelevel is above 0 */ 173 if (val == 0 && is_securelevel_above(0)) { 174 return EPERM; 175 } 176 177 *(int *)rnode->sysctl_data = val; 178 return 0; 179} 180 181/* 182 * Generic sysctl extensions handler for user mount and set CPU affinity 183 * rights. Checks the following conditions: 184 * - setting value to 0 is always permitted (decrease user rights) 185 * - setting value != 0 is not permitted when securelevel is above 0 (increase 186 * user rights). 187 */ 188int 189sysctl_extensions_user_handler(SYSCTLFN_ARGS) 190{ 191 struct sysctlnode node; 192 int val, error; 193 194 val = *(int *)rnode->sysctl_data; 195 196 node = *rnode; 197 node.sysctl_data = &val; 198 199 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 200 if (error || newp == NULL) 201 return error; 202 203 /* shortcut */ 204 if (val == *(int *)rnode->sysctl_data) 205 return 0; 206 207 /* we cannot grant more rights to users when securelevel is above 0 */ 208 if (val != 0 && is_securelevel_above(0)) { 209 return EPERM; 210 } 211 212 *(int *)rnode->sysctl_data = val; 213 return 0; 214} 215 216/* 217 * Query secmodel_securelevel(9) to know whether securelevel is strictly 218 * above 'level' or not. 219 * Returns true if it is, false otherwise (when securelevel is absent or 220 * securelevel is at or below 'level'). 221 */ 222static bool 223is_securelevel_above(int level) 224{ 225 bool above; 226 int error; 227 228 error = secmodel_eval("org.netbsd.secmodel.securelevel", 229 "is-securelevel-above", KAUTH_ARG(level), &above); 230 if (error == 0 && above) 231 return true; 232 else 233 return false; 234} 235 236static void 237secmodel_extensions_init(void) 238{ 239 240 curtain = 0; 241 user_set_cpu_affinity = 0; 242#ifdef PT_SETDBREGS 243 user_set_dbregs = 0; 244#endif 245} 246 247static void 248secmodel_extensions_start(void) 249{ 250 251 l_process = kauth_listen_scope(KAUTH_SCOPE_PROCESS, 252 secmodel_extensions_process_cb, NULL); 253 l_network = kauth_listen_scope(KAUTH_SCOPE_NETWORK, 254 secmodel_extensions_network_cb, NULL); 255 secmodel_extensions_vfs_start(); 256} 257 258static void 259secmodel_extensions_stop(void) 260{ 261 262 secmodel_extensions_vfs_stop(); 263 kauth_unlisten_scope(l_process); 264 kauth_unlisten_scope(l_network); 265} 266 267static int 268extensions_modcmd(modcmd_t cmd, void *arg) 269{ 270 int error = 0; 271 272 switch (cmd) { 273 case MODULE_CMD_INIT: 274 error = secmodel_register(&extensions_sm, 275 SECMODEL_EXTENSIONS_ID, SECMODEL_EXTENSIONS_NAME, 276 NULL, NULL, NULL); 277 if (error != 0) 278 printf("extensions_modcmd::init: secmodel_register " 279 "returned %d\n", error); 280 281 secmodel_extensions_init(); 282 secmodel_extensions_start(); 283 break; 284 285 case MODULE_CMD_FINI: 286 secmodel_extensions_stop(); 287 288 error = secmodel_deregister(extensions_sm); 289 if (error != 0) 290 printf("extensions_modcmd::fini: secmodel_deregister " 291 "returned %d\n", error); 292 293 break; 294 295 case MODULE_CMD_AUTOUNLOAD: 296 error = EPERM; 297 break; 298 299 default: 300 error = ENOTTY; 301 break; 302 } 303 304 return (error); 305} 306 307static int 308secmodel_extensions_process_cb(kauth_cred_t cred, kauth_action_t action, 309 void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) 310{ 311 int result; 312 enum kauth_process_req req; 313 314 result = KAUTH_RESULT_DEFER; 315 req = (enum kauth_process_req)(uintptr_t)arg1; 316 317 switch (action) { 318 case KAUTH_PROCESS_CANSEE: 319 switch (req) { 320 case KAUTH_REQ_PROCESS_CANSEE_ARGS: 321 case KAUTH_REQ_PROCESS_CANSEE_ENTRY: 322 case KAUTH_REQ_PROCESS_CANSEE_OPENFILES: 323 case KAUTH_REQ_PROCESS_CANSEE_EPROC: 324 if (curtain != 0) { 325 struct proc *p = arg0; 326 327 /* 328 * Only process' owner and root can see 329 * through curtain 330 */ 331 if (!kauth_cred_uidmatch(cred, p->p_cred)) { 332 int error; 333 bool isroot = false; 334 335 error = secmodel_eval( 336 "org.netbsd.secmodel.suser", 337 "is-root", cred, &isroot); 338 if (error == 0 && !isroot) 339 result = KAUTH_RESULT_DENY; 340 } 341 } 342 343 break; 344 345 case KAUTH_REQ_PROCESS_CANSEE_KPTR: 346 default: 347 break; 348 } 349 350 break; 351 352 case KAUTH_PROCESS_SCHEDULER_SETAFFINITY: 353 if (user_set_cpu_affinity != 0) { 354 struct proc *p = arg0; 355 356 if (kauth_cred_uidmatch(cred, p->p_cred)) 357 result = KAUTH_RESULT_ALLOW; 358 } 359 break; 360 361 default: 362 break; 363 } 364 365 return (result); 366} 367 368static int 369secmodel_extensions_network_cb(kauth_cred_t cred, kauth_action_t action, 370 void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) 371{ 372 int result; 373 enum kauth_network_req req; 374 375 result = KAUTH_RESULT_DEFER; 376 req = (enum kauth_network_req)(uintptr_t)arg0; 377 378 if (action != KAUTH_NETWORK_SOCKET || 379 req != KAUTH_REQ_NETWORK_SOCKET_CANSEE) 380 return result; 381 382 if (curtain != 0) { 383 struct socket *so = (struct socket *)arg1; 384 385 if (__predict_false(so == NULL || so->so_cred == NULL)) 386 return KAUTH_RESULT_DENY; 387 388 if (!kauth_cred_uidmatch(cred, so->so_cred)) { 389 int error; 390 bool isroot = false; 391 392 error = secmodel_eval("org.netbsd.secmodel.suser", 393 "is-root", cred, &isroot); 394 if (error == 0 && !isroot) 395 result = KAUTH_RESULT_DENY; 396 } 397 } 398 399 return (result); 400} 401