Skip to content

Commit

Permalink
tests/fanotify: add test for FANOTIFY event
Browse files Browse the repository at this point in the history
Smoke test for FANOTIFY audit events.

Signed-off-by: Ondrej Moris <omoris@redhat.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
  • Loading branch information
The-Mule authored and pcmoore committed Dec 12, 2019
1 parent 33a94af commit e3790a0
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 1 deletion.
3 changes: 2 additions & 1 deletion tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ TESTS := \
syscalls_file \
syscall_module \
syscall_socketcall \
user_msg
user_msg \
fanotify

# apply any ABI restrictions to the tests
ifneq ($(MACHINE),$(filter i386 x86_64,$(MACHINE)))
Expand Down
1 change: 1 addition & 0 deletions tests/fanotify/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
trigger
9 changes: 9 additions & 0 deletions tests/fanotify/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
TARGETS=$(patsubst %.c,%,$(wildcard *.c))

all: $(TARGETS)

fanotify: fanotify.c
$(CC) $(CFLAGS) -o $@ $^

clean:
rm -f $(TARGETS)
170 changes: 170 additions & 0 deletions tests/fanotify/fanotify.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/fanotify.h>
#include <unistd.h>

/* Read all available fanotify events from the file descriptor 'fd' */

static void
handle_events(int fd)
{
const struct fanotify_event_metadata *metadata;
struct fanotify_event_metadata buf[200];
ssize_t len;
char path[PATH_MAX];
ssize_t path_len;
char procfd_path[PATH_MAX];
struct fanotify_response response;

/* Loop while events can be read from fanotify file descriptor */

for(;;) {

/* Read some events */

len = read(fd, (void *) &buf, sizeof(buf));
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}

/* Check if end of available data reached */

if (len <= 0)
break;

/* Point to the first event in the buffer */

metadata = buf;

/* Loop over all events in the buffer */

while (FAN_EVENT_OK(metadata, len)) {

/* Check that run-time and compile-time structures match */

if (metadata->vers != FANOTIFY_METADATA_VERSION) {
fprintf(stderr,
"Mismatch of fanotify metadata version.\n");
exit(EXIT_FAILURE);
}

/* metadata->fd contains either FAN_NOFD, indicating a
queue overflow, or a file descriptor (a nonnegative
integer). Here, we simply ignore queue overflow. */

if (metadata->fd >= 0) {

/* Handle open permission event */

if (metadata->mask & FAN_OPEN_PERM) {
printf("FAN_OPEN_PERM: ");

/* Allow file to be opened */

response.fd = metadata->fd;
response.response = FAN_ALLOW | FAN_AUDIT;
write(fd, &response,
sizeof(struct fanotify_response));
}

/* Handle closing of writable file event */

if (metadata->mask & FAN_CLOSE_WRITE)
printf("FAN_CLOSE_WRITE: ");

/* Retrieve and print pathname of the accessed file */

snprintf(procfd_path, sizeof(procfd_path),
"/proc/self/fd/%d", metadata->fd);
path_len = readlink(procfd_path, path,
sizeof(path) - 1);
if (path_len == -1) {
perror("readlink");
exit(EXIT_FAILURE);
}

path[path_len] = '\0';
printf("File %s\n", path);

/* Close the file descriptor of the event */

close(metadata->fd);
}

/* Advance to next event */

metadata = FAN_EVENT_NEXT(metadata, len);
}
}
}

int
main(int argc, char *argv[])
{
int fd, poll_num;
nfds_t nfds;
struct pollfd fds[2];

/* Check mount point is supplied */
if (argc != 2) {
fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
exit(EXIT_FAILURE);
}

/* Create the file descriptor for accessing the fanotify API */
fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK |
FAN_ENABLE_AUDIT,
O_RDONLY);
if (fd == -1) {
perror("fanotify_init");
exit(EXIT_FAILURE);
}

/* Mark the mount for:
- permission events before opening files
- notification events after closing a write-enabled
file descriptor */
if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
FAN_OPEN_PERM | FAN_CLOSE_WRITE, -1,
argv[1]) == -1) {
perror("fanotify_mark");
exit(EXIT_FAILURE);
}

/* Prepare for polling */
nfds = 1;

/* Fanotify input */
fds[0].fd = fd;
fds[0].events = POLLIN;

/* This is the loop to wait for incoming events */
printf("Listening for events.\n");

while (1) {
poll_num = poll(fds, nfds, -1);
if (poll_num == -1) {
if (errno == EINTR) /* Interrupted by a signal */
continue; /* Restart poll() */

perror("poll"); /* Unexpected error */
exit(EXIT_FAILURE);
}

if (poll_num > 0) {
if (fds[0].revents & POLLIN) {

/* Fanotify events are available */
handle_events(fd);
}
}
}

printf("Listening for events stopped.\n");
exit(EXIT_SUCCESS);
}
104 changes: 104 additions & 0 deletions tests/fanotify/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/perl

use strict;

use Test;
BEGIN { plan tests => 4 }

use IPC::Open3;
use File::Temp qw/ tempfile unlink0 /;

my $basedir = $0;
$basedir =~ s|(.*)/[^/]*|$1|;

sub key_gen {
my @chars = ( "A" .. "Z", "a" .. "z" );
my $key = "testsuite-" . time . "-";
$key .= $chars[ rand @chars ] for 1 .. 8;
return $key;
}

# SETUP
# =====

# Clean audit rules.
system("auditctl -D >& /dev/null");

# Create sinks.
( my $fh_out, my $stdout ) = tempfile(
TEMPLATE => '/tmp/audit-testsuite-out-XXXX',
UNLINK => 1
);
( my $fh_err, my $stderr ) = tempfile(
TEMPLATE => '/tmp/audit-testsuite-err-XXXX',
UNLINK => 1
);

# Create test rule.
my $key = key_gen();
my $testfile = "/tmp/$key";
system("auditctl -w $testfile -k $key");

# Start fanotify watcher in the background.
my $fanotify_pid = open3( undef, undef, undef, "$basedir/fanotify /tmp" );
sleep 3;

# TRIGGER
# =======

# Open testfile to trigger fanotify events.
open( my $fh_test, ">", $testfile );
close($fh_test);

# CLEAN-UP
# ========

# Stop fanotify watcher.
if ( $fanotify_pid > 0 ) {
eval {
local $SIG{ALRM} = sub { kill 9, $fanotify_pid; };
alarm 6;
waitpid( $fanotify_pid, 0 );
alarm 0;
};
}

# Empty rules.
system("auditctl -D >& /dev/null");

# Remove testfile.
unlink0( $fh_test, $testfile );

# VERIFY
# ======

# Check that filter rule generated some events.
my $result = system("ausearch -i -k $key > $stdout 2> $stderr");

# Some events were triggered.
ok( $result == 0 );

# Check that correct events were generated.
my $fanotify_found = 0;
my $syscall_found = 0;
my $path_found = 0;
while ( my $line = <$fh_out> ) {
if ( $line =~ /^type=FANOTIFY .* resp=allow/ ) {
$fanotify_found = 1;
}
elsif ( $line =~ /^type=SYSCALL .* syscall=open(at)? success=yes/ ) {
$syscall_found = 1;
}
elsif ( $line =~ /^type=PATH .* name=$testfile/ ) {
$path_found = 1;
}
}

# FANOTIFY event was found.
ok( $fanotify_found == 1 );

# SYSCALL event was found.
ok( $syscall_found == 1 );

# PATH event was found.
ok( $path_found == 1 );

0 comments on commit e3790a0

Please sign in to comment.