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
17static int fd;
18
19#define MAX_AUDIT_MESSAGE_LENGTH	8970
20struct audit_message {
21	struct nlmsghdr nlh;
22	union {
23		struct audit_status s;
24		char data[MAX_AUDIT_MESSAGE_LENGTH];
25	} u;
26};
27
28int 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
48int 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
79int 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
94int 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
133void cleanup(int sig)
134{
135	audit_set(fd, AUDIT_STATUS_ENABLED, 0);
136	close(fd);
137	if (sig)
138		exit(0);
139}
140
141int 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