/* libaudit.c -- 
 * Copyright 2004-2009,2012,2014,2016-17,2020 Red Hat Inc.
 * 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>
 *      Richard Guy Briggs <rgb@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/types.h>
#include <sys/socket.h> /* AF_MAX */
#ifdef HAVE_LIBCAP_NG
#include <cap-ng.h>
#endif
#ifdef WITH_IO_URING
#include <linux/io_uring.h>
#endif
#include "libaudit.h"
#include "private.h"
#include "errormsg.h"
#include "common.h"

/* #defines for the audit failure query  */
#define CONFIG_FILE "/etc/libaudit.conf"
#ifndef IORING_OP_LAST
#define IORING_OP_LAST 37
#endif

/* 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;
int _audit_exeadded = 0;
int _audit_filterfsadded = 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 *auid);
static int audit_name_to_gid(const char *name, gid_t *gid);
static char* filter_supported_syscalls(const char* syscalls, int machine) __attr_dealloc_free;

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 subsystem, 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),
			"%s: Error sending status request (%s)",
			get_progname(), strerror(-rc));
	return rc;
}

/*
 * 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|O_CLOEXEC);
	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_gid != 0 && (st.st_mode & S_IWGRP) == S_IWGRP) {
		audit_msg(LOG_ERR, "Error - %s is group writable", 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, "rme");
	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 && !audit_can_control()) {
		/* 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);

	if (audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0))
		; // intentionally empty
	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 == 1 || \
    HAVE_DECL_AUDIT_STATUS_BACKLOG_WAIT_TIME == 1
	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_reset_lost(int fd)
{
	int rc;
	int seq;
	struct audit_status s;

	if ((audit_get_features() & AUDIT_FEATURE_BITMAP_LOST_RESET) == 0)
		return -EAU_FIELDNOSUPPORT;

	memset(&s, 0, sizeof(s));
	s.mask = AUDIT_STATUS_LOST;
	s.lost = 0;
	rc = __audit_send(fd, AUDIT_SET, &s, sizeof(s), &seq);
	if (rc < 0)
		audit_msg(audit_priority(errno),
			"Error sending lost reset request (%s)",
			strerror(-rc));
	return rc;
}

int audit_reset_backlog_wait_time_actual(int fd)
{
	int rc = -1;

#if HAVE_DECL_AUDIT_STATUS_BACKLOG_WAIT_TIME_ACTUAL == 1
	struct audit_status s;
	int seq;

	memset(&s, 0, sizeof(s));
	s.mask          = AUDIT_STATUS_BACKLOG_WAIT_TIME_ACTUAL;
	s.backlog_wait_time_actual = 0;
	rc = __audit_send(fd, AUDIT_SET, &s, sizeof(s), &seq);
	if (rc < 0)
		audit_msg(audit_priority(errno),
			"Error sending backlog wait time actual reset 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 == 1
	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
}

int audit_request_features(int fd)
{
#if HAVE_DECL_AUDIT_FEATURE_VERSION == 1
	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
}

extern int  audit_set_loginuid_immutable(int fd)
{
#if HAVE_DECL_AUDIT_FEATURE_VERSION == 1
	return audit_set_feature(fd, AUDIT_FEATURE_LOGINUID_IMMUTABLE, 1, 1);
#else
	errno = EINVAL;
	return -1;
#endif
}

#define AUDIT_FEATURES_UNSET 0xFFFFFFFF
#define AUDIT_FEATURES_UNSUPPORTED 0xEFFFFFFF
static uint32_t features_bitmap = AUDIT_FEATURES_UNSET;
static void load_feature_bitmap(void)
{
	int rc, fd;

	fd = audit_open();
	if (fd < 0) {
		features_bitmap = AUDIT_FEATURES_UNSUPPORTED;
		return;
	}

#if defined(HAVE_STRUCT_AUDIT_STATUS_FEATURE_BITMAP)
	if (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... */
				features_bitmap = rep.status->feature_bitmap;
				audit_close(fd);
				return;
			}
		}
	}
#endif
	features_bitmap = AUDIT_FEATURES_UNSUPPORTED;
	audit_close(fd);
}

uint32_t audit_get_features(void)
{
	if (features_bitmap == AUDIT_FEATURES_UNSET)
		load_feature_bitmap();

	if (features_bitmap == AUDIT_FEATURES_UNSUPPORTED)
		return 0;

	return features_bitmap;
}

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;
}

