integrity-v5.16

-----BEGIN PGP SIGNATURE-----
 
 iIoEABYIADIWIQQdXVVFGN5XqKr1Hj7LwZzRsCrn5QUCYYELkRQcem9oYXJAbGlu
 dXguaWJtLmNvbQAKCRDLwZzRsCrn5egFAP99TYmvDcP7sdcOpDzNCTviirj3PCF9
 jiYcVJDvkRousQD9GclOwWTUoGg382zRSopR9IT/wNhLFz/JIGwGhZrHwwA=
 =a74o
 -----END PGP SIGNATURE-----

Merge tag 'integrity-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity

Pull integrity subsystem updates from Mimi Zohar:
 "Other than the new gid IMA policy rule support and the RCU locking
  fix, the couple of remaining changes are minor/trivial (e.g.
  __ro_after_init, replacing strscpy)"

* tag 'integrity-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity:
  evm: mark evm_fixmode as __ro_after_init
  ima: Use strscpy instead of strlcpy
  ima_policy: Remove duplicate 'the' in docs comment
  ima: add gid support
  ima: fix uid code style problems
  ima: fix deadlock when traversing "ima_default_rules".
This commit is contained in:
Linus Torvalds 2021-11-02 10:51:28 -07:00
commit c150d66bd5
4 changed files with 208 additions and 47 deletions

View File

@ -22,8 +22,9 @@ Description:
action: measure | dont_measure | appraise | dont_appraise |
audit | hash | dont_hash
condition:= base | lsm [option]
base: [[func=] [mask=] [fsmagic=] [fsuuid=] [uid=]
[euid=] [fowner=] [fsname=]]
base: [[func=] [mask=] [fsmagic=] [fsuuid=] [fsname=]
[uid=] [euid=] [gid=] [egid=]
[fowner=] [fgroup=]]
lsm: [[subj_user=] [subj_role=] [subj_type=]
[obj_user=] [obj_role=] [obj_type=]]
option: [[appraise_type=]] [template=] [permit_directio]
@ -40,7 +41,10 @@ Description:
fsuuid:= file system UUID (e.g 8bcbe394-4f13-4144-be8e-5aa9ea2ce2f6)
uid:= decimal value
euid:= decimal value
gid:= decimal value
egid:= decimal value
fowner:= decimal value
fgroup:= decimal value
lsm: are LSM specific
option:
appraise_type:= [imasig] [imasig|modsig]

View File

@ -78,7 +78,7 @@ static struct xattr_list evm_config_default_xattrnames[] = {
LIST_HEAD(evm_config_xattrnames);
static int evm_fixmode;
static int evm_fixmode __ro_after_init;
static int __init evm_set_fixmode(char *str)
{
if (strncmp(str, "fix", 3) == 0)

View File

@ -403,7 +403,7 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *namebuf)
}
if (!pathname) {
strlcpy(namebuf, path->dentry->d_name.name, NAME_MAX);
strscpy(namebuf, path->dentry->d_name.name, NAME_MAX);
pathname = namebuf;
}

View File

