diff options
author | Mikhail Kurinnoi <viewizard@viewizard.com> | 2017-01-27 11:23:01 -0500 |
---|---|---|
committer | Mimi Zohar <zohar@linux.vnet.ibm.com> | 2017-03-13 07:01:24 -0400 |
commit | 3dd0c8d06511c7c61c62305fcf431ca28884d263 (patch) | |
tree | 53dd75846d5bc3ac098bd9fdd08c94ae804c22a2 /security | |
parent | 1ac202e978e18f045006d75bd549612620c6ec3a (diff) |
ima: provide ">" and "<" operators for fowner/uid/euid rules.
For now we have only "=" operator for fowner/uid/euid rules. This
patch provide two more operators - ">" and "<" in order to make
fowner/uid/euid rules more flexible.
Examples of usage.
Appraise all files owned by special and system users (SYS_UID_MAX 999):
appraise fowner<1000
Don't appraise files owned by normal users (UID_MIN 1000):
dont_appraise fowner>999
Appraise all files owned by users with UID 1000-1010:
dont_appraise fowner>1010
appraise fowner>999
Changelog v3:
- Removed code duplication in ima_parse_rule().
- Fix ima_policy_show() - (Mimi)
Changelog v2:
- Fixed default policy rules.
Signed-off-by: Mikhail Kurinnoi <viewizard@viewizard.com>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
security/integrity/ima/ima_policy.c | 115 +++++++++++++++++++++++++++---------
1 file changed, 87 insertions(+), 28 deletions(-)
Diffstat (limited to 'security')
-rw-r--r-- | security/integrity/ima/ima_policy.c | 115 |
1 files changed, 87 insertions, 28 deletions
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index e8498a3f4887..3ab1067db624 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c | |||
@@ -64,6 +64,8 @@ struct ima_rule_entry { | |||
64 | u8 fsuuid[16]; | 64 | u8 fsuuid[16]; |
65 | kuid_t uid; | 65 | kuid_t uid; |
66 | kuid_t fowner; | 66 | kuid_t fowner; |
67 | bool (*uid_op)(kuid_t, kuid_t); /* Handlers for operators */ | ||
68 | bool (*fowner_op)(kuid_t, kuid_t); /* uid_eq(), uid_gt(), uid_lt() */ | ||
67 | int pcr; | 69 | int pcr; |
68 | struct { | 70 | struct { |
69 | void *rule; /* LSM file metadata specific */ | 71 | void *rule; /* LSM file metadata specific */ |
@@ -103,7 +105,8 @@ static struct ima_rule_entry original_measurement_rules[] __ro_after_init = { | |||
103 | {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, | 105 | {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, |
104 | .flags = IMA_FUNC | IMA_MASK}, | 106 | .flags = IMA_FUNC | IMA_MASK}, |
105 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, | 107 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, |
106 | .uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_MASK | IMA_UID}, | 108 | .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, |
109 | .flags = IMA_FUNC | IMA_MASK | IMA_UID}, | ||
107 | {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, | 110 | {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, |
108 | {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, | 111 | {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, |
109 | }; | 112 | }; |
@@ -114,9 +117,11 @@ static struct ima_rule_entry default_measurement_rules[] __ro_after_init = { | |||
114 | {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, | 117 | {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, |
115 | .flags = IMA_FUNC | IMA_MASK}, | 118 | .flags = IMA_FUNC | IMA_MASK}, |
116 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, | 119 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, |
117 | .uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_INMASK | IMA_EUID}, | 120 | .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, |
121 | .flags = IMA_FUNC | IMA_INMASK | IMA_EUID}, | ||
118 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, | 122 | {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, |
119 | .uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_INMASK | IMA_UID}, | 123 | .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, |
124 | .flags = IMA_FUNC | IMA_INMASK | IMA_UID}, | ||
120 | {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, | 125 | {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, |
121 | {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, | 126 | {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, |
122 | {.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC}, | 127 | {.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC}, |
@@ -139,10 +144,11 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { | |||
139 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, | 144 | .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, |
140 | #endif | 145 | #endif |
141 | #ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT | 146 | #ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT |
142 | {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER}, | 147 | {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, |
148 | .flags = IMA_FOWNER}, | ||
143 | #else | 149 | #else |
144 | /* force signature */ | 150 | /* force signature */ |
145 | {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, | 151 | {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, |
146 | .flags = IMA_FOWNER | IMA_DIGSIG_REQUIRED}, | 152 | .flags = IMA_FOWNER | IMA_DIGSIG_REQUIRED}, |
147 | #endif | 153 | #endif |
148 | }; | 154 | }; |
@@ -240,19 +246,20 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, | |||
240 | if ((rule->flags & IMA_FSUUID) && | 246 | if ((rule->flags & IMA_FSUUID) && |
241 | memcmp(rule->fsuuid, inode->i_sb->s_uuid, sizeof(rule->fsuuid))) | 247 | memcmp(rule->fsuuid, inode->i_sb->s_uuid, sizeof(rule->fsuuid))) |
242 | return false; | 248 | return false; |
243 | if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid)) | 249 | if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid)) |
244 | return false; | 250 | return false; |
245 | if (rule->flags & IMA_EUID) { | 251 | if (rule->flags & IMA_EUID) { |
246 | if (has_capability_noaudit(current, CAP_SETUID)) { | 252 | if (has_capability_noaudit(current, CAP_SETUID)) { |
247 | if (!uid_eq(rule->uid, cred->euid) | 253 | if (!rule->uid_op(cred->euid, rule->uid) |
248 | && !uid_eq(rule->uid, cred->suid) | 254 | && !rule->uid_op(cred->suid, rule->uid) |
249 | && !uid_eq(rule->uid, cred->uid)) | 255 | && !rule->uid_op(cred->uid, rule->uid)) |
250 | return false; | 256 | return false; |
251 | } else if (!uid_eq(rule->uid, cred->euid)) | 257 | } else if (!rule->uid_op(cred->euid, rule->uid)) |
252 | return false; | 258 | return false; |
253 | } | 259 | } |
254 | 260 | ||
255 | if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid)) | 261 | if ((rule->flags & IMA_FOWNER) && |
262 | !rule->fowner_op(inode->i_uid, rule->fowner)) | ||
256 | return false; | 263 | return false; |
257 | for (i = 0; i < MAX_LSM_RULES; i++) { | 264 | for (i = 0; i < MAX_LSM_RULES; i++) { |
258 | int rc = 0; | 265 | int rc = 0; |
@@ -486,7 +493,9 @@ enum { | |||
486 | Opt_obj_user, Opt_obj_role, Opt_obj_type, | 493 | Opt_obj_user, Opt_obj_role, Opt_obj_type, |
487 | Opt_subj_user, Opt_subj_role, Opt_subj_type, | 494 | Opt_subj_user, Opt_subj_role, Opt_subj_type, |
488 | Opt_func, Opt_mask, Opt_fsmagic, | 495 | Opt_func, Opt_mask, Opt_fsmagic, |
489 | Opt_fsuuid, Opt_uid, Opt_euid, Opt_fowner, | 496 | Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq, |
497 | Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt, | ||
498 | Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, | ||
490 | Opt_appraise_type, Opt_permit_directio, | 499 | Opt_appraise_type, Opt_permit_directio, |
491 | Opt_pcr | 500 | Opt_pcr |
492 | }; | 501 | }; |
@@ -507,9 +516,15 @@ static match_table_t policy_tokens = { | |||
507 | {Opt_mask, "mask=%s"}, | 516 | {Opt_mask, "mask=%s"}, |
508 | {Opt_fsmagic, "fsmagic=%s"}, | 517 | {Opt_fsmagic, "fsmagic=%s"}, |
509 | {Opt_fsuuid, "fsuuid=%s"}, | 518 | {Opt_fsuuid, "fsuuid=%s"}, |
510 | {Opt_uid, "uid=%s"}, | 519 | {Opt_uid_eq, "uid=%s"}, |
511 | {Opt_euid, "euid=%s"}, | 520 | {Opt_euid_eq, "euid=%s"}, |
512 | {Opt_fowner, "fowner=%s"}, | 521 | {Opt_fowner_eq, "fowner=%s"}, |
522 | {Opt_uid_gt, "uid>%s"}, | ||
523 | {Opt_euid_gt, "euid>%s"}, | ||
524 | {Opt_fowner_gt, "fowner>%s"}, | ||
525 | {Opt_uid_lt, "uid<%s"}, | ||
526 | {Opt_euid_lt, "euid<%s"}, | ||
527 | {Opt_fowner_lt, "fowner<%s"}, | ||
513 | {Opt_appraise_type, "appraise_type=%s"}, | 528 | {Opt_appraise_type, "appraise_type=%s"}, |
514 | {Opt_permit_directio, "permit_directio"}, | 529 | {Opt_permit_directio, "permit_directio"}, |
515 | {Opt_pcr, "pcr=%s"}, | 530 | {Opt_pcr, "pcr=%s"}, |
@@ -541,24 +556,37 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry, | |||
541 | return result; | 556 | return result; |
542 | } | 557 | } |
543 | 558 | ||
544 | static void ima_log_string(struct audit_buffer *ab, char *key, char *value) | 559 | static void ima_log_string_op(struct audit_buffer *ab, char *key, char *value, |
560 | bool (*rule_operator)(kuid_t, kuid_t)) | ||
545 | { | 561 | { |
546 | audit_log_format(ab, "%s=", key); | 562 | if (rule_operator == &uid_gt) |
563 | audit_log_format(ab, "%s>", key); | ||
564 | else if (rule_operator == &uid_lt) | ||
565 | audit_log_format(ab, "%s<", key); | ||
566 | else | ||
567 | audit_log_format(ab, "%s=", key); | ||
547 | audit_log_untrustedstring(ab, value); | 568 | audit_log_untrustedstring(ab, value); |
548 | audit_log_format(ab, " "); | 569 | audit_log_format(ab, " "); |
549 | } | 570 | } |
571 | static void ima_log_string(struct audit_buffer *ab, char *key, char *value) | ||
572 | { | ||
573 | ima_log_string_op(ab, key, value, NULL); | ||
574 | } | ||
550 | 575 | ||
551 | static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) | 576 | static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) |
552 | { | 577 | { |
553 | struct audit_buffer *ab; | 578 | struct audit_buffer *ab; |
554 | char *from; | 579 | char *from; |
555 | char *p; | 580 | char *p; |
581 | bool uid_token; | ||
556 | int result = 0; | 582 | int result = 0; |
557 | 583 | ||
558 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); | 584 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); |
559 | 585 | ||
560 | entry->uid = INVALID_UID; | 586 | entry->uid = INVALID_UID; |
561 | entry->fowner = INVALID_UID; | 587 | entry->fowner = INVALID_UID; |
588 | entry->uid_op = &uid_eq; | ||
589 | entry->fowner_op = &uid_eq; | ||
562 | entry->action = UNKNOWN; | 590 | entry->action = UNKNOWN; |
563 | while ((p = strsep(&rule, " \t")) != NULL) { | 591 | while ((p = strsep(&rule, " \t")) != NULL) { |
564 | substring_t args[MAX_OPT_ARGS]; | 592 | substring_t args[MAX_OPT_ARGS]; |
@@ -694,11 +722,21 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) | |||
694 | if (!result) | 722 | if (!result) |
695 | entry->flags |= IMA_FSUUID; | 723 | entry->flags |= IMA_FSUUID; |
696 | break; | 724 | break; |
697 | case Opt_uid: | 725 | case Opt_uid_gt: |
698 | ima_log_string(ab, "uid", args[0].from); | 726 | case Opt_euid_gt: |
699 | case Opt_euid: | 727 | entry->uid_op = &uid_gt; |
700 | if (token == Opt_euid) | 728 | case Opt_uid_lt: |
701 | ima_log_string(ab, "euid", args[0].from); | 729 | case Opt_euid_lt: |
730 | if ((token == Opt_uid_lt) || (token == Opt_euid_lt)) | ||
731 | entry->uid_op = &uid_lt; | ||
732 | case Opt_uid_eq: | ||
733 | case Opt_euid_eq: | ||
734 | uid_token = (token == Opt_uid_eq) || | ||
735 | (token == Opt_uid_gt) || | ||
736 | (token == Opt_uid_lt); | ||
737 | |||
738 | ima_log_string_op(ab, uid_token ? "uid" : "euid", | ||
739 | args[0].from, entry->uid_op); | ||
702 | 740 | ||
703 | if (uid_valid(entry->uid)) { | 741 | if (uid_valid(entry->uid)) { |
704 | result = -EINVAL; | 742 | result = -EINVAL; |
@@ -713,12 +751,18 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) | |||
713 | (uid_t)lnum != lnum) | 751 | (uid_t)lnum != lnum) |
714 | result = -EINVAL; | 752 | result = -EINVAL; |
715 | else | 753 | else |
716 | entry->flags |= (token == Opt_uid) | 754 | entry->flags |= uid_token |
717 | ? IMA_UID : IMA_EUID; | 755 | ? IMA_UID : IMA_EUID; |
718 | } | 756 | } |
719 | break; | 757 | break; |
720 | case Opt_fowner: | 758 | case Opt_fowner_gt: |
721 | ima_log_string(ab, "fowner", args[0].from); | 759 | entry->fowner_op = &uid_gt; |
760 | case Opt_fowner_lt: | ||
761 | if (token == Opt_fowner_lt) | ||
762 | entry->fowner_op = &uid_lt; | ||
763 | case Opt_fowner_eq: | ||
764 | ima_log_string_op(ab, "fowner", args[0].from, | ||
765 | entry->fowner_op); | ||
722 | 766 | ||
723 | if (uid_valid(entry->fowner)) { | 767 | if (uid_valid(entry->fowner)) { |
724 | result = -EINVAL; | 768 | result = -EINVAL; |
@@ -1049,19 +1093,34 @@ int ima_policy_show(struct seq_file *m, void *v) | |||
1049 | 1093 | ||
1050 | if (entry->flags & IMA_UID) { | 1094 | if (entry->flags & IMA_UID) { |
1051 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); | 1095 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); |
1052 | seq_printf(m, pt(Opt_uid), tbuf); | 1096 | if (entry->uid_op == &uid_gt) |
1097 | seq_printf(m, pt(Opt_uid_gt), tbuf); | ||
1098 | else if (entry->uid_op == &uid_lt) | ||
1099 | seq_printf(m, pt(Opt_uid_lt), tbuf); | ||
1100 | else | ||
1101 | seq_printf(m, pt(Opt_uid_eq), tbuf); | ||
1053 | seq_puts(m, " "); | 1102 | seq_puts(m, " "); |
1054 | } | 1103 | } |
1055 | 1104 | ||
1056 | if (entry->flags & IMA_EUID) { | 1105 | if (entry->flags & IMA_EUID) { |
1057 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); | 1106 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); |
1058 | seq_printf(m, pt(Opt_euid), tbuf); | 1107 | if (entry->uid_op == &uid_gt) |
1108 | seq_printf(m, pt(Opt_euid_gt), tbuf); | ||
1109 | else if (entry->uid_op == &uid_lt) | ||
1110 | seq_printf(m, pt(Opt_euid_lt), tbuf); | ||
1111 | else | ||
1112 | seq_printf(m, pt(Opt_euid_eq), tbuf); | ||
1059 | seq_puts(m, " "); | 1113 | seq_puts(m, " "); |
1060 | } | 1114 | } |
1061 | 1115 | ||
1062 | if (entry->flags & IMA_FOWNER) { | 1116 | if (entry->flags & IMA_FOWNER) { |
1063 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner)); | 1117 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner)); |
1064 | seq_printf(m, pt(Opt_fowner), tbuf); | 1118 | if (entry->fowner_op == &uid_gt) |
1119 | seq_printf(m, pt(Opt_fowner_gt), tbuf); | ||
1120 | else if (entry->fowner_op == &uid_lt) | ||
1121 | seq_printf(m, pt(Opt_fowner_lt), tbuf); | ||
1122 | else | ||
1123 | seq_printf(m, pt(Opt_fowner_eq), tbuf); | ||
1065 | seq_puts(m, " "); | 1124 | seq_puts(m, " "); |
1066 | } | 1125 | } |
1067 | 1126 | ||