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: &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: &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, val: 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, val: 1);
161 audit_set(fd, AUDIT_STATUS_PID, val: getpid());
162
163 while (1)
164 readlog(fd);
165}
166

source code of linux/tools/testing/selftests/netfilter/audit_logread.c