Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

audit: log nftables configuration change events once per table

Reduce logging of nftables events to a level similar to iptables.
Restore the table field to list the table, adding the generation.

Indicate the op as the most significant operation in the event.

A couple of sample events:

type=PROCTITLE msg=audit(2021-03-18 09:30:49.801:143) : proctitle=/usr/bin/python3 -s /usr/sbin/firewalld --nofork --nopid
type=SYSCALL msg=audit(2021-03-18 09:30:49.801:143) : arch=x86_64 syscall=sendmsg success=yes exit=172 a0=0x6 a1=0x7ffdcfcbe650 a2=0x0 a3=0x7ffdcfcbd52c items=0 ppid=1 pid=367 auid=unset uid=root gid=root euid=root suid=root fsuid=root egid=roo
t sgid=root fsgid=root tty=(none) ses=unset comm=firewalld exe=/usr/bin/python3.9 subj=system_u:system_r:firewalld_t:s0 key=(null)
type=NETFILTER_CFG msg=audit(2021-03-18 09:30:49.801:143) : table=firewalld:2 family=ipv6 entries=1 op=nft_register_table pid=367 subj=system_u:system_r:firewalld_t:s0 comm=firewalld
type=NETFILTER_CFG msg=audit(2021-03-18 09:30:49.801:143) : table=firewalld:2 family=ipv4 entries=1 op=nft_register_table pid=367 subj=system_u:system_r:firewalld_t:s0 comm=firewalld
type=NETFILTER_CFG msg=audit(2021-03-18 09:30:49.801:143) : table=firewalld:2 family=inet entries=1 op=nft_register_table pid=367 subj=system_u:system_r:firewalld_t:s0 comm=firewalld

type=PROCTITLE msg=audit(2021-03-18 09:30:49.839:144) : proctitle=/usr/bin/python3 -s /usr/sbin/firewalld --nofork --nopid
type=SYSCALL msg=audit(2021-03-18 09:30:49.839:144) : arch=x86_64 syscall=sendmsg success=yes exit=22792 a0=0x6 a1=0x7ffdcfcbe650 a2=0x0 a3=0x7ffdcfcbd52c items=0 ppid=1 pid=367 auid=unset uid=root gid=root euid=root suid=root fsuid=root egid=r
oot sgid=root fsgid=root tty=(none) ses=unset comm=firewalld exe=/usr/bin/python3.9 subj=system_u:system_r:firewalld_t:s0 key=(null)
type=NETFILTER_CFG msg=audit(2021-03-18 09:30:49.839:144) : table=firewalld:3 family=ipv6 entries=30 op=nft_register_chain pid=367 subj=system_u:system_r:firewalld_t:s0 comm=firewalld
type=NETFILTER_CFG msg=audit(2021-03-18 09:30:49.839:144) : table=firewalld:3 family=ipv4 entries=30 op=nft_register_chain pid=367 subj=system_u:system_r:firewalld_t:s0 comm=firewalld
type=NETFILTER_CFG msg=audit(2021-03-18 09:30:49.839:144) : table=firewalld:3 family=inet entries=165 op=nft_register_chain pid=367 subj=system_u:system_r:firewalld_t:s0 comm=firewalld

The issue was originally documented in
https://github.com/linux-audit/audit-kernel/issues/124

Signed-off-by: Richard Guy Briggs <rgb@redhat.com>
Acked-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Richard Guy Briggs and committed by
Pablo Neira Ayuso
c520292f cefa31a9

