Commit 6ad11bdd authored by Linus Torvalds's avatar Linus Torvalds

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

Pull audit updates from Paul Moore:
 "We didn't have anything to send for v4.16, but we're back with a
  little more than usual for v4.17.

  Eleven patches in total, most fall into the small fix category, but
  there are three non-trivial changes worth calling out:

   - the audit entry filter is being removed after deprecating it for
     quite a while (years of no one really using it because it turns out
     to be not very practical)

   - created our own version of "__mutex_owner()" because the locking
     folks were upset we were using theirs

   - improved our handling of kernel command line parameters to make
     them more forgiving

   - we fixed auditing of symlink operations

  Everything passes the audit-testsuite and as of a few minutes ago it
  merges well with your tree"

* tag 'audit-pr-20180403' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit:
  audit: add refused symlink to audit_names
  audit: remove path param from link denied function
  audit: link denied should not directly generate PATH record
  audit: make ANOM_LINK obey audit_enabled and audit_dummy_context
  audit: do not panic on invalid boot parameter
  audit: track the owner of the command mutex ourselves
  audit: return on memory error to avoid null pointer dereference
  audit: bail before bug check if audit disabled
  audit: deprecate the AUDIT_FILTER_ENTRY filter
  audit: session ID should not set arch quick field pointer
  audit: update bugtracker and source URIs
