forked from linux-audit/audit-testsuite
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests/fanotify: add test for FANOTIFY event
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
Showing
5 changed files
with
286 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
trigger |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ); |