// SPDX-License-Identifier: GPL-2.0 #define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <poll.h> #include <signal.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #include <linux/audit.h> #include <linux/netlink.h> static int fd; #define MAX_AUDIT_MESSAGE_LENGTH 8970 struct audit_message { struct nlmsghdr nlh; union { struct audit_status s; char data[MAX_AUDIT_MESSAGE_LENGTH]; } u; }; int audit_recv(int fd, struct audit_message *rep) { struct sockaddr_nl addr; socklen_t addrlen = sizeof(addr); int ret; do { ret = recvfrom(fd, rep, sizeof(*rep), 0, (struct sockaddr *)&addr, &addrlen); } while (ret < 0 && errno == EINTR); if (ret < 0 || addrlen != sizeof(addr) || addr.nl_pid != 0 || rep->nlh.nlmsg_type == NLMSG_ERROR) /* short-cut for now */ return -1; return ret; } int audit_send(int fd, uint16_t type, uint32_t key, uint32_t val) { static int seq = 0; struct audit_message msg = { .nlh = { .nlmsg_len = NLMSG_SPACE(sizeof(msg.u.s)), .nlmsg_type = type, .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, .nlmsg_seq = ++seq, }, .u.s = { .mask = key, .enabled = key == AUDIT_STATUS_ENABLED ? val : 0, .pid = key == AUDIT_STATUS_PID ? val : 0, } }; struct sockaddr_nl addr = { .nl_family = AF_NETLINK, }; int ret; do { ret = sendto(fd, &msg, msg.nlh.nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); } while (ret < 0 && errno == EINTR); if (ret != (int)msg.nlh.nlmsg_len) return -1; return 0; } int audit_set(int fd, uint32_t key, uint32_t val) { struct audit_message rep = { 0 }; int ret; ret = audit_send(fd, AUDIT_SET, key, val); if (ret) return ret; ret = audit_recv(fd, &rep); if (ret < 0) return ret; return 0; } int readlog(int fd) { struct audit_message rep = { 0 }; int ret = audit_recv(fd, &rep); const char *sep = ""; char *k, *v; if (ret < 0) return ret; if (rep.nlh.nlmsg_type != AUDIT_NETFILTER_CFG) return 0; /* skip the initial "audit(...): " part */ strtok(rep.u.data, " "); while ((k = strtok(NULL, "="))) { v = strtok(NULL, " "); /* these vary and/or are uninteresting, ignore */ if (!strcmp(k, "pid") || !strcmp(k, "comm") || !strcmp(k, "subj")) continue; /* strip the varying sequence number */ if (!strcmp(k, "table")) *strchrnul(v, ':') = '\0'; printf("%s%s=%s", sep, k, v); sep = " "; } if (*sep) { printf("\n"); fflush(stdout); } return 0; } void cleanup(int sig) { audit_set(fd, AUDIT_STATUS_ENABLED, 0); close(fd); if (sig) exit(0); } int main(int argc, char **argv) { struct sigaction act = { .sa_handler = cleanup, }; fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT); if (fd < 0) { perror("Can't open netlink socket"); return -1; } if (sigaction(SIGTERM, &act, NULL) < 0 || sigaction(SIGINT, &act, NULL) < 0) { perror("Can't set signal handler"); close(fd); return -1; } audit_set(fd, AUDIT_STATUS_ENABLED, 1); audit_set(fd, AUDIT_STATUS_PID, getpid()); while (1) readlog(fd); }