On Fri, 2013-05-24 at 12:11 -0400, Eric Paris wrote:
> The audit_status structure was not designed with extensibility in mind.
> Define a new AUDIT_SET_FEATURE message type which takes a new structure
> of bits where things can be enabled/disabled/locked one at a time.  This
> structure should be able to grow in the future while maintaining forward
> and backward compatibility (based loosly on the ideas from capabilities
> and prctl)
> 
> This does not actually add any features, but is just infrastructure to
> allow new on/off types of audit system features.
> 
> Signed-off-by: Eric Paris <[email protected]>

Attached you will find the test program I used to check that things were
working correctly.  It should give an idea to Steve how we can program
the features support in userspace.  I believe it fits very nicely to
have a new syntax in audit.rules to set (and lock if needed/wanted)
these features.

netlink.c is just some helper code I stole from the audit tree to get
some functions which weren't exposed externally.  The only part really
interesting is test.c.

You will also need the include/uapi/linux/audit.h file from this patch
to build test.c

-Eric
all: test

test: test.c
	gcc -o test -Wall -W test.c netlink.c -laudit
#include <libaudit.h>
#include <string.h>
#include <linux/audit.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <sys/poll.h>
#include <stdio.h>
#include "private.h"

int check_ack(int fd)
{
	int rc, retries = 80;
	struct audit_reply rep;
	struct pollfd pfd[1];

retry:
	pfd[0].fd = fd;
	pfd[0].events = POLLIN;
	do {
		rc = poll(pfd, 1, 500); /* .5 second */
	} while (rc < 0 && errno == EINTR);

	/* We don't look at rc from above as it doesn't matter. We are 
	 * going to try to read nonblocking just in case packet shows up. */

	/* NOTE: whatever is returned is treated as the errno */
	rc = audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, MSG_PEEK);
	if (rc == -EAGAIN && retries) {
		retries--;
		goto retry;
	} else if (rc < 0)
		return rc;
	else if (rc == 0)
		return -EINVAL; /* This can't happen anymore */
	else if (rc > 0 && rep.type == NLMSG_ERROR) {
		int error = rep.error->error;
		/* Eat the message */
		(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;
}

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)) == 0)
			return sequence;
		else
			return retval; 
	}
	if (retval < 0) 
		return -errno;

	return 0;
}

int get_reply(int fd, void *data, size_t data_len)
{
	int len;
	struct sockaddr_nl nladdr;
	socklen_t nladdrlen = sizeof(nladdr);

	if (fd < 0)
		return -EBADF;

retry:
	len = recvfrom(fd, data, data_len, 0,
		(struct sockaddr*)&nladdr, &nladdrlen);

	if (len < 0) {
		if (errno == EINTR)
			goto retry;
		return -errno;
	}
	if (nladdrlen != sizeof(nladdr))
		return -EPROTO;
	if (nladdr.nl_pid)
		return -EINVAL;

	return len;
}

int check_ack(int fd);
int audit_send(int fd, int type, const void *data, unsigned int size);
int get_reply(int fd, void *data, size_t data_len);
#include <libaudit.h>
#include <string.h>
#include <linux/audit.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <sys/poll.h>
#include <stdio.h>
#include <stdlib.h>
#include "private.h"

#define UNSET_FEATURE_MASK	AUDIT_FEATURE_TO_MASK(AUDIT_FEATURE_ONLY_UNSET_LOGINUID)
#define IMMUTABLE_FEATURE_MASK	AUDIT_FEATURE_TO_MASK(AUDIT_FEATURE_LOGINUID_IMMUTABLE)

static void print_af(char *prefix, struct audit_features *af)
{
	fprintf(stdout, "%s vers=%d mask=%08x feature=%08x lock=%08x\n", prefix, af->vers, af->mask, af->features, af->lock);
}

static int get_and_print(int fd)
{
	struct audit_features af, *paf;
	char buf[4096];
	int rc;

	/* get the features */
	rc = audit_send(fd, AUDIT_GET_FEATURE, &af, sizeof(af));
	if (rc < 0)
		return rc;

	rc = get_reply(fd, buf, sizeof(buf));
	if (rc < 0)
		return rc;

	paf = NLMSG_DATA(buf);
	print_af("FROM:", paf);
	return 0;
}

static int set_features(int fd, struct audit_features *af)
{
	int rc;

	print_af("TO:", af);
	rc = audit_send(fd, AUDIT_SET_FEATURE, af, sizeof(*af));
	if (rc < 0) {
		perror("audit_send");
		return rc;
	}

	rc = get_and_print(fd);
	if (rc < 0)
		return rc;

	return 0;
}

int main(int argc, char *argv[])
{
	int fd;
	int rc;
	struct audit_features af;
	unsigned int mask = UNSET_FEATURE_MASK | IMMUTABLE_FEATURE_MASK;
	unsigned int features = 0;
	unsigned int lock = 0;

	if (argc < 4) {
		fprintf(stderr, "Dude, gets your args together, unset, immut, lock\n");
		return -EINVAL;
	}
	if (atoi(argv[1]))
		features |= UNSET_FEATURE_MASK;
	if (atoi(argv[2]))
		features |= IMMUTABLE_FEATURE_MASK;
	if (atoi(argv[3]))
		lock = mask;

	fd = audit_open();
	if (fd < 0)
		return fd;

	rc = get_and_print(fd);
	if (rc < 0)
		return rc;

	memset(&af, 0, sizeof(af));
	/* set new features */
	af.vers = AUDIT_FEATURE_VERSION;
	af.mask = mask;
	af.features = features;
	af.lock = lock;

	rc = set_features(fd, &af);
	if (rc < 0)
		return rc;

	return 0;
}
--
Linux-audit mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/linux-audit

Reply via email to