char *audit_format_signal_info(char *buf, int len, const char *op,
			       const struct audit_reply *rep, const char *res)
{
	struct stat sb;
	char path[32], ses[16];
	ssize_t rlen;
	snprintf(path, sizeof(path), "/proc/%u", rep->signal_info->pid);
	int fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
	if (fd >= 0) {
		if (fstat(fd, &sb) < 0)
			sb.st_uid = -1;
		close(fd);
	} else
		sb.st_uid = -1;
	snprintf(path, sizeof(path), "/proc/%u/sessionid",
		 rep->signal_info->pid);
	fd = open(path, O_RDONLY|O_CLOEXEC, rep->signal_info->pid);
	if (fd < 0)
		strcpy(ses, "4294967295");
	else {
		do {
			rlen = read(fd, ses, sizeof(ses));
		} while (rlen < 0 && errno == EINTR);
		close(fd);
		if (rlen < 0 || (size_t)rlen >= sizeof(ses))
			strcpy(ses, "4294967295");
		else
			ses[rlen] = 0;
	}
	if (rep->len == 24)
		snprintf(buf, len, "op=%s auid=%u uid=%u ses=%s pid=%d res=%s",
			op, rep->signal_info->uid, sb.st_uid, ses,
			rep->signal_info->pid, res);
	else
		snprintf(buf, len, "op=%s auid=%u uid=%u ses=%s pid=%d subj=%s res=%s",
			op,rep->signal_info->uid, sb.st_uid, ses,
			rep->signal_info->pid,
			rep->signal_info->ctx, res);
	return buf;
}

int audit_update_watch_perms(struct audit_rule_data *rule, int perms)
{
	unsigned int i, done=0;

	if (rule->field_count < 1) {
		audit_msg(LOG_ERR,
			 "Permissions should be preceded by other fields");
		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)) {
			audit_msg(LOG_ERR,
				  "Too many fields when adding permissions");
			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_watch_dir(int type, struct audit_rule_data **rulep,
			const char *path)
{
	size_t len = strlen(path);
	struct audit_rule_data *rule = *rulep, *tmp;

	if (rule && rule->field_count) {
		audit_msg(LOG_ERR, "Rule is not empty");
		return -1;
	}
	if (type != AUDIT_WATCH && type != AUDIT_DIR) {
		audit_msg(LOG_ERR, "Invalid type used");
		return -1;
	}

	// Use a temporary pointer to prevent memory leaks
	tmp = realloc(rule, len + sizeof(*rule));
	if (tmp == NULL) {
		free(rule);
		*rulep = NULL;
		audit_msg(LOG_ERR, "Cannot realloc memory!");
		return -1;
	}

	*rulep = tmp;
	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;
}


int audit_add_rule_data(int fd, struct audit_rule_data *rule,
                        int flags, int action)
{
	int rc;

	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;

	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 = calloc(1, sizeof(*cmd) + len1 + len2);

	if (!cmd) {
		audit_msg(LOG_ERR, "Cannot allocate memory!");
		return -ENOMEM;
	}

	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 retrieve the loginuid or -1 if there
 * is an error.
 */
uid_t audit_getloginuid(void)
{
	uid_t uid;
	int in;
	ssize_t len;
	char buf[16];

	errno = 0;
	in = open("/proc/self/loginuid", O_NOFOLLOW|O_RDONLY|O_CLOEXEC);
	if (in < 0)
		return -1;
	do {
		len = read(in, buf, sizeof(buf));
	} while (len < 0 && errno == EINTR);
	close(in);
	if (len < 0 || (size_t)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|O_CLOEXEC);
	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;
}

/*
 * This function will retrieve the login session or -2 if there
 * is an error.
 */
uint32_t audit_get_session(void)
{
	uint32_t ses;
	int in;
	ssize_t len;
	char buf[16];

	errno = 0;
	in = open("/proc/self/sessionid", O_NOFOLLOW|O_RDONLY|O_CLOEXEC);
	if (in < 0)
		return -2;
	do {
		len = read(in, buf, sizeof(buf));
	} while (len < 0 && errno == EINTR);
	close(in);
	if (len < 0 || (size_t)len >= sizeof(buf))
		return -2;
	buf[len] = 0;
	errno = 0;
	ses = strtoul(buf, 0, 10);
	if (errno)
		return -2;
	else
		return ses;
}

static 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;
	_audit_syscalladded = 1;
	return 0;
}

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; 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((unsigned char)scall[0]))
			nr = strtol(scall, NULL, 0);
	}
	if (nr >= 0)
		return audit_rule_syscall_data(rule, nr);
	return -1;
}

