1 1.16 riastrad /* $NetBSD: secmodel_extensions.c,v 1.16 2023/04/22 13:54:19 riastradh Exp $ */ 2 1.1 jym /*- 3 1.1 jym * Copyright (c) 2011 Elad Efrat <elad (at) NetBSD.org> 4 1.1 jym * All rights reserved. 5 1.1 jym * 6 1.1 jym * Redistribution and use in source and binary forms, with or without 7 1.1 jym * modification, are permitted provided that the following conditions 8 1.1 jym * are met: 9 1.1 jym * 1. Redistributions of source code must retain the above copyright 10 1.1 jym * notice, this list of conditions and the following disclaimer. 11 1.1 jym * 2. Redistributions in binary form must reproduce the above copyright 12 1.1 jym * notice, this list of conditions and the following disclaimer in the 13 1.1 jym * documentation and/or other materials provided with the distribution. 14 1.1 jym * 3. The name of the author may not be used to endorse or promote products 15 1.1 jym * derived from this software without specific prior written permission. 16 1.1 jym * 17 1.1 jym * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 1.1 jym * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 1.1 jym * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 1.1 jym * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 1.1 jym * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 1.1 jym * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 1.1 jym * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 1.1 jym * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 1.1 jym * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 1.1 jym * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 1.1 jym */ 28 1.1 jym 29 1.1 jym #include <sys/cdefs.h> 30 1.16 riastrad __KERNEL_RCSID(0, "$NetBSD: secmodel_extensions.c,v 1.16 2023/04/22 13:54:19 riastradh Exp $"); 31 1.1 jym 32 1.1 jym #include <sys/types.h> 33 1.1 jym #include <sys/param.h> 34 1.1 jym #include <sys/kauth.h> 35 1.1 jym 36 1.1 jym #include <sys/mount.h> 37 1.1 jym #include <sys/socketvar.h> 38 1.1 jym #include <sys/sysctl.h> 39 1.1 jym #include <sys/proc.h> 40 1.8 kamil #include <sys/ptrace.h> 41 1.1 jym #include <sys/module.h> 42 1.1 jym 43 1.1 jym #include <secmodel/secmodel.h> 44 1.1 jym #include <secmodel/extensions/extensions.h> 45 1.16 riastrad #include <secmodel/extensions/extensions_impl.h> 46 1.1 jym 47 1.1 jym MODULE(MODULE_CLASS_SECMODEL, extensions, NULL); 48 1.1 jym 49 1.1 jym static int curtain; 50 1.1 jym static int user_set_cpu_affinity; 51 1.1 jym 52 1.8 kamil #ifdef PT_SETDBREGS 53 1.8 kamil int user_set_dbregs; 54 1.8 kamil #endif 55 1.8 kamil 56 1.16 riastrad static kauth_listener_t l_process, l_network; 57 1.1 jym 58 1.1 jym static secmodel_t extensions_sm; 59 1.1 jym 60 1.1 jym static void secmodel_extensions_init(void); 61 1.1 jym static void secmodel_extensions_start(void); 62 1.1 jym static void secmodel_extensions_stop(void); 63 1.1 jym 64 1.1 jym static void sysctl_security_extensions_setup(struct sysctllog **); 65 1.1 jym static int sysctl_extensions_curtain_handler(SYSCTLFN_PROTO); 66 1.1 jym static bool is_securelevel_above(int); 67 1.1 jym 68 1.1 jym static int secmodel_extensions_process_cb(kauth_cred_t, kauth_action_t, 69 1.1 jym void *, void *, void *, void *, void *); 70 1.1 jym static int secmodel_extensions_network_cb(kauth_cred_t, kauth_action_t, 71 1.1 jym void *, void *, void *, void *, void *); 72 1.1 jym 73 1.12 pgoyette SYSCTL_SETUP(sysctl_security_extensions_setup, 74 1.12 pgoyette "security extensions sysctl") 75 1.1 jym { 76 1.4 jym const struct sysctlnode *rnode, *rnode2; 77 1.1 jym 78 1.1 jym sysctl_createv(clog, 0, NULL, &rnode, 79 1.1 jym CTLFLAG_PERMANENT, 80 1.1 jym CTLTYPE_NODE, "models", NULL, 81 1.1 jym NULL, 0, NULL, 0, 82 1.6 pooka CTL_SECURITY, CTL_CREATE, CTL_EOL); 83 1.1 jym 84 1.4 jym /* Compatibility: security.models.bsd44 */ 85 1.4 jym rnode2 = rnode; 86 1.4 jym sysctl_createv(clog, 0, &rnode2, &rnode2, 87 1.4 jym CTLFLAG_PERMANENT, 88 1.4 jym CTLTYPE_NODE, "bsd44", NULL, 89 1.4 jym NULL, 0, NULL, 0, 90 1.4 jym CTL_CREATE, CTL_EOL); 91 1.4 jym 92 1.4 jym /* Compatibility: security.models.bsd44.curtain */ 93 1.4 jym sysctl_createv(clog, 0, &rnode2, NULL, 94 1.4 jym CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 95 1.4 jym CTLTYPE_INT, "curtain", 96 1.4 jym SYSCTL_DESCR("Curtain information about objects to "\ 97 1.4 jym "users not owning them."), 98 1.4 jym sysctl_extensions_curtain_handler, 0, &curtain, 0, 99 1.4 jym CTL_CREATE, CTL_EOL); 100 1.4 jym 101 1.1 jym sysctl_createv(clog, 0, &rnode, &rnode, 102 1.1 jym CTLFLAG_PERMANENT, 103 1.1 jym CTLTYPE_NODE, "extensions", NULL, 104 1.1 jym NULL, 0, NULL, 0, 105 1.1 jym CTL_CREATE, CTL_EOL); 106 1.1 jym 107 1.1 jym sysctl_createv(clog, 0, &rnode, NULL, 108 1.1 jym CTLFLAG_PERMANENT, 109 1.1 jym CTLTYPE_STRING, "name", NULL, 110 1.1 jym NULL, 0, __UNCONST(SECMODEL_EXTENSIONS_NAME), 0, 111 1.1 jym CTL_CREATE, CTL_EOL); 112 1.1 jym 113 1.1 jym sysctl_createv(clog, 0, &rnode, NULL, 114 1.1 jym CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 115 1.1 jym CTLTYPE_INT, "curtain", 116 1.1 jym SYSCTL_DESCR("Curtain information about objects to "\ 117 1.1 jym "users not owning them."), 118 1.1 jym sysctl_extensions_curtain_handler, 0, &curtain, 0, 119 1.1 jym CTL_CREATE, CTL_EOL); 120 1.1 jym 121 1.1 jym sysctl_createv(clog, 0, &rnode, NULL, 122 1.1 jym CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 123 1.1 jym CTLTYPE_INT, "user_set_cpu_affinity", 124 1.1 jym SYSCTL_DESCR("Whether unprivileged users may control "\ 125 1.1 jym "CPU affinity."), 126 1.1 jym sysctl_extensions_user_handler, 0, 127 1.1 jym &user_set_cpu_affinity, 0, 128 1.1 jym CTL_CREATE, CTL_EOL); 129 1.1 jym 130 1.8 kamil #ifdef PT_SETDBREGS 131 1.8 kamil sysctl_createv(clog, 0, &rnode, NULL, 132 1.8 kamil CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 133 1.8 kamil CTLTYPE_INT, "user_set_dbregs", 134 1.8 kamil SYSCTL_DESCR("Whether unprivileged users may set "\ 135 1.8 kamil "CPU Debug Registers."), 136 1.8 kamil sysctl_extensions_user_handler, 0, 137 1.8 kamil &user_set_dbregs, 0, 138 1.8 kamil CTL_CREATE, CTL_EOL); 139 1.8 kamil #endif 140 1.8 kamil 141 1.1 jym /* Compatibility: security.curtain */ 142 1.6 pooka sysctl_createv(clog, 0, NULL, NULL, 143 1.1 jym CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 144 1.1 jym CTLTYPE_INT, "curtain", 145 1.1 jym SYSCTL_DESCR("Curtain information about objects to "\ 146 1.1 jym "users not owning them."), 147 1.1 jym sysctl_extensions_curtain_handler, 0, &curtain, 0, 148 1.6 pooka CTL_SECURITY, CTL_CREATE, CTL_EOL); 149 1.16 riastrad 150 1.16 riastrad secmodel_extensions_vfs_sysctl(clog, rnode); 151 1.1 jym } 152 1.1 jym 153 1.1 jym static int 154 1.1 jym sysctl_extensions_curtain_handler(SYSCTLFN_ARGS) 155 1.1 jym { 156 1.1 jym struct sysctlnode node; 157 1.1 jym int val, error; 158 1.1 jym 159 1.1 jym val = *(int *)rnode->sysctl_data; 160 1.1 jym 161 1.1 jym node = *rnode; 162 1.1 jym node.sysctl_data = &val; 163 1.1 jym 164 1.1 jym error = sysctl_lookup(SYSCTLFN_CALL(&node)); 165 1.1 jym if (error || newp == NULL) 166 1.1 jym return error; 167 1.1 jym 168 1.1 jym /* shortcut */ 169 1.1 jym if (val == *(int *)rnode->sysctl_data) 170 1.1 jym return 0; 171 1.1 jym 172 1.1 jym /* curtain cannot be disabled when securelevel is above 0 */ 173 1.1 jym if (val == 0 && is_securelevel_above(0)) { 174 1.1 jym return EPERM; 175 1.1 jym } 176 1.1 jym 177 1.1 jym *(int *)rnode->sysctl_data = val; 178 1.1 jym return 0; 179 1.1 jym } 180 1.1 jym 181 1.1 jym /* 182 1.1 jym * Generic sysctl extensions handler for user mount and set CPU affinity 183 1.1 jym * rights. Checks the following conditions: 184 1.1 jym * - setting value to 0 is always permitted (decrease user rights) 185 1.1 jym * - setting value != 0 is not permitted when securelevel is above 0 (increase 186 1.1 jym * user rights). 187 1.1 jym */ 188 1.16 riastrad int 189 1.1 jym sysctl_extensions_user_handler(SYSCTLFN_ARGS) 190 1.1 jym { 191 1.1 jym struct sysctlnode node; 192 1.1 jym int val, error; 193 1.1 jym 194 1.1 jym val = *(int *)rnode->sysctl_data; 195 1.1 jym 196 1.1 jym node = *rnode; 197 1.1 jym node.sysctl_data = &val; 198 1.1 jym 199 1.1 jym error = sysctl_lookup(SYSCTLFN_CALL(&node)); 200 1.1 jym if (error || newp == NULL) 201 1.1 jym return error; 202 1.1 jym 203 1.1 jym /* shortcut */ 204 1.1 jym if (val == *(int *)rnode->sysctl_data) 205 1.1 jym return 0; 206 1.1 jym 207 1.1 jym /* we cannot grant more rights to users when securelevel is above 0 */ 208 1.1 jym if (val != 0 && is_securelevel_above(0)) { 209 1.1 jym return EPERM; 210 1.1 jym } 211 1.1 jym 212 1.1 jym *(int *)rnode->sysctl_data = val; 213 1.1 jym return 0; 214 1.1 jym } 215 1.1 jym 216 1.1 jym /* 217 1.1 jym * Query secmodel_securelevel(9) to know whether securelevel is strictly 218 1.1 jym * above 'level' or not. 219 1.1 jym * Returns true if it is, false otherwise (when securelevel is absent or 220 1.1 jym * securelevel is at or below 'level'). 221 1.1 jym */ 222 1.1 jym static bool 223 1.1 jym is_securelevel_above(int level) 224 1.1 jym { 225 1.1 jym bool above; 226 1.1 jym int error; 227 1.1 jym 228 1.1 jym error = secmodel_eval("org.netbsd.secmodel.securelevel", 229 1.1 jym "is-securelevel-above", KAUTH_ARG(level), &above); 230 1.1 jym if (error == 0 && above) 231 1.1 jym return true; 232 1.1 jym else 233 1.1 jym return false; 234 1.1 jym } 235 1.1 jym 236 1.1 jym static void 237 1.1 jym secmodel_extensions_init(void) 238 1.1 jym { 239 1.1 jym 240 1.1 jym curtain = 0; 241 1.1 jym user_set_cpu_affinity = 0; 242 1.8 kamil #ifdef PT_SETDBREGS 243 1.8 kamil user_set_dbregs = 0; 244 1.8 kamil #endif 245 1.1 jym } 246 1.1 jym 247 1.1 jym static void 248 1.1 jym secmodel_extensions_start(void) 249 1.1 jym { 250 1.1 jym 251 1.1 jym l_process = kauth_listen_scope(KAUTH_SCOPE_PROCESS, 252 1.1 jym secmodel_extensions_process_cb, NULL); 253 1.1 jym l_network = kauth_listen_scope(KAUTH_SCOPE_NETWORK, 254 1.1 jym secmodel_extensions_network_cb, NULL); 255 1.16 riastrad secmodel_extensions_vfs_start(); 256 1.1 jym } 257 1.1 jym 258 1.1 jym static void 259 1.1 jym secmodel_extensions_stop(void) 260 1.1 jym { 261 1.1 jym 262 1.16 riastrad secmodel_extensions_vfs_stop(); 263 1.1 jym kauth_unlisten_scope(l_process); 264 1.1 jym kauth_unlisten_scope(l_network); 265 1.1 jym } 266 1.1 jym 267 1.1 jym static int 268 1.1 jym extensions_modcmd(modcmd_t cmd, void *arg) 269 1.1 jym { 270 1.1 jym int error = 0; 271 1.1 jym 272 1.1 jym switch (cmd) { 273 1.1 jym case MODULE_CMD_INIT: 274 1.1 jym error = secmodel_register(&extensions_sm, 275 1.1 jym SECMODEL_EXTENSIONS_ID, SECMODEL_EXTENSIONS_NAME, 276 1.1 jym NULL, NULL, NULL); 277 1.1 jym if (error != 0) 278 1.1 jym printf("extensions_modcmd::init: secmodel_register " 279 1.1 jym "returned %d\n", error); 280 1.1 jym 281 1.1 jym secmodel_extensions_init(); 282 1.1 jym secmodel_extensions_start(); 283 1.1 jym break; 284 1.1 jym 285 1.1 jym case MODULE_CMD_FINI: 286 1.1 jym secmodel_extensions_stop(); 287 1.1 jym 288 1.1 jym error = secmodel_deregister(extensions_sm); 289 1.1 jym if (error != 0) 290 1.1 jym printf("extensions_modcmd::fini: secmodel_deregister " 291 1.1 jym "returned %d\n", error); 292 1.1 jym 293 1.1 jym break; 294 1.1 jym 295 1.1 jym case MODULE_CMD_AUTOUNLOAD: 296 1.1 jym error = EPERM; 297 1.1 jym break; 298 1.1 jym 299 1.1 jym default: 300 1.1 jym error = ENOTTY; 301 1.1 jym break; 302 1.1 jym } 303 1.1 jym 304 1.1 jym return (error); 305 1.1 jym } 306 1.1 jym 307 1.1 jym static int 308 1.1 jym secmodel_extensions_process_cb(kauth_cred_t cred, kauth_action_t action, 309 1.1 jym void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) 310 1.1 jym { 311 1.1 jym int result; 312 1.1 jym enum kauth_process_req req; 313 1.1 jym 314 1.1 jym result = KAUTH_RESULT_DEFER; 315 1.11 joerg req = (enum kauth_process_req)(uintptr_t)arg1; 316 1.1 jym 317 1.1 jym switch (action) { 318 1.1 jym case KAUTH_PROCESS_CANSEE: 319 1.1 jym switch (req) { 320 1.1 jym case KAUTH_REQ_PROCESS_CANSEE_ARGS: 321 1.1 jym case KAUTH_REQ_PROCESS_CANSEE_ENTRY: 322 1.1 jym case KAUTH_REQ_PROCESS_CANSEE_OPENFILES: 323 1.9 maxv case KAUTH_REQ_PROCESS_CANSEE_EPROC: 324 1.1 jym if (curtain != 0) { 325 1.1 jym struct proc *p = arg0; 326 1.1 jym 327 1.1 jym /* 328 1.1 jym * Only process' owner and root can see 329 1.1 jym * through curtain 330 1.1 jym */ 331 1.1 jym if (!kauth_cred_uidmatch(cred, p->p_cred)) { 332 1.1 jym int error; 333 1.1 jym bool isroot = false; 334 1.1 jym 335 1.1 jym error = secmodel_eval( 336 1.1 jym "org.netbsd.secmodel.suser", 337 1.1 jym "is-root", cred, &isroot); 338 1.1 jym if (error == 0 && !isroot) 339 1.1 jym result = KAUTH_RESULT_DENY; 340 1.1 jym } 341 1.1 jym } 342 1.1 jym 343 1.1 jym break; 344 1.1 jym 345 1.10 maxv case KAUTH_REQ_PROCESS_CANSEE_KPTR: 346 1.1 jym default: 347 1.1 jym break; 348 1.1 jym } 349 1.1 jym 350 1.1 jym break; 351 1.1 jym 352 1.1 jym case KAUTH_PROCESS_SCHEDULER_SETAFFINITY: 353 1.1 jym if (user_set_cpu_affinity != 0) { 354 1.2 jym struct proc *p = arg0; 355 1.2 jym 356 1.2 jym if (kauth_cred_uidmatch(cred, p->p_cred)) 357 1.2 jym result = KAUTH_RESULT_ALLOW; 358 1.1 jym } 359 1.1 jym break; 360 1.1 jym 361 1.1 jym default: 362 1.1 jym break; 363 1.1 jym } 364 1.1 jym 365 1.1 jym return (result); 366 1.1 jym } 367 1.1 jym 368 1.1 jym static int 369 1.1 jym secmodel_extensions_network_cb(kauth_cred_t cred, kauth_action_t action, 370 1.1 jym void *cookie, void *arg0, void *arg1, void *arg2, void *arg3) 371 1.1 jym { 372 1.1 jym int result; 373 1.1 jym enum kauth_network_req req; 374 1.1 jym 375 1.1 jym result = KAUTH_RESULT_DEFER; 376 1.11 joerg req = (enum kauth_network_req)(uintptr_t)arg0; 377 1.1 jym 378 1.1 jym if (action != KAUTH_NETWORK_SOCKET || 379 1.1 jym req != KAUTH_REQ_NETWORK_SOCKET_CANSEE) 380 1.1 jym return result; 381 1.1 jym 382 1.1 jym if (curtain != 0) { 383 1.1 jym struct socket *so = (struct socket *)arg1; 384 1.1 jym 385 1.5 martin if (__predict_false(so == NULL || so->so_cred == NULL)) 386 1.5 martin return KAUTH_RESULT_DENY; 387 1.5 martin 388 1.1 jym if (!kauth_cred_uidmatch(cred, so->so_cred)) { 389 1.1 jym int error; 390 1.1 jym bool isroot = false; 391 1.1 jym 392 1.1 jym error = secmodel_eval("org.netbsd.secmodel.suser", 393 1.1 jym "is-root", cred, &isroot); 394 1.1 jym if (error == 0 && !isroot) 395 1.1 jym result = KAUTH_RESULT_DENY; 396 1.1 jym } 397 1.1 jym } 398 1.1 jym 399 1.1 jym return (result); 400 1.1 jym } 401