audit.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. /*
  3. * Landlock audit helpers
  4. *
  5. * Copyright © 2024-2025 Microsoft Corporation
  6. */
  7. #define _GNU_SOURCE
  8. #include <errno.h>
  9. #include <linux/audit.h>
  10. #include <linux/limits.h>
  11. #include <linux/netlink.h>
  12. #include <regex.h>
  13. #include <stdbool.h>
  14. #include <stdint.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <sys/socket.h>
  19. #include <sys/time.h>
  20. #include <unistd.h>
  21. #include "kselftest.h"
  22. #ifndef ARRAY_SIZE
  23. #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
  24. #endif
  25. #define REGEX_LANDLOCK_PREFIX "^audit([0-9.:]\\+): domain=\\([0-9a-f]\\+\\)"
  26. struct audit_filter {
  27. __u32 record_type;
  28. size_t exe_len;
  29. char exe[PATH_MAX];
  30. };
  31. struct audit_message {
  32. struct nlmsghdr header;
  33. union {
  34. struct audit_status status;
  35. struct audit_features features;
  36. struct audit_rule_data rule;
  37. struct nlmsgerr err;
  38. char data[PATH_MAX + 200];
  39. };
  40. };
  41. static const struct timeval audit_tv_dom_drop = {
  42. /*
  43. * Because domain deallocation is tied to asynchronous credential
  44. * freeing, receiving such event may take some time. In practice,
  45. * on a small VM, it should not exceed 100k usec, but let's wait up
  46. * to 1 second to be safe.
  47. */
  48. .tv_sec = 1,
  49. };
  50. static const struct timeval audit_tv_default = {
  51. .tv_usec = 1,
  52. };
  53. static int audit_send(const int fd, const struct audit_message *const msg)
  54. {
  55. struct sockaddr_nl addr = {
  56. .nl_family = AF_NETLINK,
  57. };
  58. int ret;
  59. do {
  60. ret = sendto(fd, msg, msg->header.nlmsg_len, 0,
  61. (struct sockaddr *)&addr, sizeof(addr));
  62. } while (ret < 0 && errno == EINTR);
  63. if (ret < 0)
  64. return -errno;
  65. if (ret != msg->header.nlmsg_len)
  66. return -E2BIG;
  67. return 0;
  68. }
  69. static int audit_recv(const int fd, struct audit_message *msg)
  70. {
  71. struct sockaddr_nl addr;
  72. socklen_t addrlen = sizeof(addr);
  73. struct audit_message msg_tmp;
  74. int err;
  75. if (!msg)
  76. msg = &msg_tmp;
  77. do {
  78. err = recvfrom(fd, msg, sizeof(*msg), 0,
  79. (struct sockaddr *)&addr, &addrlen);
  80. } while (err < 0 && errno == EINTR);
  81. if (err < 0)
  82. return -errno;
  83. if (addrlen != sizeof(addr) || addr.nl_pid != 0)
  84. return -EINVAL;
  85. /* Checks Netlink error or end of messages. */
  86. if (msg->header.nlmsg_type == NLMSG_ERROR)
  87. return msg->err.error;
  88. return 0;
  89. }
  90. static int audit_request(const int fd,
  91. const struct audit_message *const request,
  92. struct audit_message *reply)
  93. {
  94. struct audit_message msg_tmp;
  95. bool first_reply = true;
  96. int err;
  97. err = audit_send(fd, request);
  98. if (err)
  99. return err;
  100. if (!reply)
  101. reply = &msg_tmp;
  102. do {
  103. if (first_reply)
  104. first_reply = false;
  105. else
  106. reply = &msg_tmp;
  107. err = audit_recv(fd, reply);
  108. if (err)
  109. return err;
  110. } while (reply->header.nlmsg_type != NLMSG_ERROR &&
  111. reply->err.msg.nlmsg_type != request->header.nlmsg_type);
  112. return reply->err.error;
  113. }
  114. static int audit_filter_exe(const int audit_fd,
  115. const struct audit_filter *const filter,
  116. const __u16 type)
  117. {
  118. struct audit_message msg = {
  119. .header = {
  120. .nlmsg_len = NLMSG_SPACE(sizeof(msg.rule)) +
  121. NLMSG_ALIGN(filter->exe_len),
  122. .nlmsg_type = type,
  123. .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
  124. },
  125. .rule = {
  126. .flags = AUDIT_FILTER_EXCLUDE,
  127. .action = AUDIT_NEVER,
  128. .field_count = 1,
  129. .fields[0] = filter->record_type,
  130. .fieldflags[0] = AUDIT_NOT_EQUAL,
  131. .values[0] = filter->exe_len,
  132. .buflen = filter->exe_len,
  133. }
  134. };
  135. if (filter->record_type != AUDIT_EXE)
  136. return -EINVAL;
  137. memcpy(msg.rule.buf, filter->exe, filter->exe_len);
  138. return audit_request(audit_fd, &msg, NULL);
  139. }
  140. static int audit_filter_drop(const int audit_fd, const __u16 type)
  141. {
  142. struct audit_message msg = {
  143. .header = {
  144. .nlmsg_len = NLMSG_SPACE(sizeof(msg.rule)),
  145. .nlmsg_type = type,
  146. .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
  147. },
  148. .rule = {
  149. .flags = AUDIT_FILTER_EXCLUDE,
  150. .action = AUDIT_NEVER,
  151. .field_count = 1,
  152. .fields[0] = AUDIT_MSGTYPE,
  153. .fieldflags[0] = AUDIT_NOT_EQUAL,
  154. .values[0] = AUDIT_LANDLOCK_DOMAIN,
  155. }
  156. };
  157. return audit_request(audit_fd, &msg, NULL);
  158. }
  159. static int audit_set_status(int fd, __u32 key, __u32 val)
  160. {
  161. const struct audit_message msg = {
  162. .header = {
  163. .nlmsg_len = NLMSG_SPACE(sizeof(msg.status)),
  164. .nlmsg_type = AUDIT_SET,
  165. .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
  166. },
  167. .status = {
  168. .mask = key,
  169. .enabled = key == AUDIT_STATUS_ENABLED ? val : 0,
  170. .pid = key == AUDIT_STATUS_PID ? val : 0,
  171. }
  172. };
  173. return audit_request(fd, &msg, NULL);
  174. }
  175. /* Returns a pointer to the last filled character of @dst, which is `\0`. */
  176. static __maybe_unused char *regex_escape(const char *const src, char *dst,
  177. size_t dst_size)
  178. {
  179. char *d = dst;
  180. for (const char *s = src; *s; s++) {
  181. switch (*s) {
  182. case '$':
  183. case '*':
  184. case '.':
  185. case '[':
  186. case '\\':
  187. case ']':
  188. case '^':
  189. if (d >= dst + dst_size - 2)
  190. return (char *)-ENOMEM;
  191. *d++ = '\\';
  192. *d++ = *s;
  193. break;
  194. default:
  195. if (d >= dst + dst_size - 1)
  196. return (char *)-ENOMEM;
  197. *d++ = *s;
  198. }
  199. }
  200. if (d >= dst + dst_size - 1)
  201. return (char *)-ENOMEM;
  202. *d = '\0';
  203. return d;
  204. }
  205. /*
  206. * @domain_id: The domain ID extracted from the audit message (if the first part
  207. * of @pattern is REGEX_LANDLOCK_PREFIX). It is set to 0 if the domain ID is
  208. * not found.
  209. */
  210. static int audit_match_record(int audit_fd, const __u16 type,
  211. const char *const pattern, __u64 *domain_id)
  212. {
  213. struct audit_message msg;
  214. int ret, err = 0;
  215. bool matches_record = !type;
  216. regmatch_t matches[2];
  217. regex_t regex;
  218. ret = regcomp(&regex, pattern, 0);
  219. if (ret)
  220. return -EINVAL;
  221. do {
  222. memset(&msg, 0, sizeof(msg));
  223. err = audit_recv(audit_fd, &msg);
  224. if (err)
  225. goto out;
  226. if (msg.header.nlmsg_type == type)
  227. matches_record = true;
  228. } while (!matches_record);
  229. ret = regexec(&regex, msg.data, ARRAY_SIZE(matches), matches, 0);
  230. if (ret) {
  231. printf("DATA: %s\n", msg.data);
  232. printf("ERROR: no match for pattern: %s\n", pattern);
  233. err = -ENOENT;
  234. }
  235. if (domain_id) {
  236. *domain_id = 0;
  237. if (matches[1].rm_so != -1) {
  238. int match_len = matches[1].rm_eo - matches[1].rm_so;
  239. /* The maximal characters of a 2^64 hexadecimal number is 17. */
  240. char dom_id[18];
  241. if (match_len > 0 && match_len < sizeof(dom_id)) {
  242. memcpy(dom_id, msg.data + matches[1].rm_so,
  243. match_len);
  244. dom_id[match_len] = '\0';
  245. if (domain_id)
  246. *domain_id = strtoull(dom_id, NULL, 16);
  247. }
  248. }
  249. }
  250. out:
  251. regfree(&regex);
  252. return err;
  253. }
  254. static int __maybe_unused matches_log_domain_allocated(int audit_fd, pid_t pid,
  255. __u64 *domain_id)
  256. {
  257. static const char log_template[] = REGEX_LANDLOCK_PREFIX
  258. " status=allocated mode=enforcing pid=%d uid=[0-9]\\+"
  259. " exe=\"[^\"]\\+\" comm=\".*_test\"$";
  260. char log_match[sizeof(log_template) + 10];
  261. int log_match_len;
  262. log_match_len =
  263. snprintf(log_match, sizeof(log_match), log_template, pid);
  264. if (log_match_len > sizeof(log_match))
  265. return -E2BIG;
  266. return audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match,
  267. domain_id);
  268. }
  269. static int __maybe_unused matches_log_domain_deallocated(
  270. int audit_fd, unsigned int num_denials, __u64 *domain_id)
  271. {
  272. static const char log_template[] = REGEX_LANDLOCK_PREFIX
  273. " status=deallocated denials=%u$";
  274. char log_match[sizeof(log_template) + 10];
  275. int log_match_len;
  276. log_match_len = snprintf(log_match, sizeof(log_match), log_template,
  277. num_denials);
  278. if (log_match_len > sizeof(log_match))
  279. return -E2BIG;
  280. return audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match,
  281. domain_id);
  282. }
  283. struct audit_records {
  284. size_t access;
  285. size_t domain;
  286. };
  287. static int audit_count_records(int audit_fd, struct audit_records *records)
  288. {
  289. struct audit_message msg;
  290. int err;
  291. records->access = 0;
  292. records->domain = 0;
  293. do {
  294. memset(&msg, 0, sizeof(msg));
  295. err = audit_recv(audit_fd, &msg);
  296. if (err) {
  297. if (err == -EAGAIN)
  298. return 0;
  299. else
  300. return err;
  301. }
  302. switch (msg.header.nlmsg_type) {
  303. case AUDIT_LANDLOCK_ACCESS:
  304. records->access++;
  305. break;
  306. case AUDIT_LANDLOCK_DOMAIN:
  307. records->domain++;
  308. break;
  309. }
  310. } while (true);
  311. return 0;
  312. }
  313. static int audit_init(void)
  314. {
  315. int fd, err;
  316. fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
  317. if (fd < 0)
  318. return -errno;
  319. err = audit_set_status(fd, AUDIT_STATUS_ENABLED, 1);
  320. if (err)
  321. return err;
  322. err = audit_set_status(fd, AUDIT_STATUS_PID, getpid());
  323. if (err)
  324. return err;
  325. /* Sets a timeout for negative tests. */
  326. err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default,
  327. sizeof(audit_tv_default));
  328. if (err)
  329. return -errno;
  330. return fd;
  331. }
  332. static int audit_init_filter_exe(struct audit_filter *filter, const char *path)
  333. {
  334. char *absolute_path = NULL;
  335. /* It is assume that there is not already filtering rules. */
  336. filter->record_type = AUDIT_EXE;
  337. if (!path) {
  338. int ret = readlink("/proc/self/exe", filter->exe,
  339. sizeof(filter->exe) - 1);
  340. if (ret < 0)
  341. return -errno;
  342. filter->exe_len = ret;
  343. return 0;
  344. }
  345. absolute_path = realpath(path, NULL);
  346. if (!absolute_path)
  347. return -errno;
  348. /* No need for the terminating NULL byte. */
  349. filter->exe_len = strlen(absolute_path);
  350. if (filter->exe_len > sizeof(filter->exe))
  351. return -E2BIG;
  352. memcpy(filter->exe, absolute_path, filter->exe_len);
  353. free(absolute_path);
  354. return 0;
  355. }
  356. static int audit_cleanup(int audit_fd, struct audit_filter *filter)
  357. {
  358. struct audit_filter new_filter;
  359. if (audit_fd < 0 || !filter) {
  360. int err;
  361. /*
  362. * Simulates audit_init_with_exe_filter() when called from
  363. * FIXTURE_TEARDOWN_PARENT().
  364. */
  365. audit_fd = audit_init();
  366. if (audit_fd < 0)
  367. return audit_fd;
  368. filter = &new_filter;
  369. err = audit_init_filter_exe(filter, NULL);
  370. if (err)
  371. return err;
  372. }
  373. /* Filters might not be in place. */
  374. audit_filter_exe(audit_fd, filter, AUDIT_DEL_RULE);
  375. audit_filter_drop(audit_fd, AUDIT_DEL_RULE);
  376. /*
  377. * Because audit_cleanup() might not be called by the test auditd
  378. * process, it might not be possible to explicitly set it. Anyway,
  379. * AUDIT_STATUS_ENABLED will implicitly be set to 0 when the auditd
  380. * process will exit.
  381. */
  382. return close(audit_fd);
  383. }
  384. static int audit_init_with_exe_filter(struct audit_filter *filter)
  385. {
  386. int fd, err;
  387. fd = audit_init();
  388. if (fd < 0)
  389. return fd;
  390. err = audit_init_filter_exe(filter, NULL);
  391. if (err)
  392. return err;
  393. err = audit_filter_exe(fd, filter, AUDIT_ADD_RULE);
  394. if (err)
  395. return err;
  396. return fd;
  397. }