int audit_rule_io_uringbyname_data(struct audit_rule_data *rule,
                                  const char *scall)
{
#ifdef WITH_IO_URING
	int nr;

	if (!strcmp(scall, "all")) {
		int i, rc = 0;
		for (i = 0; i < IORING_OP_LAST && !rc; i++) {
			// while names resolve
			if (audit_uringop_to_name(i))
				rc = audit_rule_syscall_data(rule, i);
		}
		return rc;
	}
	nr = audit_name_to_uringop(scall);
	if (nr < 0) {
		if (isdigit((unsigned char)scall[0]))
			nr = strtol(scall, NULL, 0);
	}
	if (nr >= 0)
		return audit_rule_syscall_data(rule, nr);
#endif
	return -1;
}

int audit_rule_interfield_comp_data(struct audit_rule_data **rulep,
					 char *pair,
					 int flags)
{
	char *f = pair;
	char       *v;
	int        op;
	int        field1, field2;
	struct audit_rule_data *rule = *rulep;

	if (f == NULL)
		return -EAU_FILTERMISSING;

	if (rule->field_count >= (AUDIT_MAX_FIELDS - 1))
		return -EAU_FIELDTOOMANY;

	/* 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 -EAU_OPEQNOTEQ;
	}

	if (*f == 0)
		return -EAU_COMPFIELDNAME;

	if (*v == 0)
		return -EAU_COMPVAL;

	if ((field1 = audit_name_to_field(f)) < 0)
		return -EAU_COMPFIELDUNKNOWN;

	if ((field2 = audit_name_to_field(v)) < 0)
		return -EAU_COMPVALUNKNOWN;

	/* Interfield comparison can only be in exit filter */
	if (flags != AUDIT_FILTER_EXIT)
		return -EAU_EXITONLY;

	// 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 -EAU_COMPINCOMPAT;
			}
			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 -EAU_COMPINCOMPAT;
			}
			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 -EAU_COMPINCOMPAT;
			}
			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 -EAU_COMPINCOMPAT;
			}
			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 -EAU_COMPINCOMPAT;
			}
			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 -EAU_COMPINCOMPAT;
			}
			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 -EAU_COMPINCOMPAT;
			}
			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 -EAU_COMPINCOMPAT;
			}
			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 -EAU_COMPINCOMPAT;
			}
			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 -EAU_COMPINCOMPAT;
			}
			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 -EAU_COMPINCOMPAT;
			}
			break;
		default:
			return -EAU_COMPINCOMPAT;
			break;
	}
	rule->field_count++;
	return 0;
}

int audit_determine_machine(const char *arch)
{	// What do we want? i686, x86_64 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;
	else if (bits == ~__AUDIT_ARCH_64BIT && machine == MACH_RISCV64)
		machine = MACH_RISCV32;

	/* 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_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; /* 32 bit only */
			break;
#endif
#ifdef WITH_AARCH64
		case MACH_AARCH64:
			if (bits && bits != __AUDIT_ARCH_64BIT)
				return -6; /* 64 bit only */
			break;
#endif
#ifdef WITH_RISCV
		case MACH_RISCV32:
			if (bits == __AUDIT_ARCH_64BIT)
				return -6;
			break;
#endif
		case MACH_86_64:   /* fallthrough */
		case MACH_PPC64:   /* fallthrough */
		case MACH_S390X:   /* fallthrough */
		case MACH_IO_URING:
		case MACH_RISCV64: /* fallthrough */
			break;
		case MACH_PPC64LE: /* 64 bit only */
			if (bits && bits != __AUDIT_ARCH_64BIT)
				return -6;
			break;
		default:
			return -6;
	}
	return machine;
}

int _audit_parse_syscall(const char *optarg, struct audit_rule_data *rule)
{
	int retval = 0;
	char *saved;

	if (strchr(optarg, ',')) {
		char *ptr, *tmp = strdup(optarg);
		if (tmp == NULL)
			return -1;
		ptr = strtok_r(tmp, ",", &saved);
		while (ptr) {
			retval = audit_rule_syscallbyname_data(rule, ptr);
			if (retval != 0) {
				if (retval == -1) {
					audit_msg(LOG_ERR,
						"Syscall name unknown: %s",
						ptr);
					retval = -3; // error reported
				}
				break;
			}
			ptr = strtok_r(NULL, ",", &saved);
		}
		free(tmp);
		return retval;
	}

	return audit_rule_syscallbyname_data(rule, optarg);
}

/*
 * Filters unsupported syscalls from a comma-separated string based
 * on the given architecture. Returns a new string with supported syscalls
 * or NULL on error.
 */
static char* filter_supported_syscalls(const char* syscalls, int machine)
{
	if (syscalls == NULL) {
		return NULL;
	}

	char buf[512] = "";
	char* ptr = buf;
	const char* delimiter = ",";

	char* syscalls_copy = strdup(syscalls);
	if (syscalls_copy == NULL)
		return NULL;

	char* token = strtok(syscalls_copy, delimiter);
	int first = 1; // Track if this is the first syscall being added

	while (token != NULL) {
		if (audit_name_to_syscall(token, machine) != -1) {
			if (!first)
				*ptr++ = ',';
			ptr = stpcpy(ptr, token);
			first = 0;
		}
		token = strtok(NULL, delimiter);
	}

	free(syscalls_copy);

	// If no valid syscalls were found, return NULL
	if (ptr == buf) {
		return NULL;
	}

	return strdup(buf);
}

static int audit_add_perm_syscalls(int perm, struct audit_rule_data *rule)
{
	// We only get here if syscall notation is being used in the rule.
	// The -w -p -k options are handled in auditctl itself. See
	// audit_setup_watch_name and audit_setup_perms. Therefore if no
	// arch declared, we leave the old behavior for backwards compatibility
	// and just warn about performance.
	if (_audit_elf == 0) {
		audit_msg(LOG_INFO, "perm used without an arch is slower");
		return 0;
	}

	const int machine = audit_elf_to_machine(_audit_elf);
	const char *syscalls = audit_perm_to_name(perm);
	const char *syscalls_to_use;

	// The permtab table is hardcoded, but some syscalls, like rename
	// on arm64, are unavailable on certain architectures. To ensure compatibility,
	// we must avoid creating rules with unsupported syscalls.
	char* filtered_syscalls = filter_supported_syscalls(syscalls, machine);
	if (filtered_syscalls == NULL) {
		// use original syscalls in case we failed to parse - should not happen
		syscalls_to_use = syscalls;
		audit_msg(LOG_WARNING, "Filtering syscalls failed; using original syscalls.");
	} else {
		syscalls_to_use = filtered_syscalls;
	}

	int rc = _audit_parse_syscall(syscalls_to_use, rule);
	switch (rc)
	{
	case 0:
		_audit_syscalladded = 1;
		break;
	case -1: // Should never happen
		audit_msg(LOG_ERR, "Syscall name unknown: %s", syscalls_to_use);
		break;
	default: // Error reported - do nothing here
		break;
	}

	free(filtered_syscalls);
	return rc;
}

