Commit 40224c41 authored by Curtis Veit's avatar Curtis Veit Committed by Mimi Zohar
Browse files

ima: add gid support



IMA currently supports the concept of rules based on uid where the rule
is based on the uid of the file owner or the uid of the user accessing
the file. Provide the ability to have similar rules based on gid.

Signed-off-by: default avatarCurtis Veit <veit@vpieng.com>
Co-developed-by: default avatarAlex Henrie <alexh@vpitech.com>
Signed-off-by: default avatarAlex Henrie <alexh@vpitech.com>
Reviewed-by: default avatarPetr Vorel <pvorel@suse.cz>
Signed-off-by: default avatarMimi Zohar <zohar@linux.ibm.com>
parent 30d8764a
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -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]
+174 −27
Original line number Diff line number Diff line
@@ -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;
	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
 */

/*
@@ -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;
@@ -991,16 +1012,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
@@ -1027,13 +1051,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"},
@@ -1077,22 +1110,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);
}

/*
@@ -1167,7 +1214,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;

@@ -1178,7 +1226,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;
@@ -1190,7 +1239,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;
@@ -1198,7 +1248,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;

@@ -1210,7 +1260,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;

@@ -1280,7 +1330,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;

@@ -1288,9 +1338,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];
@@ -1508,12 +1562,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;
@@ -1528,8 +1582,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:
@@ -1540,8 +1629,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;
@@ -1559,6 +1647,32 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
					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,
@@ -1945,6 +2059,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)
@@ -1956,6 +2092,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);