@ -36,6 +36,9 @@
#define IMA_KEYRINGS 0x0400
#define IMA_LABEL 0x0800
#define IMA_VALIDATE_ALGOS 0x1000
#define IMA_GID 0x2000
#define IMA_EGID 0x4000
#define IMA_FGROUP 0x8000
#define UNKNOWN 0
#define MEASURE 0x0001 /* same as IMA_MEASURE */
@ -78,9 +81,13 @@ struct ima_rule_entry {
unsigned long fsmagic;
uuid_t fsuuid;
kuid_t uid;
kgid_t gid;
kuid_t fowner;
bool (*uid_op)(kuid_t, kuid_t); /* Handlers for operators */
bool (*fowner_op)(kuid_t, kuid_t); /* uid_eq(), uid_gt(), uid_lt() */
kgid_t fgroup;
bool (*uid_op)(kuid_t cred_uid, kuid_t rule_uid); /* Handlers for operators */
bool (*gid_op)(kgid_t cred_gid, kgid_t rule_gid);
bool (*fowner_op)(kuid_t cred_uid, kuid_t rule_uid); /* uid_eq(), uid_gt(), uid_lt() */
bool (*fgroup_op)(kgid_t cred_gid, kgid_t rule_gid); /* gid_eq(), gid_gt(), gid_lt() */
int pcr;
unsigned int allowed_algos; /* bitfield of allowed hash algorithms */
struct {
@ -104,7 +111,8 @@ static_assert(
/*
* Without LSM specific knowledge, the default policy can only be
* written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner
* written in terms of .action, .func, .mask, .fsmagic, .uid, .gid,
* .fowner, and .fgroup
*/
/*
@ -228,7 +236,7 @@ static struct ima_rule_entry *arch_policy_entry __ro_after_init;
static LIST_HEAD(ima_default_rules);
static LIST_HEAD(ima_policy_rules);
static LIST_HEAD(ima_temp_rules);
static struct list_head *ima_rules = &ima_default_rules;
static struct list_head __rcu *ima_rules = (struct list_head __rcu *)(&ima_default_rules);
static int ima_policy __initdata;
@ -582,10 +590,23 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
} else if (!rule->uid_op(cred->euid, rule->uid))
return false;
}
if ((rule->flags & IMA_GID) && !rule->gid_op(cred->gid, rule->gid))
return false;
if (rule->flags & IMA_EGID) {
if (has_capability_noaudit(current, CAP_SETGID)) {
if (!rule->gid_op(cred->egid, rule->gid)
&& !rule->gid_op(cred->sgid, rule->gid)
&& !rule->gid_op(cred->gid, rule->gid))
return false;
} else if (!rule->gid_op(cred->egid, rule->gid))
return false;
}
if ((rule->flags & IMA_FOWNER) &&
!rule->fowner_op(i_uid_into_mnt(mnt_userns, inode), rule->fowner))
return false;
if ((rule->flags & IMA_FGROUP) &&
!rule->fgroup_op(i_gid_into_mnt(mnt_userns, inode), rule->fgroup))
return false;
for (i = 0; i < MAX_LSM_RULES; i++) {
int rc = 0;
u32 osid;
@ -675,12 +696,14 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
{
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
struct list_head *ima_rules_tmp;
if (template_desc && !*template_desc)
*template_desc = ima_template_desc_current();
rcu_read_lock();
list_for_each_entry_rcu(entry, ima_rules, list) {
ima_rules_tmp = rcu_dereference(ima_rules);
list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
if (!(entry->action & actmask))
continue;
@ -741,9 +764,11 @@ void ima_update_policy_flags(void)
{
struct ima_rule_entry *entry;
int new_policy_flag = 0;
struct list_head *ima_rules_tmp;
rcu_read_lock();
list_for_each_entry(entry, ima_rules, list) {
ima_rules_tmp = rcu_dereference(ima_rules);
list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
/*
* SETXATTR_CHECK rules do not implement a full policy check
* because rule checking would probably have an important
@ -845,7 +870,7 @@ static int __init ima_init_arch_policy(void)
char rule[255];
int result;
result = strlcpy(rule, *rules, sizeof(rule));
result = strscpy(rule, *rules, sizeof(rule));
INIT_LIST_HEAD(&arch_policy_entry[i].list);
result = ima_parse_rule(rule, &arch_policy_entry[i]);
@ -864,8 +889,7 @@ static int __init ima_init_arch_policy(void)
/**
* ima_init_policy - initialize the default measure rules.
*
* ima_rules points to either the ima_default_rules or the
* the new ima_policy_rules.
* ima_rules points to either the ima_default_rules or the new ima_policy_rules.
*/
void __init ima_init_policy(void)
{
@ -968,10 +992,10 @@ void ima_update_policy(void)
list_splice_tail_init_rcu(&ima_temp_rules, policy, synchronize_rcu);
if (ima_rules != policy) {
if (ima_rules != (struct list_head __rcu *)policy) {
ima_policy_flag = 0;
ima_rules = policy;
rcu_assign_pointer(ima_rules, policy);
/*
* IMA architecture specific policy rules are specified
* as strings and converted to an array of ima_entry_rules
@ -987,16 +1011,19 @@ void ima_update_policy(void)
}
/* Keep the enumeration in sync with the policy_tokens! */
enum {
enum policy_opt {
Opt_measure, Opt_dont_measure,
Opt_appraise, Opt_dont_appraise,
Opt_audit, Opt_hash, Opt_dont_hash,
Opt_obj_user, Opt_obj_role, Opt_obj_type,
Opt_subj_user, Opt_subj_role, Opt_subj_type,
Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname,
Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq,
Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt,
Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, Opt_fsuuid,
Opt_uid_eq, Opt_euid_eq, Opt_gid_eq, Opt_egid_eq,
Opt_fowner_eq, Opt_fgroup_eq,
Opt_uid_gt, Opt_euid_gt, Opt_gid_gt, Opt_egid_gt,
Opt_fowner_gt, Opt_fgroup_gt,
Opt_uid_lt, Opt_euid_lt, Opt_gid_lt, Opt_egid_lt,
Opt_fowner_lt, Opt_fgroup_lt,
Opt_appraise_type, Opt_appraise_flag, Opt_appraise_algos,
Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings,
Opt_label, Opt_err
@ -1023,13 +1050,22 @@ static const match_table_t policy_tokens = {
{Opt_fsuuid, "fsuuid=%s"},
{Opt_uid_eq, "uid=%s"},
{Opt_euid_eq, "euid=%s"},
{Opt_gid_eq, "gid=%s"},
{Opt_egid_eq, "egid=%s"},
{Opt_fowner_eq, "fowner=%s"},
{Opt_fgroup_eq, "fgroup=%s"},
{Opt_uid_gt, "uid>%s"},
{Opt_euid_gt, "euid>%s"},
{Opt_gid_gt, "gid>%s"},
{Opt_egid_gt, "egid>%s"},
{Opt_fowner_gt, "fowner>%s"},
{Opt_fgroup_gt, "fgroup>%s"},
{Opt_uid_lt, "uid<%s"},
{Opt_euid_lt, "euid<%s"},
{Opt_gid_lt, "gid<%s"},
{Opt_egid_lt, "egid<%s"},
{Opt_fowner_lt, "fowner<%s"},
{Opt_fgroup_lt, "fgroup<%s"},
{Opt_appraise_type, "appraise_type=%s"},
{Opt_appraise_flag, "appraise_flag=%s"},
{Opt_appraise_algos, "appraise_algos=%s"},
@ -1061,7 +1097,7 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
pr_warn("rule for LSM \'%s\' is undefined\n",
entry->lsm[lsm_rule].args_p);
if (ima_rules == &ima_default_rules) {
if (ima_rules == (struct list_head __rcu *)(&ima_default_rules)) {
kfree(entry->lsm[lsm_rule].args_p);
entry->lsm[lsm_rule].args_p = NULL;
result = -EINVAL;
@ -1073,22 +1109,36 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
}
static void ima_log_string_op(struct audit_buffer *ab, char *key, char *value,
bool (*rule_operator)(kuid_t, kuid_t))
enum policy_opt rule_operator)
{
if (!ab)
return;
if (rule_operator == &uid_gt)
switch (rule_operator) {
case Opt_uid_gt:
case Opt_euid_gt:
case Opt_gid_gt:
case Opt_egid_gt:
case Opt_fowner_gt:
case Opt_fgroup_gt:
audit_log_format(ab, "%s>", key);
else if (rule_operator == &uid_lt)
break;
case Opt_uid_lt:
case Opt_euid_lt:
case Opt_gid_lt:
case Opt_egid_lt:
case Opt_fowner_lt:
case Opt_fgroup_lt:
audit_log_format(ab, "%s<", key);
else
break;
default:
audit_log_format(ab, "%s=", key);
}
audit_log_format(ab, "%s ", value);
}
static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
{
ima_log_string_op(ab, key, value, NULL);
ima_log_string_op(ab, key, value, Opt_err);
}
/*
@ -1163,7 +1213,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC |
IMA_UID | IMA_FOWNER | IMA_FSUUID |
IMA_INMASK | IMA_EUID | IMA_PCR |
IMA_FSNAME | IMA_DIGSIG_REQUIRED |
IMA_FSNAME | IMA_GID | IMA_EGID |
IMA_FGROUP | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS))
return false;
@ -1174,7 +1225,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC |
IMA_UID | IMA_FOWNER | IMA_FSUUID |
IMA_INMASK | IMA_EUID | IMA_PCR |
IMA_FSNAME | IMA_DIGSIG_REQUIRED |
IMA_FSNAME | IMA_GID | IMA_EGID |
IMA_FGROUP | IMA_DIGSIG_REQUIRED |
IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED |
IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS))
return false;
@ -1186,7 +1238,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->flags & ~(IMA_FUNC | IMA_FSMAGIC | IMA_UID |
IMA_FOWNER | IMA_FSUUID | IMA_EUID |
IMA_PCR | IMA_FSNAME))
IMA_PCR | IMA_FSNAME | IMA_GID | IMA_EGID |
IMA_FGROUP))
return false;
break;
@ -1194,7 +1247,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->action & ~(MEASURE | DONT_MEASURE))
return false;
if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_PCR |
if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_GID | IMA_PCR |
IMA_KEYRINGS))
return false;
@ -1206,7 +1259,7 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
if (entry->action & ~(MEASURE | DONT_MEASURE))
return false;
if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_PCR |
if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_GID | IMA_PCR |
IMA_LABEL))
return false;
@ -1276,7 +1329,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
struct audit_buffer *ab;
char *from;
char *p;
bool uid_token;
bool eid_token; /* either euid or egid */
struct ima_template_desc *template_desc;
int result = 0;
@ -1284,9 +1337,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
AUDIT_INTEGRITY_POLICY_RULE);
entry->uid = INVALID_UID;
entry->gid = INVALID_GID;
entry->fowner = INVALID_UID;
entry->fgroup = INVALID_GID;
entry->uid_op = &uid_eq;
entry->gid_op = &gid_eq;
entry->fowner_op = &uid_eq;
entry->fgroup_op = &gid_eq;
entry->action = UNKNOWN;
while ((p = strsep(&rule, " \t")) != NULL) {
substring_t args[MAX_OPT_ARGS];
@ -1504,12 +1561,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
fallthrough;
case Opt_uid_eq:
case Opt_euid_eq:
uid_token = (token == Opt_uid_eq) ||
(token == Opt_uid_gt) ||
(token == Opt_uid_lt);
eid_token = (token == Opt_euid_eq) ||
(token == Opt_euid_gt) ||
(token == Opt_euid_lt);
ima_log_string_op(ab, uid_token ? "uid" : "euid",
args[0].from, entry->uid_op);
ima_log_string_op(ab, eid_token ? "euid" : "uid",
args[0].from, token);
if (uid_valid(entry->uid)) {
result = -EINVAL;
@ -1524,8 +1581,43 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
(uid_t)lnum != lnum)
result = -EINVAL;
else
entry->flags |= uid_token
? IMA_UID : IMA_EUID;
entry->flags |= eid_token
? IMA_EUID : IMA_UID;
}
break;
case Opt_gid_gt:
case Opt_egid_gt:
entry->gid_op = &gid_gt;
fallthrough;
case Opt_gid_lt:
case Opt_egid_lt:
if ((token == Opt_gid_lt) || (token == Opt_egid_lt))
entry->gid_op = &gid_lt;
fallthrough;
case Opt_gid_eq:
case Opt_egid_eq:
eid_token = (token == Opt_egid_eq) ||
(token == Opt_egid_gt) ||
(token == Opt_egid_lt);
ima_log_string_op(ab, eid_token ? "egid" : "gid",
args[0].from, token);
if (gid_valid(entry->gid)) {
result = -EINVAL;
break;
}
result = kstrtoul(args[0].from, 10, &lnum);
if (!result) {
entry->gid = make_kgid(current_user_ns(),
(gid_t)lnum);
if (!gid_valid(entry->gid) ||
(((gid_t)lnum) != lnum))
result = -EINVAL;
else
entry->flags |= eid_token
? IMA_EGID : IMA_GID;
}
break;
case Opt_fowner_gt:
@ -1536,8 +1628,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
entry->fowner_op = &uid_lt;
fallthrough;
case Opt_fowner_eq:
ima_log_string_op(ab, "fowner", args[0].from,
entry->fowner_op);
ima_log_string_op(ab, "fowner", args[0].from, token);
if (uid_valid(entry->fowner)) {
result = -EINVAL;
@ -1546,13 +1637,41 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
result = kstrtoul(args[0].from, 10, &lnum);
if (!result) {
entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum);
if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum))
entry->fowner = make_kuid(current_user_ns(),
(uid_t)lnum);
if (!uid_valid(entry->fowner) ||
(((uid_t)lnum) != lnum))
result = -EINVAL;
else
entry->flags |= IMA_FOWNER;
}
break;
case Opt_fgroup_gt:
entry->fgroup_op = &gid_gt;
fallthrough;
case Opt_fgroup_lt:
if (token == Opt_fgroup_lt)
entry->fgroup_op = &gid_lt;
fallthrough;
case Opt_fgroup_eq:
ima_log_string_op(ab, "fgroup", args[0].from, token);
if (gid_valid(entry->fgroup)) {
result = -EINVAL;
break;
}
result = kstrtoul(args[0].from, 10, &lnum);
if (!result) {
entry->fgroup = make_kgid(current_user_ns(),
(gid_t)lnum);
if (!gid_valid(entry->fgroup) ||
(((gid_t)lnum) != lnum))
result = -EINVAL;
else
entry->flags |= IMA_FGROUP;
}
break;
case Opt_obj_user:
ima_log_string(ab, "obj_user", args[0].from);
result = ima_lsm_rule_init(entry, args,
@ -1768,9 +1887,11 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)
{
loff_t l = *pos;
struct ima_rule_entry *entry;
struct list_head *ima_rules_tmp;
rcu_read_lock();
list_for_each_entry_rcu(entry, ima_rules, list) {
ima_rules_tmp = rcu_dereference(ima_rules);
list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
if (!l--) {
rcu_read_unlock();
return entry;
@ -1789,7 +1910,8 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
rcu_read_unlock();
(*pos)++;
return (&entry->list == ima_rules) ? NULL : entry;
return (&entry->list == &ima_default_rules ||
&entry->list == &ima_policy_rules) ? NULL : entry;
}
void ima_policy_stop(struct seq_file *m, void *v)
@ -1936,6 +2058,28 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, " ");
}
if (entry->flags & IMA_GID) {
snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->gid));
if (entry->gid_op == &gid_gt)
seq_printf(m, pt(Opt_gid_gt), tbuf);
else if (entry->gid_op == &gid_lt)
seq_printf(m, pt(Opt_gid_lt), tbuf);
else
seq_printf(m, pt(Opt_gid_eq), tbuf);
seq_puts(m, " ");
}
if (entry->flags & IMA_EGID) {
snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->gid));
if (entry->gid_op == &gid_gt)
seq_printf(m, pt(Opt_egid_gt), tbuf);
else if (entry->gid_op == &gid_lt)
seq_printf(m, pt(Opt_egid_lt), tbuf);
else
seq_printf(m, pt(Opt_egid_eq), tbuf);
seq_puts(m, " ");
}
if (entry->flags & IMA_FOWNER) {
snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner));
if (entry->fowner_op == &uid_gt)
@ -1947,6 +2091,17 @@ int ima_policy_show(struct seq_file *m, void *v)
seq_puts(m, " ");
}
if (entry->flags & IMA_FGROUP) {
snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->fgroup));
if (entry->fgroup_op == &gid_gt)
seq_printf(m, pt(Opt_fgroup_gt), tbuf);
else if (entry->fgroup_op == &gid_lt)
seq_printf(m, pt(Opt_fgroup_lt), tbuf);
else
seq_printf(m, pt(Opt_fgroup_eq), tbuf);
seq_puts(m, " ");
}
if (entry->flags & IMA_VALIDATE_ALGOS) {
seq_puts(m, "appraise_algos=");
ima_policy_show_appraise_algos(m, entry->allowed_algos);
@ -2014,6 +2169,7 @@ bool ima_appraise_signature(enum kernel_read_file_id id)
struct ima_rule_entry *entry;
bool found = false;
enum ima_hooks func;
struct list_head *ima_rules_tmp;
if (id >= READING_MAX_ID)
return false;
@ -2021,7 +2177,8 @@ bool ima_appraise_signature(enum kernel_read_file_id id)
func = read_idmap[id] ?: FILE_CHECK;
rcu_read_lock();
list_for_each_entry_rcu(entry, ima_rules, list) {
ima_rules_tmp = rcu_dereference(ima_rules);
list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
if (entry->action != APPRAISE)
continue;