diff options
author | Ashlee Young <ashlee@wildernessvoice.com> | 2015-11-29 08:22:13 -0800 |
---|---|---|
committer | Ashlee Young <ashlee@wildernessvoice.com> | 2015-11-29 08:22:13 -0800 |
commit | df5afa4fcd9725380f94ca6476248d4cc24f889a (patch) | |
tree | 65456f62397305febf7f40778c5a413a35d094ef /framework/src/audit/lib/netlink.c | |
parent | 76f6bf922552c00546e6e85ca471eab28f56986c (diff) |
v2.4.4 audit sources
Change-Id: I9315a7408817db51edf084fb4d27fbb492785084
Signed-off-by: Ashlee Young <ashlee@wildernessvoice.com>
Diffstat (limited to 'framework/src/audit/lib/netlink.c')
-rw-r--r-- | framework/src/audit/lib/netlink.c | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/framework/src/audit/lib/netlink.c b/framework/src/audit/lib/netlink.c new file mode 100644 index 00000000..6c80c30c --- /dev/null +++ b/framework/src/audit/lib/netlink.c @@ -0,0 +1,299 @@ +/* netlink.c -- + * Copyright 2004, 2005, 2009, 2013 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 <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, int seq); + +/* + * 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 saved_errno; + int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT); + + if (fd < 0) { + saved_errno = errno; + 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)); + errno = saved_errno; + return fd; + } + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { + saved_errno = errno; + close(fd); + audit_msg(LOG_ERR, + "Error setting audit netlink socket CLOEXEC flag (%s)", + strerror(errno)); + errno = saved_errno; + return -1; + } + return fd; +} + + +void audit_close(int fd) +{ + if (fd >= 0) + close(fd); +} + + +/* + * This function returns -1 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; + socklen_t nladdrlen = sizeof(nladdr); + + if (fd < 0) + return -EBADF; + + if (block == GET_REPLY_NONBLOCKING) + block = MSG_DONTWAIT; + +retry: + len = recvfrom(fd, &rep->msg, sizeof(rep->msg), block|peek, + (struct sockaddr*)&nladdr, &nladdrlen); + + if (len < 0) { + if (errno == EINTR) + goto retry; + if (errno != EAGAIN) { + int saved_errno = errno; + audit_msg(LOG_ERR, + "Error receiving audit netlink packet (%s)", + strerror(errno)); + errno = saved_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; +} +hidden_def(audit_get_reply) + + +/* + * 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 + 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 + 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) +{ + 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 (NLMSG_SPACE(size) > MAX_AUDIT_MESSAGE_LENGTH) { + errno = EINVAL; + return -errno; + } + + if (++sequence < 0) + sequence = 1; + + 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(NLMSG_DATA(&req.nlh), 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) { + if ((retval = check_ack(fd, sequence)) == 0) + return sequence; + else + return retval; + } + if (retval < 0) + return -errno; + + return 0; +} +hidden_def(audit_send) + +/* + * 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 seq) +{ + 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 */ + (void)audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0); + + /* NLMSG_ERROR can indicate success, only report nonzero */ + if (error) { + errno = -error; + return error; + } + } + return 0; +} + |