Re: [PATCH 1/7] audit: implement generic feature setting and retrieving

Eric Paris
2013-05-24T16:28:43Z

[-- Attachment #1: Type: text/plain, Size: 1174 bytes --]

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 <eparis@redhat.com>

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

[-- Attachment #2: Makefile --]
[-- Type: text/x-makefile, Size: 71 bytes --]

all: test

test: test.c
gcc -o test -Wall -W test.c netlink.c -laudit

[-- Attachment #3: netlink.c --]
[-- Type: text/x-csrc, Size: 2752 bytes --]

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

[-- Attachment #4: private.h --]
[-- Type: text/x-chdr, Size: 146 bytes --]

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

[-- Attachment #5: test.c --]
[-- Type: text/x-csrc, Size: 1984 bytes --]

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

[-- Attachment #6: Type: text/plain, Size: 0 bytes --]