]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/security/mac_ddb/mac_ddb.c
contrib/tzdata: import tzdata 2024a
[FreeBSD/FreeBSD.git] / sys / security / mac_ddb / mac_ddb.c
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);