root/tools/testing/selftests/net/netfilter/audit_logread.c
// 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);
}