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 *)rm) 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 #ifdef VIMAGE 200 VNET_ITERATOR_DECL(vnet); 201 202 if (!have_addr) 203 return (0); 204 205 VNET_FOREACH(vnet) { 206 if ((void *)vnet == (void *)addr) 207 return (0); 208 } 209 210 return (EACCES); 211 #else 212 return (EOPNOTSUPP); 213 #endif 214 } 215 #endif 216 217 static int 218 command_match(struct db_command *cmd, struct cmd_list_item item) 219 { 220 db_validation_fn_t *vfn; 221 int n; 222 223 n = strcmp(cmd->name, item.name); 224 if (n != 0) 225 return (n); 226 227 /* Got an exact match. Update the command struct */ 228 vfn = item.validate_fn; 229 if (vfn != NULL) { 230 cmd->flag |= DB_CMD_VALIDATE; 231 cmd->mac_priv = vfn; 232 } 233 return (0); 234 } 235 236 static void 237 mac_ddb_init(struct mac_policy_conf *conf) 238 { 239 struct db_command *cmd, *prev; 240 int i, n; 241 242 /* The command lists are sorted lexographically, as are our arrays. */ 243 244 /* Register basic commands. */ 245 for (i = 0, cmd = prev = NULL; i < nitems(command_list); i++) { 246 LIST_FOREACH_FROM(cmd, &db_cmd_table, next) { 247 n = command_match(cmd, command_list[i]); 248 if (n == 0) { 249 /* Got an exact match. */ 250 prev = cmd; 251 break; 252 } else if (n > 0) { 253 /* Desired command is not registered. */ 254 break; 255 } 256 } 257 258 /* Next search begins at the previous match. */ 259 cmd = prev; 260 } 261 262 /* Register 'show' commands which require validation. */ 263 for (i = 0, cmd = prev = NULL; i < nitems(show_command_list); i++) { 264 LIST_FOREACH_FROM(cmd, &db_show_table, next) { 265 n = command_match(cmd, show_command_list[i]); 266 if (n == 0) { 267 /* Got an exact match. */ 268 prev = cmd; 269 break; 270 } else if (n > 0) { 271 /* Desired command is not registered. */ 272 break; 273 } 274 } 275 276 /* Next search begins at the previous match. */ 277 cmd = prev; 278 } 279 280 #ifdef INVARIANTS 281 /* Verify the lists are sorted correctly. */ 282 const char *a, *b; 283 284 for (i = 0; i < nitems(command_list) - 1; i++) { 285 a = command_list[i].name; 286 b = command_list[i + 1].name; 287 if (strcmp(a, b) > 0) 288 panic("%s: command_list[] not alphabetical: %s,%s", 289 __func__, a, b); 290 } 291 for (i = 0; i < nitems(show_command_list) - 1; i++) { 292 a = show_command_list[i].name; 293 b = show_command_list[i + 1].name; 294 if (strcmp(a, b) > 0) 295 panic("%s: show_command_list[] not alphabetical: %s,%s", 296 __func__, a, b); 297 } 298 #endif 299 } 300 301 static int 302 mac_ddb_command_register(struct db_command_table *table, 303 struct db_command *cmd) 304 { 305 int i, n; 306 307 if ((cmd->flag & DB_CMD_MEMSAFE) != 0) 308 return (0); 309 310 /* For other commands, search the allow-lists. */ 311 if (table == &db_show_table) { 312 for (i = 0; i < nitems(show_command_list); i++) { 313 n = command_match(cmd, show_command_list[i]); 314 if (n == 0) 315 /* Got an exact match. */ 316 return (0); 317 else if (n > 0) 318 /* Command is not in the policy list. */ 319 break; 320 } 321 } else if (table == &db_cmd_table) { 322 for (i = 0; i < nitems(command_list); i++) { 323 n = command_match(cmd, command_list[i]); 324 if (n == 0) 325 /* Got an exact match. */ 326 return (0); 327 else if (n > 0) 328 /* Command is not in the policy list. */ 329 break; 330 } 331 } 332 333 /* The command will not be registered. */ 334 return (EACCES); 335 } 336 337 static int 338 mac_ddb_command_exec(struct db_command *cmd, db_expr_t addr, 339 bool have_addr, db_expr_t count, char *modif) 340 { 341 db_validation_fn_t *vfn = cmd->mac_priv; 342 343 /* Validate the command and args based on policy. */ 344 if ((cmd->flag & DB_CMD_VALIDATE) != 0) { 345 MPASS(vfn != NULL); 346 if (vfn(addr, have_addr, count, modif) == 0) 347 return (0); 348 } else if ((cmd->flag & DB_CMD_MEMSAFE) != 0) 349 return (0); 350 351 return (EACCES); 352 } 353 354 static int 355 mac_ddb_check_backend(struct kdb_dbbe *be) 356 { 357 358 /* Only allow DDB backend to execute. */ 359 if (strcmp(be->dbbe_name, "ddb") == 0) 360 return (0); 361 362 return (EACCES); 363 } 364 365 /* 366 * Register functions with MAC Framework policy entry points. 367 */ 368 static struct mac_policy_ops mac_ddb_ops = 369 { 370 .mpo_init = mac_ddb_init, 371 372 .mpo_ddb_command_register = mac_ddb_command_register, 373 .mpo_ddb_command_exec = mac_ddb_command_exec, 374 375 .mpo_kdb_check_backend = mac_ddb_check_backend, 376 }; 377 MAC_POLICY_SET(&mac_ddb_ops, mac_ddb, "MAC/DDB", 0, NULL); 378