Merge tag 'audit-pr-20200803' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoor...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 4 Aug 2020 21:20:26 +0000 (14:20 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 4 Aug 2020 21:20:26 +0000 (14:20 -0700)
Pull audit updates from Paul Moore:
 "Aside from some smaller bug fixes, here are the highlights:

   - add a new backlog wait metric to the audit status message, this is
     intended to help admins determine how long processes have been
     waiting for the audit backlog queue to clear

   - generate audit records for nftables configuration changes

   - generate CWD audit records for for the relevant LSM audit records"

* tag 'audit-pr-20200803' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit:
  audit: report audit wait metric in audit status reply
  audit: purge audit_log_string from the intra-kernel audit API
  audit: issue CWD record to accompany LSM_AUDIT_DATA_* records
  audit: use the proper gfp flags in the audit_log_nfcfg() calls
  audit: remove unused !CONFIG_AUDITSYSCALL __audit_inode* stubs
  audit: add gfp parameter to audit_log_nfcfg
  audit: log nftables configuration change events
  audit: Use struct_size() helper in alloc_chunk

1  2 
kernel/audit.c
kernel/auditsc.c
net/netfilter/nf_tables_api.c

diff --combined kernel/audit.c
@@@ -136,6 -136,11 +136,11 @@@ u32              audit_sig_sid = 0
  */
  static atomic_t       audit_lost = ATOMIC_INIT(0);
  
+ /* Monotonically increasing sum of time the kernel has spent
+  * waiting while the backlog limit is exceeded.
+  */
+ static atomic_t audit_backlog_wait_time_actual = ATOMIC_INIT(0);
  /* Hash for inode-based rules */
  struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS];
  
@@@ -1201,17 -1206,18 +1206,18 @@@ static int audit_receive_msg(struct sk_
        case AUDIT_GET: {
                struct audit_status     s;
                memset(&s, 0, sizeof(s));
-               s.enabled               = audit_enabled;
-               s.failure               = audit_failure;
+               s.enabled                  = audit_enabled;
+               s.failure                  = audit_failure;
                /* NOTE: use pid_vnr() so the PID is relative to the current
                 *       namespace */
-               s.pid                   = auditd_pid_vnr();
-               s.rate_limit            = audit_rate_limit;
-               s.backlog_limit         = audit_backlog_limit;
-               s.lost                  = atomic_read(&audit_lost);
-               s.backlog               = skb_queue_len(&audit_queue);
-               s.feature_bitmap        = AUDIT_FEATURE_BITMAP_ALL;
-               s.backlog_wait_time     = audit_backlog_wait_time;
+               s.pid                      = auditd_pid_vnr();
+               s.rate_limit               = audit_rate_limit;
+               s.backlog_limit            = audit_backlog_limit;
+               s.lost                     = atomic_read(&audit_lost);
+               s.backlog                  = skb_queue_len(&audit_queue);
+               s.feature_bitmap           = AUDIT_FEATURE_BITMAP_ALL;
+               s.backlog_wait_time        = audit_backlog_wait_time;
+               s.backlog_wait_time_actual = atomic_read(&audit_backlog_wait_time_actual);
                audit_send_reply(skb, seq, AUDIT_GET, 0, 0, &s, sizeof(s));
                break;
        }
                        audit_log_config_change("lost", 0, lost, 1);
                        return lost;
                }
+               if (s.mask == AUDIT_STATUS_BACKLOG_WAIT_TIME_ACTUAL) {
+                       u32 actual = atomic_xchg(&audit_backlog_wait_time_actual, 0);
+                       audit_log_config_change("backlog_wait_time_actual", 0, actual, 1);
+                       return actual;
+               }
                break;
        }
        case AUDIT_GET_FEATURE:
