aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/audit/lib/libaudit.c
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/audit/lib/libaudit.c')
-rw-r--r--framework/src/audit/lib/libaudit.c1606
1 files changed, 1606 insertions, 0 deletions
diff --git a/framework/src/audit/lib/libaudit.c b/framework/src/audit/lib/libaudit.c
new file mode 100644
index 00000000..6311b813
--- /dev/null
+++ b/framework/src/audit/lib/libaudit.c
@@ -0,0 +1,1606 @@
+/* libaudit.c --
+ * Copyright 2004-2009,2012,2014 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 <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <fcntl.h> /* O_NOFOLLOW needs gnu defined */
+#include <limits.h> /* for PATH_MAX */
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "libaudit.h"
+#include "private.h"
+#include "errormsg.h"
+
+/* #defines for the audit failure query */
+#define CONFIG_FILE "/etc/libaudit.conf"
+
+/* Local prototypes */
+struct nv_pair
+{
+ const char *name;
+ const char *value;
+};
+
+struct kw_pair
+{
+ const char *name;
+ int (*parser)(const char *, int);
+};
+
+struct nv_list
+{
+ const char *name;
+ int option;
+};
+
+struct libaudit_conf
+{
+ auditfail_t failure_action;
+};
+
+static const struct nv_list failure_actions[] =
+{
+ {"ignore", FAIL_IGNORE },
+ {"log", FAIL_LOG },
+ {"terminate", FAIL_TERMINATE },
+ { NULL, 0 }
+};
+
+int _audit_permadded = 0;
+int _audit_archadded = 0;
+int _audit_syscalladded = 0;
+unsigned int _audit_elf = 0U;
+static struct libaudit_conf config;
+
+static int audit_failure_parser(const char *val, int line);
+static int audit_name_to_uid(const char *name, uid_t *uid);
+static int audit_name_to_gid(const char *name, gid_t *gid);
+
+static const struct kw_pair keywords[] =
+{
+ {"failure_action", audit_failure_parser },
+ { NULL, NULL }
+};
+
+static int audit_priority(int xerrno)
+{
+ /* If they've compiled their own kernel and did not include
+ * the audit susbsystem, they will get ECONNREFUSED. We'll
+ * demote the message to debug so its not lost entirely. */
+ if (xerrno == ECONNREFUSED)
+ return LOG_DEBUG;
+ else
+ return LOG_WARNING;
+}
+
+int audit_request_status(int fd)
+{
+ int rc = audit_send(fd, AUDIT_GET, NULL, 0);
+ if (rc < 0)
+ audit_msg(audit_priority(errno),
+ "Error sending status request (%s)", strerror(-rc));
+ return rc;
+}
+hidden_def(audit_request_status)
+
+/*
+ * Set everything to its default value
+ */
+static void clear_config(void)
+{
+ config.failure_action = FAIL_IGNORE;
+}
+
+/* Get 1 line from file */
+static char *get_line(FILE *f, char *buf, size_t len)
+{
+ if (fgets(buf, len, f)) {
+ /* remove newline */
+ char *ptr = strchr(buf, 0x0a);
+ if (ptr)
+ *ptr = 0;
+ return buf;
+ }
+ return NULL;
+}
+
+static int nv_split(char *buf, struct nv_pair *nv)
+{
+ /* Get the name part */
+ char *ptr, *saved=NULL;
+
+ nv->name = NULL;
+ nv->value = NULL;
+ ptr = audit_strsplit_r(buf, &saved);
+ if (ptr == NULL)
+ return 0; /* If there's nothing, go to next line */
+ if (ptr[0] == '#')
+ return 0; /* If there's a comment, go to next line */
+ nv->name = ptr;
+
+ /* Check for a '=' */
+ ptr = audit_strsplit_r(NULL, &saved);
+ if (ptr == NULL)
+ return 1;
+ if (strcmp(ptr, "=") != 0)
+ return 2;
+
+ /* get the value */
+ ptr = audit_strsplit_r(NULL, &saved);
+ if (ptr == NULL)
+ return 1;
+ nv->value = ptr;
+
+ /* Make sure there's nothing else */
+ ptr = audit_strsplit_r(NULL, &saved);
+ if (ptr)
+ return 1;
+
+ /* Everything is OK */
+ return 0;
+}
+
+static const struct kw_pair *kw_lookup(const char *val)
+{
+ int i = 0;
+ while (keywords[i].name != NULL) {
+ if (strcasecmp(keywords[i].name, val) == 0)
+ break;
+ i++;
+ }
+ return &keywords[i];
+}
+
+static int audit_failure_parser(const char *val, int line)
+{
+ int i;
+
+ audit_msg(LOG_DEBUG, "audit_failure_parser called with: %s", val);
+ for (i=0; failure_actions[i].name != NULL; i++) {
+ if (strcasecmp(val, failure_actions[i].name) == 0) {
+ config.failure_action = failure_actions[i].option;
+ return 0;
+ }
+ }
+ audit_msg(LOG_ERR, "Option %s not found - line %d", val, line);
+ return 1;
+}
+
+/*
+ * Read the /etc/libaudit.conf file and all tunables.
+ */
+static int load_libaudit_config(const char *path)
+{
+ int fd, rc, lineno = 1;
+ struct stat st;
+ FILE *f;
+ char buf[128];
+
+ /* open the file */
+ rc = open(path, O_NOFOLLOW|O_RDONLY);
+ if (rc < 0) {
+ if (errno != ENOENT) {
+ audit_msg(LOG_ERR, "Error opening %s (%s)",
+ path, strerror(errno));
+ return 1;
+ }
+ audit_msg(LOG_WARNING,
+ "Config file %s doesn't exist, skipping", path);
+ return 0;
+ }
+ fd = rc;
+
+ /* check the file's permissions: owned by root, not world writable,
+ * not symlink.
+ */
+ audit_msg(LOG_DEBUG, "Config file %s opened for parsing", path);
+ if (fstat(fd, &st) < 0) {
+ audit_msg(LOG_ERR, "Error fstat'ing %s (%s)",
+ path, strerror(errno));
+ close(fd);
+ return 1;
+ }
+ if (st.st_uid != 0) {
+ audit_msg(LOG_ERR, "Error - %s isn't owned by root", path);
+ close(fd);
+ return 1;
+ }
+ if ((st.st_mode & S_IWOTH) == S_IWOTH) {
+ audit_msg(LOG_ERR, "Error - %s is world writable", path);
+ close(fd);
+ return 1;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ audit_msg(LOG_ERR, "Error - %s is not a regular file", path);
+ close(fd);
+ return 1;
+ }
+
+ /* it's ok, read line by line */
+ f = fdopen(fd, "rm");
+ if (f == NULL) {
+ audit_msg(LOG_ERR, "Error - fdopen failed (%s)",
+ strerror(errno));
+ close(fd);
+ return 1;
+ }
+
+ while (get_line(f, buf, sizeof(buf))) {
+ // convert line into name-value pair
+ const struct kw_pair *kw;
+ struct nv_pair nv;
+ rc = nv_split(buf, &nv);
+ switch (rc) {
+ case 0: // fine
+ break;
+ case 1: // not the right number of tokens.
+ audit_msg(LOG_ERR,
+ "Wrong number of arguments for line %d in %s",
+ lineno, path);
+ break;
+ case 2: // no '=' sign
+ audit_msg(LOG_ERR,
+ "Missing equal sign for line %d in %s",
+ lineno, path);
+ break;
+ default: // something else went wrong...
+ audit_msg(LOG_ERR,
+ "Unknown error for line %d in %s",
+ lineno, path);
+ break;
+ }
+ if (nv.name == NULL) {
+ lineno++;
+ continue;
+ }
+ if (nv.value == NULL) {
+ fclose(f);
+ return 1;
+ }
+
+ /* identify keyword or error */
+ kw = kw_lookup(nv.name);
+ if (kw->name == NULL) {
+ audit_msg(LOG_ERR,
+ "Unknown keyword \"%s\" in line %d of %s",
+ nv.name, lineno, path);
+ fclose(f);
+ return 1;
+ }
+
+ /* dispatch to keyword's local parser */
+ rc = kw->parser(nv.value, lineno);
+ if (rc != 0) {
+ fclose(f);
+ return 1; // local parser puts message out
+ }
+
+ lineno++;
+ }
+
+ fclose(f);
+ return 0;
+}
+
+
+/*
+ * This function is called to get the value of the failure_action
+ * tunable stored in /etc/libaudit.conf. The function returns 1 if
+ * the tunable is not found or there is an error. If the tunable is found,
+ * 0 is returned the the tunable value is saved in the failmode parameter.
+ */
+int get_auditfail_action(auditfail_t *failmode)
+{
+ clear_config();
+
+ if (load_libaudit_config(CONFIG_FILE)) {
+ *failmode = config.failure_action;
+ return 1;
+ }
+
+ *failmode = config.failure_action;
+ return 0;
+}
+
+int audit_set_enabled(int fd, uint32_t enabled)
+{
+ int rc;
+ struct audit_status s;
+
+ memset(&s, 0, sizeof(s));
+ s.mask = AUDIT_STATUS_ENABLED;
+ s.enabled = enabled;
+ rc = audit_send(fd, AUDIT_SET, &s, sizeof(s));
+ if (rc < 0)
+ audit_msg(audit_priority(errno),
+ "Error sending enable request (%s)", strerror(-rc));
+ return rc;
+}
+
+/*
+ * This function will return 0 if auditing is NOT enabled and
+ * 1 if enabled, and -1 on error.
+ */
+int audit_is_enabled(int fd)
+{
+ int rc;
+
+ if (fd < 0)
+ return 0;
+
+ if ((rc = audit_request_status(fd)) > 0) {
+ struct audit_reply rep;
+ int i;
+ int timeout = 40; /* tenths of seconds */
+ struct pollfd pfd[1];
+
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN;
+
+ for (i = 0; i < timeout; i++) {
+ do {
+ rc = poll(pfd, 1, 100);
+ } while (rc < 0 && errno == EINTR);
+
+ rc = audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING,0);
+ if (rc > 0) {
+ /* If we get done or error, break out */
+ if (rep.type == NLMSG_DONE ||
+ rep.type == NLMSG_ERROR)
+ break;
+
+ /* If its not status, keep looping */
+ if (rep.type != AUDIT_GET)
+ continue;
+
+ /* Found it... */
+ return rep.status->enabled;
+ }
+ }
+ }
+ if (rc == -ECONNREFUSED) {
+ /* This is here to let people that build their own kernel
+ and disable the audit system get in. ECONNREFUSED is
+ issued by the kernel when there is "no on listening". */
+ return 0;
+ } else if (rc == -EPERM && getuid() != 0) {
+ /* If we get this, then the kernel supports auditing
+ * but we don't have enough privilege to write to the
+ * socket. Therefore, we have already been authenticated
+ * and we are a common user. Just act as though auditing
+ * is not enabled. Any other error we take seriously.
+ * This is here basically to satisfy Xscreensaver. */
+ return 0;
+ }
+ return -1;
+}
+
+int audit_set_failure(int fd, uint32_t failure)
+{
+ int rc;
+ struct audit_status s;
+
+ memset(&s, 0, sizeof(s));
+ s.mask = AUDIT_STATUS_FAILURE;
+ s.failure = failure;
+ rc = audit_send(fd, AUDIT_SET, &s, sizeof(s));
+ if (rc < 0)
+ audit_msg(audit_priority(errno),
+ "Error sending failure mode request (%s)",
+ strerror(-rc));
+ return rc;
+}
+
+/*
+ * This function returns -1 on error and 1 on success.
+ */
+int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode)
+{
+ struct audit_status s;
+ struct audit_reply rep;
+ struct pollfd pfd[1];
+ int rc;
+
+ memset(&s, 0, sizeof(s));
+ s.mask = AUDIT_STATUS_PID;
+ s.pid = pid;
+ rc = audit_send(fd, AUDIT_SET, &s, sizeof(s));
+ if (rc < 0) {
+ audit_msg(audit_priority(errno),
+ "Error setting audit daemon pid (%s)",
+ strerror(-rc));
+ return rc;
+ }
+ if (wmode == WAIT_NO)
+ return 1;
+
+ /* Now we'll see if there's any reply message. This only
+ happens on error. It is not fatal if there is no message.
+ As a matter of fact, we don't do anything with the message
+ besides gobble it. */
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN;
+ do {
+ rc = poll(pfd, 1, 100); /* .1 second */
+ } while (rc < 0 && errno == EINTR);
+
+ (void)audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
+ return 1;
+}
+
+int audit_set_rate_limit(int fd, uint32_t limit)
+{
+ int rc;
+ struct audit_status s;
+
+ memset(&s, 0, sizeof(s));
+ s.mask = AUDIT_STATUS_RATE_LIMIT;
+ s.rate_limit = limit;
+ rc = audit_send(fd, AUDIT_SET, &s, sizeof(s));
+ if (rc < 0)
+ audit_msg(audit_priority(errno),
+ "Error sending rate limit request (%s)",
+ strerror(-rc));
+ return rc;
+}
+
+int audit_set_backlog_limit(int fd, uint32_t limit)
+{
+ int rc;
+ struct audit_status s;
+
+ memset(&s, 0, sizeof(s));
+ s.mask = AUDIT_STATUS_BACKLOG_LIMIT;
+ s.backlog_limit = limit;
+ rc = audit_send(fd, AUDIT_SET, &s, sizeof(s));
+ if (rc < 0)
+ audit_msg(audit_priority(errno),
+ "Error sending backlog limit request (%s)",
+ strerror(-rc));
+ return rc;
+}
+
+int audit_set_backlog_wait_time(int fd, uint32_t bwt)
+{
+ int rc = -1;
+#if HAVE_DECL_AUDIT_VERSION_BACKLOG_WAIT_TIME
+ struct audit_status s;
+
+ memset(&s, 0, sizeof(s));
+ s.mask = AUDIT_STATUS_BACKLOG_WAIT_TIME;
+ s.backlog_wait_time = bwt;
+ rc = audit_send(fd, AUDIT_SET, &s, sizeof(s));
+ if (rc < 0)
+ audit_msg(audit_priority(errno),
+ "Error sending backlog limit request (%s)",
+ strerror(-rc));
+#endif
+ return rc;
+}
+
+int audit_set_feature(int fd, unsigned feature, unsigned value, unsigned lock)
+{
+#if HAVE_DECL_AUDIT_FEATURE_VERSION
+ int rc;
+ struct audit_features f;
+
+ memset(&f, 0, sizeof(f));
+ f.mask = AUDIT_FEATURE_TO_MASK(feature);
+ if (value)
+ f.features = AUDIT_FEATURE_TO_MASK(feature);
+ if (lock)
+ f.lock = AUDIT_FEATURE_TO_MASK(feature);
+ rc = audit_send(fd, AUDIT_SET_FEATURE, &f, sizeof(f));
+ if (rc < 0)
+ audit_msg(audit_priority(errno),
+ "Error setting feature (%s)",
+ strerror(-rc));
+ return rc;
+#else
+ errno = EINVAL;
+ return -1;
+#endif
+}
+hidden_def(audit_set_feature)
+
+int audit_request_features(int fd)
+{
+#if HAVE_DECL_AUDIT_FEATURE_VERSION
+ int rc;
+ struct audit_features f;
+
+ memset(&f, 0, sizeof(f));
+ rc = audit_send(fd, AUDIT_GET_FEATURE, &f, sizeof(f));
+ if (rc < 0)
+ audit_msg(audit_priority(errno),
+ "Error getting feature (%s)",
+ strerror(-rc));
+ return rc;
+#else
+ errno = EINVAL;
+ return -1;
+#endif
+}
+hidden_def(audit_request_features)
+
+extern int audit_set_loginuid_immutable(int fd)
+{
+#if HAVE_DECL_AUDIT_FEATURE_VERSION
+ return audit_set_feature(fd, AUDIT_FEATURE_LOGINUID_IMMUTABLE, 1, 1);
+#else
+ errno = EINVAL;
+ return -1;
+#endif
+}
+
+int audit_request_rules_list_data(int fd)
+{
+ int rc = audit_send(fd, AUDIT_LIST_RULES, NULL, 0);
+ if (rc < 0 && rc != -EINVAL)
+ audit_msg(audit_priority(errno),
+ "Error sending rule list data request (%s)",
+ strerror(-rc));
+ return rc;
+}
+
+int audit_request_signal_info(int fd)
+{
+ int rc = audit_send(fd, AUDIT_SIGNAL_INFO, NULL, 0);
+ if (rc < 0)
+ audit_msg(LOG_WARNING,
+ "Error sending signal_info request (%s)",
+ strerror(-rc));
+ return rc;
+}
+
+int audit_update_watch_perms(struct audit_rule_data *rule, int perms)
+{
+ int i, done=0;
+
+ if (rule->field_count < 1)
+ return -1;
+
+ // First see if we have an entry we are updating
+ for (i=0; i< rule->field_count; i++) {
+ if (rule->fields[i] == AUDIT_PERM) {
+ rule->values[i] = perms;
+ done = 1;
+ }
+ }
+ if (!done) {
+ // If not check to see if we have room to add a field
+ if (rule->field_count >= (AUDIT_MAX_FIELDS - 1))
+ return -2;
+
+ // Add the perm
+ rule->fields[rule->field_count] = AUDIT_PERM;
+ rule->fieldflags[rule->field_count] = AUDIT_EQUAL;
+ rule->values[rule->field_count] = perms;
+ rule->field_count++;
+ }
+ return 0;
+}
+
+int audit_add_watch(struct audit_rule_data **rulep, const char *path)
+{
+ return audit_add_watch_dir(AUDIT_WATCH, rulep, path);
+}
+
+int audit_add_dir(struct audit_rule_data **rulep, const char *path)
+{
+ return audit_add_watch_dir(AUDIT_DIR, rulep, path);
+}
+
+int audit_add_watch_dir(int type, struct audit_rule_data **rulep,
+ const char *path)
+{
+ size_t len = strlen(path);
+ struct audit_rule_data *rule = *rulep;
+
+ if (rule && rule->field_count) {
+ audit_msg(LOG_ERR, "Rule is not empty\n");
+ return -1;
+ }
+ if (type != AUDIT_WATCH && type != AUDIT_DIR) {
+ audit_msg(LOG_ERR, "Invalid type used\n");
+ return -1;
+ }
+
+ *rulep = realloc(rule, len + sizeof(*rule));
+ if (*rulep == NULL) {
+ free(rule);
+ audit_msg(LOG_ERR, "Cannot realloc memory!\n");
+ return -1;
+ }
+ rule = *rulep;
+ memset(rule, 0, len + sizeof(*rule));
+
+ rule->flags = AUDIT_FILTER_EXIT;
+ rule->action = AUDIT_ALWAYS;
+ audit_rule_syscallbyname_data(rule, "all");
+ rule->field_count = 2;
+ rule->fields[0] = type;
+ rule->values[0] = len;
+ rule->fieldflags[0] = AUDIT_EQUAL;
+ rule->buflen = len;
+ memcpy(&rule->buf[0], path, len);
+
+ // Default to all permissions
+ rule->fields[1] = AUDIT_PERM;
+ rule->fieldflags[1] = AUDIT_EQUAL;
+ rule->values[1] = AUDIT_PERM_READ | AUDIT_PERM_WRITE |
+ AUDIT_PERM_EXEC | AUDIT_PERM_ATTR;
+
+ _audit_permadded = 1;
+
+ return 0;
+}
+hidden_def(audit_add_watch_dir)
+
+int audit_add_rule_data(int fd, struct audit_rule_data *rule,
+ int flags, int action)
+{
+ int rc;
+
+ if (flags == AUDIT_FILTER_ENTRY) {
+ audit_msg(LOG_WARNING, "Use of entry filter is deprecated");
+ return -2;
+ }
+ rule->flags = flags;
+ rule->action = action;
+ rc = audit_send(fd, AUDIT_ADD_RULE, rule,
+ sizeof(struct audit_rule_data) + rule->buflen);
+ if (rc < 0)
+ audit_msg(audit_priority(errno),
+ "Error sending add rule data request (%s)",
+ errno == EEXIST ?
+ "Rule exists" : strerror(-rc));
+ return rc;
+}
+
+int audit_delete_rule_data(int fd, struct audit_rule_data *rule,
+ int flags, int action)
+{
+ int rc;
+
+ if (flags == AUDIT_FILTER_ENTRY) {
+ audit_msg(LOG_WARNING, "Use of entry filter is deprecated");
+ return -2;
+ }
+ rule->flags = flags;
+ rule->action = action;
+ rc = audit_send(fd, AUDIT_DEL_RULE, rule,
+ sizeof(struct audit_rule_data) + rule->buflen);
+ if (rc < 0) {
+ if (rc == -ENOENT)
+ audit_msg(LOG_WARNING,
+ "Error sending delete rule request (No rule matches)");
+ else
+ audit_msg(audit_priority(errno),
+ "Error sending delete rule data request (%s)",
+ strerror(-rc));
+ }
+ return rc;
+}
+
+/*
+ * This function is part of the directory auditing code
+ */
+int audit_trim_subtrees(int fd)
+{
+ int rc = audit_send(fd, AUDIT_TRIM, NULL, 0);
+ if (rc < 0)
+ audit_msg(audit_priority(errno),
+ "Error sending trim subtrees command (%s)",
+ strerror(-rc));
+ return rc;
+}
+
+/*
+ * This function is part of the directory auditing code
+ */
+int audit_make_equivalent(int fd, const char *mount_point,
+ const char *subtree)
+{
+ int rc;
+ size_t len1 = strlen(mount_point);
+ size_t len2 = strlen(subtree);
+ struct {
+ uint32_t sizes[2];
+ unsigned char buf[];
+ } *cmd = malloc(sizeof(*cmd) + len1 + len2);
+
+ memset(cmd, 0, sizeof(*cmd) + len1 + len2);
+
+ cmd->sizes[0] = len1;
+ cmd->sizes[1] = len2;
+ memcpy(&cmd->buf[0], mount_point, len1);
+ memcpy(&cmd->buf[len1], subtree, len2);
+
+ rc = audit_send(fd, AUDIT_MAKE_EQUIV, cmd, sizeof(*cmd) + len1 + len2);
+ if (rc < 0)
+ audit_msg(audit_priority(errno),
+ "Error sending make_equivalent command (%s)",
+ strerror(-rc));
+ free(cmd);
+ return rc;
+}
+
+/*
+ * This function will retreive the loginuid or -1 if there
+ * is an error.
+ */
+uid_t audit_getloginuid(void)
+{
+ uid_t uid;
+ int len, in;
+ char buf[16];
+
+ errno = 0;
+ in = open("/proc/self/loginuid", O_NOFOLLOW|O_RDONLY);
+ if (in < 0)
+ return -1;
+ do {
+ len = read(in, buf, sizeof(buf));
+ } while (len < 0 && errno == EINTR);
+ close(in);
+ if (len < 0 || len >= sizeof(buf))
+ return -1;
+ buf[len] = 0;
+ errno = 0;
+ uid = strtol(buf, 0, 10);
+ if (errno)
+ return -1;
+ else
+ return uid;
+}
+
+/*
+ * This function returns 0 on success and 1 on failure
+ */
+int audit_setloginuid(uid_t uid)
+{
+ char loginuid[16];
+ int o, count, rc = 0;
+
+ errno = 0;
+ count = snprintf(loginuid, sizeof(loginuid), "%u", uid);
+ o = open("/proc/self/loginuid", O_NOFOLLOW|O_WRONLY|O_TRUNC);
+ if (o >= 0) {
+ int block, offset = 0;
+
+ while (count > 0) {
+ block = write(o, &loginuid[offset], (unsigned)count);
+
+ if (block < 0) {
+ if (errno == EINTR)
+ continue;
+ audit_msg(LOG_ERR, "Error writing loginuid");
+ close(o);
+ return 1;
+ }
+ offset += block;
+ count -= block;
+ }
+ close(o);
+ } else {
+ audit_msg(LOG_ERR, "Error opening /proc/self/loginuid");
+ rc = 1;
+ }
+ return rc;
+}
+
+int audit_rule_syscall_data(struct audit_rule_data *rule, int scall)
+{
+ int word = AUDIT_WORD(scall);
+ int bit = AUDIT_BIT(scall);
+
+ if (word >= (AUDIT_BITMASK_SIZE-1))
+ return -1;
+ rule->mask[word] |= bit;
+ return 0;
+}
+hidden_def(audit_rule_syscall_data)
+
+int audit_rule_syscallbyname_data(struct audit_rule_data *rule,
+ const char *scall)
+{
+ int nr, i;
+ int machine;
+
+ if (!strcmp(scall, "all")) {
+ for (i = 0; i < (AUDIT_BITMASK_SIZE-1); i++)
+ rule->mask[i] = ~0;
+ return 0;
+ }
+ if (!_audit_elf)
+ machine = audit_detect_machine();
+ else
+ machine = audit_elf_to_machine(_audit_elf);
+ if (machine < 0)
+ return -2;
+ nr = audit_name_to_syscall(scall, machine);
+ if (nr < 0) {
+ if (isdigit(scall[0]))
+ nr = strtol(scall, NULL, 0);
+ }
+ if (nr >= 0)
+ return audit_rule_syscall_data(rule, nr);
+ return -1;
+}
+hidden_def(audit_rule_syscallbyname_data)
+
+int audit_rule_interfield_comp_data(struct audit_rule_data **rulep,
+ const char *pair,
+ int flags) {
+ const char *f = pair;
+ char *v;
+ int op;
+ int field1, field2;
+ struct audit_rule_data *rule = *rulep;
+
+ if (f == NULL)
+ return -1;
+
+ if (rule->field_count >= (AUDIT_MAX_FIELDS - 1))
+ return -28;
+
+ /* look for 2-char operators first
+ then look for 1-char operators afterwards
+ when found, null out the bytes under the operators to split
+ and set value pointer just past operator bytes
+ */
+ if ( (v = strstr(pair, "!=")) ) {
+ *v++ = '\0';
+ *v++ = '\0';
+ op = AUDIT_NOT_EQUAL;
+ } else if ( (v = strstr(pair, "=")) ) {
+ *v++ = '\0';
+ op = AUDIT_EQUAL;
+ } else {
+ return -13;
+ }
+
+ if (*f == 0)
+ return -24;
+
+ if (*v == 0)
+ return -25;
+
+ if ((field1 = audit_name_to_field(f)) < 0)
+ return -26;
+
+ if ((field2 = audit_name_to_field(v)) < 0)
+ return -27;
+
+ /* Interfield comparison can only be in exit filter */
+ if (flags != AUDIT_FILTER_EXIT)
+ return -7;
+
+ // It should always be AUDIT_FIELD_COMPARE
+ rule->fields[rule->field_count] = AUDIT_FIELD_COMPARE;
+ rule->fieldflags[rule->field_count] = op;
+ /* oh god, so many permutations */
+ switch (field1)
+ {
+ /* UID comparison */
+ case AUDIT_EUID:
+ switch(field2) {
+ case AUDIT_LOGINUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_EUID;
+ break;
+ case AUDIT_FSUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_FSUID;
+ break;
+ case AUDIT_OBJ_UID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_OBJ_UID;
+ break;
+ case AUDIT_SUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_SUID;
+ break;
+ case AUDIT_UID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_EUID;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case AUDIT_FSUID:
+ switch(field2) {
+ case AUDIT_LOGINUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_FSUID;
+ break;
+ case AUDIT_EUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_FSUID;
+ break;
+ case AUDIT_OBJ_UID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_FSUID_TO_OBJ_UID;
+ break;
+ case AUDIT_SUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_SUID_TO_FSUID;
+ break;
+ case AUDIT_UID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_FSUID;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case AUDIT_LOGINUID:
+ switch(field2) {
+ case AUDIT_EUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_EUID;
+ break;
+ case AUDIT_FSUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_FSUID;
+ break;
+ case AUDIT_OBJ_UID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_OBJ_UID;
+ break;
+ case AUDIT_SUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_SUID;
+ break;
+ case AUDIT_UID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_AUID;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case AUDIT_SUID:
+ switch(field2) {
+ case AUDIT_LOGINUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_SUID;
+ break;
+ case AUDIT_EUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_SUID;
+ break;
+ case AUDIT_FSUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_SUID_TO_FSUID;
+ break;
+ case AUDIT_OBJ_UID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_SUID_TO_OBJ_UID;
+ break;
+ case AUDIT_UID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_SUID;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case AUDIT_OBJ_UID:
+ switch(field2) {
+ case AUDIT_LOGINUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_AUID_TO_OBJ_UID;
+ break;
+ case AUDIT_EUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EUID_TO_OBJ_UID;
+ break;
+ case AUDIT_FSUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_FSUID_TO_OBJ_UID;
+ break;
+ case AUDIT_UID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_OBJ_UID;
+ break;
+ case AUDIT_SUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_SUID_TO_OBJ_UID;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case AUDIT_UID:
+ switch(field2) {
+ case AUDIT_LOGINUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_AUID;
+ break;
+ case AUDIT_EUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_EUID;
+ break;
+ case AUDIT_FSUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_FSUID;
+ break;
+ case AUDIT_OBJ_UID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_OBJ_UID;
+ break;
+ case AUDIT_SUID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_UID_TO_SUID;
+ break;
+ default:
+ return -1;
+ }
+ break;
+
+ /* GID comparisons */
+ case AUDIT_EGID:
+ switch(field2) {
+ case AUDIT_FSGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_FSGID;
+ break;
+ case AUDIT_GID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_EGID;
+ break;
+ case AUDIT_OBJ_GID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_OBJ_GID;
+ break;
+ case AUDIT_SGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_SGID;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case AUDIT_FSGID:
+ switch(field2) {
+ case AUDIT_SGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_SGID_TO_FSGID;
+ break;
+ case AUDIT_GID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_FSGID;
+ break;
+ case AUDIT_OBJ_GID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_FSGID_TO_OBJ_GID;
+ break;
+ case AUDIT_EGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_FSGID;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case AUDIT_GID:
+ switch(field2) {
+ case AUDIT_EGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_EGID;
+ break;
+ case AUDIT_FSGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_FSGID;
+ break;
+ case AUDIT_OBJ_GID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_OBJ_GID;
+ break;
+ case AUDIT_SGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_SGID;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case AUDIT_OBJ_GID:
+ switch(field2) {
+ case AUDIT_EGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_OBJ_GID;
+ break;
+ case AUDIT_FSGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_FSGID_TO_OBJ_GID;
+ break;
+ case AUDIT_GID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_OBJ_GID;
+ break;
+ case AUDIT_SGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_SGID_TO_OBJ_GID;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case AUDIT_SGID:
+ switch(field2) {
+ case AUDIT_FSGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_SGID_TO_FSGID;
+ break;
+ case AUDIT_GID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_GID_TO_SGID;
+ break;
+ case AUDIT_OBJ_GID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_SGID_TO_OBJ_GID;
+ break;
+ case AUDIT_EGID:
+ rule->values[rule->field_count] = AUDIT_COMPARE_EGID_TO_SGID;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ break;
+ }
+ rule->field_count++;
+ return 0;
+}
+
+int audit_determine_machine(const char *arch)
+{ // What do we want? i686, x86_64, ia64 or b64, b32
+ int machine;
+ unsigned int bits = 0;
+
+ if (strcasecmp("b64", arch) == 0) {
+ bits = __AUDIT_ARCH_64BIT;
+ machine = audit_detect_machine();
+ } else if (strcasecmp("b32", arch) == 0) {
+ bits = ~__AUDIT_ARCH_64BIT;
+ machine = audit_detect_machine();
+ } else {
+ machine = audit_name_to_machine(arch);
+ if (machine < 0) {
+ // See if its numeric
+ unsigned int ival;
+ errno = 0;
+ ival = strtoul(arch, NULL, 16);
+ if (errno)
+ return -4;
+ machine = audit_elf_to_machine(ival);
+ }
+ }
+
+ if (machine < 0)
+ return -4;
+
+ /* Here's where we fixup the machine. For example, they give
+ * x86_64 & want 32 bits we translate that to i686. */
+ if (bits == ~__AUDIT_ARCH_64BIT && machine == MACH_86_64)
+ machine = MACH_X86;
+ else if (bits == ~__AUDIT_ARCH_64BIT && machine == MACH_PPC64)
+ machine = MACH_PPC;
+ else if (bits == ~__AUDIT_ARCH_64BIT && machine == MACH_S390X)
+ machine = MACH_S390;
+ else if (bits == ~__AUDIT_ARCH_64BIT && machine == MACH_AARCH64)
+ machine = MACH_ARM;
+
+ /* Check for errors - return -6
+ * We don't allow 32 bit machines to specify 64 bit. */
+ switch (machine)
+ {
+ case MACH_X86:
+ if (bits == __AUDIT_ARCH_64BIT)
+ return -6;
+ break;
+ case MACH_IA64:
+ if (bits == ~__AUDIT_ARCH_64BIT)
+ return -6;
+ break;
+ case MACH_PPC:
+ if (bits == __AUDIT_ARCH_64BIT)
+ return -6;
+ break;
+ case MACH_S390:
+ if (bits == __AUDIT_ARCH_64BIT)
+ return -6;
+ break;
+#ifdef WITH_ARM
+ case MACH_ARM:
+ if (bits == __AUDIT_ARCH_64BIT)
+ return -6; // Deadcode - just incase of mistake
+ break;
+#endif
+#ifdef WITH_AARCH64
+ case MACH_AARCH64:
+ if (bits && bits != __AUDIT_ARCH_64BIT)
+ return -6;
+ break;
+#endif
+ case MACH_86_64: /* fallthrough */
+ case MACH_PPC64: /* fallthrough */
+ case MACH_PPC64LE: /* fallthrough */
+ case MACH_S390X: /* fallthrough */
+ break;
+ default:
+ return -6;
+ }
+ return machine;
+}
+
+int audit_rule_fieldpair_data(struct audit_rule_data **rulep, const char *pair,
+ int flags)
+{
+ const char *f = pair;
+ char *v;
+ int op;
+ int field;
+ int vlen;
+ int offset;
+ struct audit_rule_data *rule = *rulep;
+
+ if (f == NULL)
+ return -1;
+
+ if (rule->field_count >= (AUDIT_MAX_FIELDS - 1))
+ return -28;
+
+ /* look for 2-char operators first
+ then look for 1-char operators afterwards
+ when found, null out the bytes under the operators to split
+ and set value pointer just past operator bytes
+ */
+ if ( (v = strstr(pair, "!=")) ) {
+ *v++ = '\0';
+ *v++ = '\0';
+ op = AUDIT_NOT_EQUAL;
+ } else if ( (v = strstr(pair, ">=")) ) {
+ *v++ = '\0';
+ *v++ = '\0';
+ op = AUDIT_GREATER_THAN_OR_EQUAL;
+ } else if ( (v = strstr(pair, "<=")) ) {
+ *v++ = '\0';
+ *v++ = '\0';
+ op = AUDIT_LESS_THAN_OR_EQUAL;
+ } else if ( (v = strstr(pair, "&=")) ) {
+ *v++ = '\0';
+ *v++ = '\0';
+ op = AUDIT_BIT_TEST;
+ } else if ( (v = strstr(pair, "=")) ) {
+ *v++ = '\0';
+ op = AUDIT_EQUAL;
+ } else if ( (v = strstr(pair, ">")) ) {
+ *v++ = '\0';
+ op = AUDIT_GREATER_THAN;
+ } else if ( (v = strstr(pair, "<")) ) {
+ *v++ = '\0';
+ op = AUDIT_LESS_THAN;
+ } else if ( (v = strstr(pair, "&")) ) {
+ *v++ = '\0';
+ op = AUDIT_BIT_MASK;
+ }
+
+ if (v == NULL)
+ return -1;
+
+ if (*f == 0)
+ return -22;
+
+ if (*v == 0)
+ return -20;
+
+ if ((field = audit_name_to_field(f)) < 0)
+ return -2;
+
+ /* Exclude filter can be used only with MSGTYPE field */
+ if (flags == AUDIT_FILTER_EXCLUDE && field != AUDIT_MSGTYPE)
+ return -12;
+
+ rule->fields[rule->field_count] = field;
+ rule->fieldflags[rule->field_count] = op;
+ switch (field)
+ {
+ case AUDIT_UID:
+ case AUDIT_EUID:
+ case AUDIT_SUID:
+ case AUDIT_FSUID:
+ case AUDIT_LOGINUID:
+ case AUDIT_OBJ_UID:
+ // Do positive & negative separate for 32 bit systems
+ vlen = strlen(v);
+ if (isdigit((char)*(v)))
+ rule->values[rule->field_count] =
+ strtoul(v, NULL, 0);
+ else if (vlen >= 2 && *(v)=='-' &&
+ (isdigit((char)*(v+1))))
+ rule->values[rule->field_count] =
+ strtol(v, NULL, 0);
+ else {
+ if (strcmp(v, "unset") == 0)
+ rule->values[rule->field_count] =
+ 4294967295;
+ else if (audit_name_to_uid(v,
+ &rule->values[rule->field_count])) {
+ audit_msg(LOG_ERR, "Unknown user: %s",
+ v);
+ return -2;
+ }
+ }
+ break;
+ case AUDIT_GID:
+ case AUDIT_EGID:
+ case AUDIT_SGID:
+ case AUDIT_FSGID:
+ case AUDIT_OBJ_GID:
+ if (isdigit((char)*(v)))
+ rule->values[rule->field_count] =
+ strtol(v, NULL, 0);
+ else {
+ if (audit_name_to_gid(v,
+ &rule->values[rule->field_count])) {
+ audit_msg(LOG_ERR, "Unknown group: %s",
+ v);
+ return -2;
+ }
+ }
+ break;
+ case AUDIT_EXIT:
+ if (flags != AUDIT_FILTER_EXIT)
+ return -7;
+ vlen = strlen(v);
+ if (isdigit((char)*(v)))
+ rule->values[rule->field_count] =
+ strtol(v, NULL, 0);
+ else if (vlen >= 2 && *(v)=='-' &&
+ (isdigit((char)*(v+1))))
+ rule->values[rule->field_count] =
+ strtol(v, NULL, 0);
+ else {
+ rule->values[rule->field_count] =
+ audit_name_to_errno(v);
+ if (rule->values[rule->field_count] == 0)
+ return -15;
+ }
+ break;
+ case AUDIT_MSGTYPE:
+ if (flags != AUDIT_FILTER_EXCLUDE &&
+ flags != AUDIT_FILTER_USER)
+ return -9;
+
+ if (isdigit((char)*(v)))
+ rule->values[rule->field_count] =
+ strtol(v, NULL, 0);
+ else
+ if (audit_name_to_msg_type(v) > 0)
+ rule->values[rule->field_count] =
+ audit_name_to_msg_type(v);
+ else
+ return -8;
+ break;
+ /* These next few are strings */
+ case AUDIT_OBJ_USER:
+ case AUDIT_OBJ_ROLE:
+ case AUDIT_OBJ_TYPE:
+ case AUDIT_OBJ_LEV_LOW:
+ case AUDIT_OBJ_LEV_HIGH:
+ case AUDIT_WATCH:
+ case AUDIT_DIR:
+ /* Watch & object filtering is invalid on anything
+ * but exit */
+ if (flags != AUDIT_FILTER_EXIT)
+ return -7;
+ if (field == AUDIT_WATCH || field == AUDIT_DIR)
+ _audit_permadded = 1;
+
+ /* fallthrough */
+ case AUDIT_SUBJ_USER:
+ case AUDIT_SUBJ_ROLE:
+ case AUDIT_SUBJ_TYPE:
+ case AUDIT_SUBJ_SEN:
+ case AUDIT_SUBJ_CLR:
+ case AUDIT_FILTERKEY:
+ if (field == AUDIT_FILTERKEY && !(_audit_syscalladded || _audit_permadded))
+ return -19;
+ vlen = strlen(v);
+ if (field == AUDIT_FILTERKEY &&
+ vlen > AUDIT_MAX_KEY_LEN)
+ return -11;
+ else if (vlen > PATH_MAX)
+ return -11;
+ rule->values[rule->field_count] = vlen;
+ offset = rule->buflen;
+ rule->buflen += vlen;
+ *rulep = realloc(rule, sizeof(*rule) + rule->buflen);
+ if (*rulep == NULL) {
+ free(rule);
+ audit_msg(LOG_ERR, "Cannot realloc memory!\n");
+ return -3;
+ } else {
+ rule = *rulep;
+ }
+ strncpy(&rule->buf[offset], v, vlen);
+
+ break;
+ case AUDIT_ARCH:
+ if (_audit_syscalladded)
+ return -3;
+ if (!(op == AUDIT_NOT_EQUAL || op == AUDIT_EQUAL))
+ return -13;
+ if (isdigit((char)*(v))) {
+ int machine;
+
+ errno = 0;
+ _audit_elf = strtoul(v, NULL, 0);
+ if (errno)
+ return -5;
+
+ // Make sure we have a valid mapping
+ machine = audit_elf_to_machine(_audit_elf);
+ if (machine < 0)
+ return -5;
+ }
+ else {
+ const char *arch=v;
+ unsigned int machine, elf;
+ machine = audit_determine_machine(arch);
+ /* OK, we have the machine type, now convert
+ to elf. */
+ elf = audit_machine_to_elf(machine);
+ if (elf == 0)
+ return -5;
+
+ _audit_elf = elf;
+ }
+ rule->values[rule->field_count] = _audit_elf;
+ _audit_archadded = 1;
+ break;
+ case AUDIT_PERM:
+ if (flags != AUDIT_FILTER_EXIT)
+ return -7;
+ else if (op != AUDIT_EQUAL)
+ return -13;
+ else {
+ unsigned int i, len, val = 0;
+
+ len = strlen(v);
+ if (len > 4)
+ return -11;
+
+ for (i = 0; i < len; i++) {
+ switch (tolower(v[i])) {
+ case 'r':
+ val |= AUDIT_PERM_READ;
+ break;
+ case 'w':
+ val |= AUDIT_PERM_WRITE;
+ break;
+ case 'x':
+ val |= AUDIT_PERM_EXEC;
+ break;
+ case 'a':
+ val |= AUDIT_PERM_ATTR;
+ break;
+ default:
+ return -14;
+ }
+ }
+ rule->values[rule->field_count] = val;
+ }
+ break;
+ case AUDIT_FILETYPE:
+ if (!(flags == AUDIT_FILTER_EXIT || flags == AUDIT_FILTER_ENTRY))
+ return -17;
+ rule->values[rule->field_count] =
+ audit_name_to_ftype(v);
+ if ((int)rule->values[rule->field_count] < 0) {
+ return -16;
+ }
+ break;
+ case AUDIT_ARG0...AUDIT_ARG3:
+ vlen = strlen(v);
+ if (isdigit((char)*(v)))
+ rule->values[rule->field_count] =
+ strtoul(v, NULL, 0);
+ else if (vlen >= 2 && *(v)=='-' &&
+ (isdigit((char)*(v+1))))
+ rule->values[rule->field_count] =
+ strtol(v, NULL, 0);
+ else
+ return -21;
+ break;
+ case AUDIT_DEVMAJOR...AUDIT_INODE:
+ case AUDIT_SUCCESS:
+ if (flags != AUDIT_FILTER_EXIT)
+ return -7;
+ /* fallthrough */
+ default:
+ if (field == AUDIT_INODE) {
+ if (!(op == AUDIT_NOT_EQUAL ||
+ op == AUDIT_EQUAL))
+ return -13;
+ }
+
+ if (field == AUDIT_PPID && !(flags == AUDIT_FILTER_EXIT
+ || flags == AUDIT_FILTER_ENTRY))
+ return -17;
+
+ if (!isdigit((char)*(v)))
+ return -21;
+
+ if (field == AUDIT_INODE)
+ rule->values[rule->field_count] =
+ strtoul(v, NULL, 0);
+ else
+ rule->values[rule->field_count] =
+ strtol(v, NULL, 0);
+ break;
+ }
+ rule->field_count++;
+ return 0;
+}
+
+void audit_rule_free_data(struct audit_rule_data *rule)
+{
+ free(rule);
+}
+
+static int audit_name_to_uid(const char *name, uid_t *uid)
+{
+ struct passwd *pw;
+
+ pw = getpwnam(name);
+ if (pw == NULL)
+ return 1;
+
+ memset(pw->pw_passwd, ' ', strlen(pw->pw_passwd));
+ *uid = pw->pw_uid;
+ return 0;
+}
+
+static int audit_name_to_gid(const char *name, gid_t *gid)
+{
+ struct group *gr;
+
+ gr = getgrnam(name);
+ if (gr == NULL)
+ return 1;
+
+ *gid = gr->gr_gid;
+ return 0;
+}
+
+int audit_detect_machine(void)
+{
+ struct utsname uts;
+ if (uname(&uts) == 0)
+// strcpy(uts.machine, "x86_64");
+ return audit_name_to_machine(uts.machine);
+ return -1;
+}
+hidden_def(audit_detect_machine)
+
+#ifndef NO_TABLES
+void audit_number_to_errmsg(int errnumber, const char *opt)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(err_msgtab)/sizeof(struct msg_tab); i++) {
+ if (err_msgtab[i].key == errnumber) {
+ switch (err_msgtab[i].position)
+ {
+ case 0:
+ fprintf(stderr, "%s\n",
+ err_msgtab[i].cvalue);
+ break;
+ case 1:
+ fprintf(stderr, "%s %s\n", opt,
+ err_msgtab[i].cvalue);
+ break;
+ case 2:
+ fprintf(stderr, "%s %s\n",
+ err_msgtab[i].cvalue, opt);
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+ }
+}
+#endif