-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests: netfilter: Test nf_tables audit logging
[ Upstream commit e8dbde5 ] Compare NETFILTER_CFG type audit logs emitted from kernel upon ruleset modifications against expected output. Signed-off-by: Phil Sutter <phil@nwl.cc> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Stable-dep-of: 0d880dc ("netfilter: nf_tables: Deduplicate nft_register_obj audit logs") Signed-off-by: Sasha Levin <sashal@kernel.org>
- Loading branch information
Showing
5 changed files
with
277 additions
and
2 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
# SPDX-License-Identifier: GPL-2.0-only | ||
nf-queue | ||
connect_close | ||
audit_logread |
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,165 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
#define _GNU_SOURCE | ||
#include <errno.h> | ||
#include <fcntl.h> | ||
#include <poll.h> | ||
#include <signal.h> | ||
#include <stdint.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <sys/socket.h> | ||
#include <unistd.h> | ||
#include <linux/audit.h> | ||
#include <linux/netlink.h> | ||
|
||
static int fd; | ||
|
||
#define MAX_AUDIT_MESSAGE_LENGTH 8970 | ||
struct audit_message { | ||
struct nlmsghdr nlh; | ||
union { | ||
struct audit_status s; | ||
char data[MAX_AUDIT_MESSAGE_LENGTH]; | ||
} u; | ||
}; | ||
|
||
int audit_recv(int fd, struct audit_message *rep) | ||
{ | ||
struct sockaddr_nl addr; | ||
socklen_t addrlen = sizeof(addr); | ||
int ret; | ||
|
||
do { | ||
ret = recvfrom(fd, rep, sizeof(*rep), 0, | ||
(struct sockaddr *)&addr, &addrlen); | ||
} while (ret < 0 && errno == EINTR); | ||
|
||
if (ret < 0 || | ||
addrlen != sizeof(addr) || | ||
addr.nl_pid != 0 || | ||
rep->nlh.nlmsg_type == NLMSG_ERROR) /* short-cut for now */ | ||
return -1; | ||
|
||
return ret; | ||
} | ||
|
||
int audit_send(int fd, uint16_t type, uint32_t key, uint32_t val) | ||
{ | ||
static int seq = 0; | ||
struct audit_message msg = { | ||
.nlh = { | ||
.nlmsg_len = NLMSG_SPACE(sizeof(msg.u.s)), | ||
.nlmsg_type = type, | ||
.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, | ||
.nlmsg_seq = ++seq, | ||
}, | ||
.u.s = { | ||
.mask = key, | ||
.enabled = key == AUDIT_STATUS_ENABLED ? val : 0, | ||
.pid = key == AUDIT_STATUS_PID ? val : 0, | ||
} | ||
}; | ||
struct sockaddr_nl addr = { | ||
.nl_family = AF_NETLINK, | ||
}; | ||
int ret; | ||
|
||
do { | ||
ret = sendto(fd, &msg, msg.nlh.nlmsg_len, 0, | ||
(struct sockaddr *)&addr, sizeof(addr)); | ||
} while (ret < 0 && errno == EINTR); | ||
|
||
if (ret != (int)msg.nlh.nlmsg_len) | ||
return -1; | ||
return 0; | ||
} | ||
|
||
int audit_set(int fd, uint32_t key, uint32_t val) | ||
{ | ||
struct audit_message rep = { 0 }; | ||
int ret; | ||
|
||
ret = audit_send(fd, AUDIT_SET, key, val); | ||
if (ret) | ||
return ret; | ||
|
||
ret = audit_recv(fd, &rep); | ||
if (ret < 0) | ||
return ret; | ||
return 0; | ||
} | ||
|
||
int readlog(int fd) | ||
{ | ||
struct audit_message rep = { 0 }; | ||
int ret = audit_recv(fd, &rep); | ||
const char *sep = ""; | ||
char *k, *v; | ||
|
||
if (ret < 0) | ||
return ret; | ||
|
||
if (rep.nlh.nlmsg_type != AUDIT_NETFILTER_CFG) | ||
return 0; | ||
|
||
/* skip the initial "audit(...): " part */ | ||
strtok(rep.u.data, " "); | ||
|
||
while ((k = strtok(NULL, "="))) { | ||
v = strtok(NULL, " "); | ||
|
||
/* these vary and/or are uninteresting, ignore */ | ||
if (!strcmp(k, "pid") || | ||
!strcmp(k, "comm") || | ||
!strcmp(k, "subj")) | ||
continue; | ||
|
||
/* strip the varying sequence number */ | ||
if (!strcmp(k, "table")) | ||
*strchrnul(v, ':') = '\0'; | ||
|
||
printf("%s%s=%s", sep, k, v); | ||
sep = " "; | ||
} | ||
if (*sep) { | ||
printf("\n"); | ||
fflush(stdout); | ||
} | ||
return 0; | ||
} | ||
|
||
void cleanup(int sig) | ||
{ | ||
audit_set(fd, AUDIT_STATUS_ENABLED, 0); | ||
close(fd); | ||
if (sig) | ||
exit(0); | ||
} | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
struct sigaction act = { | ||
.sa_handler = cleanup, | ||
}; | ||
|
||
fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT); | ||
if (fd < 0) { | ||
perror("Can't open netlink socket"); | ||
return -1; | ||
} | ||
|
||
if (sigaction(SIGTERM, &act, NULL) < 0 || | ||
sigaction(SIGINT, &act, NULL) < 0) { | ||
perror("Can't set signal handler"); | ||
close(fd); | ||
return -1; | ||
} | ||
|
||
audit_set(fd, AUDIT_STATUS_ENABLED, 1); | ||
audit_set(fd, AUDIT_STATUS_PID, getpid()); | ||
|
||
while (1) | ||
readlog(fd); | ||
} |
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 |
---|---|---|
|
@@ -6,3 +6,4 @@ CONFIG_NFT_REDIR=m | |
CONFIG_NFT_MASQ=m | ||
CONFIG_NFT_FLOW_OFFLOAD=m | ||
CONFIG_NF_CT_NETLINK=m | ||
CONFIG_AUDIT=y |
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,108 @@ | ||
#!/bin/bash | ||
# SPDX-License-Identifier: GPL-2.0 | ||
# | ||
# Check that audit logs generated for nft commands are as expected. | ||
|
||
SKIP_RC=4 | ||
RC=0 | ||
|
||
nft --version >/dev/null 2>&1 || { | ||
echo "SKIP: missing nft tool" | ||
exit $SKIP_RC | ||
} | ||
|
||
logfile=$(mktemp) | ||
echo "logging into $logfile" | ||
./audit_logread >"$logfile" & | ||
logread_pid=$! | ||
trap 'kill $logread_pid; rm -f $logfile' EXIT | ||
exec 3<"$logfile" | ||
|
||
do_test() { # (cmd, log) | ||
echo -n "testing for cmd: $1 ... " | ||
cat <&3 >/dev/null | ||
$1 >/dev/null || exit 1 | ||
sleep 0.1 | ||
res=$(diff -a -u <(echo "$2") - <&3) | ||
[ $? -eq 0 ] && { echo "OK"; return; } | ||
echo "FAIL" | ||
echo "$res" | ||
((RC++)) | ||
} | ||
|
||
nft flush ruleset | ||
|
||
for table in t1 t2; do | ||
do_test "nft add table $table" \ | ||
"table=$table family=2 entries=1 op=nft_register_table" | ||
|
||
do_test "nft add chain $table c1" \ | ||
"table=$table family=2 entries=1 op=nft_register_chain" | ||
|
||
do_test "nft add chain $table c2; add chain $table c3" \ | ||
"table=$table family=2 entries=2 op=nft_register_chain" | ||
|
||
cmd="add rule $table c1 counter" | ||
|
||
do_test "nft $cmd" \ | ||
"table=$table family=2 entries=1 op=nft_register_rule" | ||
|
||
do_test "nft $cmd; $cmd" \ | ||
"table=$table family=2 entries=2 op=nft_register_rule" | ||
|
||
cmd="" | ||
sep="" | ||
for chain in c2 c3; do | ||
for i in {1..3}; do | ||
cmd+="$sep add rule $table $chain counter" | ||
sep=";" | ||
done | ||
done | ||
do_test "nft $cmd" \ | ||
"table=$table family=2 entries=6 op=nft_register_rule" | ||
done | ||
|
||
do_test 'nft reset rules t1 c2' \ | ||
'table=t1 family=2 entries=3 op=nft_reset_rule' | ||
|
||
do_test 'nft reset rules table t1' \ | ||
'table=t1 family=2 entries=3 op=nft_reset_rule | ||
table=t1 family=2 entries=3 op=nft_reset_rule | ||
table=t1 family=2 entries=3 op=nft_reset_rule' | ||
|
||
do_test 'nft reset rules' \ | ||
'table=t1 family=2 entries=3 op=nft_reset_rule | ||
table=t1 family=2 entries=3 op=nft_reset_rule | ||
table=t1 family=2 entries=3 op=nft_reset_rule | ||
table=t2 family=2 entries=3 op=nft_reset_rule | ||
table=t2 family=2 entries=3 op=nft_reset_rule | ||
table=t2 family=2 entries=3 op=nft_reset_rule' | ||
|
||
for ((i = 0; i < 500; i++)); do | ||
echo "add rule t2 c3 counter accept comment \"rule $i\"" | ||
done | do_test 'nft -f -' \ | ||
'table=t2 family=2 entries=500 op=nft_register_rule' | ||
|
||
do_test 'nft reset rules t2 c3' \ | ||
'table=t2 family=2 entries=189 op=nft_reset_rule | ||
table=t2 family=2 entries=188 op=nft_reset_rule | ||
table=t2 family=2 entries=126 op=nft_reset_rule' | ||
|
||
do_test 'nft reset rules t2' \ | ||
'table=t2 family=2 entries=3 op=nft_reset_rule | ||
table=t2 family=2 entries=3 op=nft_reset_rule | ||
table=t2 family=2 entries=186 op=nft_reset_rule | ||
table=t2 family=2 entries=188 op=nft_reset_rule | ||
table=t2 family=2 entries=129 op=nft_reset_rule' | ||
|
||
do_test 'nft reset rules' \ | ||
'table=t1 family=2 entries=3 op=nft_reset_rule | ||
table=t1 family=2 entries=3 op=nft_reset_rule | ||
table=t1 family=2 entries=3 op=nft_reset_rule | ||
table=t2 family=2 entries=3 op=nft_reset_rule | ||
table=t2 family=2 entries=3 op=nft_reset_rule | ||
table=t2 family=2 entries=180 op=nft_reset_rule | ||
table=t2 family=2 entries=188 op=nft_reset_rule | ||
table=t2 family=2 entries=135 op=nft_reset_rule' | ||
|
||
exit $RC |