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 | |
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>
-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". |