diff options
author | Petko Manolov <petkan@mip-labs.com> | 2015-12-02 10:47:56 -0500 |
---|---|---|
committer | Mimi Zohar <zohar@linux.vnet.ibm.com> | 2015-12-15 10:01:43 -0500 |
commit | 80eae209d63ac6361c7b445f7e7e41f39c044772 (patch) | |
tree | db99b638e2688529f6f61756ffae56b64a95311b | |
parent | 41c89b64d7184a780f12f2cccdabe65cb2408893 (diff) |
IMA: allow reading back the current IMA policy
It is often useful to be able to read back the IMA policy. It is
even more important after introducing CONFIG_IMA_WRITE_POLICY.
This option allows the root user to see the current policy rules.
Signed-off-by: Zbigniew Jasinski <z.jasinski@samsung.com>
Signed-off-by: Petko Manolov <petkan@mip-labs.com>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
-rw-r--r-- | security/integrity/ima/Kconfig | 10 | ||||
-rw-r--r-- | security/integrity/ima/ima.h | 15 | ||||
-rw-r--r-- | security/integrity/ima/ima_fs.c | 29 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 207 |
4 files changed, 253 insertions, 8 deletions
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 8d5e6e0e0937..e54a8a8dae94 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig | |||
@@ -118,6 +118,16 @@ config IMA_WRITE_POLICY | |||
118 | 118 | ||
119 | If unsure, say N. | 119 | If unsure, say N. |
120 | 120 | ||
121 | config IMA_READ_POLICY | ||
122 | bool "Enable reading back the current IMA policy" | ||
123 | depends on IMA | ||
124 | default y if IMA_WRITE_POLICY | ||
125 | default n if !IMA_WRITE_POLICY | ||
126 | help | ||
127 | It is often useful to be able to read back the IMA policy. It is | ||
128 | even more important after introducing CONFIG_IMA_WRITE_POLICY. | ||
129 | This option allows the root user to see the current policy rules. | ||
130 | |||
121 | config IMA_APPRAISE | 131 | config IMA_APPRAISE |
122 | bool "Appraise integrity measurements" | 132 | bool "Appraise integrity measurements" |
123 | depends on IMA | 133 | depends on IMA |
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 9e82367f5190..917407fb7e94 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h | |||
@@ -166,6 +166,10 @@ void ima_update_policy(void); | |||
166 | void ima_update_policy_flag(void); | 166 | void ima_update_policy_flag(void); |
167 | ssize_t ima_parse_add_rule(char *); | 167 | ssize_t ima_parse_add_rule(char *); |
168 | void ima_delete_rules(void); | 168 | void ima_delete_rules(void); |
169 | void *ima_policy_start(struct seq_file *m, loff_t *pos); | ||
170 | void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos); | ||
171 | void ima_policy_stop(struct seq_file *m, void *v); | ||
172 | int ima_policy_show(struct seq_file *m, void *v); | ||
169 | 173 | ||
170 | /* Appraise integrity measurements */ | 174 | /* Appraise integrity measurements */ |
171 | #define IMA_APPRAISE_ENFORCE 0x01 | 175 | #define IMA_APPRAISE_ENFORCE 0x01 |
@@ -250,5 +254,12 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op, | |||
250 | { | 254 | { |
251 | return -EINVAL; | 255 | return -EINVAL; |
252 | } | 256 | } |
253 | #endif /* CONFIG_IMA_LSM_RULES */ | 257 | #endif /* CONFIG_IMA_TRUSTED_KEYRING */ |
254 | #endif | 258 | |
259 | #ifdef CONFIG_IMA_READ_POLICY | ||
260 | #define POLICY_FILE_FLAGS (S_IWUSR | S_IRUSR) | ||
261 | #else | ||
262 | #define POLICY_FILE_FLAGS S_IWUSR | ||
263 | #endif /* CONFIG_IMA_WRITE_POLICY */ | ||
264 | |||
265 | #endif /* __LINUX_IMA_H */ | ||
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index a3cf5c0ab501..eebb985fd083 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c | |||
@@ -311,14 +311,31 @@ enum ima_fs_flags { | |||
311 | 311 | ||
312 | static unsigned long ima_fs_flags; | 312 | static unsigned long ima_fs_flags; |
313 | 313 | ||
314 | #ifdef CONFIG_IMA_READ_POLICY | ||
315 | static const struct seq_operations ima_policy_seqops = { | ||
316 | .start = ima_policy_start, | ||
317 | .next = ima_policy_next, | ||
318 | .stop = ima_policy_stop, | ||
319 | .show = ima_policy_show, | ||
320 | }; | ||
321 | #endif | ||
322 | |||
314 | /* | 323 | /* |
315 | * ima_open_policy: sequentialize access to the policy file | 324 | * ima_open_policy: sequentialize access to the policy file |
316 | */ | 325 | */ |
317 | static int ima_open_policy(struct inode *inode, struct file *filp) | 326 | static int ima_open_policy(struct inode *inode, struct file *filp) |
318 | { | 327 | { |
319 | /* No point in being allowed to open it if you aren't going to write */ | 328 | if (!(filp->f_flags & O_WRONLY)) { |
320 | if (!(filp->f_flags & O_WRONLY)) | 329 | #ifndef CONFIG_IMA_READ_POLICY |
321 | return -EACCES; | 330 | return -EACCES; |
331 | #else | ||
332 | if ((filp->f_flags & O_ACCMODE) != O_RDONLY) | ||
333 | return -EACCES; | ||
334 | if (!capable(CAP_SYS_ADMIN)) | ||
335 | return -EPERM; | ||
336 | return seq_open(filp, &ima_policy_seqops); | ||
337 | #endif | ||
338 | } | ||
322 | if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags)) | 339 | if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags)) |
323 | return -EBUSY; | 340 | return -EBUSY; |
324 | return 0; | 341 | return 0; |
@@ -335,6 +352,9 @@ static int ima_release_policy(struct inode *inode, struct file *file) | |||
335 | { | 352 | { |
336 | const char *cause = valid_policy ? "completed" : "failed"; | 353 | const char *cause = valid_policy ? "completed" : "failed"; |
337 | 354 | ||
355 | if ((file->f_flags & O_ACCMODE) == O_RDONLY) | ||
356 | return 0; | ||
357 | |||
338 | pr_info("IMA: policy update %s\n", cause); | 358 | pr_info("IMA: policy update %s\n", cause); |
339 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, | 359 | integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, |
340 | "policy_update", cause, !valid_policy, 0); | 360 | "policy_update", cause, !valid_policy, 0); |
@@ -345,6 +365,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) | |||
345 | clear_bit(IMA_FS_BUSY, &ima_fs_flags); | 365 | clear_bit(IMA_FS_BUSY, &ima_fs_flags); |
346 | return 0; | 366 | return 0; |
347 | } | 367 | } |
368 | |||
348 | ima_update_policy(); | 369 | ima_update_policy(); |
349 | #ifndef CONFIG_IMA_WRITE_POLICY | 370 | #ifndef CONFIG_IMA_WRITE_POLICY |
350 | securityfs_remove(ima_policy); | 371 | securityfs_remove(ima_policy); |
@@ -358,6 +379,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) | |||
358 | static const struct file_operations ima_measure_policy_ops = { | 379 | static const struct file_operations ima_measure_policy_ops = { |
359 | .open = ima_open_policy, | 380 | .open = ima_open_policy, |
360 | .write = ima_write_policy, | 381 | .write = ima_write_policy, |
382 | .read = seq_read, | ||
361 | .release = ima_release_policy, | 383 | .release = ima_release_policy, |
362 | .llseek = generic_file_llseek, | 384 | .llseek = generic_file_llseek, |
363 | }; | 385 | }; |
@@ -395,8 +417,7 @@ int __init ima_fs_init(void) | |||
395 | if (IS_ERR(violations)) | 417 | if (IS_ERR(violations)) |
396 | goto out; | 418 | goto out; |
397 | 419 | ||
398 | ima_policy = securityfs_create_file("policy", | 420 | ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS, |
399 | S_IWUSR, | ||
400 | ima_dir, NULL, | 421 | ima_dir, NULL, |
401 | &ima_measure_policy_ops); | 422 | &ima_measure_policy_ops); |
402 | if (IS_ERR(ima_policy)) | 423 | if (IS_ERR(ima_policy)) |
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 10a0a9b9e22d..2f4e0f5f31e2 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
19 | #include <linux/rculist.h> | 19 | #include <linux/rculist.h> |
20 | #include <linux/genhd.h> | 20 | #include <linux/genhd.h> |
21 | #include <linux/seq_file.h> | ||
21 | 22 | ||
22 | #include "ima.h" | 23 | #include "ima.h" |
23 | 24 | ||
@@ -458,8 +459,8 @@ enum { | |||
458 | Opt_obj_user, Opt_obj_role, Opt_obj_type, | 459 | Opt_obj_user, Opt_obj_role, Opt_obj_type, |
459 | Opt_subj_user, Opt_subj_role, Opt_subj_type, | 460 | Opt_subj_user, Opt_subj_role, Opt_subj_type, |
460 | Opt_func, Opt_mask, Opt_fsmagic, | 461 | Opt_func, Opt_mask, Opt_fsmagic, |
461 | Opt_uid, Opt_euid, Opt_fowner, | 462 | Opt_fsuuid, Opt_uid, Opt_euid, Opt_fowner, |
462 | Opt_appraise_type, Opt_fsuuid, Opt_permit_directio | 463 | Opt_appraise_type, Opt_permit_directio |
463 | }; | 464 | }; |
464 | 465 | ||
465 | static match_table_t policy_tokens = { | 466 | static match_table_t policy_tokens = { |
@@ -828,3 +829,205 @@ void ima_delete_rules(void) | |||
828 | kfree(entry); | 829 | kfree(entry); |
829 | } | 830 | } |
830 | } | 831 | } |
832 | |||
833 | #ifdef CONFIG_IMA_READ_POLICY | ||
834 | enum { | ||
835 | mask_exec = 0, mask_write, mask_read, mask_append | ||
836 | }; | ||
837 | |||
838 | static char *mask_tokens[] = { | ||
839 | "MAY_EXEC", | ||
840 | "MAY_WRITE", | ||
841 | "MAY_READ", | ||
842 | "MAY_APPEND" | ||
843 | }; | ||
844 | |||
845 | enum { | ||
846 | func_file = 0, func_mmap, func_bprm, | ||
847 | func_module, func_firmware, func_post | ||
848 | }; | ||
849 | |||
850 | static char *func_tokens[] = { | ||
851 | "FILE_CHECK", | ||
852 | "MMAP_CHECK", | ||
853 | "BPRM_CHECK", | ||
854 | "MODULE_CHECK", | ||
855 | "FIRMWARE_CHECK", | ||
856 | "POST_SETATTR" | ||
857 | }; | ||
858 | |||
859 | void *ima_policy_start(struct seq_file *m, loff_t *pos) | ||
860 | { | ||
861 | loff_t l = *pos; | ||
862 | struct ima_rule_entry *entry; | ||
863 | |||
864 | rcu_read_lock(); | ||
865 | list_for_each_entry_rcu(entry, ima_rules, list) { | ||
866 | if (!l--) { | ||
867 | rcu_read_unlock(); | ||
868 | return entry; | ||
869 | } | ||
870 | } | ||
871 | rcu_read_unlock(); | ||
872 | return NULL; | ||
873 | } | ||
874 | |||
875 | void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos) | ||
876 | { | ||
877 | struct ima_rule_entry *entry = v; | ||
878 | |||
879 | rcu_read_lock(); | ||
880 | entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list); | ||
881 | rcu_read_unlock(); | ||
882 | (*pos)++; | ||
883 | |||
884 | return (&entry->list == ima_rules) ? NULL : entry; | ||
885 | } | ||
886 | |||
887 | void ima_policy_stop(struct seq_file *m, void *v) | ||
888 | { | ||
889 | } | ||
890 | |||
891 | #define pt(token) policy_tokens[token + Opt_err].pattern | ||
892 | #define mt(token) mask_tokens[token] | ||
893 | #define ft(token) func_tokens[token] | ||
894 | |||
895 | int ima_policy_show(struct seq_file *m, void *v) | ||
896 | { | ||
897 | struct ima_rule_entry *entry = v; | ||
898 | int i = 0; | ||
899 | char tbuf[64] = {0,}; | ||
900 | |||
901 | rcu_read_lock(); | ||
902 | |||
903 | if (entry->action & MEASURE) | ||
904 | seq_puts(m, pt(Opt_measure)); | ||
905 | if (entry->action & DONT_MEASURE) | ||
906 | seq_puts(m, pt(Opt_dont_measure)); | ||
907 | if (entry->action & APPRAISE) | ||
908 | seq_puts(m, pt(Opt_appraise)); | ||
909 | if (entry->action & DONT_APPRAISE) | ||
910 | seq_puts(m, pt(Opt_dont_appraise)); | ||
911 | if (entry->action & AUDIT) | ||
912 | seq_puts(m, pt(Opt_audit)); | ||
913 | |||
914 | seq_puts(m, " "); | ||
915 | |||
916 | if (entry->flags & IMA_FUNC) { | ||
917 | switch (entry->func) { | ||
918 | case FILE_CHECK: | ||
919 | seq_printf(m, pt(Opt_func), ft(func_file)); | ||
920 | break; | ||
921 | case MMAP_CHECK: | ||
922 | seq_printf(m, pt(Opt_func), ft(func_mmap)); | ||
923 | break; | ||
924 | case BPRM_CHECK: | ||
925 | seq_printf(m, pt(Opt_func), ft(func_bprm)); | ||
926 | break; | ||
927 | case MODULE_CHECK: | ||
928 | seq_printf(m, pt(Opt_func), ft(func_module)); | ||
929 | break; | ||
930 | case FIRMWARE_CHECK: | ||
931 | seq_printf(m, pt(Opt_func), ft(func_firmware)); | ||
932 | break; | ||
933 | case POST_SETATTR: | ||
934 | seq_printf(m, pt(Opt_func), ft(func_post)); | ||
935 | break; | ||
936 | default: | ||
937 | snprintf(tbuf, sizeof(tbuf), "%d", entry->func); | ||
938 | seq_printf(m, pt(Opt_func), tbuf); | ||
939 | break; | ||
940 | } | ||
941 | seq_puts(m, " "); | ||
942 | } | ||
943 | |||
944 | if (entry->flags & IMA_MASK) { | ||
945 | if (entry->mask & MAY_EXEC) | ||
946 | seq_printf(m, pt(Opt_mask), mt(mask_exec)); | ||
947 | if (entry->mask & MAY_WRITE) | ||
948 | seq_printf(m, pt(Opt_mask), mt(mask_write)); | ||
949 | if (entry->mask & MAY_READ) | ||
950 | seq_printf(m, pt(Opt_mask), mt(mask_read)); | ||
951 | if (entry->mask & MAY_APPEND) | ||
952 | seq_printf(m, pt(Opt_mask), mt(mask_append)); | ||
953 | seq_puts(m, " "); | ||
954 | } | ||
955 | |||
956 | if (entry->flags & IMA_FSMAGIC) { | ||
957 | snprintf(tbuf, sizeof(tbuf), "0x%lx", entry->fsmagic); | ||
958 | seq_printf(m, pt(Opt_fsmagic), tbuf); | ||
959 | seq_puts(m, " "); | ||
960 | } | ||
961 | |||
962 | if (entry->flags & IMA_FSUUID) { | ||
963 | seq_puts(m, "fsuuid="); | ||
964 | for (i = 0; i < ARRAY_SIZE(entry->fsuuid); ++i) { | ||
965 | switch (i) { | ||
966 | case 4: | ||
967 | case 6: | ||
968 | case 8: | ||
969 | case 10: | ||
970 | seq_puts(m, "-"); | ||
971 | } | ||
972 | seq_printf(m, "%x", entry->fsuuid[i]); | ||
973 | } | ||
974 | seq_puts(m, " "); | ||
975 | } | ||
976 | |||
977 | if (entry->flags & IMA_UID) { | ||
978 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); | ||
979 | seq_printf(m, pt(Opt_uid), tbuf); | ||
980 | seq_puts(m, " "); | ||
981 | } | ||
982 | |||
983 | if (entry->flags & IMA_EUID) { | ||
984 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); | ||
985 | seq_printf(m, pt(Opt_euid), tbuf); | ||
986 | seq_puts(m, " "); | ||
987 | } | ||
988 | |||
989 | if (entry->flags & IMA_FOWNER) { | ||
990 | snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner)); | ||
991 | seq_printf(m, pt(Opt_fowner), tbuf); | ||
992 | seq_puts(m, " "); | ||
993 | } | ||
994 | |||
995 | for (i = 0; i < MAX_LSM_RULES; i++) { | ||
996 | if (entry->lsm[i].rule) { | ||
997 | switch (i) { | ||
998 | case LSM_OBJ_USER: | ||
999 | seq_printf(m, pt(Opt_obj_user), | ||
1000 | (char *)entry->lsm[i].args_p); | ||
1001 | break; | ||
1002 | case LSM_OBJ_ROLE: | ||
1003 | seq_printf(m, pt(Opt_obj_role), | ||
1004 | (char *)entry->lsm[i].args_p); | ||
1005 | break; | ||
1006 | case LSM_OBJ_TYPE: | ||
1007 | seq_printf(m, pt(Opt_obj_type), | ||
1008 | (char *)entry->lsm[i].args_p); | ||
1009 | break; | ||
1010 | case LSM_SUBJ_USER: | ||
1011 | seq_printf(m, pt(Opt_subj_user), | ||
1012 | (char *)entry->lsm[i].args_p); | ||
1013 | break; | ||
1014 | case LSM_SUBJ_ROLE: | ||
1015 | seq_printf(m, pt(Opt_subj_role), | ||
1016 | (char *)entry->lsm[i].args_p); | ||
1017 | break; | ||
1018 | case LSM_SUBJ_TYPE: | ||
1019 | seq_printf(m, pt(Opt_subj_type), | ||
1020 | (char *)entry->lsm[i].args_p); | ||
1021 | break; | ||
1022 | } | ||
1023 | } | ||
1024 | } | ||
1025 | if (entry->flags & IMA_DIGSIG_REQUIRED) | ||
1026 | seq_puts(m, "appraise_type=imasig "); | ||
1027 | if (entry->flags & IMA_PERMIT_DIRECTIO) | ||
1028 | seq_puts(m, "permit_directio "); | ||
1029 | rcu_read_unlock(); | ||
1030 | seq_puts(m, "\n"); | ||
1031 | return 0; | ||
1032 | } | ||
1033 | #endif /* CONFIG_IMA_READ_POLICY */ | ||