Commit d58e0da854376841ac99defeb117a83f086715c6
Committed by
James Morris
1 parent
5dbe3040c7
Exists in
master
and in
6 other branches
TOMOYO: Add environment variable name restriction support.
This patch adds support for checking environment variable's names. Although TOMOYO already provides ability to check argv[]/envp[] passed to execve() requests, file execute /bin/sh exec.envp["LD_LIBRARY_PATH"]="bar" will reject execution of /bin/sh if environment variable LD_LIBRARY_PATH is not defined. To grant execution of /bin/sh if LD_LIBRARY_PATH is not defined, administrators have to specify like file execute /bin/sh exec.envp["LD_LIBRARY_PATH"]="/system/lib" file execute /bin/sh exec.envp["LD_LIBRARY_PATH"]=NULL . Since there are many environment variables whereas conditional checks are applied as "&&", it is difficult to cover all combinations. Therefore, this patch supports conditional checks that are applied as "||", by specifying like file execute /bin/sh misc env LD_LIBRARY_PATH exec.envp["LD_LIBRARY_PATH"]="/system/lib" which means "grant execution of /bin/sh if environment variable is not defined or is defined and its value is /system/lib". Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Signed-off-by: James Morris <jmorris@namei.org>
Showing 7 changed files with 266 additions and 10 deletions Side-by-side Diff
security/tomoyo/Makefile
1 | -obj-y = audit.o common.o condition.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o | |
1 | +obj-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o | |
2 | 2 | |
3 | 3 | $(obj)/policy/profile.conf: |
4 | 4 | @mkdir -p $(obj)/policy/ |
security/tomoyo/common.c
... | ... | @@ -20,6 +20,7 @@ |
20 | 20 | /* String table for /sys/kernel/security/tomoyo/profile */ |
21 | 21 | const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX |
22 | 22 | + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { |
23 | + /* CONFIG::file group */ | |
23 | 24 | [TOMOYO_MAC_FILE_EXECUTE] = "execute", |
24 | 25 | [TOMOYO_MAC_FILE_OPEN] = "open", |
25 | 26 | [TOMOYO_MAC_FILE_CREATE] = "create", |
26 | 27 | |
... | ... | @@ -43,7 +44,11 @@ |
43 | 44 | [TOMOYO_MAC_FILE_MOUNT] = "mount", |
44 | 45 | [TOMOYO_MAC_FILE_UMOUNT] = "unmount", |
45 | 46 | [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root", |
47 | + /* CONFIG::misc group */ | |
48 | + [TOMOYO_MAC_ENVIRON] = "env", | |
49 | + /* CONFIG group */ | |
46 | 50 | [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", |
51 | + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc", | |
47 | 52 | }; |
48 | 53 | |
49 | 54 | /* String table for conditions. */ |
... | ... | @@ -133,7 +138,8 @@ |
133 | 138 | /* String table for categories. */ |
134 | 139 | static const char * const tomoyo_category_keywords |
135 | 140 | [TOMOYO_MAX_MAC_CATEGORY_INDEX] = { |
136 | - [TOMOYO_MAC_CATEGORY_FILE] = "file", | |
141 | + [TOMOYO_MAC_CATEGORY_FILE] = "file", | |
142 | + [TOMOYO_MAC_CATEGORY_MISC] = "misc", | |
137 | 143 | }; |
138 | 144 | |
139 | 145 | /* Permit policy management by non-root user? */ |
140 | 146 | |
141 | 147 | |
... | ... | @@ -1036,11 +1042,13 @@ |
1036 | 1042 | static const struct { |
1037 | 1043 | const char *keyword; |
1038 | 1044 | int (*write) (struct tomoyo_acl_param *); |
1039 | - } tomoyo_callback[1] = { | |
1045 | + } tomoyo_callback[2] = { | |
1040 | 1046 | { "file ", tomoyo_write_file }, |
1047 | + { "misc ", tomoyo_write_misc }, | |
1041 | 1048 | }; |
1042 | 1049 | u8 i; |
1043 | - for (i = 0; i < 1; i++) { | |
1050 | + | |
1051 | + for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) { | |
1044 | 1052 | if (!tomoyo_str_starts(¶m.data, |
1045 | 1053 | tomoyo_callback[i].keyword)) |
1046 | 1054 | continue; |
... | ... | @@ -1375,6 +1383,12 @@ |
1375 | 1383 | tomoyo_print_name_union(head, &ptr->dir_name); |
1376 | 1384 | tomoyo_print_name_union(head, &ptr->fs_type); |
1377 | 1385 | tomoyo_print_number_union(head, &ptr->flags); |
1386 | + } else if (acl_type == TOMOYO_TYPE_ENV_ACL) { | |
1387 | + struct tomoyo_env_acl *ptr = | |
1388 | + container_of(acl, typeof(*ptr), head); | |
1389 | + | |
1390 | + tomoyo_set_group(head, "misc env "); | |
1391 | + tomoyo_set_string(head, ptr->env->name); | |
1378 | 1392 | } |
1379 | 1393 | if (acl->cond) { |
1380 | 1394 | head->r.print_cond_part = true; |
security/tomoyo/common.h
... | ... | @@ -196,6 +196,7 @@ |
196 | 196 | TOMOYO_TYPE_PATH_NUMBER_ACL, |
197 | 197 | TOMOYO_TYPE_MKDEV_ACL, |
198 | 198 | TOMOYO_TYPE_MOUNT_ACL, |
199 | + TOMOYO_TYPE_ENV_ACL, | |
199 | 200 | }; |
200 | 201 | |
201 | 202 | /* Index numbers for access controls with one pathname. */ |
202 | 203 | |
... | ... | @@ -300,12 +301,14 @@ |
300 | 301 | TOMOYO_MAC_FILE_MOUNT, |
301 | 302 | TOMOYO_MAC_FILE_UMOUNT, |
302 | 303 | TOMOYO_MAC_FILE_PIVOT_ROOT, |
304 | + TOMOYO_MAC_ENVIRON, | |
303 | 305 | TOMOYO_MAX_MAC_INDEX |
304 | 306 | }; |
305 | 307 | |
306 | 308 | /* Index numbers for category of functionality. */ |
307 | 309 | enum tomoyo_mac_category_index { |
308 | 310 | TOMOYO_MAC_CATEGORY_FILE, |
311 | + TOMOYO_MAC_CATEGORY_MISC, | |
309 | 312 | TOMOYO_MAX_MAC_CATEGORY_INDEX |
310 | 313 | }; |
311 | 314 | |
... | ... | @@ -397,6 +400,9 @@ |
397 | 400 | u8 operation; |
398 | 401 | } path_number; |
399 | 402 | struct { |
403 | + const struct tomoyo_path_info *name; | |
404 | + } environ; | |
405 | + struct { | |
400 | 406 | const struct tomoyo_path_info *type; |
401 | 407 | const struct tomoyo_path_info *dir; |
402 | 408 | const struct tomoyo_path_info *dev; |
... | ... | @@ -638,6 +644,12 @@ |
638 | 644 | struct tomoyo_number_union flags; |
639 | 645 | }; |
640 | 646 | |
647 | +/* Structure for "misc env" directive in domain policy. */ | |
648 | +struct tomoyo_env_acl { | |
649 | + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_ENV_ACL */ | |
650 | + const struct tomoyo_path_info *env; /* environment variable */ | |
651 | +}; | |
652 | + | |
641 | 653 | /* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */ |
642 | 654 | struct tomoyo_acl_param { |
643 | 655 | char *data; |
... | ... | @@ -820,6 +832,7 @@ |
820 | 832 | int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, |
821 | 833 | struct path *path, const int flag); |
822 | 834 | int tomoyo_close_control(struct tomoyo_io_buffer *head); |
835 | +int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env); | |
823 | 836 | int tomoyo_find_next_domain(struct linux_binprm *bprm); |
824 | 837 | int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, |
825 | 838 | const u8 index); |
... | ... | @@ -860,6 +873,7 @@ |
860 | 873 | int tomoyo_write_aggregator(struct tomoyo_acl_param *param); |
861 | 874 | int tomoyo_write_file(struct tomoyo_acl_param *param); |
862 | 875 | int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type); |
876 | +int tomoyo_write_misc(struct tomoyo_acl_param *param); | |
863 | 877 | int tomoyo_write_transition_control(struct tomoyo_acl_param *param, |
864 | 878 | const u8 type); |
865 | 879 | ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, |
security/tomoyo/domain.c
... | ... | @@ -563,6 +563,92 @@ |
563 | 563 | } |
564 | 564 | |
565 | 565 | /** |
566 | + * tomoyo_environ - Check permission for environment variable names. | |
567 | + * | |
568 | + * @ee: Pointer to "struct tomoyo_execve". | |
569 | + * | |
570 | + * Returns 0 on success, negative value otherwise. | |
571 | + */ | |
572 | +static int tomoyo_environ(struct tomoyo_execve *ee) | |
573 | +{ | |
574 | + struct tomoyo_request_info *r = &ee->r; | |
575 | + struct linux_binprm *bprm = ee->bprm; | |
576 | + /* env_page.data is allocated by tomoyo_dump_page(). */ | |
577 | + struct tomoyo_page_dump env_page = { }; | |
578 | + char *arg_ptr; /* Size is TOMOYO_EXEC_TMPSIZE bytes */ | |
579 | + int arg_len = 0; | |
580 | + unsigned long pos = bprm->p; | |
581 | + int offset = pos % PAGE_SIZE; | |
582 | + int argv_count = bprm->argc; | |
583 | + int envp_count = bprm->envc; | |
584 | + int error = -ENOMEM; | |
585 | + | |
586 | + ee->r.type = TOMOYO_MAC_ENVIRON; | |
587 | + ee->r.profile = r->domain->profile; | |
588 | + ee->r.mode = tomoyo_get_mode(r->domain->ns, ee->r.profile, | |
589 | + TOMOYO_MAC_ENVIRON); | |
590 | + if (!r->mode || !envp_count) | |
591 | + return 0; | |
592 | + arg_ptr = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); | |
593 | + if (!arg_ptr) | |
594 | + goto out; | |
595 | + while (error == -ENOMEM) { | |
596 | + if (!tomoyo_dump_page(bprm, pos, &env_page)) | |
597 | + goto out; | |
598 | + pos += PAGE_SIZE - offset; | |
599 | + /* Read. */ | |
600 | + while (argv_count && offset < PAGE_SIZE) { | |
601 | + if (!env_page.data[offset++]) | |
602 | + argv_count--; | |
603 | + } | |
604 | + if (argv_count) { | |
605 | + offset = 0; | |
606 | + continue; | |
607 | + } | |
608 | + while (offset < PAGE_SIZE) { | |
609 | + const unsigned char c = env_page.data[offset++]; | |
610 | + | |
611 | + if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) { | |
612 | + if (c == '=') { | |
613 | + arg_ptr[arg_len++] = '\0'; | |
614 | + } else if (c == '\\') { | |
615 | + arg_ptr[arg_len++] = '\\'; | |
616 | + arg_ptr[arg_len++] = '\\'; | |
617 | + } else if (c > ' ' && c < 127) { | |
618 | + arg_ptr[arg_len++] = c; | |
619 | + } else { | |
620 | + arg_ptr[arg_len++] = '\\'; | |
621 | + arg_ptr[arg_len++] = (c >> 6) + '0'; | |
622 | + arg_ptr[arg_len++] | |
623 | + = ((c >> 3) & 7) + '0'; | |
624 | + arg_ptr[arg_len++] = (c & 7) + '0'; | |
625 | + } | |
626 | + } else { | |
627 | + arg_ptr[arg_len] = '\0'; | |
628 | + } | |
629 | + if (c) | |
630 | + continue; | |
631 | + if (tomoyo_env_perm(r, arg_ptr)) { | |
632 | + error = -EPERM; | |
633 | + break; | |
634 | + } | |
635 | + if (!--envp_count) { | |
636 | + error = 0; | |
637 | + break; | |
638 | + } | |
639 | + arg_len = 0; | |
640 | + } | |
641 | + offset = 0; | |
642 | + } | |
643 | +out: | |
644 | + if (r->mode != TOMOYO_CONFIG_ENFORCING) | |
645 | + error = 0; | |
646 | + kfree(env_page.data); | |
647 | + kfree(arg_ptr); | |
648 | + return error; | |
649 | +} | |
650 | + | |
651 | +/** | |
566 | 652 | * tomoyo_find_next_domain - Find a domain. |
567 | 653 | * |
568 | 654 | * @bprm: Pointer to "struct linux_binprm". |
... | ... | @@ -581,6 +667,7 @@ |
581 | 667 | bool reject_on_transition_failure = false; |
582 | 668 | struct tomoyo_path_info rn = { }; /* real name */ |
583 | 669 | struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS); |
670 | + | |
584 | 671 | if (!ee) |
585 | 672 | return -ENOMEM; |
586 | 673 | ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); |
... | ... | @@ -713,6 +800,10 @@ |
713 | 800 | bprm->cred->security = domain; |
714 | 801 | if (need_kfree) |
715 | 802 | kfree(rn.name); |
803 | + if (!retval) { | |
804 | + ee->r.domain = domain; | |
805 | + retval = tomoyo_environ(ee); | |
806 | + } | |
716 | 807 | kfree(ee->tmp); |
717 | 808 | kfree(ee->dump.data); |
718 | 809 | kfree(ee); |
... | ... | @@ -732,7 +823,8 @@ |
732 | 823 | struct tomoyo_page_dump *dump) |
733 | 824 | { |
734 | 825 | struct page *page; |
735 | - /* dump->data is released by tomoyo_finish_execve(). */ | |
826 | + | |
827 | + /* dump->data is released by tomoyo_find_next_domain(). */ | |
736 | 828 | if (!dump->data) { |
737 | 829 | dump->data = kzalloc(PAGE_SIZE, GFP_NOFS); |
738 | 830 | if (!dump->data) |
... | ... | @@ -753,6 +845,7 @@ |
753 | 845 | * So do I. |
754 | 846 | */ |
755 | 847 | char *kaddr = kmap_atomic(page, KM_USER0); |
848 | + | |
756 | 849 | dump->page = page; |
757 | 850 | memcpy(dump->data + offset, kaddr + offset, |
758 | 851 | PAGE_SIZE - offset); |
security/tomoyo/environ.c
1 | +/* | |
2 | + * security/tomoyo/environ.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2011 NTT DATA CORPORATION | |
5 | + */ | |
6 | + | |
7 | +#include "common.h" | |
8 | + | |
9 | +/** | |
10 | + * tomoyo_check_env_acl - Check permission for environment variable's name. | |
11 | + * | |
12 | + * @r: Pointer to "struct tomoyo_request_info". | |
13 | + * @ptr: Pointer to "struct tomoyo_acl_info". | |
14 | + * | |
15 | + * Returns true if granted, false otherwise. | |
16 | + */ | |
17 | +static bool tomoyo_check_env_acl(struct tomoyo_request_info *r, | |
18 | + const struct tomoyo_acl_info *ptr) | |
19 | +{ | |
20 | + const struct tomoyo_env_acl *acl = | |
21 | + container_of(ptr, typeof(*acl), head); | |
22 | + | |
23 | + return tomoyo_path_matches_pattern(r->param.environ.name, acl->env); | |
24 | +} | |
25 | + | |
26 | +/** | |
27 | + * tomoyo_audit_env_log - Audit environment variable name log. | |
28 | + * | |
29 | + * @r: Pointer to "struct tomoyo_request_info". | |
30 | + * | |
31 | + * Returns 0 on success, negative value otherwise. | |
32 | + */ | |
33 | +static int tomoyo_audit_env_log(struct tomoyo_request_info *r) | |
34 | +{ | |
35 | + return tomoyo_supervisor(r, "misc env %s\n", | |
36 | + r->param.environ.name->name); | |
37 | +} | |
38 | + | |
39 | +/** | |
40 | + * tomoyo_env_perm - Check permission for environment variable's name. | |
41 | + * | |
42 | + * @r: Pointer to "struct tomoyo_request_info". | |
43 | + * @env: The name of environment variable. | |
44 | + * | |
45 | + * Returns 0 on success, negative value otherwise. | |
46 | + * | |
47 | + * Caller holds tomoyo_read_lock(). | |
48 | + */ | |
49 | +int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env) | |
50 | +{ | |
51 | + struct tomoyo_path_info environ; | |
52 | + int error; | |
53 | + | |
54 | + if (!env || !*env) | |
55 | + return 0; | |
56 | + environ.name = env; | |
57 | + tomoyo_fill_path_info(&environ); | |
58 | + r->param_type = TOMOYO_TYPE_ENV_ACL; | |
59 | + r->param.environ.name = &environ; | |
60 | + do { | |
61 | + tomoyo_check_acl(r, tomoyo_check_env_acl); | |
62 | + error = tomoyo_audit_env_log(r); | |
63 | + } while (error == TOMOYO_RETRY_REQUEST); | |
64 | + return error; | |
65 | +} | |
66 | + | |
67 | +/** | |
68 | + * tomoyo_same_env_acl - Check for duplicated "struct tomoyo_env_acl" entry. | |
69 | + * | |
70 | + * @a: Pointer to "struct tomoyo_acl_info". | |
71 | + * @b: Pointer to "struct tomoyo_acl_info". | |
72 | + * | |
73 | + * Returns true if @a == @b, false otherwise. | |
74 | + */ | |
75 | +static bool tomoyo_same_env_acl(const struct tomoyo_acl_info *a, | |
76 | + const struct tomoyo_acl_info *b) | |
77 | +{ | |
78 | + const struct tomoyo_env_acl *p1 = container_of(a, typeof(*p1), head); | |
79 | + const struct tomoyo_env_acl *p2 = container_of(b, typeof(*p2), head); | |
80 | + | |
81 | + return p1->env == p2->env; | |
82 | +} | |
83 | + | |
84 | +/** | |
85 | + * tomoyo_write_env - Write "struct tomoyo_env_acl" list. | |
86 | + * | |
87 | + * @param: Pointer to "struct tomoyo_acl_param". | |
88 | + * | |
89 | + * Returns 0 on success, negative value otherwise. | |
90 | + * | |
91 | + * Caller holds tomoyo_read_lock(). | |
92 | + */ | |
93 | +static int tomoyo_write_env(struct tomoyo_acl_param *param) | |
94 | +{ | |
95 | + struct tomoyo_env_acl e = { .head.type = TOMOYO_TYPE_ENV_ACL }; | |
96 | + int error = -ENOMEM; | |
97 | + const char *data = tomoyo_read_token(param); | |
98 | + | |
99 | + if (!tomoyo_correct_word(data) || strchr(data, '=')) | |
100 | + return -EINVAL; | |
101 | + e.env = tomoyo_get_name(data); | |
102 | + if (!e.env) | |
103 | + return error; | |
104 | + error = tomoyo_update_domain(&e.head, sizeof(e), param, | |
105 | + tomoyo_same_env_acl, NULL); | |
106 | + tomoyo_put_name(e.env); | |
107 | + return error; | |
108 | +} | |
109 | + | |
110 | +/** | |
111 | + * tomoyo_write_misc - Update environment variable list. | |
112 | + * | |
113 | + * @param: Pointer to "struct tomoyo_acl_param". | |
114 | + * | |
115 | + * Returns 0 on success, negative value otherwise. | |
116 | + */ | |
117 | +int tomoyo_write_misc(struct tomoyo_acl_param *param) | |
118 | +{ | |
119 | + if (tomoyo_str_starts(¶m->data, "env ")) | |
120 | + return tomoyo_write_env(param); | |
121 | + return -EINVAL; | |
122 | +} |
security/tomoyo/gc.c
... | ... | @@ -36,6 +36,7 @@ |
36 | 36 | [TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl), |
37 | 37 | [TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl), |
38 | 38 | [TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl), |
39 | + [TOMOYO_TYPE_ENV_ACL] = sizeof(struct tomoyo_env_acl), | |
39 | 40 | }; |
40 | 41 | |
41 | 42 | /** |
... | ... | @@ -291,6 +292,14 @@ |
291 | 292 | tomoyo_put_name_union(&entry->dir_name); |
292 | 293 | tomoyo_put_name_union(&entry->fs_type); |
293 | 294 | tomoyo_put_number_union(&entry->flags); |
295 | + } | |
296 | + break; | |
297 | + case TOMOYO_TYPE_ENV_ACL: | |
298 | + { | |
299 | + struct tomoyo_env_acl *entry = | |
300 | + container_of(acl, typeof(*entry), head); | |
301 | + | |
302 | + tomoyo_put_name(entry->env); | |
294 | 303 | } |
295 | 304 | break; |
296 | 305 | } |
security/tomoyo/util.c
... | ... | @@ -42,6 +42,8 @@ |
42 | 42 | [TOMOYO_MAC_FILE_MOUNT] = TOMOYO_MAC_CATEGORY_FILE, |
43 | 43 | [TOMOYO_MAC_FILE_UMOUNT] = TOMOYO_MAC_CATEGORY_FILE, |
44 | 44 | [TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE, |
45 | + /* CONFIG::misc group */ | |
46 | + [TOMOYO_MAC_ENVIRON] = TOMOYO_MAC_CATEGORY_MISC, | |
45 | 47 | }; |
46 | 48 | |
47 | 49 | /** |
48 | 50 | |
49 | 51 | |
50 | 52 | |
... | ... | @@ -920,15 +922,17 @@ |
920 | 922 | const u8 index) |
921 | 923 | { |
922 | 924 | u8 mode; |
923 | - const u8 category = TOMOYO_MAC_CATEGORY_FILE; | |
925 | + struct tomoyo_profile *p; | |
926 | + | |
924 | 927 | if (!tomoyo_policy_loaded) |
925 | 928 | return TOMOYO_CONFIG_DISABLED; |
926 | - mode = tomoyo_profile(ns, profile)->config[index]; | |
929 | + p = tomoyo_profile(ns, profile); | |
930 | + mode = p->config[index]; | |
927 | 931 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) |
928 | - mode = tomoyo_profile(ns, profile)->config | |
929 | - [category + TOMOYO_MAX_MAC_INDEX]; | |
932 | + mode = p->config[tomoyo_index2category[index] | |
933 | + + TOMOYO_MAX_MAC_INDEX]; | |
930 | 934 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) |
931 | - mode = tomoyo_profile(ns, profile)->default_config; | |
935 | + mode = p->default_config; | |
932 | 936 | return mode & 3; |
933 | 937 | } |
934 | 938 |