@@@ -1800,7 -1812,7 +1812,7 @@@ struct audit_buffer *audit_log_start(st
  {
        struct audit_buffer *ab;
        struct timespec64 t;
 -      unsigned int uninitialized_var(serial);
 +      unsigned int serial;
  
        if (audit_initialized != AUDIT_INITIALIZED)
                return NULL;
                        /* sleep if we are allowed and we haven't exhausted our
                         * backlog wait limit */
                        if (gfpflags_allow_blocking(gfp_mask) && (stime > 0)) {
+                               long rtime = stime;
                                DECLARE_WAITQUEUE(wait, current);
  
                                add_wait_queue_exclusive(&audit_backlog_wait,
                                                         &wait);
                                set_current_state(TASK_UNINTERRUPTIBLE);
-                               stime = schedule_timeout(stime);
+                               stime = schedule_timeout(rtime);
+                               atomic_add(rtime - stime, &audit_backlog_wait_time_actual);
                                remove_wait_queue(&audit_backlog_wait, &wait);
                        } else {
                                if (audit_rate_check() && printk_ratelimit())
        }
  
        audit_get_stamp(ab->ctx, &t, &serial);
 -      audit_clear_dummy(ab->ctx);
        audit_log_format(ab, "audit(%llu.%03lu:%u): ",
                         (unsigned long long)t.tv_sec, t.tv_nsec/1000000, serial);
  
@@@ -2079,13 -2095,13 +2094,13 @@@ void audit_log_d_path(struct audit_buff
        /* We will allow 11 spaces for ' (deleted)' to be appended */
        pathname = kmalloc(PATH_MAX+11, ab->gfp_mask);
        if (!pathname) {
-               audit_log_string(ab, "<no_memory>");
+               audit_log_format(ab, "\"<no_memory>\"");
                return;
        }
        p = d_path(path, pathname, PATH_MAX+11);
        if (IS_ERR(p)) { /* Should never happen since we send PATH_MAX */
                /* FIXME: can we save some information here? */
-               audit_log_string(ab, "<too_long>");
+               audit_log_format(ab, "\"<too_long>\"");
        } else
                audit_log_untrustedstring(ab, p);
        kfree(pathname);
diff --combined kernel/auditsc.c
@@@ -75,6 -75,7 +75,7 @@@
  #include <linux/uaccess.h>
  #include <linux/fsnotify_backend.h>
  #include <uapi/linux/limits.h>
+ #include <uapi/linux/netfilter/nf_tables.h>
  
  #include "audit.h"
  
@@@ -136,9 -137,26 +137,26 @@@ struct audit_nfcfgop_tab 
  };
  
  static const struct audit_nfcfgop_tab audit_nfcfgs[] = {
-       { AUDIT_XT_OP_REGISTER,         "register"      },
-       { AUDIT_XT_OP_REPLACE,          "replace"       },
-       { AUDIT_XT_OP_UNREGISTER,       "unregister"    },
+       { AUDIT_XT_OP_REGISTER,                 "xt_register"              },
+       { AUDIT_XT_OP_REPLACE,                  "xt_replace"               },
+       { AUDIT_XT_OP_UNREGISTER,               "xt_unregister"            },
+       { AUDIT_NFT_OP_TABLE_REGISTER,          "nft_register_table"       },
+       { AUDIT_NFT_OP_TABLE_UNREGISTER,        "nft_unregister_table"     },
+       { AUDIT_NFT_OP_CHAIN_REGISTER,          "nft_register_chain"       },
+       { AUDIT_NFT_OP_CHAIN_UNREGISTER,        "nft_unregister_chain"     },
+       { AUDIT_NFT_OP_RULE_REGISTER,           "nft_register_rule"        },
+       { AUDIT_NFT_OP_RULE_UNREGISTER,         "nft_unregister_rule"      },
+       { AUDIT_NFT_OP_SET_REGISTER,            "nft_register_set"         },
+       { AUDIT_NFT_OP_SET_UNREGISTER,          "nft_unregister_set"       },
+       { AUDIT_NFT_OP_SETELEM_REGISTER,        "nft_register_setelem"     },
+       { AUDIT_NFT_OP_SETELEM_UNREGISTER,      "nft_unregister_setelem"   },
+       { AUDIT_NFT_OP_GEN_REGISTER,            "nft_register_gen"         },
+       { AUDIT_NFT_OP_OBJ_REGISTER,            "nft_register_obj"         },
+       { AUDIT_NFT_OP_OBJ_UNREGISTER,          "nft_unregister_obj"       },
+       { AUDIT_NFT_OP_OBJ_RESET,               "nft_reset_obj"            },
+       { AUDIT_NFT_OP_FLOWTABLE_REGISTER,      "nft_register_flowtable"   },
+       { AUDIT_NFT_OP_FLOWTABLE_UNREGISTER,    "nft_unregister_flowtable" },
+       { AUDIT_NFT_OP_INVALID,                 "nft_invalid"              },
  };
  
  static int audit_match_perm(struct audit_context *ctx, int mask)
@@@ -1417,9 -1435,6 +1435,9 @@@ static void audit_log_proctitle(void
        struct audit_context *context = audit_context();
        struct audit_buffer *ab;
  
 +      if (!context || context->dummy)
 +              return;
 +
        ab = audit_log_start(context, GFP_KERNEL, AUDIT_PROCTITLE);
        if (!ab)
                return; /* audit_panic or being filtered */
@@@ -1876,6 -1891,20 +1894,20 @@@ __audit_reusename(const __user char *up
        return NULL;
  }
  
+ inline void _audit_getcwd(struct audit_context *context)
+ {
+       if (!context->pwd.dentry)
+               get_fs_pwd(current->fs, &context->pwd);
+ }
+ void __audit_getcwd(void)
+ {
+       struct audit_context *context = audit_context();
+       if (context->in_syscall)
+               _audit_getcwd(context);
+ }
  /**
   * __audit_getname - add a name to the list
   * @name: name to add
@@@ -1900,8 -1929,7 +1932,7 @@@ void __audit_getname(struct filename *n
        name->aname = n;
        name->refcnt++;
  
-       if (!context->pwd.dentry)
-               get_fs_pwd(current->fs, &context->pwd);
+       _audit_getcwd(context);
  }
  
  static inline int audit_copy_fcaps(struct audit_names *name,
@@@ -2557,12 -2585,12 +2588,12 @@@ void __audit_ntp_log(const struct audit
  }
  
  void __audit_log_nfcfg(const char *name, u8 af, unsigned int nentries,
-                      enum audit_nfcfgop op)
+                      enum audit_nfcfgop op, gfp_t gfp)
  {
        struct audit_buffer *ab;
        char comm[sizeof(current->comm)];
  
-       ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_NETFILTER_CFG);
+       ab = audit_log_start(audit_context(), gfp, AUDIT_NETFILTER_CFG);
        if (!ab)
                return;
        audit_log_format(ab, "table=%s family=%u entries=%u op=%s",
@@@ -12,6 -12,7 +12,7 @@@
  #include <linux/netlink.h>
  #include <linux/vmalloc.h>
  #include <linux/rhashtable.h>
+ #include <linux/audit.h>
  #include <linux/netfilter.h>
  #include <linux/netfilter/nfnetlink.h>
  #include <linux/netfilter/nf_tables.h>
@@@ -188,6 -189,24 +189,6 @@@ static void nft_netdev_unregister_hooks
                nf_unregister_net_hook(net, &hook->ops);
  }
  
 -static int nft_register_basechain_hooks(struct net *net, int family,
 -                                      struct nft_base_chain *basechain)
 -{
 -      if (family == NFPROTO_NETDEV)
 -              return nft_netdev_register_hooks(net, &basechain->hook_list);
 -
 -      return nf_register_net_hook(net, &basechain->ops);
 -}
 -
 -static void nft_unregister_basechain_hooks(struct net *net, int family,
 -                                         struct nft_base_chain *basechain)
 -{
 -      if (family == NFPROTO_NETDEV)
 -              nft_netdev_unregister_hooks(net, &basechain->hook_list);
 -      else
 -              nf_unregister_net_hook(net, &basechain->ops);
 -}
 -
  static int nf_tables_register_hook(struct net *net,
                                   const struct nft_table *table,
                                   struct nft_chain *chain)
        if (basechain->type->ops_register)
                return basechain->type->ops_register(net, ops);
  
 -      return nft_register_basechain_hooks(net, table->family, basechain);
 +      if (table->family == NFPROTO_NETDEV)
 +              return nft_netdev_register_hooks(net, &basechain->hook_list);
 +
 +      return nf_register_net_hook(net, &basechain->ops);
  }
  
  static void nf_tables_unregister_hook(struct net *net,
        if (basechain->type->ops_unregister)
                return basechain->type->ops_unregister(net, ops);
  
 -      nft_unregister_basechain_hooks(net, table->family, basechain);
 +      if (table->family == NFPROTO_NETDEV)
 +              nft_netdev_unregister_hooks(net, &basechain->hook_list);
 +      else
 +              nf_unregister_net_hook(net, &basechain->ops);
  }
  
  static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
@@@ -681,6 -694,17 +682,17 @@@ static void nf_tables_table_notify(cons
  {
        struct sk_buff *skb;
        int err;
+       char *buf = kasprintf(GFP_KERNEL, "%s:%llu;?:0",
+                             ctx->table->name, ctx->table->handle);
+       audit_log_nfcfg(buf,
+                       ctx->family,
+                       ctx->table->use,
+                       event == NFT_MSG_NEWTABLE ?
+                               AUDIT_NFT_OP_TABLE_REGISTER :
+                               AUDIT_NFT_OP_TABLE_UNREGISTER,
+                       GFP_KERNEL);
+       kfree(buf);
  
        if (!ctx->report &&
            !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
@@@ -820,7 -844,8 +832,7 @@@ static void nft_table_disable(struct ne
                if (cnt && i++ == cnt)
                        break;
  
 -              nft_unregister_basechain_hooks(net, table->family,
 -                                             nft_base_chain(chain));
 +              nf_tables_unregister_hook(net, table, chain);
        }
  }
  
@@@ -835,7 -860,8 +847,7 @@@ static int nf_tables_table_enable(struc
                if (!nft_is_base_chain(chain))
                        continue;
  
 -              err = nft_register_basechain_hooks(net, table->family,
 -                                                 nft_base_chain(chain));
 +              err = nf_tables_register_hook(net, table, chain);
                if (err < 0)
                        goto err_register_hooks;
  
@@@ -880,12 -906,11 +892,12 @@@ static int nf_tables_updtable(struct nf
                nft_trans_table_enable(trans) = false;
        } else if (!(flags & NFT_TABLE_F_DORMANT) &&
                   ctx->table->flags & NFT_TABLE_F_DORMANT) {
 +              ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
                ret = nf_tables_table_enable(ctx->net, ctx->table);
 -              if (ret >= 0) {
 -                      ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
 +              if (ret >= 0)
                        nft_trans_table_enable(trans) = true;
 -              }
 +              else
 +                      ctx->table->flags |= NFT_TABLE_F_DORMANT;
        }
        if (ret < 0)
                goto err;
@@@ -1415,6 -1440,18 +1427,18 @@@ static void nf_tables_chain_notify(cons
  {
        struct sk_buff *skb;
        int err;
+       char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu",
+                             ctx->table->name, ctx->table->handle,
+                             ctx->chain->name, ctx->chain->handle);
+       audit_log_nfcfg(buf,
+                       ctx->family,
+                       ctx->chain->use,
+                       event == NFT_MSG_NEWCHAIN ?
+                               AUDIT_NFT_OP_CHAIN_REGISTER :
+                               AUDIT_NFT_OP_CHAIN_UNREGISTER,
+                       GFP_KERNEL);
+       kfree(buf);
  
        if (!ctx->report &&
            !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
@@@ -2680,6 -2717,18 +2704,18 @@@ static void nf_tables_rule_notify(cons
  {
        struct sk_buff *skb;
        int err;
+       char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu",
+                             ctx->table->name, ctx->table->handle,
+                             ctx->chain->name, ctx->chain->handle);
+       audit_log_nfcfg(buf,
+                       ctx->family,
+                       rule->handle,
+                       event == NFT_MSG_NEWRULE ?
+                               AUDIT_NFT_OP_RULE_REGISTER :
+                               AUDIT_NFT_OP_RULE_UNREGISTER,
+                       GFP_KERNEL);
+       kfree(buf);
  
        if (!ctx->report &&
            !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
@@@ -3682,6 -3731,18 +3718,18 @@@ static void nf_tables_set_notify(const 
        struct sk_buff *skb;
        u32 portid = ctx->portid;
        int err;
+       char *buf = kasprintf(gfp_flags, "%s:%llu;%s:%llu",
+                             ctx->table->name, ctx->table->handle,
+                             set->name, set->handle);
+       audit_log_nfcfg(buf,
+                       ctx->family,
+                       set->field_count,
+                       event == NFT_MSG_NEWSET ?
+                               AUDIT_NFT_OP_SET_REGISTER :
+                               AUDIT_NFT_OP_SET_UNREGISTER,
+                       gfp_flags);
+       kfree(buf);
  
        if (!ctx->report &&
            !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
@@@ -4798,6 -4859,18 +4846,18 @@@ static void nf_tables_setelem_notify(co
        u32 portid = ctx->portid;
        struct sk_buff *skb;
        int err;
+       char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu",
+                             ctx->table->name, ctx->table->handle,
+                             set->name, set->handle);
+       audit_log_nfcfg(buf,
+                       ctx->family,
+                       set->handle,
+                       event == NFT_MSG_NEWSETELEM ?
+                               AUDIT_NFT_OP_SETELEM_REGISTER :
+                               AUDIT_NFT_OP_SETELEM_UNREGISTER,
+                       GFP_KERNEL);
+       kfree(buf);
  
        if (!ctx->report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
                return;
@@@ -5879,6 -5952,20 +5939,20 @@@ static int nf_tables_dump_obj(struct sk
                            obj->ops->type->type != filter->type)
                                goto cont;
  
+                       if (reset) {
+                               char *buf = kasprintf(GFP_ATOMIC,
+                                                     "%s:%llu;?:0",
+                                                     table->name,
+                                                     table->handle);
+                               audit_log_nfcfg(buf,
+                                               family,
+                                               obj->handle,
+                                               AUDIT_NFT_OP_OBJ_RESET,
+                                               GFP_ATOMIC);
+                               kfree(buf);
+                       }
                        if (nf_tables_fill_obj_info(skb, net, NETLINK_CB(cb->skb).portid,
                                                    cb->nlh->nlmsg_seq,
                                                    NFT_MSG_NEWOBJ,
@@@ -5989,6 -6076,18 +6063,18 @@@ static int nf_tables_getobj(struct net 
        if (NFNL_MSG_TYPE(nlh->nlmsg_type) == NFT_MSG_GETOBJ_RESET)
                reset = true;
  
+       if (reset) {
+               char *buf = kasprintf(GFP_ATOMIC, "%s:%llu;?:0",
+                                     table->name, table->handle);
+               audit_log_nfcfg(buf,
+                               family,
+                               obj->handle,
+                               AUDIT_NFT_OP_OBJ_RESET,
+                               GFP_ATOMIC);
+               kfree(buf);
+       }
        err = nf_tables_fill_obj_info(skb2, net, NETLINK_CB(skb).portid,
                                      nlh->nlmsg_seq, NFT_MSG_NEWOBJ, 0,
                                      family, table, obj, reset);
@@@ -6064,6 -6163,17 +6150,17 @@@ void nft_obj_notify(struct net *net, co
  {
        struct sk_buff *skb;
        int err;
+       char *buf = kasprintf(gfp, "%s:%llu;?:0",
+                             table->name, table->handle);
+       audit_log_nfcfg(buf,
+                       family,
+                       obj->handle,
+                       event == NFT_MSG_NEWOBJ ?
+                               AUDIT_NFT_OP_OBJ_REGISTER :
+                               AUDIT_NFT_OP_OBJ_UNREGISTER,
+                       gfp);
+       kfree(buf);
  
        if (!report &&
            !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
@@@ -6537,22 -6647,12 +6634,22 @@@ err1
        return err;
  }
  
 +static void nft_flowtable_hook_release(struct nft_flowtable_hook *flowtable_hook)
 +{
 +      struct nft_hook *this, *next;
 +
 +      list_for_each_entry_safe(this, next, &flowtable_hook->list, list) {
 +              list_del(&this->list);
 +              kfree(this);
 +      }
 +}
 +
  static int nft_delflowtable_hook(struct nft_ctx *ctx,
                                 struct nft_flowtable *flowtable)
  {
        const struct nlattr * const *nla = ctx->nla;
        struct nft_flowtable_hook flowtable_hook;
 -      struct nft_hook *this, *next, *hook;
 +      struct nft_hook *this, *hook;
        struct nft_trans *trans;
        int err;
  
        if (err < 0)
                return err;
  
 -      list_for_each_entry_safe(this, next, &flowtable_hook.list, list) {
 +      list_for_each_entry(this, &flowtable_hook.list, list) {
                hook = nft_hook_list_find(&flowtable->hook_list, this);
                if (!hook) {
                        err = -ENOENT;
                        goto err_flowtable_del_hook;
                }
                hook->inactive = true;
 -              list_del(&this->list);
 -              kfree(this);
        }
  
        trans = nft_trans_alloc(ctx, NFT_MSG_DELFLOWTABLE,
                                sizeof(struct nft_trans_flowtable));
 -      if (!trans)
 -              return -ENOMEM;
 +      if (!trans) {
 +              err = -ENOMEM;
 +              goto err_flowtable_del_hook;
 +      }
  
        nft_trans_flowtable(trans) = flowtable;
        nft_trans_flowtable_update(trans) = true;
        INIT_LIST_HEAD(&nft_trans_flowtable_hooks(trans));
 +      nft_flowtable_hook_release(&flowtable_hook);
  
        list_add_tail(&trans->list, &ctx->net->nft.commit_list);
  
        return 0;
  
  err_flowtable_del_hook:
 -      list_for_each_entry(hook, &flowtable_hook.list, list)
 +      list_for_each_entry(this, &flowtable_hook.list, list) {
 +              hook = nft_hook_list_find(&flowtable->hook_list, this);
 +              if (!hook)
 +                      break;
 +
                hook->inactive = false;
 +      }
 +      nft_flowtable_hook_release(&flowtable_hook);
  
        return err;
  }
@@@ -6860,6 -6953,18 +6957,18 @@@ static void nf_tables_flowtable_notify(
  {
        struct sk_buff *skb;
        int err;
+       char *buf = kasprintf(GFP_KERNEL, "%s:%llu;%s:%llu",
+                             flowtable->table->name, flowtable->table->handle,
+                             flowtable->name, flowtable->handle);
+       audit_log_nfcfg(buf,
+                       ctx->family,
+                       flowtable->hooknum,
+                       event == NFT_MSG_NEWFLOWTABLE ?
+                               AUDIT_NFT_OP_FLOWTABLE_REGISTER :
+                               AUDIT_NFT_OP_FLOWTABLE_UNREGISTER,
+                       GFP_KERNEL);
+       kfree(buf);
  
        if (ctx->report &&
            !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
@@@ -6981,6 -7086,9 +7090,9 @@@ static void nf_tables_gen_notify(struc
        struct sk_buff *skb2;
        int err;
  
+       audit_log_nfcfg("?:0;?:0", 0, net->nft.base_seq,
+                       AUDIT_NFT_OP_GEN_REGISTER, GFP_KERNEL);
        if (nlmsg_report(nlh) &&
            !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
                return;