Commit 9d99b164 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'audit-pr-20200601' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit

Pull audit updates from Paul Moore:
 "Summary of the significant patches:

   - Record information about binds/unbinds to the audit multicast
     socket. This helps identify which processes have/had access to the
     information in the audit stream.

   - Cleanup and add some additional information to the netfilter
     configuration events collected by audit.

   - Fix some of the audit error handling code so we don't leak network
     namespace references"

* tag 'audit-pr-20200601' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit:
  audit: add subj creds to NETFILTER_CFG record to
  audit: Replace zero-length array with flexible-array
  audit: make symbol 'audit_nfcfgs' static
  netfilter: add audit table unregister actions
  audit: tidy and extend netfilter_cfg x_tables
  audit: log audit netlink multicast bind and unbind
  audit: fix a net reference leak in audit_list_rules_send()
  audit: fix a net reference leak in audit_send_reply()
parents 91681e84 9d44a121
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
struct audit_sig_info { struct audit_sig_info {
uid_t uid; uid_t uid;
pid_t pid; pid_t pid;
char ctx[0]; char ctx[];
}; };
struct audit_buffer; struct audit_buffer;
...@@ -94,6 +94,12 @@ struct audit_ntp_data { ...@@ -94,6 +94,12 @@ struct audit_ntp_data {
struct audit_ntp_data {}; struct audit_ntp_data {};
#endif #endif
enum audit_nfcfgop {
AUDIT_XT_OP_REGISTER,
AUDIT_XT_OP_REPLACE,
AUDIT_XT_OP_UNREGISTER,
};
extern int is_audit_feature_set(int which); extern int is_audit_feature_set(int which);
extern int __init audit_register_class(int class, unsigned *list); extern int __init audit_register_class(int class, unsigned *list);
...@@ -379,6 +385,8 @@ extern void __audit_log_kern_module(char *name); ...@@ -379,6 +385,8 @@ extern void __audit_log_kern_module(char *name);
extern void __audit_fanotify(unsigned int response); extern void __audit_fanotify(unsigned int response);
extern void __audit_tk_injoffset(struct timespec64 offset); extern void __audit_tk_injoffset(struct timespec64 offset);
extern void __audit_ntp_log(const struct audit_ntp_data *ad); extern void __audit_ntp_log(const struct audit_ntp_data *ad);
extern void __audit_log_nfcfg(const char *name, u8 af, unsigned int nentries,
enum audit_nfcfgop op);
static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp) static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp)
{ {
...@@ -514,6 +522,14 @@ static inline void audit_ntp_log(const struct audit_ntp_data *ad) ...@@ -514,6 +522,14 @@ static inline void audit_ntp_log(const struct audit_ntp_data *ad)
__audit_ntp_log(ad); __audit_ntp_log(ad);
} }
static inline void audit_log_nfcfg(const char *name, u8 af,
unsigned int nentries,
enum audit_nfcfgop op)
{
if (audit_enabled)
__audit_log_nfcfg(name, af, nentries, op);
}
extern int audit_n_rules; extern int audit_n_rules;
extern int audit_signals; extern int audit_signals;
#else /* CONFIG_AUDITSYSCALL */ #else /* CONFIG_AUDITSYSCALL */
...@@ -646,6 +662,12 @@ static inline void audit_ntp_log(const struct audit_ntp_data *ad) ...@@ -646,6 +662,12 @@ static inline void audit_ntp_log(const struct audit_ntp_data *ad)
static inline void audit_ptrace(struct task_struct *t) static inline void audit_ptrace(struct task_struct *t)
{ } { }
static inline void audit_log_nfcfg(const char *name, u8 af,
unsigned int nentries,
enum audit_nfcfgop op)
{ }
#define audit_n_rules 0 #define audit_n_rules 0
#define audit_signals 0 #define audit_signals 0
#endif /* CONFIG_AUDITSYSCALL */ #endif /* CONFIG_AUDITSYSCALL */
......
...@@ -117,6 +117,7 @@ ...@@ -117,6 +117,7 @@
#define AUDIT_TIME_INJOFFSET 1332 /* Timekeeping offset injected */ #define AUDIT_TIME_INJOFFSET 1332 /* Timekeeping offset injected */
#define AUDIT_TIME_ADJNTPVAL 1333 /* NTP value adjustment */ #define AUDIT_TIME_ADJNTPVAL 1333 /* NTP value adjustment */
#define AUDIT_BPF 1334 /* BPF subsystem */ #define AUDIT_BPF 1334 /* BPF subsystem */
#define AUDIT_EVENT_LISTENER 1335 /* Task joined multicast read socket */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
......
...@@ -880,7 +880,7 @@ static int kauditd_thread(void *dummy) ...@@ -880,7 +880,7 @@ static int kauditd_thread(void *dummy)
return 0; return 0;
} }
int audit_send_list(void *_dest) int audit_send_list_thread(void *_dest)
{ {
struct audit_netlink_list *dest = _dest; struct audit_netlink_list *dest = _dest;
struct sk_buff *skb; struct sk_buff *skb;
...@@ -924,19 +924,30 @@ struct sk_buff *audit_make_reply(int seq, int type, int done, ...@@ -924,19 +924,30 @@ struct sk_buff *audit_make_reply(int seq, int type, int done,
return NULL; return NULL;
} }
static void audit_free_reply(struct audit_reply *reply)
{
if (!reply)
return;
if (reply->skb)
kfree_skb(reply->skb);
if (reply->net)
put_net(reply->net);
kfree(reply);
}
static int audit_send_reply_thread(void *arg) static int audit_send_reply_thread(void *arg)
{ {
struct audit_reply *reply = (struct audit_reply *)arg; struct audit_reply *reply = (struct audit_reply *)arg;
struct sock *sk = audit_get_sk(reply->net);
audit_ctl_lock(); audit_ctl_lock();
audit_ctl_unlock(); audit_ctl_unlock();
/* Ignore failure. It'll only happen if the sender goes away, /* Ignore failure. It'll only happen if the sender goes away,
because our timeout is set to infinite. */ because our timeout is set to infinite. */
netlink_unicast(sk, reply->skb, reply->portid, 0); netlink_unicast(audit_get_sk(reply->net), reply->skb, reply->portid, 0);
put_net(reply->net); reply->skb = NULL;
kfree(reply); audit_free_reply(reply);
return 0; return 0;
} }
...@@ -950,35 +961,32 @@ static int audit_send_reply_thread(void *arg) ...@@ -950,35 +961,32 @@ static int audit_send_reply_thread(void *arg)
* @payload: payload data * @payload: payload data
* @size: payload size * @size: payload size
* *
* Allocates an skb, builds the netlink message, and sends it to the port id. * Allocates a skb, builds the netlink message, and sends it to the port id.
* No failure notifications.
*/ */
static void audit_send_reply(struct sk_buff *request_skb, int seq, int type, int done, static void audit_send_reply(struct sk_buff *request_skb, int seq, int type, int done,
int multi, const void *payload, int size) int multi, const void *payload, int size)
{ {
struct net *net = sock_net(NETLINK_CB(request_skb).sk);
struct sk_buff *skb;
struct task_struct *tsk; struct task_struct *tsk;
struct audit_reply *reply = kmalloc(sizeof(struct audit_reply), struct audit_reply *reply;
GFP_KERNEL);
reply = kzalloc(sizeof(*reply), GFP_KERNEL);
if (!reply) if (!reply)
return; return;
skb = audit_make_reply(seq, type, done, multi, payload, size); reply->skb = audit_make_reply(seq, type, done, multi, payload, size);
if (!skb) if (!reply->skb)
goto out; goto err;
reply->net = get_net(sock_net(NETLINK_CB(request_skb).sk));
reply->net = get_net(net);
reply->portid = NETLINK_CB(request_skb).portid; reply->portid = NETLINK_CB(request_skb).portid;
reply->skb = skb;
tsk = kthread_run(audit_send_reply_thread, reply, "audit_send_reply"); tsk = kthread_run(audit_send_reply_thread, reply, "audit_send_reply");
if (!IS_ERR(tsk)) if (IS_ERR(tsk))
return; goto err;
kfree_skb(skb);
out: return;
kfree(reply);
err:
audit_free_reply(reply);
} }
/* /*
...@@ -1525,20 +1533,60 @@ static void audit_receive(struct sk_buff *skb) ...@@ -1525,20 +1533,60 @@ static void audit_receive(struct sk_buff *skb)
audit_ctl_unlock(); audit_ctl_unlock();
} }
/* Log information about who is connecting to the audit multicast socket */
static void audit_log_multicast(int group, const char *op, int err)
{
const struct cred *cred;
struct tty_struct *tty;
char comm[sizeof(current->comm)];
struct audit_buffer *ab;
if (!audit_enabled)
return;
ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_EVENT_LISTENER);
if (!ab)
return;
cred = current_cred();
tty = audit_get_tty();
audit_log_format(ab, "pid=%u uid=%u auid=%u tty=%s ses=%u",
task_pid_nr(current),
from_kuid(&init_user_ns, cred->uid),
from_kuid(&init_user_ns, audit_get_loginuid(current)),
tty ? tty_name(tty) : "(none)",
audit_get_sessionid(current));
audit_put_tty(tty);
audit_log_task_context(ab); /* subj= */
audit_log_format(ab, " comm=");
audit_log_untrustedstring(ab, get_task_comm(comm, current));
audit_log_d_path_exe(ab, current->mm); /* exe= */
audit_log_format(ab, " nl-mcgrp=%d op=%s res=%d", group, op, !err);
audit_log_end(ab);
}
/* Run custom bind function on netlink socket group connect or bind requests. */ /* Run custom bind function on netlink socket group connect or bind requests. */
static int audit_bind(struct net *net, int group) static int audit_multicast_bind(struct net *net, int group)
{ {
int err = 0;
if (!capable(CAP_AUDIT_READ)) if (!capable(CAP_AUDIT_READ))
return -EPERM; err = -EPERM;
audit_log_multicast(group, "connect", err);
return err;
}
return 0; static void audit_multicast_unbind(struct net *net, int group)
{
audit_log_multicast(group, "disconnect", 0);
} }
static int __net_init audit_net_init(struct net *net) static int __net_init audit_net_init(struct net *net)
{ {
struct netlink_kernel_cfg cfg = { struct netlink_kernel_cfg cfg = {
.input = audit_receive, .input = audit_receive,
.bind = audit_bind, .bind = audit_multicast_bind,
.unbind = audit_multicast_unbind,
.flags = NL_CFG_F_NONROOT_RECV, .flags = NL_CFG_F_NONROOT_RECV,
.groups = AUDIT_NLGRP_MAX, .groups = AUDIT_NLGRP_MAX,
}; };
......
...@@ -229,7 +229,7 @@ struct audit_netlink_list { ...@@ -229,7 +229,7 @@ struct audit_netlink_list {
struct sk_buff_head q; struct sk_buff_head q;
}; };
int audit_send_list(void *_dest); int audit_send_list_thread(void *_dest);
extern int selinux_audit_rule_update(void); extern int selinux_audit_rule_update(void);
......
...@@ -1161,11 +1161,8 @@ int audit_rule_change(int type, int seq, void *data, size_t datasz) ...@@ -1161,11 +1161,8 @@ int audit_rule_change(int type, int seq, void *data, size_t datasz)
*/ */
int audit_list_rules_send(struct sk_buff *request_skb, int seq) int audit_list_rules_send(struct sk_buff *request_skb, int seq)
{ {
u32 portid = NETLINK_CB(request_skb).portid;
struct net *net = sock_net(NETLINK_CB(request_skb).sk);
struct task_struct *tsk; struct task_struct *tsk;
struct audit_netlink_list *dest; struct audit_netlink_list *dest;
int err = 0;
/* We can't just spew out the rules here because we might fill /* We can't just spew out the rules here because we might fill
* the available socket buffer space and deadlock waiting for * the available socket buffer space and deadlock waiting for
...@@ -1173,25 +1170,26 @@ int audit_list_rules_send(struct sk_buff *request_skb, int seq) ...@@ -1173,25 +1170,26 @@ int audit_list_rules_send(struct sk_buff *request_skb, int seq)
* happen if we're actually running in the context of auditctl * happen if we're actually running in the context of auditctl
* trying to _send_ the stuff */ * trying to _send_ the stuff */
dest = kmalloc(sizeof(struct audit_netlink_list), GFP_KERNEL); dest = kmalloc(sizeof(*dest), GFP_KERNEL);
if (!dest) if (!dest)
return -ENOMEM; return -ENOMEM;
dest->net = get_net(net); dest->net = get_net(sock_net(NETLINK_CB(request_skb).sk));
dest->portid = portid; dest->portid = NETLINK_CB(request_skb).portid;
skb_queue_head_init(&dest->q); skb_queue_head_init(&dest->q);
mutex_lock(&audit_filter_mutex); mutex_lock(&audit_filter_mutex);
audit_list_rules(seq, &dest->q); audit_list_rules(seq, &dest->q);
mutex_unlock(&audit_filter_mutex); mutex_unlock(&audit_filter_mutex);
tsk = kthread_run(audit_send_list, dest, "audit_send_list"); tsk = kthread_run(audit_send_list_thread, dest, "audit_send_list");
if (IS_ERR(tsk)) { if (IS_ERR(tsk)) {
skb_queue_purge(&dest->q); skb_queue_purge(&dest->q);
put_net(dest->net);
kfree(dest); kfree(dest);
err = PTR_ERR(tsk); return PTR_ERR(tsk);
} }
return err; return 0;
} }
int audit_comparator(u32 left, u32 op, u32 right) int audit_comparator(u32 left, u32 op, u32 right)
......
...@@ -130,6 +130,17 @@ struct audit_tree_refs { ...@@ -130,6 +130,17 @@ struct audit_tree_refs {
struct audit_chunk *c[31]; struct audit_chunk *c[31];
}; };
struct audit_nfcfgop_tab {
enum audit_nfcfgop op;
const char *s;
};
static const struct audit_nfcfgop_tab audit_nfcfgs[] = {
{ AUDIT_XT_OP_REGISTER, "register" },
{ AUDIT_XT_OP_REPLACE, "replace" },
{ AUDIT_XT_OP_UNREGISTER, "unregister" },
};
static int audit_match_perm(struct audit_context *ctx, int mask) static int audit_match_perm(struct audit_context *ctx, int mask)
{ {
unsigned n; unsigned n;
...@@ -2542,6 +2553,26 @@ void __audit_ntp_log(const struct audit_ntp_data *ad) ...@@ -2542,6 +2553,26 @@ void __audit_ntp_log(const struct audit_ntp_data *ad)
audit_log_ntp_val(ad, "adjust", AUDIT_NTP_ADJUST); audit_log_ntp_val(ad, "adjust", AUDIT_NTP_ADJUST);
} }
void __audit_log_nfcfg(const char *name, u8 af, unsigned int nentries,
enum audit_nfcfgop op)
{
struct audit_buffer *ab;
char comm[sizeof(current->comm)];
ab = audit_log_start(audit_context(), GFP_KERNEL, AUDIT_NETFILTER_CFG);
if (!ab)
return;
audit_log_format(ab, "table=%s family=%u entries=%u op=%s",
name, af, nentries, audit_nfcfgs[op].s);
audit_log_format(ab, " pid=%u", task_pid_nr(current));
audit_log_task_context(ab); /* subj= */
audit_log_format(ab, " comm=");
audit_log_untrustedstring(ab, get_task_comm(comm, current));
audit_log_end(ab);
}
EXPORT_SYMBOL_GPL(__audit_log_nfcfg);
static void audit_log_task(struct audit_buffer *ab) static void audit_log_task(struct audit_buffer *ab)
{ {
kuid_t auid, uid; kuid_t auid, uid;
......
...@@ -1046,14 +1046,8 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl, ...@@ -1046,14 +1046,8 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
vfree(table); vfree(table);
vfree(counterstmp); vfree(counterstmp);
#ifdef CONFIG_AUDIT audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries,
if (audit_enabled) { AUDIT_XT_OP_REPLACE);
audit_log(audit_context(), GFP_KERNEL,
AUDIT_NETFILTER_CFG,
"table=%s family=%u entries=%u",
repl->name, AF_BRIDGE, repl->nentries);
}
#endif
return ret; return ret;
free_unlock: free_unlock:
...@@ -1128,6 +1122,8 @@ static void __ebt_unregister_table(struct net *net, struct ebt_table *table) ...@@ -1128,6 +1122,8 @@ static void __ebt_unregister_table(struct net *net, struct ebt_table *table)
mutex_lock(&ebt_mutex); mutex_lock(&ebt_mutex);
list_del(&table->list); list_del(&table->list);
mutex_unlock(&ebt_mutex); mutex_unlock(&ebt_mutex);
audit_log_nfcfg(table->name, AF_BRIDGE, table->private->nentries,
AUDIT_XT_OP_UNREGISTER);
EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size,
ebt_cleanup_entry, net, NULL); ebt_cleanup_entry, net, NULL);
if (table->private->nentries) if (table->private->nentries)
...@@ -1221,6 +1217,8 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table, ...@@ -1221,6 +1217,8 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table,
*res = NULL; *res = NULL;
} }
audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries,
AUDIT_XT_OP_REGISTER);
return ret; return ret;
free_unlock: free_unlock:
mutex_unlock(&ebt_mutex); mutex_unlock(&ebt_mutex);
......
...@@ -1408,15 +1408,9 @@ xt_replace_table(struct xt_table *table, ...@@ -1408,15 +1408,9 @@ xt_replace_table(struct xt_table *table,
} }
} }
#ifdef CONFIG_AUDIT audit_log_nfcfg(table->name, table->af, private->number,
if (audit_enabled) { !private->number ? AUDIT_XT_OP_REGISTER :
audit_log(audit_context(), GFP_KERNEL, AUDIT_XT_OP_REPLACE);
AUDIT_NETFILTER_CFG,
"table=%s family=%u entries=%u",
table->name, table->af, private->number);
}
#endif
return private; return private;
} }
EXPORT_SYMBOL_GPL(xt_replace_table); EXPORT_SYMBOL_GPL(xt_replace_table);
...@@ -1478,6 +1472,8 @@ void *xt_unregister_table(struct xt_table *table) ...@@ -1478,6 +1472,8 @@ void *xt_unregister_table(struct xt_table *table)
private = table->private; private = table->private;
list_del(&table->list); list_del(&table->list);
mutex_unlock(&xt[table->af].mutex); mutex_unlock(&xt[table->af].mutex);
audit_log_nfcfg(table->name, table->af, private->number,
AUDIT_XT_OP_UNREGISTER);
kfree(table); kfree(table);
return private; return private;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment