mac_ddb.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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. #include <sys/param.h>
  31. #include <sys/jail.h>
  32. #include <sys/kdb.h>
  33. #include <sys/module.h>
  34. #include <sys/mount.h>
  35. #include <sys/proc.h>
  36. #include <sys/queue.h>
  37. #include <sys/rman.h>
  38. #include <sys/sysctl.h>
  39. #include <net/vnet.h>
  40. #include <ddb/ddb.h>
  41. #include <ddb/db_command.h>
  42. #include <security/mac/mac_policy.h>
  43. /*
  44. * This module provides a limited interface to the ddb(4) kernel debugger. The
  45. * intent is to allow execution of useful debugging commands while disallowing
  46. * the execution of commands which may be used to inspect/modify arbitrary
  47. * system memory.
  48. *
  49. * Commands which are deterministic in their output or effect and that have
  50. * been flagged with DB_CMD_MEMSAFE in their definition will be allowed.
  51. *
  52. * Other commands are valid within this context so long as there is some
  53. * constraint placed on their input arguments. This applies to most 'show'
  54. * commands which accept an arbitrary address. If the provided address can be
  55. * validated as a real instance of the object (e.g. the 'show proc' address
  56. * points to a real struct proc in the process list), then the command may be
  57. * executed. This module defines several validation functions which are used to
  58. * conditionally allow or block the execution of some commands. For these
  59. * commands we define and apply the DB_CMD_VALIDATE flag.
  60. *
  61. * Any other commands not flagged with DM_CMD_MEMSAFE or DB_CMD_VALIDATE are
  62. * considered unsafe for execution.
  63. */
  64. #define DB_CMD_VALIDATE DB_MAC1
  65. typedef int db_validation_fn_t(db_expr_t addr, bool have_addr, db_expr_t count,
  66. char *modif);
  67. static db_validation_fn_t db_thread_valid;
  68. static db_validation_fn_t db_show_ffs_valid;
  69. static db_validation_fn_t db_show_prison_valid;
  70. static db_validation_fn_t db_show_proc_valid;
  71. static db_validation_fn_t db_show_rman_valid;
  72. #ifdef VIMAGE
  73. static db_validation_fn_t db_show_vnet_valid;
  74. #endif
  75. struct cmd_list_item {
  76. const char *name;
  77. db_validation_fn_t *validate_fn;
  78. };
  79. /* List of top-level ddb(4) commands which are allowed by this policy. */
  80. static const struct cmd_list_item command_list[] = {
  81. { "thread", db_thread_valid },
  82. };
  83. /* List of ddb(4) 'show' commands which are allowed by this policy. */
  84. static const struct cmd_list_item show_command_list[] = {
  85. { "ffs", db_show_ffs_valid },
  86. { "prison", db_show_prison_valid },
  87. { "proc", db_show_proc_valid },
  88. { "rman", db_show_rman_valid },
  89. { "thread", db_thread_valid },
  90. #ifdef VIMAGE
  91. { "vnet", db_show_vnet_valid },
  92. #endif
  93. };
  94. static int
  95. db_thread_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
  96. {
  97. struct thread *thr;
  98. lwpid_t tid;
  99. /* Default will show the current proc. */
  100. if (!have_addr)
  101. return (0);
  102. /* Validate the provided addr OR tid against the thread list. */
  103. tid = db_hex2dec(addr);
  104. for (thr = kdb_thr_first(); thr != NULL; thr = kdb_thr_next(thr)) {
  105. if ((void *)thr == (void *)addr || tid == thr->td_tid)
  106. return (0);
  107. }
  108. return (EACCES);
  109. }
  110. static int
  111. db_show_ffs_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
  112. {
  113. struct mount *mp;
  114. /* No addr will show all mounts. */
  115. if (!have_addr)
  116. return (0);
  117. TAILQ_FOREACH(mp, &mountlist, mnt_list)
  118. if ((void *)mp == (void *)addr)
  119. return (0);
  120. return (EACCES);
  121. }
  122. static int
  123. db_show_prison_valid(db_expr_t addr, bool have_addr, db_expr_t count,
  124. char *modif)
  125. {
  126. struct prison *pr;
  127. int pr_id;
  128. if (!have_addr || addr == 0)
  129. return (0);
  130. /* prison can match by pointer address or ID. */
  131. pr_id = (int)addr;
  132. TAILQ_FOREACH(pr, &allprison, pr_list)
  133. if (pr->pr_id == pr_id || (void *)pr == (void *)addr)
  134. return (0);
  135. return (EACCES);
  136. }
  137. static int
  138. db_show_proc_valid(db_expr_t addr, bool have_addr, db_expr_t count,
  139. char *modif)
  140. {
  141. struct proc *p;
  142. int i;
  143. /* Default will show the current proc. */
  144. if (!have_addr)
  145. return (0);
  146. for (i = 0; i <= pidhash; i++) {
  147. LIST_FOREACH(p, &pidhashtbl[i], p_hash) {
  148. if ((void *)p == (void *)addr)
  149. return (0);
  150. }
  151. }
  152. return (EACCES);
  153. }
  154. static int
  155. db_show_rman_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
  156. {
  157. struct rman *rm;
  158. TAILQ_FOREACH(rm, &rman_head, rm_link) {
  159. if ((void *)rm == (void *)addr)
  160. return (0);
  161. }
  162. return (EACCES);
  163. }
  164. #ifdef VIMAGE
  165. static int
  166. db_show_vnet_valid(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
  167. {
  168. VNET_ITERATOR_DECL(vnet);
  169. if (!have_addr)
  170. return (0);
  171. VNET_FOREACH(vnet) {
  172. if ((void *)vnet == (void *)addr)
  173. return (0);
  174. }
  175. return (EACCES);
  176. }
  177. #endif
  178. static int
  179. command_match(struct db_command *cmd, struct cmd_list_item item)
  180. {
  181. db_validation_fn_t *vfn;
  182. int n;
  183. n = strcmp(cmd->name, item.name);
  184. if (n != 0)
  185. return (n);
  186. /* Got an exact match. Update the command struct */
  187. vfn = item.validate_fn;
  188. if (vfn != NULL) {
  189. cmd->flag |= DB_CMD_VALIDATE;
  190. cmd->mac_priv = vfn;
  191. }
  192. return (0);
  193. }
  194. static void
  195. mac_ddb_init(struct mac_policy_conf *conf)
  196. {
  197. struct db_command *cmd, *prev;
  198. int i, n;
  199. /* The command lists are sorted lexographically, as are our arrays. */
  200. /* Register basic commands. */
  201. for (i = 0, cmd = prev = NULL; i < nitems(command_list); i++) {
  202. LIST_FOREACH_FROM(cmd, &db_cmd_table, next) {
  203. n = command_match(cmd, command_list[i]);
  204. if (n == 0) {
  205. /* Got an exact match. */
  206. prev = cmd;
  207. break;
  208. } else if (n > 0) {
  209. /* Desired command is not registered. */
  210. break;
  211. }
  212. }
  213. /* Next search begins at the previous match. */
  214. cmd = prev;
  215. }
  216. /* Register 'show' commands which require validation. */
  217. for (i = 0, cmd = prev = NULL; i < nitems(show_command_list); i++) {
  218. LIST_FOREACH_FROM(cmd, &db_show_table, next) {
  219. n = command_match(cmd, show_command_list[i]);
  220. if (n == 0) {
  221. /* Got an exact match. */
  222. prev = cmd;
  223. break;
  224. } else if (n > 0) {
  225. /* Desired command is not registered. */
  226. break;
  227. }
  228. }
  229. /* Next search begins at the previous match. */
  230. cmd = prev;
  231. }
  232. #ifdef INVARIANTS
  233. /* Verify the lists are sorted correctly. */
  234. const char *a, *b;
  235. for (i = 0; i < nitems(command_list) - 1; i++) {
  236. a = command_list[i].name;
  237. b = command_list[i + 1].name;
  238. if (strcmp(a, b) > 0)
  239. panic("%s: command_list[] not alphabetical: %s,%s",
  240. __func__, a, b);
  241. }
  242. for (i = 0; i < nitems(show_command_list) - 1; i++) {
  243. a = show_command_list[i].name;
  244. b = show_command_list[i + 1].name;
  245. if (strcmp(a, b) > 0)
  246. panic("%s: show_command_list[] not alphabetical: %s,%s",
  247. __func__, a, b);
  248. }
  249. #endif
  250. }
  251. static int
  252. mac_ddb_command_register(struct db_command_table *table,
  253. struct db_command *cmd)
  254. {
  255. int i, n;
  256. if ((cmd->flag & DB_CMD_MEMSAFE) != 0)
  257. return (0);
  258. /* For other commands, search the allow-lists. */
  259. if (table == &db_show_table) {
  260. for (i = 0; i < nitems(show_command_list); i++) {
  261. n = command_match(cmd, show_command_list[i]);
  262. if (n == 0)
  263. /* Got an exact match. */
  264. return (0);
  265. else if (n > 0)
  266. /* Command is not in the policy list. */
  267. break;
  268. }
  269. } else if (table == &db_cmd_table) {
  270. for (i = 0; i < nitems(command_list); i++) {
  271. n = command_match(cmd, command_list[i]);
  272. if (n == 0)
  273. /* Got an exact match. */
  274. return (0);
  275. else if (n > 0)
  276. /* Command is not in the policy list. */
  277. break;
  278. }
  279. }
  280. /* The command will not be registered. */
  281. return (EACCES);
  282. }
  283. static int
  284. mac_ddb_command_exec(struct db_command *cmd, db_expr_t addr,
  285. bool have_addr, db_expr_t count, char *modif)
  286. {
  287. db_validation_fn_t *vfn = cmd->mac_priv;
  288. /* Validate the command and args based on policy. */
  289. if ((cmd->flag & DB_CMD_VALIDATE) != 0) {
  290. MPASS(vfn != NULL);
  291. if (vfn(addr, have_addr, count, modif) == 0)
  292. return (0);
  293. } else if ((cmd->flag & DB_CMD_MEMSAFE) != 0)
  294. return (0);
  295. return (EACCES);
  296. }
  297. static int
  298. mac_ddb_check_backend(struct kdb_dbbe *be)
  299. {
  300. /* Only allow DDB backend to execute. */
  301. if (strcmp(be->dbbe_name, "ddb") == 0)
  302. return (0);
  303. return (EACCES);
  304. }
  305. /*
  306. * Register functions with MAC Framework policy entry points.
  307. */
  308. static struct mac_policy_ops mac_ddb_ops =
  309. {
  310. .mpo_init = mac_ddb_init,
  311. .mpo_ddb_command_register = mac_ddb_command_register,
  312. .mpo_ddb_command_exec = mac_ddb_command_exec,
  313. .mpo_kdb_check_backend = mac_ddb_check_backend,
  314. };
  315. MAC_POLICY_SET(&mac_ddb_ops, mac_ddb, "MAC/DDB", 0, NULL);