1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021-2022 Klara Systems 5 * 6 * This software was developed by Mitchell Horne <mhorne@FreeBSD.org> 7 * under sponsorship from Juniper Networks and Klara Systems. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/jail.h> 33 #include <sys/kdb.h> 34 #include <sys/module.h> 35 #include <sys/mount.h> 36 #include <sys/proc.h> 37 #include <sys/queue.h> 38 #include <sys/rman.h> 39 #include <sys/sysctl.h> 40 41 #include <net/vnet.h> 42 43 #include <ddb/ddb.h> 44 #include <ddb/db_command.h> 45 46 #include <security/mac/mac_policy.h> 47 48 /* 49 * This module provides a limited interface to the ddb(4) kernel debugger. The 50 * intent is to allow execution of useful debugging commands while disallowing 51 * the execution of commands which may be used to inspect/modify arbitrary 52 * system memory. 53 * 54 * Commands which are deterministic in their output or effect and that have 55 * been flagged with DB_CMD_MEMSAFE in their definition will be allowed. 56 * 57 * Other commands are valid within this context so long as there is some 58 * constraint placed on their input arguments. This applies to most 'show' 59 * commands which accept an arbitrary address. If the provided address can be 60 * validated as a real instance of the object (e.g. the 'show proc' address 61 * points to a real struct proc in the process list), then the command may be 62 * executed. This module defines several validation functions which are used to 63 * conditionally allow or block the execution of some commands. For these 64 * commands we define and apply the DB_CMD_VALIDATE flag. 65 * 66 * Any other commands not flagged with DM_CMD_MEMSAFE or DB_CMD_VALIDATE are 67 * considered unsafe for execution. 68 */ 69 70 #define DB_CMD_VALIDATE DB_MAC1 71 72 typedef int db_validation_fn_t(db_expr_t addr, bool have_addr, db_expr_t count, 73 char *modif); 74 75 static db_validation_fn_t db_thread_valid; 76 static db_validation_fn_t db_show_ffs_valid; 77 static db_validation_fn_t db_show_prison_valid; 78 static db_validation_fn_t db_show_proc_valid; 79 static db_validation_fn_t db_show_rman_valid; 80 #ifdef VIMAGE 81 static db_validation_fn_t db_show_vnet_valid; 82 #endif 83 84 struct cmd_list_item { 85 const char *name; 86 db_validation_fn_t *validate_fn; 87 }; 88 89 /* List of top-level ddb(4) commands which are allowed by this policy. */ 90 static const struct cmd_list_item command_list[] = { 91 { "thread", db_thread_valid }, 92 }; 93 94 /* List of ddb(4) 'show' commands which are allowed by this policy. */ 95 static const struct cmd_list_item show_command_list[] = { 96 { "ffs", db_show_ffs_valid }, 97 { "prison", db_show_prison_valid }, 98 { "proc", db_show_proc_valid }, 99 { "rman", db_show_rman_valid }, 100 { "thread", db_thread_valid }, 101 #ifdef VIMAGE 102 { "vnet", db_show_vnet_valid }, 103 #endif 104 }; 105 106 static int 107 db_thread_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 108 { 109 struct thread *thr; 110 lwpid_t tid; 111 112 /* Default will show the current proc. */ 113 if (!have_addr) 114 return (0); 115 116 /* Validate the provided addr OR tid against the thread list. */ 117 tid = db_hex2dec(addr); 118 for (thr = kdb_thr_first(); thr != NULL; thr = kdb_thr_next(thr)) { 119 if ((void *)thr == (void *)addr || tid == thr->td_tid) 120 return (0); 121 } 122 123 return (EACCES); 124 } 125 126 static int 127 db_show_ffs_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 128 { 129 struct mount *mp; 130 131 /* No addr will show all mounts. */ 132 if (!have_addr) 133 return (0); 134 135 TAILQ_FOREACH(mp, &mountlist, mnt_list) 136 if ((void *)mp == (void *)addr) 137 return (0); 138 139 return (EACCES); 140 } 141 142 static int 143 db_show_prison_valid(db_expr_t addr, bool have_addr, db_expr_t count, 144 char *modif) 145 { 146 struct prison *pr; 147 int pr_id; 148 149 if (!have_addr || addr == 0) 150 return (0); 151 152 /* prison can match by pointer address or ID. */ 153 pr_id = (int)addr; 154 TAILQ_FOREACH(pr, &allprison, pr_list) 155 if (pr->pr_id == pr_id || (void *)pr == (void *)addr) 156 return (0); 157 158 return (EACCES); 159 } 160 161 static int 162 db_show_proc_valid(db_expr_t addr, bool have_addr, db_expr_t count, 163 char *modif) 164 { 165 struct proc *p; 166 int i; 167 168 /* Default will show the current proc. */ 169 if (!have_addr) 170 return (0); 171 172 for (i = 0; i <= pidhash; i++) { 173 LIST_FOREACH(p, &pidhashtbl[i], p_hash) { 174 if ((void *)p == (void *)addr) 175 return (0); 176 } 177 } 178 179 return (EACCES); 180 } 181 182 static int 183 db_show_rman_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 184 { 185 struct rman *rm; 186 187 TAILQ_FOREACH(rm, &rman_head, rm_link) { 188 if ((void *)rm == (void *)addr) 189 return (0); 190 } 191 192 return (EACCES); 193 } 194 195 #ifdef VIMAGE 196 static int 197 db_show_vnet_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 198 { 199 VNET_ITERATOR_DECL(vnet); 200 201 if (!have_addr) 202 return (0); 203 204 VNET_FOREACH(vnet) { 205 if ((void *)vnet == (void *)addr) 206 return (0); 207 } 208 209 return (EACCES); 210 } 211 #endif 212 213 static int 214 command_match(struct db_command *cmd, struct cmd_list_item item) 215 { 216 db_validation_fn_t *vfn; 217 int n; 218 219 n = strcmp(cmd->name, item.name); 220 if (n != 0) 221 return (n); 222 223 /* Got an exact match. Update the command struct */ 224 vfn = item.validate_fn; 225 if (vfn != NULL) { 226 cmd->flag |= DB_CMD_VALIDATE; 227 cmd->mac_priv = vfn; 228 } 229 return (0); 230 } 231 232 static void 233 mac_ddb_init(struct mac_policy_conf *conf) 234 { 235 struct db_command *cmd, *prev; 236 int i, n; 237 238 /* The command lists are sorted lexographically, as are our arrays. */ 239 240 /* Register basic commands. */ 241 for (i = 0, cmd = prev = NULL; i < nitems(command_list); i++) { 242 LIST_FOREACH_FROM(cmd, &db_cmd_table, next) { 243 n = command_match(cmd, command_list[i]); 244 if (n == 0) { 245 /* Got an exact match. */ 246 prev = cmd; 247 break; 248 } else if (n > 0) { 249 /* Desired command is not registered. */ 250 break; 251 } 252 } 253 254 /* Next search begins at the previous match. */ 255 cmd = prev; 256 } 257 258 /* Register 'show' commands which require validation. */ 259 for (i = 0, cmd = prev = NULL; i < nitems(show_command_list); i++) { 260 LIST_FOREACH_FROM(cmd, &db_show_table, next) { 261 n = command_match(cmd, show_command_list[i]); 262 if (n == 0) { 263 /* Got an exact match. */ 264 prev = cmd; 265 break; 266 } else if (n > 0) { 267 /* Desired command is not registered. */ 268 break; 269 } 270 } 271 272 /* Next search begins at the previous match. */ 273 cmd = prev; 274 } 275 276 #ifdef INVARIANTS 277 /* Verify the lists are sorted correctly. */ 278 const char *a, *b; 279 280 for (i = 0; i < nitems(command_list) - 1; i++) { 281 a = command_list[i].name; 282 b = command_list[i + 1].name; 283 if (strcmp(a, b) > 0) 284 panic("%s: command_list[] not alphabetical: %s,%s", 285 __func__, a, b); 286 } 287 for (i = 0; i < nitems(show_command_list) - 1; i++) { 288 a = show_command_list[i].name; 289 b = show_command_list[i + 1].name; 290 if (strcmp(a, b) > 0) 291 panic("%s: show_command_list[] not alphabetical: %s,%s", 292 __func__, a, b); 293 } 294 #endif 295 } 296 297 static int 298 mac_ddb_command_register(struct db_command_table *table, 299 struct db_command *cmd) 300 { 301 int i, n; 302 303 if ((cmd->flag & DB_CMD_MEMSAFE) != 0) 304 return (0); 305 306 /* For other commands, search the allow-lists. */ 307 if (table == &db_show_table) { 308 for (i = 0; i < nitems(show_command_list); i++) { 309 n = command_match(cmd, show_command_list[i]); 310 if (n == 0) 311 /* Got an exact match. */ 312 return (0); 313 else if (n > 0) 314 /* Command is not in the policy list. */ 315 break; 316 } 317 } else if (table == &db_cmd_table) { 318 for (i = 0; i < nitems(command_list); i++) { 319 n = command_match(cmd, command_list[i]); 320 if (n == 0) 321 /* Got an exact match. */ 322 return (0); 323 else if (n > 0) 324 /* Command is not in the policy list. */ 325 break; 326 } 327 } 328 329 /* The command will not be registered. */ 330 return (EACCES); 331 } 332 333 static int 334 mac_ddb_command_exec(struct db_command *cmd, db_expr_t addr, 335 bool have_addr, db_expr_t count, char *modif) 336 { 337 db_validation_fn_t *vfn = cmd->mac_priv; 338 339 /* Validate the command and args based on policy. */ 340 if ((cmd->flag & DB_CMD_VALIDATE) != 0) { 341 MPASS(vfn != NULL); 342 if (vfn(addr, have_addr, count, modif) == 0) 343 return (0); 344 } else if ((cmd->flag & DB_CMD_MEMSAFE) != 0) 345 return (0); 346 347 return (EACCES); 348 } 349 350 static int 351 mac_ddb_check_backend(struct kdb_dbbe *be) 352 { 353 354 /* Only allow DDB backend to execute. */ 355 if (strcmp(be->dbbe_name, "ddb") == 0) 356 return (0); 357 358 return (EACCES); 359 } 360 361 /* 362 * Register functions with MAC Framework policy entry points. 363 */ 364 static struct mac_policy_ops mac_ddb_ops = 365 { 366 .mpo_init = mac_ddb_init, 367 368 .mpo_ddb_command_register = mac_ddb_command_register, 369 .mpo_ddb_command_exec = mac_ddb_command_exec, 370 371 .mpo_kdb_check_backend = mac_ddb_check_backend, 372 }; 373 MAC_POLICY_SET(&mac_ddb_ops, mac_ddb, "MAC/DDB", 0, NULL); 374