From 6ccd045630054c99ba1bb35673db12cfcf1eea58 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 20 Apr 2010 10:20:54 -0400 Subject: ima: handle multiple rules per write Currently IMA will only accept one rule per write(). This patch allows IMA to accept writes which contain multiple rules but only processes one rule per write. \n is used as the delimiter between rules. IMA will return a short write indicating that it only accepted up to the first \n. This allows simple userspace utilities like cat to be used to load an IMA policy instead of needing a special userspace utility that understood 'one write per rule' Signed-off-by: Eric Paris Acked-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/ima/ima_policy.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'security/integrity/ima/ima_policy.c') diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 4759d0f99335..49998f90e441 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -261,7 +261,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); entry->action = -1; - while ((p = strsep(&rule, " \n")) != NULL) { + while ((p = strsep(&rule, " ")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; unsigned long lnum; @@ -269,7 +269,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) if (result < 0) break; if (!*p) - continue; + break; token = match_token(p, policy_tokens, args); switch (token) { case Opt_measure: @@ -373,7 +373,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) if (entry->action == UNKNOWN) result = -EINVAL; - audit_log_format(ab, "res=%d", !result ? 0 : 1); + audit_log_format(ab, "res=%d", !!result); audit_log_end(ab); return result; } @@ -383,13 +383,14 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) * @rule - ima measurement policy rule * * Uses a mutex to protect the policy list from multiple concurrent writers. - * Returns 0 on success, an error code on failure. + * Returns the length of the rule parsed, an error code on failure */ -int ima_parse_add_rule(char *rule) +ssize_t ima_parse_add_rule(char *rule) { const char *op = "update_policy"; + char *p; struct ima_measure_rule_entry *entry; - int result = 0; + ssize_t result, len; int audit_info = 0; /* Prevent installed policy from changing */ @@ -409,8 +410,11 @@ int ima_parse_add_rule(char *rule) INIT_LIST_HEAD(&entry->list); - result = ima_parse_rule(rule, entry); + p = strsep(&rule, "\n"); + len = strlen(p) + 1; + result = ima_parse_rule(p, entry); if (!result) { + result = len; mutex_lock(&ima_measure_mutex); list_add_tail(&entry->list, &measure_policy_rules); mutex_unlock(&ima_measure_mutex); -- cgit v1.2.3 From 7b62e162129c3b28d51016774e0c7c57c710c452 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 20 Apr 2010 10:21:01 -0400 Subject: IMA: do not allow the same rule to specify the same thing twice IMA will accept rules which specify things twice and will only pay attention to the last one. We should reject such rules. Signed-off-by: Eric Paris Acked-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/ima/ima_policy.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'security/integrity/ima/ima_policy.c') diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 49998f90e441..c771a2036691 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -245,6 +245,9 @@ static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry, { int result; + if (entry->lsm[lsm_rule].rule) + return -EINVAL; + entry->lsm[lsm_rule].type = audit_type; result = security_filter_rule_init(entry->lsm[lsm_rule].type, Audit_equal, args, @@ -260,6 +263,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); + entry->uid = -1; entry->action = -1; while ((p = strsep(&rule, " ")) != NULL) { substring_t args[MAX_OPT_ARGS]; @@ -274,14 +278,26 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) switch (token) { case Opt_measure: audit_log_format(ab, "%s ", "measure"); + + if (entry->action != UNKNOWN) + result = -EINVAL; + entry->action = MEASURE; break; case Opt_dont_measure: audit_log_format(ab, "%s ", "dont_measure"); + + if (entry->action != UNKNOWN) + result = -EINVAL; + entry->action = DONT_MEASURE; break; case Opt_func: audit_log_format(ab, "func=%s ", args[0].from); + + if (entry->func) + result = -EINVAL; + if (strcmp(args[0].from, "FILE_CHECK") == 0) entry->func = FILE_CHECK; /* PATH_CHECK is for backwards compat */ @@ -298,6 +314,10 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) break; case Opt_mask: audit_log_format(ab, "mask=%s ", args[0].from); + + if (entry->mask) + result = -EINVAL; + if ((strcmp(args[0].from, "MAY_EXEC")) == 0) entry->mask = MAY_EXEC; else if (strcmp(args[0].from, "MAY_WRITE") == 0) @@ -313,6 +333,12 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) break; case Opt_fsmagic: audit_log_format(ab, "fsmagic=%s ", args[0].from); + + if (entry->fsmagic) { + result = -EINVAL; + break; + } + result = strict_strtoul(args[0].from, 16, &entry->fsmagic); if (!result) @@ -320,6 +346,12 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) break; case Opt_uid: audit_log_format(ab, "uid=%s ", args[0].from); + + if (entry->uid != -1) { + result = -EINVAL; + break; + } + result = strict_strtoul(args[0].from, 10, &lnum); if (!result) { entry->uid = (uid_t) lnum; @@ -370,7 +402,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) break; } } - if (entry->action == UNKNOWN) + if (!result && (entry->action == UNKNOWN)) result = -EINVAL; audit_log_format(ab, "res=%d", !!result); -- cgit v1.2.3 From b9035b1fd7933c11e68dbbf49b530cc43bf1da65 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 20 Apr 2010 10:21:07 -0400 Subject: IMA: set entry->action to UNKNOWN rather than hard coding ima_parse_rule currently sets entry->action = -1 and then later tests if (entry->action == UNKNOWN). It is true that UNKNOWN == -1 but actually setting it to UNKNOWN makes a lot more sense in case things change in the future. Signed-off-by: Eric Paris Acked-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/ima/ima_policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/integrity/ima/ima_policy.c') diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index c771a2036691..012701157378 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -264,7 +264,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); entry->uid = -1; - entry->action = -1; + entry->action = UNKNOWN; while ((p = strsep(&rule, " ")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; -- cgit v1.2.3 From e9d393bf8660fbbbe00617015224342bac3ea6fc Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 20 Apr 2010 10:21:13 -0400 Subject: IMA: reject policies with unknown entries Currently the ima policy load code will print what it doesn't understand but really I think it should reject any policy it doesn't understand. This patch makes it so! Signed-off-by: Eric Paris Acked-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/ima/ima_policy.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security/integrity/ima/ima_policy.c') diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 012701157378..dee2dc062bcc 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -398,6 +398,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) AUDIT_SUBJ_TYPE); break; case Opt_err: + result = -EINVAL; audit_log_format(ab, "UNKNOWN=%s ", p); break; } -- cgit v1.2.3 From 28ef4002ec7b4be27f1110b83e255df8159c786a Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 20 Apr 2010 10:21:18 -0400 Subject: IMA: handle whitespace better IMA parser will fail if whitespace is used in any way other than a single space. Using a tab or even using 2 spaces in a row will result in a policy being rejected. This patch makes the kernel ignore whitespace a bit better. Signed-off-by: Eric Paris Acked-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/ima/ima_policy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'security/integrity/ima/ima_policy.c') diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index dee2dc062bcc..1bc9e31ae250 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -265,15 +265,15 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) entry->uid = -1; entry->action = UNKNOWN; - while ((p = strsep(&rule, " ")) != NULL) { + while ((p = strsep(&rule, " \t")) != NULL) { substring_t args[MAX_OPT_ARGS]; int token; unsigned long lnum; if (result < 0) break; - if (!*p) - break; + if ((*p == '\0') || (*p == ' ') || (*p == '\t')) + continue; token = match_token(p, policy_tokens, args); switch (token) { case Opt_measure: -- cgit v1.2.3 From 7233e3ee22b1506723411fe437bcf69f678e8cdd Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 20 Apr 2010 10:21:24 -0400 Subject: IMA: handle comments in policy IMA policy load parser will reject any policies with a comment. This patch will allow the parser to just ignore lines which start with a #. This is not very robust. # can ONLY be used at the very beginning of a line. Inline comments are not allowed. Signed-off-by: Eric Paris Acked-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/ima/ima_policy.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'security/integrity/ima/ima_policy.c') diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 1bc9e31ae250..babc5009756d 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -445,19 +445,26 @@ ssize_t ima_parse_add_rule(char *rule) p = strsep(&rule, "\n"); len = strlen(p) + 1; + + if (*p == '#') { + kfree(entry); + return len; + } + result = ima_parse_rule(p, entry); - if (!result) { - result = len; - mutex_lock(&ima_measure_mutex); - list_add_tail(&entry->list, &measure_policy_rules); - mutex_unlock(&ima_measure_mutex); - } else { + if (result) { kfree(entry); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, op, "invalid policy", result, audit_info); + return result; } - return result; + + mutex_lock(&ima_measure_mutex); + list_add_tail(&entry->list, &measure_policy_rules); + mutex_unlock(&ima_measure_mutex); + + return len; } /* ima_delete_rules called to cleanup invalid policy */ -- cgit v1.2.3 From 2f1506cd82e0725ba00c7146a9a9b47824a5edcf Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 20 Apr 2010 10:21:30 -0400 Subject: IMA: use audit_log_untrusted_string rather than %s Convert all of the places IMA calls audit_log_format with %s into audit_log_untrusted_string(). This is going to cause them all to get quoted, but it should make audit log injection harder. Signed-off-by: Eric Paris Acked-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/ima/ima_policy.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'security/integrity/ima/ima_policy.c') diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index babc5009756d..778a735621f1 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -255,6 +255,13 @@ static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry, return result; } +static void ima_log_string(struct audit_buffer *ab, char *key, char *value) +{ + audit_log_format(ab, "%s=", key); + audit_log_untrustedstring(ab, value); + audit_log_format(ab, " "); +} + static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) { struct audit_buffer *ab; @@ -277,7 +284,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) token = match_token(p, policy_tokens, args); switch (token) { case Opt_measure: - audit_log_format(ab, "%s ", "measure"); + ima_log_string(ab, "action", "measure"); if (entry->action != UNKNOWN) result = -EINVAL; @@ -285,7 +292,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) entry->action = MEASURE; break; case Opt_dont_measure: - audit_log_format(ab, "%s ", "dont_measure"); + ima_log_string(ab, "action", "dont_measure"); if (entry->action != UNKNOWN) result = -EINVAL; @@ -293,7 +300,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) entry->action = DONT_MEASURE; break; case Opt_func: - audit_log_format(ab, "func=%s ", args[0].from); + ima_log_string(ab, "func", args[0].from); if (entry->func) result = -EINVAL; @@ -313,7 +320,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) entry->flags |= IMA_FUNC; break; case Opt_mask: - audit_log_format(ab, "mask=%s ", args[0].from); + ima_log_string(ab, "mask", args[0].from); if (entry->mask) result = -EINVAL; @@ -332,7 +339,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) entry->flags |= IMA_MASK; break; case Opt_fsmagic: - audit_log_format(ab, "fsmagic=%s ", args[0].from); + ima_log_string(ab, "fsmagic", args[0].from); if (entry->fsmagic) { result = -EINVAL; @@ -345,7 +352,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) entry->flags |= IMA_FSMAGIC; break; case Opt_uid: - audit_log_format(ab, "uid=%s ", args[0].from); + ima_log_string(ab, "uid", args[0].from); if (entry->uid != -1) { result = -EINVAL; @@ -362,44 +369,44 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) } break; case Opt_obj_user: - audit_log_format(ab, "obj_user=%s ", args[0].from); + ima_log_string(ab, "obj_user", args[0].from); result = ima_lsm_rule_init(entry, args[0].from, LSM_OBJ_USER, AUDIT_OBJ_USER); break; case Opt_obj_role: - audit_log_format(ab, "obj_role=%s ", args[0].from); + ima_log_string(ab, "obj_role", args[0].from); result = ima_lsm_rule_init(entry, args[0].from, LSM_OBJ_ROLE, AUDIT_OBJ_ROLE); break; case Opt_obj_type: - audit_log_format(ab, "obj_type=%s ", args[0].from); + ima_log_string(ab, "obj_type", args[0].from); result = ima_lsm_rule_init(entry, args[0].from, LSM_OBJ_TYPE, AUDIT_OBJ_TYPE); break; case Opt_subj_user: - audit_log_format(ab, "subj_user=%s ", args[0].from); + ima_log_string(ab, "subj_user", args[0].from); result = ima_lsm_rule_init(entry, args[0].from, LSM_SUBJ_USER, AUDIT_SUBJ_USER); break; case Opt_subj_role: - audit_log_format(ab, "subj_role=%s ", args[0].from); + ima_log_string(ab, "subj_role", args[0].from); result = ima_lsm_rule_init(entry, args[0].from, LSM_SUBJ_ROLE, AUDIT_SUBJ_ROLE); break; case Opt_subj_type: - audit_log_format(ab, "subj_type=%s ", args[0].from); + ima_log_string(ab, "subj_type", args[0].from); result = ima_lsm_rule_init(entry, args[0].from, LSM_SUBJ_TYPE, AUDIT_SUBJ_TYPE); break; case Opt_err: + ima_log_string(ab, "UNKNOWN", p); result = -EINVAL; - audit_log_format(ab, "UNKNOWN=%s ", p); break; } } -- cgit v1.2.3