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 static db_validation_fn_t db_show_vnet_valid; 81 82 struct cmd_list_item { 83 const char *name; 84 db_validation_fn_t *validate_fn; 85 }; 86 87 /* List of top-level ddb(4) commands which are allowed by this policy. */ 88 static const struct cmd_list_item command_list[] = { 89 { "thread", db_thread_valid }, 90 }; 91 92 /* List of ddb(4) 'show' commands which are allowed by this policy. */ 93 static const struct cmd_list_item show_command_list[] = { 94 { "ffs", db_show_ffs_valid }, 95 { "prison", db_show_prison_valid }, 96 { "proc", db_show_proc_valid }, 97 { "rman", db_show_rman_valid }, 98 { "thread", db_thread_valid }, 99 { "vnet", db_show_vnet_valid }, 100 }; 101 102 static int 103 db_thread_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 104 { 105 struct thread *thr; 106 lwpid_t tid; 107 108 /* Default will show the current proc. */ 109 if (!have_addr) 110 return (0); 111 112 /* Validate the provided addr OR tid against the thread list. */ 113 tid = db_hex2dec(addr); 114 for (thr = kdb_thr_first(); thr != NULL; thr = kdb_thr_next(thr)) { 115 if ((void *)thr == (void *)addr || tid == thr->td_tid) 116 return (0); 117 } 118 119 return (EACCES); 120 } 121 122 static int 123 db_show_ffs_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 124 { 125 struct mount *mp; 126 127 /* No addr will show all mounts. */ 128 if (!have_addr) 129 return (0); 130 131 TAILQ_FOREACH(mp, &mountlist, mnt_list) 132 if ((void *)mp == (void *)addr) 133 return (0); 134 135 return (EACCES); 136 } 137 138 static int 139 db_show_prison_valid(db_expr_t addr, bool have_addr, db_expr_t count, 140 char *modif) 141 { 142 struct prison *pr; 143 int pr_id; 144 145 if (!have_addr || addr == 0) 146 return (0); 147 148 /* prison can match by pointer address or ID. */ 149 pr_id = (int)addr; 150 TAILQ_FOREACH(pr, &allprison, pr_list) 151 if (pr->pr_id == pr_id || (void *)pr == (void *)addr) 152 return (0); 153 154 return (EACCES); 155 } 156 157 static int 158 db_show_proc_valid(db_expr_t addr, bool have_addr, db_expr_t count, 159 char *modif) 160 { 161 struct proc *p; 162 int i; 163 164 /* Default will show the current proc. */ 165 if (!have_addr) 166 return (0); 167 168 for (i = 0; i <= pidhash; i++) { 169 LIST_FOREACH(p, &pidhashtbl[i], p_hash) { 170 if ((void *)p == (void *)addr) 171 return (0); 172 } 173 } 174 175 return (EACCES); 176 } 177 178 static int 179 db_show_rman_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 180 { 181 struct rman *rm; 182 183 TAILQ_FOREACH(rm, &rman_head, rm_link) { 184 if ((void *)rm == (void *)rm) 185 return (0); 186 } 187 188 return (EACCES); 189 } 190 191 static int 192 db_show_vnet_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 193 { 194 VNET_ITERATOR_DECL(vnet); 195 196 if (!have_addr) 197 return (0); 198 199 VNET_FOREACH(vnet) { 200 if ((void *)vnet == (void *)addr) 201 return (0); 202 } 203 204 return (EACCES); 205 } 206 207 static int 208 command_match(struct db_command *cmd, struct cmd_list_item item) 209 { 210 db_validation_fn_t *vfn; 211 int n; 212 213 n = strcmp(cmd->name, item.name); 214 if (n != 0) 215 return (n); 216 217 /* Got an exact match. Update the command struct */ 218 vfn = item.validate_fn; 219 if (vfn != NULL) { 220 cmd->flag |= DB_CMD_VALIDATE; 221 cmd->mac_priv = vfn; 222 } 223 return (0); 224 } 225 226 static void 227 mac_ddb_init(struct mac_policy_conf *conf) 228 { 229 struct db_command *cmd, *prev; 230 int i, n; 231 232 /* The command lists are sorted lexographically, as are our arrays. */ 233 234 /* Register basic commands. */ 235 for (i = 0, cmd = prev = NULL; i < nitems(command_list); i++) { 236 LIST_FOREACH_FROM(cmd, &db_cmd_table, next) { 237 n = command_match(cmd, command_list[i]); 238 if (n == 0) { 239 /* Got an exact match. */ 240 prev = cmd; 241 break; 242 } else if (n > 0) { 243 /* Desired command is not registered. */ 244 break; 245 } 246 } 247 248 /* Next search begins at the previous match. */ 249 cmd = prev; 250 } 251 252 /* Register 'show' commands which require validation. */ 253 for (i = 0, cmd = prev = NULL; i < nitems(show_command_list); i++) { 254 LIST_FOREACH_FROM(cmd, &db_show_table, next) { 255 n = command_match(cmd, show_command_list[i]); 256 if (n == 0) { 257 /* Got an exact match. */ 258 prev = cmd; 259 break; 260 } else if (n > 0) { 261 /* Desired command is not registered. */ 262 break; 263 } 264 } 265 266 /* Next search begins at the previous match. */ 267 cmd = prev; 268 } 269 270 #ifdef INVARIANTS 271 /* Verify the lists are sorted correctly. */ 272 const char *a, *b; 273 274 for (i = 0; i < nitems(command_list) - 1; i++) { 275 a = command_list[i].name; 276 b = command_list[i + 1].name; 277 if (strcmp(a, b) > 0) 278 panic("%s: command_list[] not alphabetical: %s,%s", 279 __func__, a, b); 280 } 281 for (i = 0; i < nitems(show_command_list) - 1; i++) { 282 a = show_command_list[i].name; 283 b = show_command_list[i + 1].name; 284 if (strcmp(a, b) > 0) 285 panic("%s: show_command_list[] not alphabetical: %s,%s", 286 __func__, a, b); 287 } 288 #endif 289 } 290 291 static int 292 mac_ddb_command_register(struct db_command_table *table, 293 struct db_command *cmd) 294 { 295 int i, n; 296 297 if ((cmd->flag & DB_CMD_MEMSAFE) != 0) 298 return (0); 299 300 /* For other commands, search the allow-lists. */ 301 if (table == &db_show_table) { 302 for (i = 0; i < nitems(show_command_list); i++) { 303 n = command_match(cmd, show_command_list[i]); 304 if (n == 0) 305 /* Got an exact match. */ 306 return (0); 307 else if (n > 0) 308 /* Command is not in the policy list. */ 309 break; 310 } 311 } else if (table == &db_cmd_table) { 312 for (i = 0; i < nitems(command_list); i++) { 313 n = command_match(cmd, 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 } 322 323 /* The command will not be registered. */ 324 return (EACCES); 325 } 326 327 static int 328 mac_ddb_command_exec(struct db_command *cmd, db_expr_t addr, 329 bool have_addr, db_expr_t count, char *modif) 330 { 331 db_validation_fn_t *vfn = cmd->mac_priv; 332 333 /* Validate the command and args based on policy. */ 334 if ((cmd->flag & DB_CMD_VALIDATE) != 0) { 335 MPASS(vfn != NULL); 336 if (vfn(addr, have_addr, count, modif) == 0) 337 return (0); 338 } else if ((cmd->flag & DB_CMD_MEMSAFE) != 0) 339 return (0); 340 341 return (EACCES); 342 } 343 344 static int 345 mac_ddb_check_backend(struct kdb_dbbe *be) 346 { 347 348 /* Only allow DDB backend to execute. */ 349 if (strcmp(be->dbbe_name, "ddb") == 0) 350 return (0); 351 352 return (EACCES); 353 } 354 355 /* 356 * Register functions with MAC Framework policy entry points. 357 */ 358 static struct mac_policy_ops mac_ddb_ops = 359 { 360 .mpo_init = mac_ddb_init, 361 362 .mpo_ddb_command_register = mac_ddb_command_register, 363 .mpo_ddb_command_exec = mac_ddb_command_exec, 364 365 .mpo_kdb_check_backend = mac_ddb_check_backend, 366 }; 367 MAC_POLICY_SET(&mac_ddb_ops, mac_ddb, "MAC/DDB", 0, NULL); 368