diff options
Diffstat (limited to 'security/integrity/ima/ima_policy.c')
-rw-r--r-- | security/integrity/ima/ima_policy.c | 138 |
1 files changed, 124 insertions, 14 deletions
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 479fca940bb5..b27535a13a79 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/magic.h> | 16 | #include <linux/magic.h> |
17 | #include <linux/parser.h> | 17 | #include <linux/parser.h> |
18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
19 | #include <linux/genhd.h> | ||
19 | 20 | ||
20 | #include "ima.h" | 21 | #include "ima.h" |
21 | 22 | ||
@@ -25,6 +26,7 @@ | |||
25 | #define IMA_FSMAGIC 0x0004 | 26 | #define IMA_FSMAGIC 0x0004 |
26 | #define IMA_UID 0x0008 | 27 | #define IMA_UID 0x0008 |
27 | #define IMA_FOWNER 0x0010 | 28 | #define IMA_FOWNER 0x0010 |
29 | #define IMA_FSUUID 0x0020 | ||
28 | 30 | ||
29 | #define UNKNOWN 0 | 31 | #define UNKNOWN 0 |
30 | #define MEASURE 0x0001 /* same as IMA_MEASURE */ | 32 | #define MEASURE 0x0001 /* same as IMA_MEASURE */ |
@@ -45,10 +47,12 @@ struct ima_rule_entry { | |||
45 | enum ima_hooks func; | 47 | enum ima_hooks func; |
46 | int mask; | 48 | int mask; |
47 | unsigned long fsmagic; | 49 | unsigned long fsmagic; |
50 | u8 fsuuid[16]; | ||
48 | kuid_t uid; | 51 | kuid_t uid; |
49 | kuid_t fowner; | 52 | kuid_t fowner; |
50 | struct { | 53 | struct { |
51 | void *rule; /* LSM file metadata specific */ | 54 | void *rule; /* LSM file metadata specific */ |
55 | void *args_p; /* audit value */ | ||
52 | int type; /* audit type */ | 56 | int type; /* audit type */ |
53 | } lsm[MAX_LSM_RULES]; | 57 | } lsm[MAX_LSM_RULES]; |
54 | }; | 58 | }; |
@@ -74,7 +78,7 @@ static struct ima_rule_entry default_rules[] = { | |||
74 | {.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC}, | 78 | {.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC}, |
75 | {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC}, | 79 | {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC}, |
76 | {.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC}, | 80 | {.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC}, |
77 | {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC, | 81 | {.action = MEASURE,.func = MMAP_CHECK,.mask = MAY_EXEC, |
78 | .flags = IMA_FUNC | IMA_MASK}, | 82 | .flags = IMA_FUNC | IMA_MASK}, |
79 | {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC, | 83 | {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC, |
80 | .flags = IMA_FUNC | IMA_MASK}, | 84 | .flags = IMA_FUNC | IMA_MASK}, |
@@ -119,6 +123,35 @@ static int __init default_appraise_policy_setup(char *str) | |||
119 | } | 123 | } |
120 | __setup("ima_appraise_tcb", default_appraise_policy_setup); | 124 | __setup("ima_appraise_tcb", default_appraise_policy_setup); |
121 | 125 | ||
126 | /* | ||
127 | * Although the IMA policy does not change, the LSM policy can be | ||
128 | * reloaded, leaving the IMA LSM based rules referring to the old, | ||
129 | * stale LSM policy. | ||
130 | * | ||
131 | * Update the IMA LSM based rules to reflect the reloaded LSM policy. | ||
132 | * We assume the rules still exist; and BUG_ON() if they don't. | ||
133 | */ | ||
134 | static void ima_lsm_update_rules(void) | ||
135 | { | ||
136 | struct ima_rule_entry *entry, *tmp; | ||
137 | int result; | ||
138 | int i; | ||
139 | |||
140 | mutex_lock(&ima_rules_mutex); | ||
141 | list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) { | ||
142 | for (i = 0; i < MAX_LSM_RULES; i++) { | ||
143 | if (!entry->lsm[i].rule) | ||
144 | continue; | ||
145 | result = security_filter_rule_init(entry->lsm[i].type, | ||
146 | Audit_equal, | ||
147 | entry->lsm[i].args_p, | ||
148 | &entry->lsm[i].rule); | ||
149 | BUG_ON(!entry->lsm[i].rule); | ||
150 | } | ||
151 | } | ||
152 | mutex_unlock(&ima_rules_mutex); | ||
153 | } | ||
154 | |||
122 | /** | 155 | /** |
123 | * ima_match_rules - determine whether an inode matches the measure rule. | 156 | * ima_match_rules - determine whether an inode matches the measure rule. |
124 | * @rule: a pointer to a rule | 157 | * @rule: a pointer to a rule |
@@ -142,6 +175,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule, | |||
142 | if ((rule->flags & IMA_FSMAGIC) | 175 | if ((rule->flags & IMA_FSMAGIC) |
143 | && rule->fsmagic != inode->i_sb->s_magic) | 176 | && rule->fsmagic != inode->i_sb->s_magic) |
144 | return false; | 177 | return false; |
178 | if ((rule->flags & IMA_FSUUID) && | ||
179 | memcmp(rule->fsuuid, inode->i_sb->s_uuid, sizeof(rule->fsuuid))) | ||
180 | return false; | ||
145 | if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid)) | 181 | if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid)) |
146 | return false; | 182 | return false; |
147 | if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid)) | 183 | if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid)) |
@@ -149,10 +185,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule, | |||
149 | for (i = 0; i < MAX_LSM_RULES; i++) { | 185 | for (i = 0; i < MAX_LSM_RULES; i++) { |
150 | int rc = 0; | 186 | int rc = 0; |
151 | u32 osid, sid; | 187 | u32 osid, sid; |
188 | int retried = 0; | ||
152 | 189 | ||
153 | if (!rule->lsm[i].rule) | 190 | if (!rule->lsm[i].rule) |
154 | continue; | 191 | continue; |
155 | 192 | retry: | |
156 | switch (i) { | 193 | switch (i) { |
157 | case LSM_OBJ_USER: | 194 | case LSM_OBJ_USER: |
158 | case LSM_OBJ_ROLE: | 195 | case LSM_OBJ_ROLE: |
@@ -176,12 +213,39 @@ static bool ima_match_rules(struct ima_rule_entry *rule, | |||
176 | default: | 213 | default: |
177 | break; | 214 | break; |
178 | } | 215 | } |
216 | if ((rc < 0) && (!retried)) { | ||
217 | retried = 1; | ||
218 | ima_lsm_update_rules(); | ||
219 | goto retry; | ||
220 | } | ||
179 | if (!rc) | 221 | if (!rc) |
180 | return false; | 222 | return false; |
181 | } | 223 | } |
182 | return true; | 224 | return true; |
183 | } | 225 | } |
184 | 226 | ||
227 | /* | ||
228 | * In addition to knowing that we need to appraise the file in general, | ||
229 | * we need to differentiate between calling hooks, for hook specific rules. | ||
230 | */ | ||
231 | static int get_subaction(struct ima_rule_entry *rule, int func) | ||
232 | { | ||
233 | if (!(rule->flags & IMA_FUNC)) | ||
234 | return IMA_FILE_APPRAISE; | ||
235 | |||
236 | switch(func) { | ||
237 | case MMAP_CHECK: | ||
238 | return IMA_MMAP_APPRAISE; | ||
239 | case BPRM_CHECK: | ||
240 | return IMA_BPRM_APPRAISE; | ||
241 | case MODULE_CHECK: | ||
242 | return IMA_MODULE_APPRAISE; | ||
243 | case FILE_CHECK: | ||
244 | default: | ||
245 | return IMA_FILE_APPRAISE; | ||
246 | } | ||
247 | } | ||
248 | |||
185 | /** | 249 | /** |
186 | * ima_match_policy - decision based on LSM and other conditions | 250 | * ima_match_policy - decision based on LSM and other conditions |
187 | * @inode: pointer to an inode for which the policy decision is being made | 251 | * @inode: pointer to an inode for which the policy decision is being made |
@@ -209,7 +273,12 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, | |||
209 | if (!ima_match_rules(entry, inode, func, mask)) | 273 | if (!ima_match_rules(entry, inode, func, mask)) |
210 | continue; | 274 | continue; |
211 | 275 | ||
276 | action |= entry->flags & IMA_ACTION_FLAGS; | ||
277 | |||
212 | action |= entry->action & IMA_DO_MASK; | 278 | action |= entry->action & IMA_DO_MASK; |
279 | if (entry->action & IMA_APPRAISE) | ||
280 | action |= get_subaction(entry, func); | ||
281 | |||
213 | if (entry->action & IMA_DO_MASK) | 282 | if (entry->action & IMA_DO_MASK) |
214 | actmask &= ~(entry->action | entry->action << 1); | 283 | actmask &= ~(entry->action | entry->action << 1); |
215 | else | 284 | else |
@@ -282,7 +351,8 @@ enum { | |||
282 | Opt_audit, | 351 | Opt_audit, |
283 | Opt_obj_user, Opt_obj_role, Opt_obj_type, | 352 | Opt_obj_user, Opt_obj_role, Opt_obj_type, |
284 | Opt_subj_user, Opt_subj_role, Opt_subj_type, | 353 | Opt_subj_user, Opt_subj_role, Opt_subj_type, |
285 | Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner | 354 | Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner, |
355 | Opt_appraise_type, Opt_fsuuid | ||
286 | }; | 356 | }; |
287 | 357 | ||
288 | static match_table_t policy_tokens = { | 358 | static match_table_t policy_tokens = { |
@@ -300,25 +370,35 @@ static match_table_t policy_tokens = { | |||
300 | {Opt_func, "func=%s"}, | 370 | {Opt_func, "func=%s"}, |
301 | {Opt_mask, "mask=%s"}, | 371 | {Opt_mask, "mask=%s"}, |
302 | {Opt_fsmagic, "fsmagic=%s"}, | 372 | {Opt_fsmagic, "fsmagic=%s"}, |
373 | {Opt_fsuuid, "fsuuid=%s"}, | ||
303 | {Opt_uid, "uid=%s"}, | 374 | {Opt_uid, "uid=%s"}, |
304 | {Opt_fowner, "fowner=%s"}, | 375 | {Opt_fowner, "fowner=%s"}, |
376 | {Opt_appraise_type, "appraise_type=%s"}, | ||
305 | {Opt_err, NULL} | 377 | {Opt_err, NULL} |
306 | }; | 378 | }; |
307 | 379 | ||
308 | static int ima_lsm_rule_init(struct ima_rule_entry *entry, | 380 | static int ima_lsm_rule_init(struct ima_rule_entry *entry, |
309 | char *args, int lsm_rule, int audit_type) | 381 | substring_t *args, int lsm_rule, int audit_type) |
310 | { | 382 | { |
311 | int result; | 383 | int result; |
312 | 384 | ||
313 | if (entry->lsm[lsm_rule].rule) | 385 | if (entry->lsm[lsm_rule].rule) |
314 | return -EINVAL; | 386 | return -EINVAL; |
315 | 387 | ||
388 | entry->lsm[lsm_rule].args_p = match_strdup(args); | ||
389 | if (!entry->lsm[lsm_rule].args_p) | ||
390 | return -ENOMEM; | ||
391 | |||
316 | entry->lsm[lsm_rule].type = audit_type; | 392 | entry->lsm[lsm_rule].type = audit_type; |
317 | result = security_filter_rule_init(entry->lsm[lsm_rule].type, | 393 | result = security_filter_rule_init(entry->lsm[lsm_rule].type, |
318 | Audit_equal, args, | 394 | Audit_equal, |
395 | entry->lsm[lsm_rule].args_p, | ||
319 | &entry->lsm[lsm_rule].rule); | 396 | &entry->lsm[lsm_rule].rule); |
320 | if (!entry->lsm[lsm_rule].rule) | 397 | if (!entry->lsm[lsm_rule].rule) { |
398 | kfree(entry->lsm[lsm_rule].args_p); | ||
321 | return -EINVAL; | 399 | return -EINVAL; |
400 | } | ||
401 | |||
322 | return result; | 402 | return result; |
323 | } | 403 | } |
324 | 404 | ||
@@ -404,8 +484,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) | |||
404 | entry->func = FILE_CHECK; | 484 | entry->func = FILE_CHECK; |
405 | else if (strcmp(args[0].from, "MODULE_CHECK") == 0) | 485 | else if (strcmp(args[0].from, "MODULE_CHECK") == 0) |
406 | entry->func = MODULE_CHECK; | 486 | entry->func = MODULE_CHECK; |
407 | else if (strcmp(args[0].from, "FILE_MMAP") == 0) | 487 | else if ((strcmp(args[0].from, "FILE_MMAP") == 0) |
408 | entry->func = FILE_MMAP; | 488 | || (strcmp(args[0].from, "MMAP_CHECK") == 0)) |
489 | entry->func = MMAP_CHECK; | ||
409 | else if (strcmp(args[0].from, "BPRM_CHECK") == 0) | 490 | else if (strcmp(args[0].from, "BPRM_CHECK") == 0) |
410 | entry->func = BPRM_CHECK; | 491 | entry->func = BPRM_CHECK; |
411 | else | 492 | else |
@@ -445,6 +526,19 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) | |||
445 | if (!result) | 526 | if (!result) |
446 | entry->flags |= IMA_FSMAGIC; | 527 | entry->flags |= IMA_FSMAGIC; |
447 | break; | 528 | break; |
529 | case Opt_fsuuid: | ||
530 | ima_log_string(ab, "fsuuid", args[0].from); | ||
531 | |||
532 | if (memchr_inv(entry->fsuuid, 0x00, | ||
533 | sizeof(entry->fsuuid))) { | ||
534 | result = -EINVAL; | ||
535 | break; | ||
536 | } | ||
537 | |||
538 | part_pack_uuid(args[0].from, entry->fsuuid); | ||
539 | entry->flags |= IMA_FSUUID; | ||
540 | result = 0; | ||
541 | break; | ||
448 | case Opt_uid: | 542 | case Opt_uid: |
449 | ima_log_string(ab, "uid", args[0].from); | 543 | ima_log_string(ab, "uid", args[0].from); |
450 | 544 | ||
@@ -481,40 +575,52 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) | |||
481 | break; | 575 | break; |
482 | case Opt_obj_user: | 576 | case Opt_obj_user: |
483 | ima_log_string(ab, "obj_user", args[0].from); | 577 | ima_log_string(ab, "obj_user", args[0].from); |
484 | result = ima_lsm_rule_init(entry, args[0].from, | 578 | result = ima_lsm_rule_init(entry, args, |
485 | LSM_OBJ_USER, | 579 | LSM_OBJ_USER, |
486 | AUDIT_OBJ_USER); | 580 | AUDIT_OBJ_USER); |
487 | break; | 581 | break; |
488 | case Opt_obj_role: | 582 | case Opt_obj_role: |
489 | ima_log_string(ab, "obj_role", args[0].from); | 583 | ima_log_string(ab, "obj_role", args[0].from); |
490 | result = ima_lsm_rule_init(entry, args[0].from, | 584 | result = ima_lsm_rule_init(entry, args, |
491 | LSM_OBJ_ROLE, | 585 | LSM_OBJ_ROLE, |
492 | AUDIT_OBJ_ROLE); | 586 | AUDIT_OBJ_ROLE); |
493 | break; | 587 | break; |
494 | case Opt_obj_type: | 588 | case Opt_obj_type: |
495 | ima_log_string(ab, "obj_type", args[0].from); | 589 | ima_log_string(ab, "obj_type", args[0].from); |
496 | result = ima_lsm_rule_init(entry, args[0].from, | 590 | result = ima_lsm_rule_init(entry, args, |
497 | LSM_OBJ_TYPE, | 591 | LSM_OBJ_TYPE, |
498 | AUDIT_OBJ_TYPE); | 592 | AUDIT_OBJ_TYPE); |
499 | break; | 593 | break; |
500 | case Opt_subj_user: | 594 | case Opt_subj_user: |
501 | ima_log_string(ab, "subj_user", args[0].from); | 595 | ima_log_string(ab, "subj_user", args[0].from); |
502 | result = ima_lsm_rule_init(entry, args[0].from, | 596 | result = ima_lsm_rule_init(entry, args, |
503 | LSM_SUBJ_USER, | 597 | LSM_SUBJ_USER, |
504 | AUDIT_SUBJ_USER); | 598 | AUDIT_SUBJ_USER); |
505 | break; | 599 | break; |
506 | case Opt_subj_role: | 600 | case Opt_subj_role: |
507 | ima_log_string(ab, "subj_role", args[0].from); | 601 | ima_log_string(ab, "subj_role", args[0].from); |
508 | result = ima_lsm_rule_init(entry, args[0].from, | 602 | result = ima_lsm_rule_init(entry, args, |
509 | LSM_SUBJ_ROLE, | 603 | LSM_SUBJ_ROLE, |
510 | AUDIT_SUBJ_ROLE); | 604 | AUDIT_SUBJ_ROLE); |
511 | break; | 605 | break; |
512 | case Opt_subj_type: | 606 | case Opt_subj_type: |
513 | ima_log_string(ab, "subj_type", args[0].from); | 607 | ima_log_string(ab, "subj_type", args[0].from); |
514 | result = ima_lsm_rule_init(entry, args[0].from, | 608 | result = ima_lsm_rule_init(entry, args, |
515 | LSM_SUBJ_TYPE, | 609 | LSM_SUBJ_TYPE, |
516 | AUDIT_SUBJ_TYPE); | 610 | AUDIT_SUBJ_TYPE); |
517 | break; | 611 | break; |
612 | case Opt_appraise_type: | ||
613 | if (entry->action != APPRAISE) { | ||
614 | result = -EINVAL; | ||
615 | break; | ||
616 | } | ||
617 | |||
618 | ima_log_string(ab, "appraise_type", args[0].from); | ||
619 | if ((strcmp(args[0].from, "imasig")) == 0) | ||
620 | entry->flags |= IMA_DIGSIG_REQUIRED; | ||
621 | else | ||
622 | result = -EINVAL; | ||
623 | break; | ||
518 | case Opt_err: | 624 | case Opt_err: |
519 | ima_log_string(ab, "UNKNOWN", p); | 625 | ima_log_string(ab, "UNKNOWN", p); |
520 | result = -EINVAL; | 626 | result = -EINVAL; |
@@ -590,9 +696,13 @@ ssize_t ima_parse_add_rule(char *rule) | |||
590 | void ima_delete_rules(void) | 696 | void ima_delete_rules(void) |
591 | { | 697 | { |
592 | struct ima_rule_entry *entry, *tmp; | 698 | struct ima_rule_entry *entry, *tmp; |
699 | int i; | ||
593 | 700 | ||
594 | mutex_lock(&ima_rules_mutex); | 701 | mutex_lock(&ima_rules_mutex); |
595 | list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) { | 702 | list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) { |
703 | for (i = 0; i < MAX_LSM_RULES; i++) | ||
704 | kfree(entry->lsm[i].args_p); | ||
705 | |||
596 | list_del(&entry->list); | 706 | list_del(&entry->list); |
597 | kfree(entry); | 707 | kfree(entry); |
598 | } | 708 | } |