parents 69824bcc ea841baf
...@@ -389,15 +389,15 @@ ...@@ -389,15 +389,15 @@
Use software keyboard repeat Use software keyboard repeat
audit= [KNL] Enable the audit sub-system audit= [KNL] Enable the audit sub-system
Format: { "0" | "1" } (0 = disabled, 1 = enabled) Format: { "0" | "1" | "off" | "on" }
0 - kernel audit is disabled and can not be enabled 0 | off - kernel audit is disabled and can not be
until the next reboot enabled until the next reboot
unset - kernel audit is initialized but disabled and unset - kernel audit is initialized but disabled and
will be fully enabled by the userspace auditd. will be fully enabled by the userspace auditd.
1 - kernel audit is initialized and partially enabled, 1 | on - kernel audit is initialized and partially
storing at most audit_backlog_limit messages in enabled, storing at most audit_backlog_limit
RAM until it is fully enabled by the userspace messages in RAM until it is fully enabled by the
auditd. userspace auditd.
Default: unset Default: unset
audit_backlog_limit= [KNL] Set the audit queue size limit. audit_backlog_limit= [KNL] Set the audit queue size limit.
......
...@@ -2504,7 +2504,6 @@ M: Paul Moore <paul@paul-moore.com> ...@@ -2504,7 +2504,6 @@ M: Paul Moore <paul@paul-moore.com>
M: Eric Paris <eparis@redhat.com> M: Eric Paris <eparis@redhat.com>
L: linux-audit@redhat.com (moderated for non-subscribers) L: linux-audit@redhat.com (moderated for non-subscribers)
W: https://github.com/linux-audit W: https://github.com/linux-audit
W: https://people.redhat.com/sgrubb/audit
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit.git
S: Supported S: Supported
F: include/linux/audit.h F: include/linux/audit.h
......
...@@ -929,7 +929,8 @@ static inline int may_follow_link(struct nameidata *nd) ...@@ -929,7 +929,8 @@ static inline int may_follow_link(struct nameidata *nd)
if (nd->flags & LOOKUP_RCU) if (nd->flags & LOOKUP_RCU)
return -ECHILD; return -ECHILD;
audit_log_link_denied("follow_link", &nd->stack[0].link); audit_inode(nd->name, nd->stack[0].link.dentry, 0);
audit_log_link_denied("follow_link");
return -EACCES; return -EACCES;
} }
...@@ -995,7 +996,7 @@ static int may_linkat(struct path *link) ...@@ -995,7 +996,7 @@ static int may_linkat(struct path *link)
if (safe_hardlink_source(inode) || inode_owner_or_capable(inode)) if (safe_hardlink_source(inode) || inode_owner_or_capable(inode))
return 0; return 0;
audit_log_link_denied("linkat", link); audit_log_link_denied("linkat");
return -EPERM; return -EPERM;
} }
......
...@@ -146,8 +146,7 @@ extern void audit_log_d_path(struct audit_buffer *ab, ...@@ -146,8 +146,7 @@ extern void audit_log_d_path(struct audit_buffer *ab,
const struct path *path); const struct path *path);
extern void audit_log_key(struct audit_buffer *ab, extern void audit_log_key(struct audit_buffer *ab,
char *key); char *key);
extern void audit_log_link_denied(const char *operation, extern void audit_log_link_denied(const char *operation);
const struct path *link);
extern void audit_log_lost(const char *message); extern void audit_log_lost(const char *message);
extern int audit_log_task_context(struct audit_buffer *ab); extern int audit_log_task_context(struct audit_buffer *ab);
...@@ -194,8 +193,7 @@ static inline void audit_log_d_path(struct audit_buffer *ab, ...@@ -194,8 +193,7 @@ static inline void audit_log_d_path(struct audit_buffer *ab,
{ } { }
static inline void audit_log_key(struct audit_buffer *ab, char *key) static inline void audit_log_key(struct audit_buffer *ab, char *key)
{ } { }
static inline void audit_log_link_denied(const char *string, static inline void audit_log_link_denied(const char *string)
const struct path *link)
{ } { }
static inline int audit_log_task_context(struct audit_buffer *ab) static inline int audit_log_task_context(struct audit_buffer *ab)
{ {
......
...@@ -38,7 +38,8 @@ ...@@ -38,7 +38,8 @@
* 6) Support low-overhead kernel-based filtering to minimize the * 6) Support low-overhead kernel-based filtering to minimize the
* information that must be passed to user-space. * information that must be passed to user-space.
* *
* Example user-space utilities: http://people.redhat.com/sgrubb/audit/ * Audit userspace, documentation, tests, and bug/issue trackers:
* https://github.com/linux-audit
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
...@@ -180,9 +181,21 @@ static char *audit_feature_names[2] = { ...@@ -180,9 +181,21 @@ static char *audit_feature_names[2] = {
"loginuid_immutable", "loginuid_immutable",
}; };
/**
/* Serialize requests from userspace. */ * struct audit_ctl_mutex - serialize requests from userspace
DEFINE_MUTEX(audit_cmd_mutex); * @lock: the mutex used for locking
* @owner: the task which owns the lock
*
* Description:
* This is the lock struct used to ensure we only process userspace requests
* in an orderly fashion. We can't simply use a mutex/lock here because we
* need to track lock ownership so we don't end up blocking the lock owner in
* audit_log_start() or similar.
*/
static struct audit_ctl_mutex {
struct mutex lock;
void *owner;
} audit_cmd_mutex;
/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting /* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
* audit records. Since printk uses a 1024 byte buffer, this buffer * audit records. Since printk uses a 1024 byte buffer, this buffer
...@@ -226,6 +239,36 @@ int auditd_test_task(struct task_struct *task) ...@@ -226,6 +239,36 @@ int auditd_test_task(struct task_struct *task)
return rc; return rc;
} }
/**
* audit_ctl_lock - Take the audit control lock
*/
void audit_ctl_lock(void)
{
mutex_lock(&audit_cmd_mutex.lock);
audit_cmd_mutex.owner = current;
}
/**
* audit_ctl_unlock - Drop the audit control lock
*/
void audit_ctl_unlock(void)
{
audit_cmd_mutex.owner = NULL;
mutex_unlock(&audit_cmd_mutex.lock);
}
/**
* audit_ctl_owner_current - Test to see if the current task owns the lock
*
* Description:
* Return true if the current task owns the audit control lock, false if it
* doesn't own the lock.
*/
static bool audit_ctl_owner_current(void)
{
return (current == audit_cmd_mutex.owner);
}
/** /**
* auditd_pid_vnr - Return the auditd PID relative to the namespace * auditd_pid_vnr - Return the auditd PID relative to the namespace
* *
...@@ -860,8 +903,8 @@ int audit_send_list(void *_dest) ...@@ -860,8 +903,8 @@ int audit_send_list(void *_dest)
struct sock *sk = audit_get_sk(dest->net); struct sock *sk = audit_get_sk(dest->net);
/* wait for parent to finish and send an ACK */ /* wait for parent to finish and send an ACK */
mutex_lock(&audit_cmd_mutex); audit_ctl_lock();
mutex_unlock(&audit_cmd_mutex); audit_ctl_unlock();
while ((skb = __skb_dequeue(&dest->q)) != NULL) while ((skb = __skb_dequeue(&dest->q)) != NULL)
netlink_unicast(sk, skb, dest->portid, 0); netlink_unicast(sk, skb, dest->portid, 0);
...@@ -902,8 +945,8 @@ static int audit_send_reply_thread(void *arg) ...@@ -902,8 +945,8 @@ 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); struct sock *sk = audit_get_sk(reply->net);
mutex_lock(&audit_cmd_mutex); audit_ctl_lock();
mutex_unlock(&audit_cmd_mutex); 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. */
...@@ -1058,6 +1101,8 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature ...@@ -1058,6 +1101,8 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature
return; return;
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE); ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_FEATURE_CHANGE);
if (!ab)
return;
audit_log_task_info(ab, current); audit_log_task_info(ab, current);
audit_log_format(ab, " feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d", audit_log_format(ab, " feature=%s old=%u new=%u old_lock=%u new_lock=%u res=%d",
audit_feature_names[which], !!old_feature, !!new_feature, audit_feature_names[which], !!old_feature, !!new_feature,
...@@ -1466,7 +1511,7 @@ static void audit_receive(struct sk_buff *skb) ...@@ -1466,7 +1511,7 @@ static void audit_receive(struct sk_buff *skb)
nlh = nlmsg_hdr(skb); nlh = nlmsg_hdr(skb);
len = skb->len; len = skb->len;
mutex_lock(&audit_cmd_mutex); audit_ctl_lock();
while (nlmsg_ok(nlh, len)) { while (nlmsg_ok(nlh, len)) {
err = audit_receive_msg(skb, nlh); err = audit_receive_msg(skb, nlh);
/* if err or if this message says it wants a response */ /* if err or if this message says it wants a response */
...@@ -1475,7 +1520,7 @@ static void audit_receive(struct sk_buff *skb) ...@@ -1475,7 +1520,7 @@ static void audit_receive(struct sk_buff *skb)
nlh = nlmsg_next(nlh, &len); nlh = nlmsg_next(nlh, &len);
} }
mutex_unlock(&audit_cmd_mutex); audit_ctl_unlock();
} }
/* Run custom bind function on netlink socket group connect or bind requests. */ /* Run custom bind function on netlink socket group connect or bind requests. */
...@@ -1547,6 +1592,9 @@ static int __init audit_init(void) ...@@ -1547,6 +1592,9 @@ static int __init audit_init(void)
for (i = 0; i < AUDIT_INODE_BUCKETS; i++) for (i = 0; i < AUDIT_INODE_BUCKETS; i++)
INIT_LIST_HEAD(&audit_inode_hash[i]); INIT_LIST_HEAD(&audit_inode_hash[i]);
mutex_init(&audit_cmd_mutex.lock);
audit_cmd_mutex.owner = NULL;
pr_info("initializing netlink subsys (%s)\n", pr_info("initializing netlink subsys (%s)\n",
audit_default ? "enabled" : "disabled"); audit_default ? "enabled" : "disabled");
register_pernet_subsys(&audit_net_ops); register_pernet_subsys(&audit_net_ops);
...@@ -1567,19 +1615,26 @@ static int __init audit_init(void) ...@@ -1567,19 +1615,26 @@ static int __init audit_init(void)
} }
postcore_initcall(audit_init); postcore_initcall(audit_init);
/* Process kernel command-line parameter at boot time. audit=0 or audit=1. */ /*
* Process kernel command-line parameter at boot time.
* audit={0|off} or audit={1|on}.
*/
static int __init audit_enable(char *str) static int __init audit_enable(char *str)
{ {
long val; if (!strcasecmp(str, "off") || !strcmp(str, "0"))
audit_default = AUDIT_OFF;
if (kstrtol(str, 0, &val)) else if (!strcasecmp(str, "on") || !strcmp(str, "1"))
panic("audit: invalid 'audit' parameter value (%s)\n", str); audit_default = AUDIT_ON;
audit_default = (val ? AUDIT_ON : AUDIT_OFF); else {
pr_err("audit: invalid 'audit' parameter value (%s)\n", str);
audit_default = AUDIT_ON;
}
if (audit_default == AUDIT_OFF) if (audit_default == AUDIT_OFF)
audit_initialized = AUDIT_DISABLED; audit_initialized = AUDIT_DISABLED;
if (audit_set_enabled(audit_default)) if (audit_set_enabled(audit_default))
panic("audit: error setting audit state (%d)\n", audit_default); pr_err("audit: error setting audit state (%d)\n",
audit_default);
pr_info("%s\n", audit_default ? pr_info("%s\n", audit_default ?
"enabled (after initialization)" : "disabled (until reboot)"); "enabled (after initialization)" : "disabled (until reboot)");
...@@ -1710,8 +1765,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, ...@@ -1710,8 +1765,7 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
* using a PID anchored in the caller's namespace * using a PID anchored in the caller's namespace
* 2. generator holding the audit_cmd_mutex - we don't want to block * 2. generator holding the audit_cmd_mutex - we don't want to block
* while holding the mutex */ * while holding the mutex */
if (!(auditd_test_task(current) || if (!(auditd_test_task(current) || audit_ctl_owner_current())) {
(current == __mutex_owner(&audit_cmd_mutex)))) {
long stime = audit_backlog_wait_time; long stime = audit_backlog_wait_time;
while (audit_backlog_limit && while (audit_backlog_limit &&
...@@ -2254,33 +2308,23 @@ EXPORT_SYMBOL(audit_log_task_info); ...@@ -2254,33 +2308,23 @@ EXPORT_SYMBOL(audit_log_task_info);
/** /**
* audit_log_link_denied - report a link restriction denial * audit_log_link_denied - report a link restriction denial
* @operation: specific link operation * @operation: specific link operation
* @link: the path that triggered the restriction
*/ */
void audit_log_link_denied(const char *operation, const struct path *link) void audit_log_link_denied(const char *operation)
{ {
struct audit_buffer *ab; struct audit_buffer *ab;
struct audit_names *name;
name = kzalloc(sizeof(*name), GFP_NOFS); if (!audit_enabled || audit_dummy_context())
if (!name)
return; return;
/* Generate AUDIT_ANOM_LINK with subject, operation, outcome. */ /* Generate AUDIT_ANOM_LINK with subject, operation, outcome. */
ab = audit_log_start(current->audit_context, GFP_KERNEL, ab = audit_log_start(current->audit_context, GFP_KERNEL,
AUDIT_ANOM_LINK); AUDIT_ANOM_LINK);
if (!ab) if (!ab)
goto out; return;
audit_log_format(ab, "op=%s", operation); audit_log_format(ab, "op=%s", operation);
audit_log_task_info(ab, current); audit_log_task_info(ab, current);
audit_log_format(ab, " res=0"); audit_log_format(ab, " res=0");
audit_log_end(ab); audit_log_end(ab);
/* Generate AUDIT_PATH record with object. */
name->type = AUDIT_TYPE_NORMAL;
audit_copy_inode(name, link->dentry, d_backing_inode(link->dentry));
audit_log_name(current->audit_context, name, link, 0, NULL);
out:
kfree(name);
} }
/** /**
......
...@@ -341,4 +341,5 @@ extern struct list_head *audit_killed_trees(void); ...@@ -341,4 +341,5 @@ extern struct list_head *audit_killed_trees(void);
#define audit_filter_inodes(t,c) AUDIT_DISABLED #define audit_filter_inodes(t,c) AUDIT_DISABLED
#endif #endif
extern struct mutex audit_cmd_mutex; extern void audit_ctl_lock(void);
extern void audit_ctl_unlock(void);
...@@ -709,7 +709,7 @@ static int prune_tree_thread(void *unused) ...@@ -709,7 +709,7 @@ static int prune_tree_thread(void *unused)
schedule(); schedule();
} }
mutex_lock(&audit_cmd_mutex); audit_ctl_lock();
mutex_lock(&audit_filter_mutex); mutex_lock(&audit_filter_mutex);
while (!list_empty(&prune_list)) { while (!list_empty(&prune_list)) {
...@@ -727,7 +727,7 @@ static int prune_tree_thread(void *unused) ...@@ -727,7 +727,7 @@ static int prune_tree_thread(void *unused)
} }
mutex_unlock(&audit_filter_mutex); mutex_unlock(&audit_filter_mutex);
mutex_unlock(&audit_cmd_mutex); audit_ctl_unlock();
} }
return 0; return 0;
} }
...@@ -924,7 +924,7 @@ static void audit_schedule_prune(void) ...@@ -924,7 +924,7 @@ static void audit_schedule_prune(void)
*/ */
void audit_kill_trees(struct list_head *list) void audit_kill_trees(struct list_head *list)
{ {
mutex_lock(&audit_cmd_mutex); audit_ctl_lock();
mutex_lock(&audit_filter_mutex); mutex_lock(&audit_filter_mutex);
while (!list_empty(list)) { while (!list_empty(list)) {
...@@ -942,7 +942,7 @@ void audit_kill_trees(struct list_head *list) ...@@ -942,7 +942,7 @@ void audit_kill_trees(struct list_head *list)
} }
mutex_unlock(&audit_filter_mutex); mutex_unlock(&audit_filter_mutex);
mutex_unlock(&audit_cmd_mutex); audit_ctl_unlock();
} }
/* /*
......
...@@ -258,8 +258,8 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule_data * ...@@ -258,8 +258,8 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule_data *
goto exit_err; goto exit_err;
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
case AUDIT_FILTER_ENTRY: case AUDIT_FILTER_ENTRY:
if (rule->action == AUDIT_ALWAYS) pr_err("AUDIT_FILTER_ENTRY is deprecated\n");
goto exit_err; goto exit_err;
case AUDIT_FILTER_EXIT: case AUDIT_FILTER_EXIT:
case AUDIT_FILTER_TASK: case AUDIT_FILTER_TASK:
#endif #endif
...@@ -496,7 +496,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, ...@@ -496,7 +496,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
if (!gid_valid(f->gid)) if (!gid_valid(f->gid))
goto exit_free; goto exit_free;
break; break;
case AUDIT_SESSIONID:
case AUDIT_ARCH: case AUDIT_ARCH:
entry->rule.arch_f = f; entry->rule.arch_f = f;
break; break;
......
...@@ -1511,30 +1511,28 @@ void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2, ...@@ -1511,30 +1511,28 @@ void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2,
struct audit_context *context = tsk->audit_context; struct audit_context *context = tsk->audit_context;
enum audit_state state; enum audit_state state;
if (!context) if (!audit_enabled || !context)
return; return;
BUG_ON(context->in_syscall || context->name_count); BUG_ON(context->in_syscall || context->name_count);
if (!audit_enabled) state = context->state;
if (state == AUDIT_DISABLED)
return; return;
context->dummy = !audit_n_rules;
if (!context->dummy && state == AUDIT_BUILD_CONTEXT) {
context->prio = 0;
if (auditd_test_task(tsk))
return;
}
context->arch = syscall_get_arch(); context->arch = syscall_get_arch();
context->major = major; context->major = major;
context->argv[0] = a1; context->argv[0] = a1;
context->argv[1] = a2; context->argv[1] = a2;
context->argv[2] = a3; context->argv[2] = a3;
context->argv[3] = a4; context->argv[3] = a4;
state = context->state;
context->dummy = !audit_n_rules;
if (!context->dummy && state == AUDIT_BUILD_CONTEXT) {
context->prio = 0;
state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]);
}
if (state == AUDIT_DISABLED)
return;
context->serial = 0; context->serial = 0;
context->ctime = current_kernel_time64(); context->ctime = current_kernel_time64();
context->in_syscall = 1; context->in_syscall = 1;
......
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