Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tetsuo Handa and committed by
James Morris
d58e0da8 5dbe3040

+266 -10
+1 -1
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/
+17 -3
security/tomoyo/common.c
··· 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", ··· 44 43 [TOMOYO_MAC_FILE_MOUNT] = "mount", 45 44 [TOMOYO_MAC_FILE_UMOUNT] = "unmount", 46 45 [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root", 46 + /* CONFIG::misc group */ 47 + [TOMOYO_MAC_ENVIRON] = "env", 48 + /* CONFIG group */ 47 49 [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", 50 + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc", 48 51 }; 49 52 50 53 /* String table for conditions. */ ··· 138 133 /* String table for categories. */ 139 134 static const char * const tomoyo_category_keywords 140 135 [TOMOYO_MAX_MAC_CATEGORY_INDEX] = { 141 - [TOMOYO_MAC_CATEGORY_FILE] = "file", 136 + [TOMOYO_MAC_CATEGORY_FILE] = "file", 137 + [TOMOYO_MAC_CATEGORY_MISC] = "misc", 142 138 }; 143 139 144 140 /* Permit policy management by non-root user? */ ··· 1042 1036 static const struct { 1043 1037 const char *keyword; 1044 1038 int (*write) (struct tomoyo_acl_param *); 1045 - } tomoyo_callback[1] = { 1039 + } tomoyo_callback[2] = { 1046 1040 { "file ", tomoyo_write_file }, 1041 + { "misc ", tomoyo_write_misc }, 1047 1042 }; 1048 1043 u8 i; 1049 - for (i = 0; i < 1; i++) { 1044 + 1045 + for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) { 1050 1046 if (!tomoyo_str_starts(&param.data, 1051 1047 tomoyo_callback[i].keyword)) 1052 1048 continue; ··· 1383 1375 tomoyo_print_name_union(head, &ptr->dir_name); 1384 1376 tomoyo_print_name_union(head, &ptr->fs_type); 1385 1377 tomoyo_print_number_union(head, &ptr->flags); 1378 + } else if (acl_type == TOMOYO_TYPE_ENV_ACL) { 1379 + struct tomoyo_env_acl *ptr = 1380 + container_of(acl, typeof(*ptr), head); 1381 + 1382 + tomoyo_set_group(head, "misc env "); 1383 + tomoyo_set_string(head, ptr->env->name); 1386 1384 } 1387 1385 if (acl->cond) { 1388 1386 head->r.print_cond_part = true;
+14
security/tomoyo/common.h
··· 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. */ ··· 301 300 TOMOYO_MAC_FILE_MOUNT, 302 301 TOMOYO_MAC_FILE_UMOUNT, 303 302 TOMOYO_MAC_FILE_PIVOT_ROOT, 303 + TOMOYO_MAC_ENVIRON, 304 304 TOMOYO_MAX_MAC_INDEX 305 305 }; 306 306 307 307 /* Index numbers for category of functionality. */ 308 308 enum tomoyo_mac_category_index { 309 309 TOMOYO_MAC_CATEGORY_FILE, 310 + TOMOYO_MAC_CATEGORY_MISC, 310 311 TOMOYO_MAX_MAC_CATEGORY_INDEX 311 312 }; 312 313 ··· 399 396 */ 400 397 u8 operation; 401 398 } path_number; 399 + struct { 400 + const struct tomoyo_path_info *name; 401 + } environ; 402 402 struct { 403 403 const struct tomoyo_path_info *type; 404 404 const struct tomoyo_path_info *dir; ··· 644 638 struct tomoyo_number_union flags; 645 639 }; 646 640 641 + /* Structure for "misc env" directive in domain policy. */ 642 + struct tomoyo_env_acl { 643 + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_ENV_ACL */ 644 + const struct tomoyo_path_info *env; /* environment variable */ 645 + }; 646 + 647 647 /* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */ 648 648 struct tomoyo_acl_param { 649 649 char *data; ··· 832 820 int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, 833 821 struct path *path, const int flag); 834 822 int tomoyo_close_control(struct tomoyo_io_buffer *head); 823 + int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env); 835 824 int tomoyo_find_next_domain(struct linux_binprm *bprm); 836 825 int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, 837 826 const u8 index); ··· 873 860 int tomoyo_write_aggregator(struct tomoyo_acl_param *param); 874 861 int tomoyo_write_file(struct tomoyo_acl_param *param); 875 862 int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type); 863 + int tomoyo_write_misc(struct tomoyo_acl_param *param); 876 864 int tomoyo_write_transition_control(struct tomoyo_acl_param *param, 877 865 const u8 type); 878 866 ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
+94 -1
security/tomoyo/domain.c
··· 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". ··· 667 581 bool reject_on_transition_failure = false; 668 582 struct tomoyo_path_info rn = { }; /* real name */ 669 583 struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS); 584 + 670 585 if (!ee) 671 586 return -ENOMEM; 672 587 ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); ··· 800 713 bprm->cred->security = domain; 801 714 if (need_kfree) 802 715 kfree(rn.name); 716 + if (!retval) { 717 + ee->r.domain = domain; 718 + retval = tomoyo_environ(ee); 719 + } 803 720 kfree(ee->tmp); 804 721 kfree(ee->dump.data); 805 722 kfree(ee); ··· 823 732 struct tomoyo_page_dump *dump) 824 733 { 825 734 struct page *page; 826 - /* dump->data is released by tomoyo_finish_execve(). */ 735 + 736 + /* dump->data is released by tomoyo_find_next_domain(). */ 827 737 if (!dump->data) { 828 738 dump->data = kzalloc(PAGE_SIZE, GFP_NOFS); 829 739 if (!dump->data) ··· 845 753 * So do I. 846 754 */ 847 755 char *kaddr = kmap_atomic(page, KM_USER0); 756 + 848 757 dump->page = page; 849 758 memcpy(dump->data + offset, kaddr + offset, 850 759 PAGE_SIZE - offset);
+122
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(&param->data, "env ")) 120 + return tomoyo_write_env(param); 121 + return -EINVAL; 122 + }
+9
security/tomoyo/gc.c
··· 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 /** ··· 292 291 tomoyo_put_name_union(&entry->dir_name); 293 292 tomoyo_put_name_union(&entry->fs_type); 294 293 tomoyo_put_number_union(&entry->flags); 294 + } 295 + break; 296 + case TOMOYO_TYPE_ENV_ACL: 297 + { 298 + struct tomoyo_env_acl *entry = 299 + container_of(acl, typeof(*entry), head); 300 + 301 + tomoyo_put_name(entry->env); 295 302 } 296 303 break; 297 304 }
+9 -5
security/tomoyo/util.c
··· 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 /** ··· 922 920 const u8 index) 923 921 { 924 922 u8 mode; 925 - const u8 category = TOMOYO_MAC_CATEGORY_FILE; 923 + struct tomoyo_profile *p; 924 + 926 925 if (!tomoyo_policy_loaded) 927 926 return TOMOYO_CONFIG_DISABLED; 928 - mode = tomoyo_profile(ns, profile)->config[index]; 927 + p = tomoyo_profile(ns, profile); 928 + mode = p->config[index]; 929 929 if (mode == TOMOYO_CONFIG_USE_DEFAULT) 930 - mode = tomoyo_profile(ns, profile)->config 931 - [category + TOMOYO_MAX_MAC_INDEX]; 930 + mode = p->config[tomoyo_index2category[index] 931 + + TOMOYO_MAX_MAC_INDEX]; 932 932 if (mode == TOMOYO_CONFIG_USE_DEFAULT) 933 - mode = tomoyo_profile(ns, profile)->default_config; 933 + mode = p->default_config; 934 934 return mode & 3; 935 935 } 936 936