Commit ed5fa559 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull audit fixes from Paul Moore:
 "Two fixes for problems found by syzbot:

   - Moving audit filter structure fields into a union caused some
     problems in the code which populates that filter structure.

     We keep the union (that idea is a good one), but we are fixing the
     code so that it doesn't needlessly set fields in the union and mess
     up the error handling.

   - The audit_receive_msg() function wasn't validating user input as
     well as it should in all cases, we add the necessary checks"

* tag 'audit-pr-20200226' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/audit:
  audit: always check the netlink payload length in audit_receive_msg()
  audit: fix error handling in audit_data_to_entry()
parents bfdc6d91 75612528
...@@ -1101,13 +1101,11 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature ...@@ -1101,13 +1101,11 @@ static void audit_log_feature_change(int which, u32 old_feature, u32 new_feature
audit_log_end(ab); audit_log_end(ab);
} }
static int audit_set_feature(struct sk_buff *skb) static int audit_set_feature(struct audit_features *uaf)
{ {
struct audit_features *uaf;
int i; int i;
BUILD_BUG_ON(AUDIT_LAST_FEATURE + 1 > ARRAY_SIZE(audit_feature_names)); BUILD_BUG_ON(AUDIT_LAST_FEATURE + 1 > ARRAY_SIZE(audit_feature_names));
uaf = nlmsg_data(nlmsg_hdr(skb));
/* if there is ever a version 2 we should handle that here */ /* if there is ever a version 2 we should handle that here */
...@@ -1175,6 +1173,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -1175,6 +1173,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{ {
u32 seq; u32 seq;
void *data; void *data;
int data_len;
int err; int err;
struct audit_buffer *ab; struct audit_buffer *ab;
u16 msg_type = nlh->nlmsg_type; u16 msg_type = nlh->nlmsg_type;
...@@ -1188,6 +1187,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -1188,6 +1187,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
seq = nlh->nlmsg_seq; seq = nlh->nlmsg_seq;
data = nlmsg_data(nlh); data = nlmsg_data(nlh);
data_len = nlmsg_len(nlh);
switch (msg_type) { switch (msg_type) {
case AUDIT_GET: { case AUDIT_GET: {
...@@ -1211,7 +1211,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -1211,7 +1211,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
struct audit_status s; struct audit_status s;
memset(&s, 0, sizeof(s)); memset(&s, 0, sizeof(s));
/* guard against past and future API changes */ /* guard against past and future API changes */
memcpy(&s, data, min_t(size_t, sizeof(s), nlmsg_len(nlh))); memcpy(&s, data, min_t(size_t, sizeof(s), data_len));
if (s.mask & AUDIT_STATUS_ENABLED) { if (s.mask & AUDIT_STATUS_ENABLED) {
err = audit_set_enabled(s.enabled); err = audit_set_enabled(s.enabled);
if (err < 0) if (err < 0)
...@@ -1315,7 +1315,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -1315,7 +1315,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return err; return err;
break; break;
case AUDIT_SET_FEATURE: case AUDIT_SET_FEATURE:
err = audit_set_feature(skb); if (data_len < sizeof(struct audit_features))
return -EINVAL;
err = audit_set_feature(data);
if (err) if (err)
return err; return err;
break; break;
...@@ -1327,6 +1329,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -1327,6 +1329,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
err = audit_filter(msg_type, AUDIT_FILTER_USER); err = audit_filter(msg_type, AUDIT_FILTER_USER);
if (err == 1) { /* match or error */ if (err == 1) { /* match or error */
char *str = data;
err = 0; err = 0;
if (msg_type == AUDIT_USER_TTY) { if (msg_type == AUDIT_USER_TTY) {
err = tty_audit_push(); err = tty_audit_push();
...@@ -1334,26 +1338,24 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -1334,26 +1338,24 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
break; break;
} }
audit_log_user_recv_msg(&ab, msg_type); audit_log_user_recv_msg(&ab, msg_type);
if (msg_type != AUDIT_USER_TTY) if (msg_type != AUDIT_USER_TTY) {
/* ensure NULL termination */
str[data_len - 1] = '\0';
audit_log_format(ab, " msg='%.*s'", audit_log_format(ab, " msg='%.*s'",
AUDIT_MESSAGE_TEXT_MAX, AUDIT_MESSAGE_TEXT_MAX,
(char *)data); str);
else { } else {
int size;
audit_log_format(ab, " data="); audit_log_format(ab, " data=");
size = nlmsg_len(nlh); if (data_len > 0 && str[data_len - 1] == '\0')
if (size > 0 && data_len--;
((unsigned char *)data)[size - 1] == '\0') audit_log_n_untrustedstring(ab, str, data_len);
size--;
audit_log_n_untrustedstring(ab, data, size);
} }
audit_log_end(ab); audit_log_end(ab);
} }
break; break;
case AUDIT_ADD_RULE: case AUDIT_ADD_RULE:
case AUDIT_DEL_RULE: case AUDIT_DEL_RULE:
if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) if (data_len < sizeof(struct audit_rule_data))
return -EINVAL; return -EINVAL;
if (audit_enabled == AUDIT_LOCKED) { if (audit_enabled == AUDIT_LOCKED) {
audit_log_common_recv_msg(audit_context(), &ab, audit_log_common_recv_msg(audit_context(), &ab,
...@@ -1365,7 +1367,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -1365,7 +1367,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
audit_log_end(ab); audit_log_end(ab);
return -EPERM; return -EPERM;
} }
err = audit_rule_change(msg_type, seq, data, nlmsg_len(nlh)); err = audit_rule_change(msg_type, seq, data, data_len);
break; break;
case AUDIT_LIST_RULES: case AUDIT_LIST_RULES:
err = audit_list_rules_send(skb, seq); err = audit_list_rules_send(skb, seq);
...@@ -1380,7 +1382,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -1380,7 +1382,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
case AUDIT_MAKE_EQUIV: { case AUDIT_MAKE_EQUIV: {
void *bufp = data; void *bufp = data;
u32 sizes[2]; u32 sizes[2];
size_t msglen = nlmsg_len(nlh); size_t msglen = data_len;
char *old, *new; char *old, *new;
err = -EINVAL; err = -EINVAL;
...@@ -1456,7 +1458,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -1456,7 +1458,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
memset(&s, 0, sizeof(s)); memset(&s, 0, sizeof(s));
/* guard against past and future API changes */ /* guard against past and future API changes */
memcpy(&s, data, min_t(size_t, sizeof(s), nlmsg_len(nlh))); memcpy(&s, data, min_t(size_t, sizeof(s), data_len));
/* check if new data is valid */ /* check if new data is valid */
if ((s.enabled != 0 && s.enabled != 1) || if ((s.enabled != 0 && s.enabled != 1) ||
(s.log_passwd != 0 && s.log_passwd != 1)) (s.log_passwd != 0 && s.log_passwd != 1))
......
...@@ -456,6 +456,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, ...@@ -456,6 +456,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
bufp = data->buf; bufp = data->buf;
for (i = 0; i < data->field_count; i++) { for (i = 0; i < data->field_count; i++) {
struct audit_field *f = &entry->rule.fields[i]; struct audit_field *f = &entry->rule.fields[i];
u32 f_val;
err = -EINVAL; err = -EINVAL;
...@@ -464,12 +465,12 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, ...@@ -464,12 +465,12 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
goto exit_free; goto exit_free;
f->type = data->fields[i]; f->type = data->fields[i];
f->val = data->values[i]; f_val = data->values[i];
/* Support legacy tests for a valid loginuid */ /* Support legacy tests for a valid loginuid */
if ((f->type == AUDIT_LOGINUID) && (f->val == AUDIT_UID_UNSET)) { if ((f->type == AUDIT_LOGINUID) && (f_val == AUDIT_UID_UNSET)) {
f->type = AUDIT_LOGINUID_SET; f->type = AUDIT_LOGINUID_SET;
f->val = 0; f_val = 0;
entry->rule.pflags |= AUDIT_LOGINUID_LEGACY; entry->rule.pflags |= AUDIT_LOGINUID_LEGACY;
} }
...@@ -485,7 +486,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, ...@@ -485,7 +486,7 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
case AUDIT_SUID: case AUDIT_SUID:
case AUDIT_FSUID: case AUDIT_FSUID:
case AUDIT_OBJ_UID: case AUDIT_OBJ_UID:
f->uid = make_kuid(current_user_ns(), f->val); f->uid = make_kuid(current_user_ns(), f_val);
if (!uid_valid(f->uid)) if (!uid_valid(f->uid))
goto exit_free; goto exit_free;
break; break;
...@@ -494,11 +495,12 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, ...@@ -494,11 +495,12 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
case AUDIT_SGID: case AUDIT_SGID:
case AUDIT_FSGID: case AUDIT_FSGID:
case AUDIT_OBJ_GID: case AUDIT_OBJ_GID:
f->gid = make_kgid(current_user_ns(), f->val); f->gid = make_kgid(current_user_ns(), f_val);
if (!gid_valid(f->gid)) if (!gid_valid(f->gid))
goto exit_free; goto exit_free;
break; break;
case AUDIT_ARCH: case AUDIT_ARCH:
f->val = f_val;
entry->rule.arch_f = f; entry->rule.arch_f = f;
break; break;
case AUDIT_SUBJ_USER: case AUDIT_SUBJ_USER:
...@@ -511,11 +513,13 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, ...@@ -511,11 +513,13 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
case AUDIT_OBJ_TYPE: case AUDIT_OBJ_TYPE:
case AUDIT_OBJ_LEV_LOW: case AUDIT_OBJ_LEV_LOW:
case AUDIT_OBJ_LEV_HIGH: case AUDIT_OBJ_LEV_HIGH:
str = audit_unpack_string(&bufp, &remain, f->val); str = audit_unpack_string(&bufp, &remain, f_val);
if (IS_ERR(str)) if (IS_ERR(str)) {
err = PTR_ERR(str);
goto exit_free; goto exit_free;
entry->rule.buflen += f->val; }
entry->rule.buflen += f_val;
f->lsm_str = str;
err = security_audit_rule_init(f->type, f->op, str, err = security_audit_rule_init(f->type, f->op, str,
(void **)&f->lsm_rule); (void **)&f->lsm_rule);
/* Keep currently invalid fields around in case they /* Keep currently invalid fields around in case they
...@@ -524,68 +528,71 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, ...@@ -524,68 +528,71 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
pr_warn("audit rule for LSM \'%s\' is invalid\n", pr_warn("audit rule for LSM \'%s\' is invalid\n",
str); str);
err = 0; err = 0;
} } else if (err)
if (err) {
kfree(str);
goto exit_free; goto exit_free;
} else
f->lsm_str = str;
break; break;
case AUDIT_WATCH: case AUDIT_WATCH:
str = audit_unpack_string(&bufp, &remain, f->val); str = audit_unpack_string(&bufp, &remain, f_val);
if (IS_ERR(str)) if (IS_ERR(str)) {
err = PTR_ERR(str);
goto exit_free; goto exit_free;
entry->rule.buflen += f->val; }
err = audit_to_watch(&entry->rule, str, f_val, f->op);
err = audit_to_watch(&entry->rule, str, f->val, f->op);
if (err) { if (err) {
kfree(str); kfree(str);
goto exit_free; goto exit_free;
} }
entry->rule.buflen += f_val;
break; break;
case AUDIT_DIR: case AUDIT_DIR:
str = audit_unpack_string(&bufp, &remain, f->val); str = audit_unpack_string(&bufp, &remain, f_val);
if (IS_ERR(str)) if (IS_ERR(str)) {
err = PTR_ERR(str);
goto exit_free; goto exit_free;
entry->rule.buflen += f->val; }
err = audit_make_tree(&entry->rule, str, f->op); err = audit_make_tree(&entry->rule, str, f->op);
kfree(str); kfree(str);
if (err) if (err)
goto exit_free; goto exit_free;
entry->rule.buflen += f_val;
break; break;
case AUDIT_INODE: case AUDIT_INODE:
f->val = f_val;
err = audit_to_inode(&entry->rule, f); err = audit_to_inode(&entry->rule, f);
if (err) if (err)
goto exit_free; goto exit_free;
break; break;
case AUDIT_FILTERKEY: case AUDIT_FILTERKEY:
if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN) if (entry->rule.filterkey || f_val > AUDIT_MAX_KEY_LEN)
goto exit_free; goto exit_free;
str = audit_unpack_string(&bufp, &remain, f->val); str = audit_unpack_string(&bufp, &remain, f_val);
if (IS_ERR(str)) if (IS_ERR(str)) {
err = PTR_ERR(str);
goto exit_free; goto exit_free;
entry->rule.buflen += f->val; }
entry->rule.buflen += f_val;
entry->rule.filterkey = str; entry->rule.filterkey = str;
break; break;
case AUDIT_EXE: case AUDIT_EXE:
if (entry->rule.exe || f->val > PATH_MAX) if (entry->rule.exe || f_val > PATH_MAX)
goto exit_free; goto exit_free;
str = audit_unpack_string(&bufp, &remain, f->val); str = audit_unpack_string(&bufp, &remain, f_val);
if (IS_ERR(str)) { if (IS_ERR(str)) {
err = PTR_ERR(str); err = PTR_ERR(str);
goto exit_free; goto exit_free;
} }
entry->rule.buflen += f->val; audit_mark = audit_alloc_mark(&entry->rule, str, f_val);
audit_mark = audit_alloc_mark(&entry->rule, str, f->val);
if (IS_ERR(audit_mark)) { if (IS_ERR(audit_mark)) {
kfree(str); kfree(str);
err = PTR_ERR(audit_mark); err = PTR_ERR(audit_mark);
goto exit_free; goto exit_free;
} }
entry->rule.buflen += f_val;
entry->rule.exe = audit_mark; entry->rule.exe = audit_mark;
break; break;
default:
f->val = f_val;
break;
} }
} }
......
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