/
githubmirror
/
audit-userspace
Обзор
Документация
Войти
/
githubmirror
/
audit-userspace
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
lib/netlink.c
301 строка
7 KB
Steve Grubb
Code cleanup
22 мар 2026, 22:58
22 мар 2026, 22:58
2f1ac7a
Код
Авторство
О чём код?
/* netlink.c -- * Copyright 2004,2005,2009,2013,2016 Red Hat Inc., Durham, North Carolina. * All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Authors: * Steve Grubb <sgrubb@redhat.com> * Rickard E. (Rik) Faith <faith@redhat.com> */ #include "config.h" #include <unistd.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <limits.h> #include <time.h> #include <sys/poll.h> #include "libaudit.h" #include "private.h" #ifndef NETLINK_AUDIT #define NETLINK_AUDIT 9 #endif static int adjust_reply(struct audit_reply *rep, int len); static int check_ack(int fd); /* * This function opens a connection to the kernel's audit * subsystem. You must be root for the call to succeed. On error, * a negative value is returned. On success, the file descriptor is * returned - which can be 0 or higher. */ int audit_open(void) { int fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT); if (fd < 0) { if (errno == EINVAL || errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT) audit_msg(LOG_ERR, "Error - audit support not in kernel"); else audit_msg(LOG_ERR, "Error opening audit netlink socket (%s)", strerror(errno)); } return fd; } void audit_close(int fd) { if (fd >= 0) close(fd); } /* * This function returns -errno on error, 0 if error response received, * and > 0 if packet OK. */ int audit_get_reply(int fd, struct audit_reply *rep, reply_t block, int peek) { int len; struct sockaddr_nl nladdr = { 0 }; socklen_t nladdrlen = sizeof(nladdr); if (fd < 0) return -EBADF; if (block == GET_REPLY_NONBLOCKING) peek |= MSG_DONTWAIT; retry: len = recvfrom(fd, &rep->msg, sizeof(rep->msg), peek, (struct sockaddr*)&nladdr, &nladdrlen); if (len < 0) { if (errno == EINTR) goto retry; if (errno != EAGAIN) audit_msg(LOG_ERR, "Error receiving audit netlink packet (%s)", strerror(errno)); return -errno; } if (nladdrlen != sizeof(nladdr)) { audit_msg(LOG_ERR, "Bad address size reading audit netlink socket"); return -EPROTO; } if (nladdr.nl_pid) { audit_msg(LOG_ERR, "Spoofed packet received on audit netlink socket"); return -EINVAL; } len = adjust_reply(rep, len); if (len == 0) len = -errno; return len; } /* * This function returns 0 on error and len on success. */ static int adjust_reply(struct audit_reply *rep, int len) { rep->type = rep->msg.nlh.nlmsg_type; rep->len = rep->msg.nlh.nlmsg_len; rep->nlh = &rep->msg.nlh; rep->status = NULL; rep->ruledata = NULL; rep->login = NULL; rep->message = NULL; rep->error = NULL; rep->signal_info = NULL; rep->conf = NULL; #if HAVE_DECL_AUDIT_FEATURE_VERSION == 1 rep->features = NULL; #endif if (!NLMSG_OK(rep->nlh, (unsigned int)len)) { if (len == sizeof(rep->msg)) { audit_msg(LOG_ERR, "Netlink event from kernel is too big"); errno = EFBIG; } else { audit_msg(LOG_ERR, "Netlink message from kernel was not OK"); errno = EBADE; } return 0; } /* Next we'll set the data structure to point to msg.data. This is * to avoid having to use casts later. */ switch (rep->type) { case NLMSG_ERROR: rep->error = NLMSG_DATA(rep->nlh); break; case AUDIT_GET: rep->status = NLMSG_DATA(rep->nlh); break; #if HAVE_DECL_AUDIT_FEATURE_VERSION == 1 case AUDIT_GET_FEATURE: rep->features = NLMSG_DATA(rep->nlh); break; #endif case AUDIT_LIST_RULES: rep->ruledata = NLMSG_DATA(rep->nlh); break; case AUDIT_USER: case AUDIT_LOGIN: case AUDIT_KERNEL: case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2: case AUDIT_FIRST_EVENT...AUDIT_INTEGRITY_LAST_MSG: rep->message = NLMSG_DATA(rep->nlh); break; case AUDIT_SIGNAL_INFO: rep->signal_info = NLMSG_DATA(rep->nlh); break; } return len; } /* * Return values: success: positive non-zero sequence number * error: -errno * short: 0 */ int __audit_send(int fd, int type, const void *data, unsigned int size, int *seq) { static int sequence = 0; struct audit_message req; int retval; struct sockaddr_nl addr; /* Due to user space library callbacks, there's a chance that a -1 for the fd could be passed. Just check for and handle it. */ if (fd < 0) { errno = EBADF; return -errno; } if (size > sizeof(req.data)) { errno = EINVAL; return -errno; } if (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) { errno = EINVAL; return -errno; } if (sequence == INT_MAX) sequence = 1; else sequence++; *seq = sequence; memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = NLMSG_SPACE(size); req.nlh.nlmsg_type = type; req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; req.nlh.nlmsg_seq = sequence; if (size && data) memcpy(req.data, data, size); memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_pid = 0; addr.nl_groups = 0; do { retval = sendto(fd, &req, req.nlh.nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr)); } while (retval < 0 && errno == EINTR); if (retval == (int)req.nlh.nlmsg_len) return check_ack(fd); if (retval < 0) { return -errno; } else if (retval > 0) { errno = EINVAL; return -errno; } return 0; } int audit_send(int fd, int type, const void *data, unsigned int size) { int rc; int seq; rc = __audit_send(fd, type, data, size, &seq); if (rc == 0) rc = seq; return rc; } /* * This function will take a peek into the next packet and see if there's * an error. If so, the error is returned and its non-zero. Otherwise a * zero is returned indicating that we don't know of any problems. */ static int check_ack(int fd) { int rc, retries = 80; struct audit_reply rep; struct pollfd pfd[1]; retry: pfd[0].fd = fd; pfd[0].events = POLLIN; do { rc = poll(pfd, 1, 500); /* .5 second */ } while (rc < 0 && errno == EINTR); /* We don't look at rc from above as it doesn't matter. We are * going to try to read nonblocking just in case packet shows up. */ /* NOTE: whatever is returned is treated as the errno */ rc = audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, MSG_PEEK); if (rc == -EAGAIN && retries) { retries--; goto retry; } else if (rc < 0) return rc; else if (rc == 0) return -EINVAL; /* This can't happen anymore */ else if (rc > 0 && rep.type == NLMSG_ERROR) { int error = rep.error->error; /* Eat the message */ if (audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0)) ; // intentionally empty /* NLMSG_ERROR can indicate success, only report nonzero */ if (error) { errno = -error; return error; } } return 0; }