diff options
| author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2011-09-10 02:25:58 -0400 |
|---|---|---|
| committer | James Morris <jmorris@namei.org> | 2011-09-13 18:27:06 -0400 |
| commit | 731d37aa70c7b9de3be6bf2c8287366223bf5ce5 (patch) | |
| tree | 8ac6028511485862572695eb91e2d461e0636182 /security | |
| parent | 1f067a682a9bd252107ac6f6946b7332fde42344 (diff) | |
TOMOYO: Allow domain transition without execve().
To be able to split permissions for Apache's CGI programs which are executed
without execve(), add special domain transition which is performed by writing
a TOMOYO's domainname to /sys/kernel/security/tomoyo/self_domain interface.
This is an API for TOMOYO-aware userland applications. However, since I expect
TOMOYO and other LSM modules to run in parallel, this patch does not use
/proc/self/attr/ interface in order to avoid conflicts with other LSM modules
when it became possible to run multiple LSM modules in parallel.
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security')
| -rw-r--r-- | security/tomoyo/common.c | 75 | ||||
| -rw-r--r-- | security/tomoyo/common.h | 16 | ||||
| -rw-r--r-- | security/tomoyo/securityfs_if.c | 122 | ||||
| -rw-r--r-- | security/tomoyo/util.c | 25 |
4 files changed, 210 insertions, 28 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 2704c384bf1e..1fd0fc1059ba 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c | |||
| @@ -1011,6 +1011,48 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, | |||
| 1011 | } | 1011 | } |
| 1012 | 1012 | ||
| 1013 | /** | 1013 | /** |
| 1014 | * tomoyo_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry. | ||
| 1015 | * | ||
| 1016 | * @a: Pointer to "struct tomoyo_acl_info". | ||
| 1017 | * @b: Pointer to "struct tomoyo_acl_info". | ||
| 1018 | * | ||
| 1019 | * Returns true if @a == @b, false otherwise. | ||
| 1020 | */ | ||
| 1021 | static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a, | ||
| 1022 | const struct tomoyo_acl_info *b) | ||
| 1023 | { | ||
| 1024 | const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head); | ||
| 1025 | const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head); | ||
| 1026 | return p1->domainname == p2->domainname; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | /** | ||
| 1030 | * tomoyo_write_task - Update task related list. | ||
| 1031 | * | ||
| 1032 | * @param: Pointer to "struct tomoyo_acl_param". | ||
| 1033 | * | ||
| 1034 | * Returns 0 on success, negative value otherwise. | ||
| 1035 | * | ||
| 1036 | * Caller holds tomoyo_read_lock(). | ||
| 1037 | */ | ||
| 1038 | static int tomoyo_write_task(struct tomoyo_acl_param *param) | ||
| 1039 | { | ||
| 1040 | int error = -EINVAL; | ||
| 1041 | if (tomoyo_str_starts(¶m->data, "manual_domain_transition ")) { | ||
| 1042 | struct tomoyo_task_acl e = { | ||
| 1043 | .head.type = TOMOYO_TYPE_MANUAL_TASK_ACL, | ||
| 1044 | .domainname = tomoyo_get_domainname(param), | ||
| 1045 | }; | ||
| 1046 | if (e.domainname) | ||
| 1047 | error = tomoyo_update_domain(&e.head, sizeof(e), param, | ||
| 1048 | tomoyo_same_task_acl, | ||
| 1049 | NULL); | ||
| 1050 | tomoyo_put_name(e.domainname); | ||
| 1051 | } | ||
| 1052 | return error; | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | /** | ||
| 1014 | * tomoyo_delete_domain - Delete a domain. | 1056 | * tomoyo_delete_domain - Delete a domain. |
| 1015 | * | 1057 | * |
| 1016 | * @domainname: The name of domain. | 1058 | * @domainname: The name of domain. |
| @@ -1068,11 +1110,12 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, | |||
| 1068 | static const struct { | 1110 | static const struct { |
| 1069 | const char *keyword; | 1111 | const char *keyword; |
| 1070 | int (*write) (struct tomoyo_acl_param *); | 1112 | int (*write) (struct tomoyo_acl_param *); |
| 1071 | } tomoyo_callback[4] = { | 1113 | } tomoyo_callback[5] = { |
| 1072 | { "file ", tomoyo_write_file }, | 1114 | { "file ", tomoyo_write_file }, |
| 1073 | { "network inet ", tomoyo_write_inet_network }, | 1115 | { "network inet ", tomoyo_write_inet_network }, |
| 1074 | { "network unix ", tomoyo_write_unix_network }, | 1116 | { "network unix ", tomoyo_write_unix_network }, |
| 1075 | { "misc ", tomoyo_write_misc }, | 1117 | { "misc ", tomoyo_write_misc }, |
| 1118 | { "task ", tomoyo_write_task }, | ||
| 1076 | }; | 1119 | }; |
| 1077 | u8 i; | 1120 | u8 i; |
| 1078 | 1121 | ||
| @@ -1343,6 +1386,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, | |||
| 1343 | if (first) | 1386 | if (first) |
| 1344 | return true; | 1387 | return true; |
| 1345 | tomoyo_print_name_union(head, &ptr->name); | 1388 | tomoyo_print_name_union(head, &ptr->name); |
| 1389 | } else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) { | ||
| 1390 | struct tomoyo_task_acl *ptr = | ||
| 1391 | container_of(acl, typeof(*ptr), head); | ||
| 1392 | tomoyo_set_group(head, "task "); | ||
| 1393 | tomoyo_set_string(head, "manual_domain_transition "); | ||
| 1394 | tomoyo_set_string(head, ptr->domainname->name); | ||
| 1346 | } else if (head->r.print_transition_related_only) { | 1395 | } else if (head->r.print_transition_related_only) { |
| 1347 | return true; | 1396 | return true; |
| 1348 | } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { | 1397 | } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { |
| @@ -2178,26 +2227,6 @@ static void tomoyo_read_version(struct tomoyo_io_buffer *head) | |||
| 2178 | } | 2227 | } |
| 2179 | } | 2228 | } |
| 2180 | 2229 | ||
| 2181 | /** | ||
| 2182 | * tomoyo_read_self_domain - Get the current process's domainname. | ||
| 2183 | * | ||
| 2184 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
| 2185 | * | ||
| 2186 | * Returns the current process's domainname. | ||
| 2187 | */ | ||
| 2188 | static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) | ||
| 2189 | { | ||
| 2190 | if (!head->r.eof) { | ||
| 2191 | /* | ||
| 2192 | * tomoyo_domain()->domainname != NULL | ||
| 2193 | * because every process belongs to a domain and | ||
| 2194 | * the domain's name cannot be NULL. | ||
| 2195 | */ | ||
| 2196 | tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); | ||
| 2197 | head->r.eof = true; | ||
| 2198 | } | ||
| 2199 | } | ||
| 2200 | |||
| 2201 | /* String table for /sys/kernel/security/tomoyo/stat interface. */ | 2230 | /* String table for /sys/kernel/security/tomoyo/stat interface. */ |
| 2202 | static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = { | 2231 | static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = { |
| 2203 | [TOMOYO_STAT_POLICY_UPDATES] = "update:", | 2232 | [TOMOYO_STAT_POLICY_UPDATES] = "update:", |
| @@ -2328,10 +2357,6 @@ int tomoyo_open_control(const u8 type, struct file *file) | |||
| 2328 | head->poll = tomoyo_poll_log; | 2357 | head->poll = tomoyo_poll_log; |
| 2329 | head->read = tomoyo_read_log; | 2358 | head->read = tomoyo_read_log; |
| 2330 | break; | 2359 | break; |
| 2331 | case TOMOYO_SELFDOMAIN: | ||
| 2332 | /* /sys/kernel/security/tomoyo/self_domain */ | ||
| 2333 | head->read = tomoyo_read_self_domain; | ||
| 2334 | break; | ||
| 2335 | case TOMOYO_PROCESS_STATUS: | 2360 | case TOMOYO_PROCESS_STATUS: |
| 2336 | /* /sys/kernel/security/tomoyo/.process_status */ | 2361 | /* /sys/kernel/security/tomoyo/.process_status */ |
| 2337 | head->write = tomoyo_write_pid; | 2362 | head->write = tomoyo_write_pid; |
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 435b3d869fc5..af82683df7ff 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h | |||
| @@ -227,6 +227,7 @@ enum tomoyo_acl_entry_type_index { | |||
| 227 | TOMOYO_TYPE_INET_ACL, | 227 | TOMOYO_TYPE_INET_ACL, |
| 228 | TOMOYO_TYPE_UNIX_ACL, | 228 | TOMOYO_TYPE_UNIX_ACL, |
| 229 | TOMOYO_TYPE_ENV_ACL, | 229 | TOMOYO_TYPE_ENV_ACL, |
| 230 | TOMOYO_TYPE_MANUAL_TASK_ACL, | ||
| 230 | }; | 231 | }; |
| 231 | 232 | ||
| 232 | /* Index numbers for access controls with one pathname. */ | 233 | /* Index numbers for access controls with one pathname. */ |
| @@ -295,7 +296,6 @@ enum tomoyo_securityfs_interface_index { | |||
| 295 | TOMOYO_EXCEPTIONPOLICY, | 296 | TOMOYO_EXCEPTIONPOLICY, |
| 296 | TOMOYO_PROCESS_STATUS, | 297 | TOMOYO_PROCESS_STATUS, |
| 297 | TOMOYO_STAT, | 298 | TOMOYO_STAT, |
| 298 | TOMOYO_SELFDOMAIN, | ||
| 299 | TOMOYO_AUDIT, | 299 | TOMOYO_AUDIT, |
| 300 | TOMOYO_VERSION, | 300 | TOMOYO_VERSION, |
| 301 | TOMOYO_PROFILE, | 301 | TOMOYO_PROFILE, |
| @@ -480,6 +480,9 @@ struct tomoyo_request_info { | |||
| 480 | unsigned long flags; | 480 | unsigned long flags; |
| 481 | int need_dev; | 481 | int need_dev; |
| 482 | } mount; | 482 | } mount; |
| 483 | struct { | ||
| 484 | const struct tomoyo_path_info *domainname; | ||
| 485 | } task; | ||
| 483 | } param; | 486 | } param; |
| 484 | struct tomoyo_acl_info *matched_acl; | 487 | struct tomoyo_acl_info *matched_acl; |
| 485 | u8 param_type; | 488 | u8 param_type; |
| @@ -680,6 +683,15 @@ struct tomoyo_domain_info { | |||
| 680 | }; | 683 | }; |
| 681 | 684 | ||
| 682 | /* | 685 | /* |
| 686 | * Structure for "task manual_domain_transition" directive. | ||
| 687 | */ | ||
| 688 | struct tomoyo_task_acl { | ||
| 689 | struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MANUAL_TASK_ACL */ | ||
| 690 | /* Pointer to domainname. */ | ||
| 691 | const struct tomoyo_path_info *domainname; | ||
| 692 | }; | ||
| 693 | |||
| 694 | /* | ||
| 683 | * Structure for "file execute", "file read", "file write", "file append", | 695 | * Structure for "file execute", "file read", "file write", "file append", |
| 684 | * "file unlink", "file getattr", "file rmdir", "file truncate", | 696 | * "file unlink", "file getattr", "file rmdir", "file truncate", |
| 685 | * "file symlink", "file chroot" and "file unmount" directive. | 697 | * "file symlink", "file chroot" and "file unmount" directive. |
| @@ -935,6 +947,8 @@ const char *tomoyo_get_exe(void); | |||
| 935 | const char *tomoyo_yesno(const unsigned int value); | 947 | const char *tomoyo_yesno(const unsigned int value); |
| 936 | const struct tomoyo_path_info *tomoyo_compare_name_union | 948 | const struct tomoyo_path_info *tomoyo_compare_name_union |
| 937 | (const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); | 949 | (const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); |
| 950 | const struct tomoyo_path_info *tomoyo_get_domainname | ||
| 951 | (struct tomoyo_acl_param *param); | ||
| 938 | const struct tomoyo_path_info *tomoyo_get_name(const char *name); | 952 | const struct tomoyo_path_info *tomoyo_get_name(const char *name); |
| 939 | const struct tomoyo_path_info *tomoyo_path_matches_group | 953 | const struct tomoyo_path_info *tomoyo_path_matches_group |
| 940 | (const struct tomoyo_path_info *pathname, const struct tomoyo_group *group); | 954 | (const struct tomoyo_path_info *pathname, const struct tomoyo_group *group); |
diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index a49c3bfd4dd5..d08296a4882b 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c | |||
| @@ -8,6 +8,124 @@ | |||
| 8 | #include "common.h" | 8 | #include "common.h" |
| 9 | 9 | ||
| 10 | /** | 10 | /** |
| 11 | * tomoyo_check_task_acl - Check permission for task operation. | ||
| 12 | * | ||
| 13 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 14 | * @ptr: Pointer to "struct tomoyo_acl_info". | ||
| 15 | * | ||
| 16 | * Returns true if granted, false otherwise. | ||
| 17 | */ | ||
| 18 | static bool tomoyo_check_task_acl(struct tomoyo_request_info *r, | ||
| 19 | const struct tomoyo_acl_info *ptr) | ||
| 20 | { | ||
| 21 | const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl), | ||
| 22 | head); | ||
| 23 | return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname); | ||
| 24 | } | ||
| 25 | |||
| 26 | /** | ||
| 27 | * tomoyo_write_self - write() for /sys/kernel/security/tomoyo/self_domain interface. | ||
| 28 | * | ||
| 29 | * @file: Pointer to "struct file". | ||
| 30 | * @buf: Domainname to transit to. | ||
| 31 | * @count: Size of @buf. | ||
| 32 | * @ppos: Unused. | ||
| 33 | * | ||
| 34 | * Returns @count on success, negative value otherwise. | ||
| 35 | * | ||
| 36 | * If domain transition was permitted but the domain transition failed, this | ||
| 37 | * function returns error rather than terminating current thread with SIGKILL. | ||
| 38 | */ | ||
| 39 | static ssize_t tomoyo_write_self(struct file *file, const char __user *buf, | ||
| 40 | size_t count, loff_t *ppos) | ||
| 41 | { | ||
| 42 | char *data; | ||
| 43 | int error; | ||
| 44 | if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10) | ||
| 45 | return -ENOMEM; | ||
| 46 | data = kzalloc(count + 1, GFP_NOFS); | ||
| 47 | if (!data) | ||
| 48 | return -ENOMEM; | ||
| 49 | if (copy_from_user(data, buf, count)) { | ||
| 50 | error = -EFAULT; | ||
| 51 | goto out; | ||
| 52 | } | ||
| 53 | tomoyo_normalize_line(data); | ||
| 54 | if (tomoyo_correct_domain(data)) { | ||
| 55 | const int idx = tomoyo_read_lock(); | ||
| 56 | struct tomoyo_path_info name; | ||
| 57 | struct tomoyo_request_info r; | ||
| 58 | name.name = data; | ||
| 59 | tomoyo_fill_path_info(&name); | ||
| 60 | /* Check "task manual_domain_transition" permission. */ | ||
| 61 | tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); | ||
| 62 | r.param_type = TOMOYO_TYPE_MANUAL_TASK_ACL; | ||
| 63 | r.param.task.domainname = &name; | ||
| 64 | tomoyo_check_acl(&r, tomoyo_check_task_acl); | ||
| 65 | if (!r.granted) | ||
| 66 | error = -EPERM; | ||
| 67 | else { | ||
| 68 | struct tomoyo_domain_info *new_domain = | ||
| 69 | tomoyo_assign_domain(data, true); | ||
| 70 | if (!new_domain) { | ||
| 71 | error = -ENOENT; | ||
| 72 | } else { | ||
| 73 | struct cred *cred = prepare_creds(); | ||
| 74 | if (!cred) { | ||
| 75 | error = -ENOMEM; | ||
| 76 | } else { | ||
| 77 | struct tomoyo_domain_info *old_domain = | ||
| 78 | cred->security; | ||
| 79 | cred->security = new_domain; | ||
| 80 | atomic_inc(&new_domain->users); | ||
| 81 | atomic_dec(&old_domain->users); | ||
| 82 | commit_creds(cred); | ||
| 83 | error = 0; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | tomoyo_read_unlock(idx); | ||
| 88 | } else | ||
| 89 | error = -EINVAL; | ||
| 90 | out: | ||
| 91 | kfree(data); | ||
| 92 | return error ? error : count; | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * tomoyo_read_self - read() for /sys/kernel/security/tomoyo/self_domain interface. | ||
| 97 | * | ||
| 98 | * @file: Pointer to "struct file". | ||
| 99 | * @buf: Domainname which current thread belongs to. | ||
| 100 | * @count: Size of @buf. | ||
| 101 | * @ppos: Bytes read by now. | ||
| 102 | * | ||
| 103 | * Returns read size on success, negative value otherwise. | ||
| 104 | */ | ||
| 105 | static ssize_t tomoyo_read_self(struct file *file, char __user *buf, | ||
| 106 | size_t count, loff_t *ppos) | ||
| 107 | { | ||
| 108 | const char *domain = tomoyo_domain()->domainname->name; | ||
| 109 | loff_t len = strlen(domain); | ||
| 110 | loff_t pos = *ppos; | ||
| 111 | if (pos >= len || !count) | ||
| 112 | return 0; | ||
| 113 | len -= pos; | ||
| 114 | if (count < len) | ||
| 115 | len = count; | ||
| 116 | if (copy_to_user(buf, domain + pos, len)) | ||
| 117 | return -EFAULT; | ||
| 118 | *ppos += len; | ||
| 119 | return len; | ||
| 120 | } | ||
| 121 | |||
| 122 | /* Operations for /sys/kernel/security/tomoyo/self_domain interface. */ | ||
| 123 | static const struct file_operations tomoyo_self_operations = { | ||
| 124 | .write = tomoyo_write_self, | ||
| 125 | .read = tomoyo_read_self, | ||
| 126 | }; | ||
| 127 | |||
| 128 | /** | ||
| 11 | * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. | 129 | * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. |
| 12 | * | 130 | * |
| 13 | * @inode: Pointer to "struct inode". | 131 | * @inode: Pointer to "struct inode". |
| @@ -135,8 +253,6 @@ static int __init tomoyo_initerface_init(void) | |||
| 135 | TOMOYO_EXCEPTIONPOLICY); | 253 | TOMOYO_EXCEPTIONPOLICY); |
| 136 | tomoyo_create_entry("audit", 0400, tomoyo_dir, | 254 | tomoyo_create_entry("audit", 0400, tomoyo_dir, |
| 137 | TOMOYO_AUDIT); | 255 | TOMOYO_AUDIT); |
| 138 | tomoyo_create_entry("self_domain", 0400, tomoyo_dir, | ||
| 139 | TOMOYO_SELFDOMAIN); | ||
| 140 | tomoyo_create_entry(".process_status", 0600, tomoyo_dir, | 256 | tomoyo_create_entry(".process_status", 0600, tomoyo_dir, |
| 141 | TOMOYO_PROCESS_STATUS); | 257 | TOMOYO_PROCESS_STATUS); |
| 142 | tomoyo_create_entry("stat", 0644, tomoyo_dir, | 258 | tomoyo_create_entry("stat", 0644, tomoyo_dir, |
| @@ -147,6 +263,8 @@ static int __init tomoyo_initerface_init(void) | |||
| 147 | TOMOYO_MANAGER); | 263 | TOMOYO_MANAGER); |
| 148 | tomoyo_create_entry("version", 0400, tomoyo_dir, | 264 | tomoyo_create_entry("version", 0400, tomoyo_dir, |
| 149 | TOMOYO_VERSION); | 265 | TOMOYO_VERSION); |
| 266 | securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL, | ||
| 267 | &tomoyo_self_operations); | ||
| 150 | return 0; | 268 | return 0; |
| 151 | } | 269 | } |
| 152 | 270 | ||
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index a1c3d9ccebfa..50e9b4c73ceb 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c | |||
| @@ -159,6 +159,31 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param) | |||
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | /** | 161 | /** |
| 162 | * tomoyo_get_domainname - Read a domainname from a line. | ||
| 163 | * | ||
| 164 | * @param: Pointer to "struct tomoyo_acl_param". | ||
| 165 | * | ||
| 166 | * Returns a domainname on success, NULL otherwise. | ||
| 167 | */ | ||
| 168 | const struct tomoyo_path_info *tomoyo_get_domainname | ||
| 169 | (struct tomoyo_acl_param *param) | ||
| 170 | { | ||
| 171 | char *start = param->data; | ||
| 172 | char *pos = start; | ||
| 173 | while (*pos) { | ||
| 174 | if (*pos++ != ' ' || *pos++ == '/') | ||
| 175 | continue; | ||
| 176 | pos -= 2; | ||
| 177 | *pos++ = '\0'; | ||
| 178 | break; | ||
| 179 | } | ||
| 180 | param->data = pos; | ||
| 181 | if (tomoyo_correct_domain(start)) | ||
| 182 | return tomoyo_get_name(start); | ||
| 183 | return NULL; | ||
| 184 | } | ||
| 185 | |||
| 186 | /** | ||
| 162 | * tomoyo_parse_ulong - Parse an "unsigned long" value. | 187 | * tomoyo_parse_ulong - Parse an "unsigned long" value. |
| 163 | * | 188 | * |
| 164 | * @result: Pointer to "unsigned long". | 189 | * @result: Pointer to "unsigned long". |