+103 -83
+103 -83
net/netfilter/nf_tables_api.c
··· 66 66 .automatic_shrinking = true, 67 67 }; 68 68 69 + struct nft_audit_data { 70 + struct nft_table *table; 71 + int entries; 72 + int op; 73 + struct list_head list; 74 + }; 75 + 76 + static const u8 nft2audit_op[NFT_MSG_MAX] = { // enum nf_tables_msg_types 77 + [NFT_MSG_NEWTABLE] = AUDIT_NFT_OP_TABLE_REGISTER, 78 + [NFT_MSG_GETTABLE] = AUDIT_NFT_OP_INVALID, 79 + [NFT_MSG_DELTABLE] = AUDIT_NFT_OP_TABLE_UNREGISTER, 80 + [NFT_MSG_NEWCHAIN] = AUDIT_NFT_OP_CHAIN_REGISTER, 81 + [NFT_MSG_GETCHAIN] = AUDIT_NFT_OP_INVALID, 82 + [NFT_MSG_DELCHAIN] = AUDIT_NFT_OP_CHAIN_UNREGISTER, 83 + [NFT_MSG_NEWRULE] = AUDIT_NFT_OP_RULE_REGISTER, 84 + [NFT_MSG_GETRULE] = AUDIT_NFT_OP_INVALID, 85 + [NFT_MSG_DELRULE] = AUDIT_NFT_OP_RULE_UNREGISTER, 86 + [NFT_MSG_NEWSET] = AUDIT_NFT_OP_SET_REGISTER, 87 + [NFT_MSG_GETSET] = AUDIT_NFT_OP_INVALID, 88 + [NFT_MSG_DELSET] = AUDIT_NFT_OP_SET_UNREGISTER, 89 + [NFT_MSG_NEWSETELEM] = AUDIT_NFT_OP_SETELEM_REGISTER, 90 + [NFT_MSG_GETSETELEM] = AUDIT_NFT_OP_INVALID, 91 + [NFT_MSG_DELSETELEM] = AUDIT_NFT_OP_SETELEM_UNREGISTER, 92 + [NFT_MSG_NEWGEN] = AUDIT_NFT_OP_GEN_REGISTER, 93 + [NFT_MSG_GETGEN] = AUDIT_NFT_OP_INVALID, 94 + [NFT_MSG_TRACE] = AUDIT_NFT_OP_INVALID, 95 + [NFT_MSG_NEWOBJ] = AUDIT_NFT_OP_OBJ_REGISTER, 96 + [NFT_MSG_GETOBJ] = AUDIT_NFT_OP_INVALID, 97 + [NFT_MSG_DELOBJ] = AUDIT_NFT_OP_OBJ_UNREGISTER, 98 + [NFT_MSG_GETOBJ_RESET] = AUDIT_NFT_OP_OBJ_RESET, 99 + [NFT_MSG_NEWFLOWTABLE] = AUDIT_NFT_OP_FLOWTABLE_REGISTER, 100 + [NFT_MSG_GETFLOWTABLE] = AUDIT_NFT_OP_INVALID, 101 + [NFT_MSG_DELFLOWTABLE] = AUDIT_NFT_OP_FLOWTABLE_UNREGISTER, 102 + }; 103 + 69 104 static void nft_validate_state_update(struct net *net, u8 new_validate_state) 70 105 { 71 106 switch (net->nft.validate_state) { ··· 753 718 { 754 719 struct sk_buff *skb; 755 720 int err; 756 - char *buf = kasprintf(GFP_KERNEL, "%s:%llu;?:0", 757 - ctx->table->name, ctx->table->handle); 758 - 759 - audit_log_nfcfg(buf, 760 - ctx->family, 761 - ctx->table->use, 762 - event == NFT_MSG_NEWTABLE ? 763 - AUDIT_NFT_OP_TABLE_REGISTER : 764 - AUDIT_NFT_OP_TABLE_UNREGISTER, 765 - GFP_KERNEL); 766 - kfree(buf); 767 721 768 722 if (!ctx->report && 769 723 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) ··· 1526 1502 { 1527 1503 struct sk_buff *skb; 1528 1504 int err; 1529 - char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu", 1530 - ctx->table->name, ctx->table->handle, 1531 - ctx->chain->name, ctx->chain->handle); 1532 - 1533 - audit_log_nfcfg(buf, 1534 - ctx->family, 1535 - ctx->chain->use, 1536 - event == NFT_MSG_NEWCHAIN ? 1537 - AUDIT_NFT_OP_CHAIN_REGISTER : 1538 - AUDIT_NFT_OP_CHAIN_UNREGISTER, 1539 - GFP_KERNEL); 1540 - kfree(buf); 1541 1505 1542 1506 if (!ctx->report && 1543 1507 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) ··· 2878 2866 { 2879 2867 struct sk_buff *skb; 2880 2868 int err; 2881 - char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu", 2882 - ctx->table->name, ctx->table->handle, 2883 - ctx->chain->name, ctx->chain->handle); 2884 - 2885 - audit_log_nfcfg(buf, 2886 - ctx->family, 2887 - rule->handle, 2888 - event == NFT_MSG_NEWRULE ? 2889 - AUDIT_NFT_OP_RULE_REGISTER : 2890 - AUDIT_NFT_OP_RULE_UNREGISTER, 2891 - GFP_KERNEL); 2892 - kfree(buf); 2893 2869 2894 2870 if (!ctx->report && 2895 2871 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) ··· 3912 3912 struct sk_buff *skb; 3913 3913 u32 portid = ctx->portid; 3914 3914 int err; 3915 - char *buf = kasprintf(gfp_flags, "%s:%llu;%s:%llu", 3916 - ctx->table->name, ctx->table->handle, 3917 - set->name, set->handle); 3918 - 3919 - audit_log_nfcfg(buf, 3920 - ctx->family, 3921 - set->field_count, 3922 - event == NFT_MSG_NEWSET ? 3923 - AUDIT_NFT_OP_SET_REGISTER : 3924 - AUDIT_NFT_OP_SET_UNREGISTER, 3925 - gfp_flags); 3926 - kfree(buf); 3927 3915 3928 3916 if (!ctx->report && 3929 3917 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) ··· 5096 5108 u32 portid = ctx->portid; 5097 5109 struct sk_buff *skb; 5098 5110 int err; 5099 - char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu", 5100 - ctx->table->name, ctx->table->handle, 5101 - set->name, set->handle); 5102 - 5103 - audit_log_nfcfg(buf, 5104 - ctx->family, 5105 - set->handle, 5106 - event == NFT_MSG_NEWSETELEM ? 5107 - AUDIT_NFT_OP_SETELEM_REGISTER : 5108 - AUDIT_NFT_OP_SETELEM_UNREGISTER, 5109 - GFP_KERNEL); 5110 - kfree(buf); 5111 5111 5112 5112 if (!ctx->report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) 5113 5113 return; ··· 6297 6321 filter->type != NFT_OBJECT_UNSPEC && 6298 6322 obj->ops->type->type != filter->type) 6299 6323 goto cont; 6300 - 6301 6324 if (reset) { 6302 6325 char *buf = kasprintf(GFP_ATOMIC, 6303 - "%s:%llu;?:0", 6326 + "%s:%u", 6304 6327 table->name, 6305 - table->handle); 6328 + net->nft.base_seq); 6306 6329 6307 6330 audit_log_nfcfg(buf, 6308 6331 family, ··· 6422 6447 reset = true; 6423 6448 6424 6449 if (reset) { 6425 - char *buf = kasprintf(GFP_ATOMIC, "%s:%llu;?:0", 6426 - table->name, table->handle); 6450 + char *buf = kasprintf(GFP_ATOMIC, "%s:%u", 6451 + table->name, net->nft.base_seq); 6427 6452 6428 6453 audit_log_nfcfg(buf, 6429 6454 family, ··· 6511 6536 { 6512 6537 struct sk_buff *skb; 6513 6538 int err; 6514 - char *buf = kasprintf(gfp, "%s:%llu;?:0", 6515 - table->name, table->handle); 6539 + char *buf = kasprintf(gfp, "%s:%u", 6540 + table->name, net->nft.base_seq); 6516 6541 6517 6542 audit_log_nfcfg(buf, 6518 6543 family, 6519 6544 obj->handle, 6520 6545 event == NFT_MSG_NEWOBJ ? 6521 - AUDIT_NFT_OP_OBJ_REGISTER : 6522 - AUDIT_NFT_OP_OBJ_UNREGISTER, 6546 + AUDIT_NFT_OP_OBJ_REGISTER : 6547 + AUDIT_NFT_OP_OBJ_UNREGISTER, 6523 6548 gfp); 6524 6549 kfree(buf); 6525 6550 ··· 7337 7362 { 7338 7363 struct sk_buff *skb; 7339 7364 int err; 7340 - char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu", 7341 - flowtable->table->name, flowtable->table->handle, 7342 - flowtable->name, flowtable->handle); 7343 - 7344 - audit_log_nfcfg(buf, 7345 - ctx->family, 7346 - flowtable->hooknum, 7347 - event == NFT_MSG_NEWFLOWTABLE ? 7348 - AUDIT_NFT_OP_FLOWTABLE_REGISTER : 7349 - AUDIT_NFT_OP_FLOWTABLE_UNREGISTER, 7350 - GFP_KERNEL); 7351 - kfree(buf); 7352 7365 7353 7366 if (!ctx->report && 7354 7367 !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) ··· 7456 7493 struct nlmsghdr *nlh = nlmsg_hdr(skb); 7457 7494 struct sk_buff *skb2; 7458 7495 int err; 7459 - 7460 - audit_log_nfcfg("?:0;?:0", 0, net->nft.base_seq, 7461 - AUDIT_NFT_OP_GEN_REGISTER, GFP_KERNEL); 7462 7496 7463 7497 if (!nlmsg_report(nlh) && 7464 7498 !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) ··· 7995 8035 WARN_ON_ONCE(!list_empty(&net->nft.notify_list)); 7996 8036 } 7997 8037 8038 + static int nf_tables_commit_audit_alloc(struct list_head *adl, 8039 + struct nft_table *table) 8040 + { 8041 + struct nft_audit_data *adp; 8042 + 8043 + list_for_each_entry(adp, adl, list) { 8044 + if (adp->table == table) 8045 + return 0; 8046 + } 8047 + adp = kzalloc(sizeof(*adp), GFP_KERNEL); 8048 + if (!adp) 8049 + return -ENOMEM; 8050 + adp->table = table; 8051 + list_add(&adp->list, adl); 8052 + return 0; 8053 + } 8054 + 8055 + static void nf_tables_commit_audit_collect(struct list_head *adl, 8056 + struct nft_table *table, u32 op) 8057 + { 8058 + struct nft_audit_data *adp; 8059 + 8060 + list_for_each_entry(adp, adl, list) { 8061 + if (adp->table == table) 8062 + goto found; 8063 + } 8064 + WARN_ONCE("table=%s not expected in commit list", table->name); 8065 + return; 8066 + found: 8067 + adp->entries++; 8068 + if (!adp->op || adp->op > op) 8069 + adp->op = op; 8070 + } 8071 + 8072 + #define AUNFTABLENAMELEN (NFT_TABLE_MAXNAMELEN + 22) 8073 + 8074 + static void nf_tables_commit_audit_log(struct list_head *adl, u32 generation) 8075 + { 8076 + struct nft_audit_data *adp, *adn; 8077 + char aubuf[AUNFTABLENAMELEN]; 8078 + 8079 + list_for_each_entry_safe(adp, adn, adl, list) { 8080 + snprintf(aubuf, AUNFTABLENAMELEN, "%s:%u", adp->table->name, 8081 + generation); 8082 + audit_log_nfcfg(aubuf, adp->table->family, adp->entries, 8083 + nft2audit_op[adp->op], GFP_KERNEL); 8084 + list_del(&adp->list); 8085 + kfree(adp); 8086 + } 8087 + } 8088 + 7998 8089 static int nf_tables_commit(struct net *net, struct sk_buff *skb) 7999 8090 { 8000 8091 struct nft_trans *trans, *next; 8001 8092 struct nft_trans_elem *te; 8002 8093 struct nft_chain *chain; 8003 8094 struct nft_table *table; 8095 + LIST_HEAD(adl); 8004 8096 int err; 8005 8097 8006 8098 if (list_empty(&net->nft.commit_list)) { ··· 8072 8060 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { 8073 8061 int ret; 8074 8062 8063 + ret = nf_tables_commit_audit_alloc(&adl, trans->ctx.table); 8064 + if (ret) { 8065 + nf_tables_commit_chain_prepare_cancel(net); 8066 + return ret; 8067 + } 8075 8068 if (trans->msg_type == NFT_MSG_NEWRULE || 8076 8069 trans->msg_type == NFT_MSG_DELRULE) { 8077 8070 chain = trans->ctx.chain; ··· 8105 8088 net->nft.gencursor = nft_gencursor_next(net); 8106 8089 8107 8090 list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) { 8091 + nf_tables_commit_audit_collect(&adl, trans->ctx.table, 8092 + trans->msg_type); 8108 8093 switch (trans->msg_type) { 8109 8094 case NFT_MSG_NEWTABLE: 8110 8095 if (nft_trans_table_update(trans)) { ··· 8259 8240 8260 8241 nft_commit_notify(net, NETLINK_CB(skb).portid); 8261 8242 nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN); 8243 + nf_tables_commit_audit_log(&adl, net->nft.base_seq); 8262 8244 nf_tables_commit_release(net); 8263 8245 8264 8246 return 0;