1 // SPDX-License-Identifier: GPL-2.0
2 
3 #define _GNU_SOURCE
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <poll.h>
7 #include <signal.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/socket.h>
13 #include <unistd.h>
14 #include <linux/audit.h>
15 #include <linux/netlink.h>
16 
17 static int fd;
18 
19 #define MAX_AUDIT_MESSAGE_LENGTH	8970
20 struct audit_message {
21 	struct nlmsghdr nlh;
22 	union {
23 		struct audit_status s;
24 		char data[MAX_AUDIT_MESSAGE_LENGTH];
25 	} u;
26 };
27 
audit_recv(int fd,struct audit_message * rep)28 int audit_recv(int fd, struct audit_message *rep)
29 {
30 	struct sockaddr_nl addr;
31 	socklen_t addrlen = sizeof(addr);
32 	int ret;
33 
34 	do {
35 		ret = recvfrom(fd, rep, sizeof(*rep), 0,
36 			       (struct sockaddr *)&addr, &addrlen);
37 	} while (ret < 0 && errno == EINTR);
38 
39 	if (ret < 0 ||
40 	    addrlen != sizeof(addr) ||
41 	    addr.nl_pid != 0 ||
42 	    rep->nlh.nlmsg_type == NLMSG_ERROR) /* short-cut for now */
43 		return -1;
44 
45 	return ret;
46 }
47 
audit_send(int fd,uint16_t type,uint32_t key,uint32_t val)48 int audit_send(int fd, uint16_t type, uint32_t key, uint32_t val)
49 {
50 	static int seq = 0;
51 	struct audit_message msg = {
52 		.nlh = {
53 			.nlmsg_len   = NLMSG_SPACE(sizeof(msg.u.s)),
54 			.nlmsg_type  = type,
55 			.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
56 			.nlmsg_seq   = ++seq,
57 		},
58 		.u.s = {
59 			.mask    = key,
60 			.enabled = key == AUDIT_STATUS_ENABLED ? val : 0,
61 			.pid     = key == AUDIT_STATUS_PID ? val : 0,
62 		}
63 	};
64 	struct sockaddr_nl addr = {
65 		.nl_family = AF_NETLINK,
66 	};
67 	int ret;
68 
69 	do {
70 		ret = sendto(fd, &msg, msg.nlh.nlmsg_len, 0,
71 			     (struct sockaddr *)&addr, sizeof(addr));
72 	} while (ret < 0 && errno == EINTR);
73 
74 	if (ret != (int)msg.nlh.nlmsg_len)
75 		return -1;
76 	return 0;
77 }
78 
audit_set(int fd,uint32_t key,uint32_t val)79 int audit_set(int fd, uint32_t key, uint32_t val)
80 {
81 	struct audit_message rep = { 0 };
82 	int ret;
83 
84 	ret = audit_send(fd, AUDIT_SET, key, val);
85 	if (ret)
86 		return ret;
87 
88 	ret = audit_recv(fd, &rep);
89 	if (ret < 0)
90 		return ret;
91 	return 0;
92 }
93 
readlog(int fd)94 int readlog(int fd)
95 {
96 	struct audit_message rep = { 0 };
97 	int ret = audit_recv(fd, &rep);
98 	const char *sep = "";
99 	char *k, *v;
100 
101 	if (ret < 0)
102 		return ret;
103 
104 	if (rep.nlh.nlmsg_type != AUDIT_NETFILTER_CFG)
105 		return 0;
106 
107 	/* skip the initial "audit(...): " part */
108 	strtok(rep.u.data, " ");
109 
110 	while ((k = strtok(NULL, "="))) {
111 		v = strtok(NULL, " ");
112 
113 		/* these vary and/or are uninteresting, ignore */
114 		if (!strcmp(k, "pid") ||
115 		    !strcmp(k, "comm") ||
116 		    !strcmp(k, "subj"))
117 			continue;
118 
119 		/* strip the varying sequence number */
120 		if (!strcmp(k, "table"))
121 			*strchrnul(v, ':') = '\0';
122 
123 		printf("%s%s=%s", sep, k, v);
124 		sep = " ";
125 	}
126 	if (*sep) {
127 		printf("\n");
128 		fflush(stdout);
129 	}
130 	return 0;
131 }
132 
cleanup(int sig)133 void cleanup(int sig)
134 {
135 	audit_set(fd, AUDIT_STATUS_ENABLED, 0);
136 	close(fd);
137 	if (sig)
138 		exit(0);
139 }
140 
main(int argc,char ** argv)141 int main(int argc, char **argv)
142 {
143 	struct sigaction act = {
144 		.sa_handler = cleanup,
145 	};
146 
147 	fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
148 	if (fd < 0) {
149 		perror("Can't open netlink socket");
150 		return -1;
151 	}
152 
153 	if (sigaction(SIGTERM, &act, NULL) < 0 ||
154 	    sigaction(SIGINT, &act, NULL) < 0) {
155 		perror("Can't set signal handler");
156 		close(fd);
157 		return -1;
158 	}
159 
160 	audit_set(fd, AUDIT_STATUS_ENABLED, 1);
161 	audit_set(fd, AUDIT_STATUS_PID, getpid());
162 
163 	while (1)
164 		readlog(fd);
165 }
166