* (Initialization happens after skb_init is called.) */
static int audit_initialized;
-/* 0 - no auditing
- * 1 - auditing enabled
- * 2 - auditing enabled and configuration is locked/unchangeable. */
+#define AUDIT_OFF 0
+#define AUDIT_ON 1
+#define AUDIT_LOCKED 2
int audit_enabled;
+int audit_ever_enabled;
/* Default state when kernel boots without any parameters. */
static int audit_default;
static void audit_set_pid(struct audit_buffer *ab, pid_t pid)
{
- struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
- nlh->nlmsg_pid = pid;
+ if (ab) {
+ struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
+ nlh->nlmsg_pid = pid;
+ }
}
void audit_panic(const char *message)
case AUDIT_FAIL_SILENT:
break;
case AUDIT_FAIL_PRINTK:
- printk(KERN_ERR "audit: %s\n", message);
+ if (printk_ratelimit())
+ printk(KERN_ERR "audit: %s\n", message);
break;
case AUDIT_FAIL_PANIC:
panic("audit: %s\n", message);
}
if (print) {
- printk(KERN_WARNING
- "audit: audit_lost=%d audit_rate_limit=%d audit_backlog_limit=%d\n",
- atomic_read(&audit_lost),
- audit_rate_limit,
- audit_backlog_limit);
+ if (printk_ratelimit())
+ printk(KERN_WARNING
+ "audit: audit_lost=%d audit_rate_limit=%d "
+ "audit_backlog_limit=%d\n",
+ atomic_read(&audit_lost),
+ audit_rate_limit,
+ audit_backlog_limit);
audit_panic(message);
}
}
-static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sid)
+static int audit_log_config_change(char *function_name, int new, int old,
+ uid_t loginuid, u32 sid, int allow_changes)
{
- int res, rc = 0, old = audit_rate_limit;
-
- /* check if we are locked */
- if (audit_enabled == 2)
- res = 0;
- else
- res = 1;
+ struct audit_buffer *ab;
+ int rc = 0;
+ ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
+ audit_log_format(ab, "%s=%d old=%d by auid=%u", function_name, new,
+ old, loginuid);
if (sid) {
char *ctx = NULL;
u32 len;
- if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) {
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_rate_limit=%d old=%d by auid=%u"
- " subj=%s res=%d",
- limit, old, loginuid, ctx, res);
+
+ rc = selinux_sid_to_string(sid, &ctx, &len);
+ if (rc) {
+ audit_log_format(ab, " sid=%u", sid);
+ allow_changes = 0; /* Something weird, deny request */
+ } else {
+ audit_log_format(ab, " subj=%s", ctx);
kfree(ctx);
- } else
- res = 0; /* Something weird, deny request */
+ }
}
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_rate_limit=%d old=%d by auid=%u res=%d",
- limit, old, loginuid, res);
-
- /* If we are allowed, make the change */
- if (res == 1)
- audit_rate_limit = limit;
- /* Not allowed, update reason */
- else if (rc == 0)
- rc = -EPERM;
+ audit_log_format(ab, " res=%d", allow_changes);
+ audit_log_end(ab);
return rc;
}
-static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid)
+static int audit_do_config_change(char *function_name, int *to_change,
+ int new, uid_t loginuid, u32 sid)
{
- int res, rc = 0, old = audit_backlog_limit;
+ int allow_changes, rc = 0, old = *to_change;
/* check if we are locked */
- if (audit_enabled == 2)
- res = 0;
+ if (audit_enabled == AUDIT_LOCKED)
+ allow_changes = 0;
else
- res = 1;
+ allow_changes = 1;
- if (sid) {
- char *ctx = NULL;
- u32 len;
- if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) {
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_backlog_limit=%d old=%d by auid=%u"
- " subj=%s res=%d",
- limit, old, loginuid, ctx, res);
- kfree(ctx);
- } else
- res = 0; /* Something weird, deny request */
+ if (audit_enabled != AUDIT_OFF) {
+ rc = audit_log_config_change(function_name, new, old,
+ loginuid, sid, allow_changes);
+ if (rc)
+ allow_changes = 0;
}
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_backlog_limit=%d old=%d by auid=%u res=%d",
- limit, old, loginuid, res);
/* If we are allowed, make the change */
- if (res == 1)
- audit_backlog_limit = limit;
+ if (allow_changes == 1)
+ *to_change = new;
/* Not allowed, update reason */
else if (rc == 0)
rc = -EPERM;
return rc;
}
-static int audit_set_enabled(int state, uid_t loginuid, u32 sid)
+static int audit_set_rate_limit(int limit, uid_t loginuid, u32 sid)
{
- int res, rc = 0, old = audit_enabled;
+ return audit_do_config_change("audit_rate_limit", &audit_rate_limit,
+ limit, loginuid, sid);
+}
- if (state < 0 || state > 2)
+static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid)
+{
+ return audit_do_config_change("audit_backlog_limit", &audit_backlog_limit,
+ limit, loginuid, sid);
+}
+
+static int audit_set_enabled(int state, uid_t loginuid, u32 sid)
+{
+ int rc;
+ if (state < AUDIT_OFF || state > AUDIT_LOCKED)
return -EINVAL;
- /* check if we are locked */
- if (audit_enabled == 2)
- res = 0;
- else
- res = 1;
+ rc = audit_do_config_change("audit_enabled", &audit_enabled, state,
+ loginuid, sid);
- if (sid) {
- char *ctx = NULL;
- u32 len;
- if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) {
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_enabled=%d old=%d by auid=%u"
- " subj=%s res=%d",
- state, old, loginuid, ctx, res);
- kfree(ctx);
- } else
- res = 0; /* Something weird, deny request */
- }
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_enabled=%d old=%d by auid=%u res=%d",
- state, old, loginuid, res);
+ if (!rc)
+ audit_ever_enabled |= !!state;
- /* If we are allowed, make the change */
- if (res == 1)
- audit_enabled = state;
- /* Not allowed, update reason */
- else if (rc == 0)
- rc = -EPERM;
return rc;
}
static int audit_set_failure(int state, uid_t loginuid, u32 sid)
{
- int res, rc = 0, old = audit_failure;
-
if (state != AUDIT_FAIL_SILENT
&& state != AUDIT_FAIL_PRINTK
&& state != AUDIT_FAIL_PANIC)
return -EINVAL;
- /* check if we are locked */
- if (audit_enabled == 2)
- res = 0;
- else
- res = 1;
-
- if (sid) {
- char *ctx = NULL;
- u32 len;
- if ((rc = selinux_sid_to_string(sid, &ctx, &len)) == 0) {
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_failure=%d old=%d by auid=%u"
- " subj=%s res=%d",
- state, old, loginuid, ctx, res);
- kfree(ctx);
- } else
- res = 0; /* Something weird, deny request */
- }
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_failure=%d old=%d by auid=%u res=%d",
- state, old, loginuid, res);
-
- /* If we are allowed, make the change */
- if (res == 1)
- audit_failure = state;
- /* Not allowed, update reason */
- else if (rc == 0)
- rc = -EPERM;
- return rc;
+ return audit_do_config_change("audit_failure", &audit_failure, state,
+ loginuid, sid);
}
static int kauditd_thread(void *dummy)
audit_pid = 0;
}
} else {
- printk(KERN_NOTICE "%s\n", skb->data + NLMSG_SPACE(0));
+ if (printk_ratelimit())
+ printk(KERN_NOTICE "%s\n", skb->data +
+ NLMSG_SPACE(0));
+ else
+ audit_log_lost("printk limit exceeded\n");
kfree_skb(skb);
}
} else {
return 0;
}
+#ifdef CONFIG_AUDIT_TREE
+static int prune_tree_thread(void *unused)
+{
+ mutex_lock(&audit_cmd_mutex);
+ audit_prune_trees();
+ mutex_unlock(&audit_cmd_mutex);
+ return 0;
+}
+
+void audit_schedule_prune(void)
+{
+ kthread_run(prune_tree_thread, NULL, "audit_prune_tree");
+}
+#endif
+
struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
int multi, void *payload, int size)
{
case AUDIT_SIGNAL_INFO:
case AUDIT_TTY_GET:
case AUDIT_TTY_SET:
+ case AUDIT_TRIM:
+ case AUDIT_MAKE_EQUIV:
if (security_netlink_recv(skb, CAP_AUDIT_CONTROL))
err = -EPERM;
break;
return err;
}
+static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type,
+ u32 pid, u32 uid, uid_t auid, u32 sid)
+{
+ int rc = 0;
+ char *ctx = NULL;
+ u32 len;
+
+ if (!audit_enabled) {
+ *ab = NULL;
+ return rc;
+ }
+
+ *ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
+ audit_log_format(*ab, "user pid=%d uid=%u auid=%u",
+ pid, uid, auid);
+ if (sid) {
+ rc = selinux_sid_to_string(sid, &ctx, &len);
+ if (rc)
+ audit_log_format(*ab, " ssid=%u", sid);
+ else
+ audit_log_format(*ab, " subj=%s", ctx);
+ kfree(ctx);
+ }
+
+ return rc;
+}
+
static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
u32 uid, pid, seq, sid;
u16 msg_type = nlh->nlmsg_type;
uid_t loginuid; /* loginuid of sender */
struct audit_sig_info *sig_data;
- char *ctx;
+ char *ctx = NULL;
u32 len;
err = audit_netlink_ok(skb, msg_type);
if (err < 0) return err;
}
if (status_get->mask & AUDIT_STATUS_PID) {
- int old = audit_pid;
- if (sid) {
- if ((err = selinux_sid_to_string(
- sid, &ctx, &len)))
- return err;
- else
- audit_log(NULL, GFP_KERNEL,
- AUDIT_CONFIG_CHANGE,
- "audit_pid=%d old=%d by auid=%u subj=%s",
- status_get->pid, old,
- loginuid, ctx);
- kfree(ctx);
- } else
- audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
- "audit_pid=%d old=%d by auid=%u",
- status_get->pid, old, loginuid);
- audit_pid = status_get->pid;
+ int new_pid = status_get->pid;
+
+ if (audit_enabled != AUDIT_OFF)
+ audit_log_config_change("audit_pid", new_pid,
+ audit_pid, loginuid,
+ sid, 1);
+
+ audit_pid = new_pid;
}
if (status_get->mask & AUDIT_STATUS_RATE_LIMIT)
err = audit_set_rate_limit(status_get->rate_limit,
if (err)
break;
}
- ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
- if (ab) {
- audit_log_format(ab,
- "user pid=%d uid=%u auid=%u",
- pid, uid, loginuid);
- if (sid) {
- if (selinux_sid_to_string(
- sid, &ctx, &len)) {
- audit_log_format(ab,
- " ssid=%u", sid);
- /* Maybe call audit_panic? */
- } else
- audit_log_format(ab,
- " subj=%s", ctx);
- kfree(ctx);
- }
- if (msg_type != AUDIT_USER_TTY)
- audit_log_format(ab, " msg='%.1024s'",
- (char *)data);
- else {
- int size;
-
- audit_log_format(ab, " msg=");
- size = nlmsg_len(nlh);
- audit_log_n_untrustedstring(ab, size,
- data);
- }
- audit_set_pid(ab, pid);
- audit_log_end(ab);
+ audit_log_common_recv_msg(&ab, msg_type, pid, uid,
+ loginuid, sid);
+
+ if (msg_type != AUDIT_USER_TTY)
+ audit_log_format(ab, " msg='%.1024s'",
+ (char *)data);
+ else {
+ int size;
+
+ audit_log_format(ab, " msg=");
+ size = nlmsg_len(nlh);
+ audit_log_n_untrustedstring(ab, size,
+ data);
}
+ audit_set_pid(ab, pid);
+ audit_log_end(ab);
}
break;
case AUDIT_ADD:
case AUDIT_DEL:
if (nlmsg_len(nlh) < sizeof(struct audit_rule))
return -EINVAL;
- if (audit_enabled == 2) {
- ab = audit_log_start(NULL, GFP_KERNEL,
- AUDIT_CONFIG_CHANGE);
- if (ab) {
- audit_log_format(ab,
- "pid=%d uid=%u auid=%u",
- pid, uid, loginuid);
- if (sid) {
- if (selinux_sid_to_string(
- sid, &ctx, &len)) {
- audit_log_format(ab,
- " ssid=%u", sid);
- /* Maybe call audit_panic? */
- } else
- audit_log_format(ab,
- " subj=%s", ctx);
- kfree(ctx);
- }
- audit_log_format(ab, " audit_enabled=%d res=0",
- audit_enabled);
- audit_log_end(ab);
- }
+ if (audit_enabled == AUDIT_LOCKED) {
+ audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE, pid,
+ uid, loginuid, sid);
+
+ audit_log_format(ab, " audit_enabled=%d res=0",
+ audit_enabled);
+ audit_log_end(ab);
return -EPERM;
}
/* fallthrough */
case AUDIT_DEL_RULE:
if (nlmsg_len(nlh) < sizeof(struct audit_rule_data))
return -EINVAL;
- if (audit_enabled == 2) {
- ab = audit_log_start(NULL, GFP_KERNEL,
- AUDIT_CONFIG_CHANGE);
- if (ab) {
- audit_log_format(ab,
- "pid=%d uid=%u auid=%u",
- pid, uid, loginuid);
- if (sid) {
- if (selinux_sid_to_string(
- sid, &ctx, &len)) {
- audit_log_format(ab,
- " ssid=%u", sid);
- /* Maybe call audit_panic? */
- } else
- audit_log_format(ab,
- " subj=%s", ctx);
- kfree(ctx);
- }
- audit_log_format(ab, " audit_enabled=%d res=0",
- audit_enabled);
- audit_log_end(ab);
- }
+ if (audit_enabled == AUDIT_LOCKED) {
+ audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE, pid,
+ uid, loginuid, sid);
+
+ audit_log_format(ab, " audit_enabled=%d res=0",
+ audit_enabled);
+ audit_log_end(ab);
return -EPERM;
}
/* fallthrough */
uid, seq, data, nlmsg_len(nlh),
loginuid, sid);
break;
+ case AUDIT_TRIM:
+ audit_trim_trees();
+
+ audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE, pid,
+ uid, loginuid, sid);
+
+ audit_log_format(ab, " op=trim res=1");
+ audit_log_end(ab);
+ break;
+ case AUDIT_MAKE_EQUIV: {
+ void *bufp = data;
+ u32 sizes[2];
+ size_t len = nlmsg_len(nlh);
+ char *old, *new;
+
+ err = -EINVAL;
+ if (len < 2 * sizeof(u32))
+ break;
+ memcpy(sizes, bufp, 2 * sizeof(u32));
+ bufp += 2 * sizeof(u32);
+ len -= 2 * sizeof(u32);
+ old = audit_unpack_string(&bufp, &len, sizes[0]);
+ if (IS_ERR(old)) {
+ err = PTR_ERR(old);
+ break;
+ }
+ new = audit_unpack_string(&bufp, &len, sizes[1]);
+ if (IS_ERR(new)) {
+ err = PTR_ERR(new);
+ kfree(old);
+ break;
+ }
+ /* OK, here comes... */
+ err = audit_tag_tree(old, new);
+
+ audit_log_common_recv_msg(&ab, AUDIT_CONFIG_CHANGE, pid,
+ uid, loginuid, sid);
+
+ audit_log_format(ab, " op=make_equiv old=");
+ audit_log_untrustedstring(ab, old);
+ audit_log_format(ab, " new=");
+ audit_log_untrustedstring(ab, new);
+ audit_log_format(ab, " res=%d", !err);
+ audit_log_end(ab);
+ kfree(old);
+ kfree(new);
+ break;
+ }
case AUDIT_SIGNAL_INFO:
err = selinux_sid_to_string(audit_sig_sid, &ctx, &len);
if (err)
skb_queue_head_init(&audit_skb_queue);
audit_initialized = 1;
audit_enabled = audit_default;
+ audit_ever_enabled |= !!audit_default;
/* Register the callback with selinux. This callback will be invoked
* when a new policy is loaded. */
printk(KERN_INFO "audit: %s%s\n",
audit_default ? "enabled" : "disabled",
audit_initialized ? "" : " (after initialization)");
- if (audit_initialized)
+ if (audit_initialized) {
audit_enabled = audit_default;
+ audit_ever_enabled |= !!audit_default;
+ }
return 1;
}
{
struct audit_buffer *ab = NULL;
struct timespec t;
- unsigned int serial;
+ unsigned int uninitialized_var(serial);
int reserve;
unsigned long timeout_start = jiffies;
remove_wait_queue(&audit_backlog_wait, &wait);
continue;
}
- if (audit_rate_check())
+ if (audit_rate_check() && printk_ratelimit())
printk(KERN_WARNING
"audit: audit_backlog=%d > "
"audit_backlog_limit=%d\n",
static inline int audit_expand(struct audit_buffer *ab, int extra)
{
struct sk_buff *skb = ab->skb;
- int ret = pskb_expand_head(skb, skb_headroom(skb), extra,
- ab->gfp_mask);
+ int oldtail = skb_tailroom(skb);
+ int ret = pskb_expand_head(skb, 0, extra, ab->gfp_mask);
+ int newtail = skb_tailroom(skb);
+
if (ret < 0) {
audit_log_lost("out of memory in audit_expand");
return 0;
}
- return skb_tailroom(skb);
+
+ skb->truesize += newtail - oldtail;
+ return newtail;
}
/*
goto out;
len = vsnprintf(skb_tail_pointer(skb), avail, fmt, args2);
}
+ va_end(args2);
if (len > 0)
skb_put(skb, len);
out:
skb_put(skb, slen + 2); /* don't include null terminator */
}
+/**
+ * audit_string_contains_control - does a string need to be logged in hex
+ * @string - string to be checked
+ * @len - max length of the string to check
+ */
+int audit_string_contains_control(const char *string, size_t len)
+{
+ const unsigned char *p;
+ for (p = string; p < (const unsigned char *)string + len && *p; p++) {
+ if (*p == '"' || *p < 0x21 || *p > 0x7f)
+ return 1;
+ }
+ return 0;
+}
+
/**
* audit_log_n_untrustedstring - log a string that may contain random characters
* @ab: audit_buffer
* The caller specifies the number of characters in the string to log, which may
* or may not be the entire string.
*/
-const char *audit_log_n_untrustedstring(struct audit_buffer *ab, size_t len,
- const char *string)
+void audit_log_n_untrustedstring(struct audit_buffer *ab, size_t len,
+ const char *string)
{
- const unsigned char *p;
-
- for (p = string; p < (const unsigned char *)string + len && *p; p++) {
- if (*p == '"' || *p < 0x21 || *p > 0x7f) {
- audit_log_hex(ab, string, len);
- return string + len + 1;
- }
- }
- audit_log_n_string(ab, len, string);
- return p + 1;
+ if (audit_string_contains_control(string, len))
+ audit_log_hex(ab, string, len);
+ else
+ audit_log_n_string(ab, len, string);
}
/**
* Same as audit_log_n_untrustedstring(), except that strlen is used to
* determine string length.
*/
-const char *audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
+void audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
{
- return audit_log_n_untrustedstring(ab, strlen(string), string);
+ audit_log_n_untrustedstring(ab, strlen(string), string);
}
/* This is a helper-function to print the escaped d_path */
skb_queue_tail(&audit_skb_queue, ab->skb);
ab->skb = NULL;
wake_up_interruptible(&kauditd_wait);
+ } else if (printk_ratelimit()) {
+ struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
+ printk(KERN_NOTICE "type=%d %s\n", nlh->nlmsg_type, ab->skb->data + NLMSG_SPACE(0));
} else {
- printk(KERN_NOTICE "%s\n", ab->skb->data + NLMSG_SPACE(0));
+ audit_log_lost("printk limit exceeded\n");
}
}
audit_buffer_free(ab);