Commit d58e0da854376841ac99defeb117a83f086715c6

Authored by Tetsuo Handa
Committed by James Morris
1 parent 5dbe3040c7

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(&param.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(&param->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