2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2021-2022 Klara Systems
6 * This software was developed by Mitchell Horne <mhorne@FreeBSD.org>
7 * under sponsorship from Juniper Networks and Klara Systems.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
31 #include <sys/param.h>
34 #include <sys/module.h>
35 #include <sys/mount.h>
37 #include <sys/queue.h>
39 #include <sys/sysctl.h>
44 #include <ddb/db_command.h>
46 #include <security/mac/mac_policy.h>
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
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.
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.
66 * Any other commands not flagged with DM_CMD_MEMSAFE or DB_CMD_VALIDATE are
67 * considered unsafe for execution.
70 #define DB_CMD_VALIDATE DB_MAC1
72 typedef int db_validation_fn_t(db_expr_t addr, bool have_addr, db_expr_t count,
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;
81 static db_validation_fn_t db_show_vnet_valid;
84 struct cmd_list_item {
86 db_validation_fn_t *validate_fn;
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 },
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 },
102 { "vnet", db_show_vnet_valid },
107 db_thread_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
112 /* Default will show the current proc. */
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)
127 db_show_ffs_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
131 /* No addr will show all mounts. */
135 TAILQ_FOREACH(mp, &mountlist, mnt_list)
136 if ((void *)mp == (void *)addr)
143 db_show_prison_valid(db_expr_t addr, bool have_addr, db_expr_t count,
149 if (!have_addr || addr == 0)
152 /* prison can match by pointer address or ID. */
154 TAILQ_FOREACH(pr, &allprison, pr_list)
155 if (pr->pr_id == pr_id || (void *)pr == (void *)addr)
162 db_show_proc_valid(db_expr_t addr, bool have_addr, db_expr_t count,
168 /* Default will show the current proc. */
172 for (i = 0; i <= pidhash; i++) {
173 LIST_FOREACH(p, &pidhashtbl[i], p_hash) {
174 if ((void *)p == (void *)addr)
183 db_show_rman_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
187 TAILQ_FOREACH(rm, &rman_head, rm_link) {
188 if ((void *)rm == (void *)addr)
197 db_show_vnet_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
199 VNET_ITERATOR_DECL(vnet);
205 if ((void *)vnet == (void *)addr)
214 command_match(struct db_command *cmd, struct cmd_list_item item)
216 db_validation_fn_t *vfn;
219 n = strcmp(cmd->name, item.name);
223 /* Got an exact match. Update the command struct */
224 vfn = item.validate_fn;
226 cmd->flag |= DB_CMD_VALIDATE;
233 mac_ddb_init(struct mac_policy_conf *conf)
235 struct db_command *cmd, *prev;
238 /* The command lists are sorted lexographically, as are our arrays. */
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]);
245 /* Got an exact match. */
249 /* Desired command is not registered. */
254 /* Next search begins at the previous match. */
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]);
263 /* Got an exact match. */
267 /* Desired command is not registered. */
272 /* Next search begins at the previous match. */
277 /* Verify the lists are sorted correctly. */
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",
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",
298 mac_ddb_command_register(struct db_command_table *table,
299 struct db_command *cmd)
303 if ((cmd->flag & DB_CMD_MEMSAFE) != 0)
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]);
311 /* Got an exact match. */
314 /* Command is not in the policy list. */
317 } else if (table == &db_cmd_table) {
318 for (i = 0; i < nitems(command_list); i++) {
319 n = command_match(cmd, command_list[i]);
321 /* Got an exact match. */
324 /* Command is not in the policy list. */
329 /* The command will not be registered. */
334 mac_ddb_command_exec(struct db_command *cmd, db_expr_t addr,
335 bool have_addr, db_expr_t count, char *modif)
337 db_validation_fn_t *vfn = cmd->mac_priv;
339 /* Validate the command and args based on policy. */
340 if ((cmd->flag & DB_CMD_VALIDATE) != 0) {
342 if (vfn(addr, have_addr, count, modif) == 0)
344 } else if ((cmd->flag & DB_CMD_MEMSAFE) != 0)
351 mac_ddb_check_backend(struct kdb_dbbe *be)
354 /* Only allow DDB backend to execute. */
355 if (strcmp(be->dbbe_name, "ddb") == 0)
362 * Register functions with MAC Framework policy entry points.
364 static struct mac_policy_ops mac_ddb_ops =
366 .mpo_init = mac_ddb_init,
368 .mpo_ddb_command_register = mac_ddb_command_register,
369 .mpo_ddb_command_exec = mac_ddb_command_exec,
371 .mpo_kdb_check_backend = mac_ddb_check_backend,
373 MAC_POLICY_SET(&mac_ddb_ops, mac_ddb, "MAC/DDB", 0, NULL);