int audit_rule_fieldpair_data(struct audit_rule_data **rulep, char *pair,
                              int flags)
{
	char *f = pair;
	char       *v;
	int        op;
	int        field;
	int        vlen;
	int        offset;
	struct audit_rule_data *rule = *rulep, *tmp;

	if (f == NULL)
		return -EAU_FILTERMISSING;

	if (rule->field_count >= (AUDIT_MAX_FIELDS - 1))
		return -EAU_FIELDTOOMANY;

	/* 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 -EAU_OPMISSING;

	if (*f == 0)
		return -EAU_FIELDNAME;

	if (*v == 0)
		return -EAU_FIELDVALMISSING;

	if ((field = audit_name_to_field(f)) < 0)
		return -EAU_FIELDUNKNOWN;

	/* Exclude filter can be used only with MSGTYPE, cred, and EXE fields
	 * when the EXTEND Feature is not present. */
	if (flags == AUDIT_FILTER_EXCLUDE) {
		uint32_t features = audit_get_features();
		if ((features & AUDIT_FEATURE_BITMAP_EXCLUDE_EXTEND) == 0) {
			switch(field) {
				case AUDIT_PID:
				case AUDIT_UID:
				case AUDIT_GID:
				case AUDIT_LOGINUID:
				case AUDIT_MSGTYPE:
				case AUDIT_SUBJ_USER:
				case AUDIT_SUBJ_ROLE:
				case AUDIT_SUBJ_TYPE:
				case AUDIT_SUBJ_SEN:
				case AUDIT_SUBJ_CLR:
				case AUDIT_EXE:
					break;
				default:
					return -EAU_MSGTYPECREDEXCLUDE;
			}
		}
	}

	/* FS filter can be used only with FSTYPE field */
	if (flags == AUDIT_FILTER_FS) {
		uint32_t features = audit_get_features();
		if ((features & AUDIT_FEATURE_BITMAP_FILTER_FS) == 0) {
			return -EAU_FILTERNOSUPPORT;
		}
	}

	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((unsigned char)*(v)))
				rule->values[rule->field_count] =
					strtoul(v, NULL, 0);
			else if (vlen >= 2 && *(v)=='-' &&
						(isdigit((unsigned 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 -EAU_PRINT_NOTHING;
				}
			}
			break;
		case AUDIT_GID:
		case AUDIT_EGID:
		case AUDIT_SGID:
		case AUDIT_FSGID:
		case AUDIT_OBJ_GID:
			if (isdigit((unsigned 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 -EAU_PRINT_NOTHING;
				}
			}
			break;
		case AUDIT_EXIT:
			if (flags != AUDIT_FILTER_EXIT)
				return -EAU_EXITONLY;
			vlen = strlen(v);
			if (isdigit((unsigned char)*(v)))
				rule->values[rule->field_count] =
					strtol(v, NULL, 0);
			else if (vlen >= 2 && *(v)=='-' &&
						(isdigit((unsigned 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 -EAU_ERRUNKNOWN;
			}
			break;
		case AUDIT_MSGTYPE:
			if (flags != AUDIT_FILTER_EXCLUDE &&
					flags != AUDIT_FILTER_USER)
				return -EAU_MSGTYPEEXCLUDEUSER;

			if (isdigit((unsigned 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 -EAU_MSGTYPEUNKNOWN;
			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 -EAU_EXITONLY;
			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:
		case AUDIT_EXE:
			if (field == AUDIT_EXE) {
				uint32_t features = audit_get_features();
				if ((features & AUDIT_FEATURE_BITMAP_EXECUTABLE_PATH) == 0)
					return -EAU_FIELDNOSUPPORT;
				if (!(op == AUDIT_NOT_EQUAL || op == AUDIT_EQUAL))
					return -EAU_OPEQNOTEQ;
				_audit_exeadded = 1;
			}
			if (field == AUDIT_FILTERKEY &&
				!(_audit_syscalladded || _audit_permadded ||
				_audit_exeadded ||
				_audit_filterfsadded))
                                return -EAU_KEYDEP;
			vlen = strlen(v);
			if (field == AUDIT_FILTERKEY &&
					vlen > AUDIT_MAX_KEY_LEN)
				return -EAU_STRTOOLONG;
			else if (vlen > PATH_MAX)
				return -EAU_STRTOOLONG;
			rule->values[rule->field_count] = vlen;
			offset = rule->buflen;
			rule->buflen += vlen;
			tmp = realloc(rule, sizeof(*rule) + rule->buflen);
			if (tmp == NULL) {
				free(rule);
				audit_msg(LOG_ERR, "Cannot realloc memory!");
				return -3;
			}
			*rulep = tmp;
			rule = tmp;
			strncpy(&rule->buf[offset], v, vlen);

			break;
		case AUDIT_ARCH:
			if (_audit_syscalladded)
				return -EAU_ARCHMISPLACED;
			if (!(op == AUDIT_NOT_EQUAL || op == AUDIT_EQUAL))
				return -EAU_OPEQNOTEQ;
			if (isdigit((unsigned char)*(v))) {
				int machine;

				errno = 0;
				_audit_elf = strtoul(v, NULL, 0);
				if (errno)
					return -EAU_ELFUNKNOWN;

				// Make sure we have a valid mapping
				machine = audit_elf_to_machine(_audit_elf);
				if (machine < 0)
					return -EAU_ELFUNKNOWN;
			}
			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 -EAU_ELFUNKNOWN;

				_audit_elf = elf;
			}
			rule->values[rule->field_count] = _audit_elf;
			_audit_archadded = 1;
			break;
		case AUDIT_PERM:
			if (!(flags == AUDIT_FILTER_EXIT ||
			      flags == AUDIT_FILTER_EXCLUDE))
				return -EAU_EXITONLY;
			else if (op != AUDIT_EQUAL)
				return -EAU_OPEQ;
			else {
				unsigned int i, len, val = 0;
				int rc;

				len = strlen(v);
				if (len > 4)
					return -EAU_STRTOOLONG;

				for (i = 0; i < len; i++) {
					switch (tolower((unsigned char)v[i])) {
						case 'r':
							val |= AUDIT_PERM_READ;
				   rc=audit_add_perm_syscalls(AUDIT_PERM_READ,rule);
							break;
						case 'w':
							val |= AUDIT_PERM_WRITE;
				   rc=audit_add_perm_syscalls(AUDIT_PERM_WRITE,rule);
							break;
						case 'x':
							val |= AUDIT_PERM_EXEC;
				   rc=audit_add_perm_syscalls(AUDIT_PERM_EXEC,rule);
							break;
						case 'a':
							val |= AUDIT_PERM_ATTR;
				   rc=audit_add_perm_syscalls(AUDIT_PERM_ATTR,rule);
							break;
						default:
							return -EAU_PERMRWXA;
					}
					if (rc)
						return -EAU_PERM_SYSCALL;
				}
				rule->values[rule->field_count] = val;
			}
			break;
		case AUDIT_FILETYPE:
			if (!(flags == AUDIT_FILTER_EXIT))
				return -EAU_EXITONLY;
			rule->values[rule->field_count] =
				audit_name_to_ftype(v);
			if ((int)rule->values[rule->field_count] < 0) {
				return -EAU_FILETYPEUNKNOWN;
			}
			break;
		case AUDIT_FSTYPE:
			if (!(flags == AUDIT_FILTER_FS))
				return -EAU_FIELDUNAVAIL;
			if (!(op == AUDIT_NOT_EQUAL || op == AUDIT_EQUAL))
				return -EAU_OPEQNOTEQ;
			if (isdigit((unsigned char)*(v)))
				rule->values[rule->field_count] =
					strtoul(v, NULL, 0);
			else
				rule->values[rule->field_count] =
					audit_name_to_fstype(v);
			if ((int)rule->values[rule->field_count] == -1) {
				return -EAU_FSTYPEUNKNOWN;
			}
			_audit_filterfsadded = 1;
			break;
		case AUDIT_ARG0...AUDIT_ARG3:
			vlen = strlen(v);
			if (isdigit((unsigned char)*(v)))
				rule->values[rule->field_count] =
					strtoul(v, NULL, 0);
			else if (vlen >= 2 && *(v)=='-' &&
						(isdigit((unsigned char)*(v+1))))
				rule->values[rule->field_count] =
					strtol(v, NULL, 0);
			else
				return -EAU_FIELDVALNUM;
			break;
		case AUDIT_SESSIONID:
			if ((audit_get_features() &
				AUDIT_FEATURE_BITMAP_SESSIONID_FILTER) == 0)
				return -EAU_FIELDNOSUPPORT;
			if (flags != AUDIT_FILTER_EXCLUDE &&
			    flags != AUDIT_FILTER_USER &&
			    flags != AUDIT_FILTER_EXIT)
				return -EAU_FIELDNOFILTER;
			// Do positive & negative separate for 32 bit systems
			vlen = strlen(v);
			if (isdigit((unsigned char)*(v)))
				rule->values[rule->field_count] =
					strtoul(v, NULL, 0);
			else if (vlen >= 2 && *(v)=='-' &&
						(isdigit((unsigned char)*(v+1))))
				rule->values[rule->field_count] =
					strtol(v, NULL, 0);
			else if (strcmp(v, "unset") == 0)
				rule->values[rule->field_count] = 4294967295;
			break;
		case AUDIT_SADDR_FAM:
			rule->values[rule->field_count] = strtoul(v, NULL, 0);
			if (rule->values[rule->field_count] >= AF_MAX)
				return -EAU_FIELDVALTOOBIG;
			break;
		case AUDIT_DEVMAJOR...AUDIT_INODE:
		case AUDIT_SUCCESS:
			if (flags != AUDIT_FILTER_EXIT)
				return -EAU_EXITONLY;
			/* fallthrough */
		default:
			if (field == AUDIT_INODE) {
				if (!(op == AUDIT_NOT_EQUAL ||
							op == AUDIT_EQUAL))
					return -EAU_OPEQNOTEQ;
			}

			if (field == AUDIT_PPID && !(flags==AUDIT_FILTER_EXIT))
				return -EAU_EXITONLY;

			if (!isdigit((unsigned char)*(v)))
				return -EAU_FIELDVALNUM;

			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;
}

struct audit_rule_data *audit_rule_create_data(void)
{
	struct audit_rule_data *rule;

	rule = malloc(sizeof(*rule));
	if (rule != NULL) {
		audit_rule_init_data(rule);
	}
	return rule;
}

void audit_rule_init_data(struct audit_rule_data *rule)
{
	memset(rule, 0, sizeof(*rule));
}

void audit_rule_free_data(struct audit_rule_data *rule)
{
	free(rule);
}

static void memset_secure(void *buf, size_t size)
{
	buf = memset(buf, '\0', size);
	__asm__ __volatile__ ("" : : "r"(buf) : "memory");
}

// This use is OK because its creating rules for the local
// machine and is looking up a local user.
static int audit_name_to_uid(const char *name, uid_t *uid)
{
	struct passwd pwd;
	struct passwd *result;
	char *buf;
	long bufsize;
	int rc;

	bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
	if (bufsize <= 0)
		bufsize = 1024;

retry:
	buf = malloc(bufsize);
	if (!buf)
		return 1;

	errno = 0;
	rc = getpwnam_r(name, &pwd, buf, bufsize, &result);
	if (rc == ERANGE) {
		free(buf);
		if (__builtin_mul_overflow(bufsize, 2, &bufsize)) {
			errno = ENOMEM;
			return 1;
		}
		goto retry;
	}
	if (result == NULL) {
		free(buf);
		/* getpwnam() might return ECONNREFUSED in some very
		 * specific cases when using LDAP.
		 * Manually set it to ENOENT so callers don't get confused
		 * with netlink's ECONNREFUSED */
		if (errno == ECONNREFUSED)
			errno = ENOENT;
		return 1;
	}

	*uid = result->pw_uid;
	memset_secure(buf, bufsize);
	free(buf);

	return 0;
}

static int audit_name_to_gid(const char *name, gid_t *gid)
{
	struct group gr;
	struct group *result;
	char *buf;
	long bufsize;
	int rc;

	bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
	if (bufsize <= 0)
		bufsize = 1024;

retry:
	buf = malloc(bufsize);
	if (!buf)
		return 1;

	errno = 0;
	rc = getgrnam_r(name, &gr, buf, bufsize, &result);
	if (rc == ERANGE) {
		free(buf);
		if (__builtin_mul_overflow(bufsize, 2, &bufsize)) {
			errno = ENOMEM;
			return 1;
		}
		goto retry;
	}
	if (result == NULL) {
		free(buf);
		/* See above for explanation. */
		if (errno == ECONNREFUSED)
			errno = ENOENT;
		return 1;
	}

	*gid = result->gr_gid;
	memset_secure(buf, bufsize);
	free(buf);

	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;
}

#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

int audit_can_control(void)
{
#ifdef HAVE_LIBCAP_NG
	void *state = capng_save_state();
	int rc = capng_have_capability(CAPNG_EFFECTIVE, CAP_AUDIT_CONTROL);
	capng_restore_state(&state);

	return rc;
#else
	return (geteuid() == 0);
#endif
}

int audit_can_write(void)
{
#ifdef HAVE_LIBCAP_NG
	void *state = capng_save_state();
	int rc = capng_have_capability(CAPNG_EFFECTIVE, CAP_AUDIT_WRITE);
	capng_restore_state(&state);

	return rc;
#else
	return (geteuid() == 0);
#endif
}

int audit_can_read(void)
{
#if defined ( HAVE_LIBCAP_NG ) && ( CAP_AUDIT_READ )
	void *state = capng_save_state();
	int rc = capng_have_capability(CAPNG_EFFECTIVE, CAP_AUDIT_READ);
	capng_restore_state(&state);

	return rc;
#else
	return (geteuid() == 0);
#endif
}

// Leave this exposed until 2027, then hide it for good.
// This allows programs already compiled to keep working.
// But new programs can't see it.
void set_aumessage_mode(message_t mode, debug_message_t debug)
{
	_set_aumessage_mode(mode, debug);
}

