audit: fix error handling in audit_data_to_entry()
Commit219ca39427
("audit: use union for audit_field values since they are mutually exclusive") combined a number of separate fields in the audit_field struct into a single union. Generally this worked just fine because they are generally mutually exclusive. Unfortunately in audit_data_to_entry() the overlap can be a problem when a specific error case is triggered that causes the error path code to attempt to cleanup an audit_field struct and the cleanup involves attempting to free a stored LSM string (the lsm_str field). Currently the code always has a non-NULL value in the audit_field.lsm_str field as the top of the for-loop transfers a value into audit_field.val (both .lsm_str and .val are part of the same union); if audit_data_to_entry() fails and the audit_field struct is specified to contain a LSM string, but the audit_field.lsm_str has not yet been properly set, the error handling code will attempt to free the bogus audit_field.lsm_str value that was set with audit_field.val at the top of the for-loop. This patch corrects this by ensuring that the audit_field.val is only set when needed (it is cleared when the audit_field struct is allocated with kcalloc()). It also corrects a few other issues to ensure that in case of error the proper error code is returned. Cc: stable@vger.kernel.org Fixes:219ca39427
("audit: use union for audit_field values since they are mutually exclusive") Reported-by: syzbot+1f4d90ead370d72e450b@syzkaller.appspotmail.com Signed-off-by: Paul Moore <paul@paul-moore.com>
This commit is contained in:
Родитель
cb5172d96d
Коммит
2ad3e17ebf
|
@ -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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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,
|
||||||
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))
|
|
||||||
goto exit_free;
|
|
||||||
entry->rule.buflen += f->val;
|
|
||||||
entry->rule.filterkey = str;
|
|
||||||
break;
|
|
||||||
case AUDIT_EXE:
|
|
||||||
if (entry->rule.exe || f->val > PATH_MAX)
|
|
||||||
goto exit_free;
|
|
||||||
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;
|
entry->rule.buflen += f_val;
|
||||||
|
entry->rule.filterkey = str;
|
||||||
audit_mark = audit_alloc_mark(&entry->rule, str, f->val);
|
break;
|
||||||
|
case AUDIT_EXE:
|
||||||
|
if (entry->rule.exe || f_val > PATH_MAX)
|
||||||
|
goto exit_free;
|
||||||
|
str = audit_unpack_string(&bufp, &remain, f_val);
|
||||||
|
if (IS_ERR(str)) {
|
||||||
|
err = PTR_ERR(str);
|
||||||
|
goto exit_free;
|
||||||
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче