The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/security/mac_ddb/mac_ddb.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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);

Cache object: 5d27e855edb5586bbf31cfde660999e5


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.