diff options
Diffstat (limited to 'security/tomoyo/domain.c')
-rw-r--r-- | security/tomoyo/domain.c | 630 |
1 files changed, 427 insertions, 203 deletions
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 35388408e47..cd0f92d88bb 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c | |||
@@ -1,9 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * security/tomoyo/domain.c | 2 | * security/tomoyo/domain.c |
3 | * | 3 | * |
4 | * Domain transition functions for TOMOYO. | 4 | * Copyright (C) 2005-2011 NTT DATA CORPORATION |
5 | * | ||
6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
7 | */ | 5 | */ |
8 | 6 | ||
9 | #include "common.h" | 7 | #include "common.h" |
@@ -20,8 +18,7 @@ struct tomoyo_domain_info tomoyo_kernel_domain; | |||
20 | * | 18 | * |
21 | * @new_entry: Pointer to "struct tomoyo_acl_info". | 19 | * @new_entry: Pointer to "struct tomoyo_acl_info". |
22 | * @size: Size of @new_entry in bytes. | 20 | * @size: Size of @new_entry in bytes. |
23 | * @is_delete: True if it is a delete request. | 21 | * @param: Pointer to "struct tomoyo_acl_param". |
24 | * @list: Pointer to "struct list_head". | ||
25 | * @check_duplicate: Callback function to find duplicated entry. | 22 | * @check_duplicate: Callback function to find duplicated entry. |
26 | * | 23 | * |
27 | * Returns 0 on success, negative value otherwise. | 24 | * Returns 0 on success, negative value otherwise. |
@@ -29,25 +26,26 @@ struct tomoyo_domain_info tomoyo_kernel_domain; | |||
29 | * Caller holds tomoyo_read_lock(). | 26 | * Caller holds tomoyo_read_lock(). |
30 | */ | 27 | */ |
31 | int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, | 28 | int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, |
32 | bool is_delete, struct list_head *list, | 29 | struct tomoyo_acl_param *param, |
33 | bool (*check_duplicate) (const struct tomoyo_acl_head | 30 | bool (*check_duplicate) (const struct tomoyo_acl_head |
34 | *, | 31 | *, |
35 | const struct tomoyo_acl_head | 32 | const struct tomoyo_acl_head |
36 | *)) | 33 | *)) |
37 | { | 34 | { |
38 | int error = is_delete ? -ENOENT : -ENOMEM; | 35 | int error = param->is_delete ? -ENOENT : -ENOMEM; |
39 | struct tomoyo_acl_head *entry; | 36 | struct tomoyo_acl_head *entry; |
37 | struct list_head *list = param->list; | ||
40 | 38 | ||
41 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 39 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
42 | return -ENOMEM; | 40 | return -ENOMEM; |
43 | list_for_each_entry_rcu(entry, list, list) { | 41 | list_for_each_entry_rcu(entry, list, list) { |
44 | if (!check_duplicate(entry, new_entry)) | 42 | if (!check_duplicate(entry, new_entry)) |
45 | continue; | 43 | continue; |
46 | entry->is_deleted = is_delete; | 44 | entry->is_deleted = param->is_delete; |
47 | error = 0; | 45 | error = 0; |
48 | break; | 46 | break; |
49 | } | 47 | } |
50 | if (error && !is_delete) { | 48 | if (error && !param->is_delete) { |
51 | entry = tomoyo_commit_ok(new_entry, size); | 49 | entry = tomoyo_commit_ok(new_entry, size); |
52 | if (entry) { | 50 | if (entry) { |
53 | list_add_tail_rcu(&entry->list, list); | 51 | list_add_tail_rcu(&entry->list, list); |
@@ -59,12 +57,25 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, | |||
59 | } | 57 | } |
60 | 58 | ||
61 | /** | 59 | /** |
60 | * tomoyo_same_acl_head - Check for duplicated "struct tomoyo_acl_info" entry. | ||
61 | * | ||
62 | * @a: Pointer to "struct tomoyo_acl_info". | ||
63 | * @b: Pointer to "struct tomoyo_acl_info". | ||
64 | * | ||
65 | * Returns true if @a == @b, false otherwise. | ||
66 | */ | ||
67 | static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a, | ||
68 | const struct tomoyo_acl_info *b) | ||
69 | { | ||
70 | return a->type == b->type && a->cond == b->cond; | ||
71 | } | ||
72 | |||
73 | /** | ||
62 | * tomoyo_update_domain - Update an entry for domain policy. | 74 | * tomoyo_update_domain - Update an entry for domain policy. |
63 | * | 75 | * |
64 | * @new_entry: Pointer to "struct tomoyo_acl_info". | 76 | * @new_entry: Pointer to "struct tomoyo_acl_info". |
65 | * @size: Size of @new_entry in bytes. | 77 | * @size: Size of @new_entry in bytes. |
66 | * @is_delete: True if it is a delete request. | 78 | * @param: Pointer to "struct tomoyo_acl_param". |
67 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
68 | * @check_duplicate: Callback function to find duplicated entry. | 79 | * @check_duplicate: Callback function to find duplicated entry. |
69 | * @merge_duplicate: Callback function to merge duplicated entry. | 80 | * @merge_duplicate: Callback function to merge duplicated entry. |
70 | * | 81 | * |
@@ -73,7 +84,7 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, | |||
73 | * Caller holds tomoyo_read_lock(). | 84 | * Caller holds tomoyo_read_lock(). |
74 | */ | 85 | */ |
75 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, | 86 | int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, |
76 | bool is_delete, struct tomoyo_domain_info *domain, | 87 | struct tomoyo_acl_param *param, |
77 | bool (*check_duplicate) (const struct tomoyo_acl_info | 88 | bool (*check_duplicate) (const struct tomoyo_acl_info |
78 | *, | 89 | *, |
79 | const struct tomoyo_acl_info | 90 | const struct tomoyo_acl_info |
@@ -82,13 +93,21 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, | |||
82 | struct tomoyo_acl_info *, | 93 | struct tomoyo_acl_info *, |
83 | const bool)) | 94 | const bool)) |
84 | { | 95 | { |
96 | const bool is_delete = param->is_delete; | ||
85 | int error = is_delete ? -ENOENT : -ENOMEM; | 97 | int error = is_delete ? -ENOENT : -ENOMEM; |
86 | struct tomoyo_acl_info *entry; | 98 | struct tomoyo_acl_info *entry; |
99 | struct list_head * const list = param->list; | ||
87 | 100 | ||
101 | if (param->data[0]) { | ||
102 | new_entry->cond = tomoyo_get_condition(param); | ||
103 | if (!new_entry->cond) | ||
104 | return -EINVAL; | ||
105 | } | ||
88 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 106 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
89 | return error; | 107 | goto out; |
90 | list_for_each_entry_rcu(entry, &domain->acl_info_list, list) { | 108 | list_for_each_entry_rcu(entry, list, list) { |
91 | if (!check_duplicate(entry, new_entry)) | 109 | if (!tomoyo_same_acl_head(entry, new_entry) || |
110 | !check_duplicate(entry, new_entry)) | ||
92 | continue; | 111 | continue; |
93 | if (merge_duplicate) | 112 | if (merge_duplicate) |
94 | entry->is_deleted = merge_duplicate(entry, new_entry, | 113 | entry->is_deleted = merge_duplicate(entry, new_entry, |
@@ -101,28 +120,50 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, | |||
101 | if (error && !is_delete) { | 120 | if (error && !is_delete) { |
102 | entry = tomoyo_commit_ok(new_entry, size); | 121 | entry = tomoyo_commit_ok(new_entry, size); |
103 | if (entry) { | 122 | if (entry) { |
104 | list_add_tail_rcu(&entry->list, &domain->acl_info_list); | 123 | list_add_tail_rcu(&entry->list, list); |
105 | error = 0; | 124 | error = 0; |
106 | } | 125 | } |
107 | } | 126 | } |
108 | mutex_unlock(&tomoyo_policy_lock); | 127 | mutex_unlock(&tomoyo_policy_lock); |
128 | out: | ||
129 | tomoyo_put_condition(new_entry->cond); | ||
109 | return error; | 130 | return error; |
110 | } | 131 | } |
111 | 132 | ||
133 | /** | ||
134 | * tomoyo_check_acl - Do permission check. | ||
135 | * | ||
136 | * @r: Pointer to "struct tomoyo_request_info". | ||
137 | * @check_entry: Callback function to check type specific parameters. | ||
138 | * | ||
139 | * Returns 0 on success, negative value otherwise. | ||
140 | * | ||
141 | * Caller holds tomoyo_read_lock(). | ||
142 | */ | ||
112 | void tomoyo_check_acl(struct tomoyo_request_info *r, | 143 | void tomoyo_check_acl(struct tomoyo_request_info *r, |
113 | bool (*check_entry) (struct tomoyo_request_info *, | 144 | bool (*check_entry) (struct tomoyo_request_info *, |
114 | const struct tomoyo_acl_info *)) | 145 | const struct tomoyo_acl_info *)) |
115 | { | 146 | { |
116 | const struct tomoyo_domain_info *domain = r->domain; | 147 | const struct tomoyo_domain_info *domain = r->domain; |
117 | struct tomoyo_acl_info *ptr; | 148 | struct tomoyo_acl_info *ptr; |
149 | bool retried = false; | ||
150 | const struct list_head *list = &domain->acl_info_list; | ||
118 | 151 | ||
119 | list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { | 152 | retry: |
153 | list_for_each_entry_rcu(ptr, list, list) { | ||
120 | if (ptr->is_deleted || ptr->type != r->param_type) | 154 | if (ptr->is_deleted || ptr->type != r->param_type) |
121 | continue; | 155 | continue; |
122 | if (check_entry(r, ptr)) { | 156 | if (!check_entry(r, ptr)) |
123 | r->granted = true; | 157 | continue; |
124 | return; | 158 | if (!tomoyo_condition(r, ptr->cond)) |
125 | } | 159 | continue; |
160 | r->granted = true; | ||
161 | return; | ||
162 | } | ||
163 | if (!retried) { | ||
164 | retried = true; | ||
165 | list = &domain->ns->acl_group[domain->group]; | ||
166 | goto retry; | ||
126 | } | 167 | } |
127 | r->granted = false; | 168 | r->granted = false; |
128 | } | 169 | } |
@@ -130,24 +171,29 @@ void tomoyo_check_acl(struct tomoyo_request_info *r, | |||
130 | /* The list for "struct tomoyo_domain_info". */ | 171 | /* The list for "struct tomoyo_domain_info". */ |
131 | LIST_HEAD(tomoyo_domain_list); | 172 | LIST_HEAD(tomoyo_domain_list); |
132 | 173 | ||
133 | struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY]; | ||
134 | struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP]; | ||
135 | |||
136 | /** | 174 | /** |
137 | * tomoyo_last_word - Get last component of a domainname. | 175 | * tomoyo_last_word - Get last component of a domainname. |
138 | * | 176 | * |
139 | * @domainname: Domainname to check. | 177 | * @name: Domainname to check. |
140 | * | 178 | * |
141 | * Returns the last word of @domainname. | 179 | * Returns the last word of @domainname. |
142 | */ | 180 | */ |
143 | static const char *tomoyo_last_word(const char *name) | 181 | static const char *tomoyo_last_word(const char *name) |
144 | { | 182 | { |
145 | const char *cp = strrchr(name, ' '); | 183 | const char *cp = strrchr(name, ' '); |
146 | if (cp) | 184 | if (cp) |
147 | return cp + 1; | 185 | return cp + 1; |
148 | return name; | 186 | return name; |
149 | } | 187 | } |
150 | 188 | ||
189 | /** | ||
190 | * tomoyo_same_transition_control - Check for duplicated "struct tomoyo_transition_control" entry. | ||
191 | * | ||
192 | * @a: Pointer to "struct tomoyo_acl_head". | ||
193 | * @b: Pointer to "struct tomoyo_acl_head". | ||
194 | * | ||
195 | * Returns true if @a == @b, false otherwise. | ||
196 | */ | ||
151 | static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a, | 197 | static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a, |
152 | const struct tomoyo_acl_head *b) | 198 | const struct tomoyo_acl_head *b) |
153 | { | 199 | { |
@@ -163,30 +209,36 @@ static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a, | |||
163 | } | 209 | } |
164 | 210 | ||
165 | /** | 211 | /** |
166 | * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list. | 212 | * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list. |
167 | * | 213 | * |
168 | * @domainname: The name of domain. Maybe NULL. | 214 | * @param: Pointer to "struct tomoyo_acl_param". |
169 | * @program: The name of program. Maybe NULL. | 215 | * @type: Type of this entry. |
170 | * @type: Type of transition. | ||
171 | * @is_delete: True if it is a delete request. | ||
172 | * | 216 | * |
173 | * Returns 0 on success, negative value otherwise. | 217 | * Returns 0 on success, negative value otherwise. |
174 | */ | 218 | */ |
175 | static int tomoyo_update_transition_control_entry(const char *domainname, | 219 | int tomoyo_write_transition_control(struct tomoyo_acl_param *param, |
176 | const char *program, | 220 | const u8 type) |
177 | const u8 type, | ||
178 | const bool is_delete) | ||
179 | { | 221 | { |
180 | struct tomoyo_transition_control e = { .type = type }; | 222 | struct tomoyo_transition_control e = { .type = type }; |
181 | int error = is_delete ? -ENOENT : -ENOMEM; | 223 | int error = param->is_delete ? -ENOENT : -ENOMEM; |
182 | if (program) { | 224 | char *program = param->data; |
225 | char *domainname = strstr(program, " from "); | ||
226 | if (domainname) { | ||
227 | *domainname = '\0'; | ||
228 | domainname += 6; | ||
229 | } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP || | ||
230 | type == TOMOYO_TRANSITION_CONTROL_KEEP) { | ||
231 | domainname = program; | ||
232 | program = NULL; | ||
233 | } | ||
234 | if (program && strcmp(program, "any")) { | ||
183 | if (!tomoyo_correct_path(program)) | 235 | if (!tomoyo_correct_path(program)) |
184 | return -EINVAL; | 236 | return -EINVAL; |
185 | e.program = tomoyo_get_name(program); | 237 | e.program = tomoyo_get_name(program); |
186 | if (!e.program) | 238 | if (!e.program) |
187 | goto out; | 239 | goto out; |
188 | } | 240 | } |
189 | if (domainname) { | 241 | if (domainname && strcmp(domainname, "any")) { |
190 | if (!tomoyo_correct_domain(domainname)) { | 242 | if (!tomoyo_correct_domain(domainname)) { |
191 | if (!tomoyo_correct_path(domainname)) | 243 | if (!tomoyo_correct_path(domainname)) |
192 | goto out; | 244 | goto out; |
@@ -196,126 +248,136 @@ static int tomoyo_update_transition_control_entry(const char *domainname, | |||
196 | if (!e.domainname) | 248 | if (!e.domainname) |
197 | goto out; | 249 | goto out; |
198 | } | 250 | } |
199 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, | 251 | param->list = ¶m->ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL]; |
200 | &tomoyo_policy_list | 252 | error = tomoyo_update_policy(&e.head, sizeof(e), param, |
201 | [TOMOYO_ID_TRANSITION_CONTROL], | ||
202 | tomoyo_same_transition_control); | 253 | tomoyo_same_transition_control); |
203 | out: | 254 | out: |
204 | tomoyo_put_name(e.domainname); | 255 | tomoyo_put_name(e.domainname); |
205 | tomoyo_put_name(e.program); | 256 | tomoyo_put_name(e.program); |
206 | return error; | 257 | return error; |
207 | } | 258 | } |
208 | 259 | ||
209 | /** | 260 | /** |
210 | * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list. | 261 | * tomoyo_scan_transition - Try to find specific domain transition type. |
211 | * | 262 | * |
212 | * @data: String to parse. | 263 | * @list: Pointer to "struct list_head". |
213 | * @is_delete: True if it is a delete request. | 264 | * @domainname: The name of current domain. |
214 | * @type: Type of this entry. | 265 | * @program: The name of requested program. |
266 | * @last_name: The last component of @domainname. | ||
267 | * @type: One of values in "enum tomoyo_transition_type". | ||
215 | * | 268 | * |
216 | * Returns 0 on success, negative value otherwise. | 269 | * Returns true if found one, false otherwise. |
270 | * | ||
271 | * Caller holds tomoyo_read_lock(). | ||
217 | */ | 272 | */ |
218 | int tomoyo_write_transition_control(char *data, const bool is_delete, | 273 | static inline bool tomoyo_scan_transition |
219 | const u8 type) | 274 | (const struct list_head *list, const struct tomoyo_path_info *domainname, |
275 | const struct tomoyo_path_info *program, const char *last_name, | ||
276 | const enum tomoyo_transition_type type) | ||
220 | { | 277 | { |
221 | char *domainname = strstr(data, " from "); | 278 | const struct tomoyo_transition_control *ptr; |
222 | if (domainname) { | 279 | list_for_each_entry_rcu(ptr, list, head.list) { |
223 | *domainname = '\0'; | 280 | if (ptr->head.is_deleted || ptr->type != type) |
224 | domainname += 6; | 281 | continue; |
225 | } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP || | 282 | if (ptr->domainname) { |
226 | type == TOMOYO_TRANSITION_CONTROL_KEEP) { | 283 | if (!ptr->is_last_name) { |
227 | domainname = data; | 284 | if (ptr->domainname != domainname) |
228 | data = NULL; | 285 | continue; |
286 | } else { | ||
287 | /* | ||
288 | * Use direct strcmp() since this is | ||
289 | * unlikely used. | ||
290 | */ | ||
291 | if (strcmp(ptr->domainname->name, last_name)) | ||
292 | continue; | ||
293 | } | ||
294 | } | ||
295 | if (ptr->program && tomoyo_pathcmp(ptr->program, program)) | ||
296 | continue; | ||
297 | return true; | ||
229 | } | 298 | } |
230 | return tomoyo_update_transition_control_entry(domainname, data, type, | 299 | return false; |
231 | is_delete); | ||
232 | } | 300 | } |
233 | 301 | ||
234 | /** | 302 | /** |
235 | * tomoyo_transition_type - Get domain transition type. | 303 | * tomoyo_transition_type - Get domain transition type. |
236 | * | 304 | * |
237 | * @domainname: The name of domain. | 305 | * @ns: Pointer to "struct tomoyo_policy_namespace". |
238 | * @program: The name of program. | 306 | * @domainname: The name of current domain. |
307 | * @program: The name of requested program. | ||
239 | * | 308 | * |
240 | * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program | 309 | * Returns TOMOYO_TRANSITION_CONTROL_TRANSIT if executing @program causes |
241 | * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing | 310 | * domain transition across namespaces, TOMOYO_TRANSITION_CONTROL_INITIALIZE if |
242 | * @program suppresses domain transition, others otherwise. | 311 | * executing @program reinitializes domain transition within that namespace, |
312 | * TOMOYO_TRANSITION_CONTROL_KEEP if executing @program stays at @domainname , | ||
313 | * others otherwise. | ||
243 | * | 314 | * |
244 | * Caller holds tomoyo_read_lock(). | 315 | * Caller holds tomoyo_read_lock(). |
245 | */ | 316 | */ |
246 | static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname, | 317 | static enum tomoyo_transition_type tomoyo_transition_type |
247 | const struct tomoyo_path_info *program) | 318 | (const struct tomoyo_policy_namespace *ns, |
319 | const struct tomoyo_path_info *domainname, | ||
320 | const struct tomoyo_path_info *program) | ||
248 | { | 321 | { |
249 | const struct tomoyo_transition_control *ptr; | ||
250 | const char *last_name = tomoyo_last_word(domainname->name); | 322 | const char *last_name = tomoyo_last_word(domainname->name); |
251 | u8 type; | 323 | enum tomoyo_transition_type type = TOMOYO_TRANSITION_CONTROL_NO_RESET; |
252 | for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) { | 324 | while (type < TOMOYO_MAX_TRANSITION_TYPE) { |
253 | next: | 325 | const struct list_head * const list = |
254 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list | 326 | &ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL]; |
255 | [TOMOYO_ID_TRANSITION_CONTROL], | 327 | if (!tomoyo_scan_transition(list, domainname, program, |
256 | head.list) { | 328 | last_name, type)) { |
257 | if (ptr->head.is_deleted || ptr->type != type) | 329 | type++; |
258 | continue; | 330 | continue; |
259 | if (ptr->domainname) { | ||
260 | if (!ptr->is_last_name) { | ||
261 | if (ptr->domainname != domainname) | ||
262 | continue; | ||
263 | } else { | ||
264 | /* | ||
265 | * Use direct strcmp() since this is | ||
266 | * unlikely used. | ||
267 | */ | ||
268 | if (strcmp(ptr->domainname->name, | ||
269 | last_name)) | ||
270 | continue; | ||
271 | } | ||
272 | } | ||
273 | if (ptr->program && | ||
274 | tomoyo_pathcmp(ptr->program, program)) | ||
275 | continue; | ||
276 | if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) { | ||
277 | /* | ||
278 | * Do not check for initialize_domain if | ||
279 | * no_initialize_domain matched. | ||
280 | */ | ||
281 | type = TOMOYO_TRANSITION_CONTROL_NO_KEEP; | ||
282 | goto next; | ||
283 | } | ||
284 | goto done; | ||
285 | } | 331 | } |
332 | if (type != TOMOYO_TRANSITION_CONTROL_NO_RESET && | ||
333 | type != TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) | ||
334 | break; | ||
335 | /* | ||
336 | * Do not check for reset_domain if no_reset_domain matched. | ||
337 | * Do not check for initialize_domain if no_initialize_domain | ||
338 | * matched. | ||
339 | */ | ||
340 | type++; | ||
341 | type++; | ||
286 | } | 342 | } |
287 | done: | ||
288 | return type; | 343 | return type; |
289 | } | 344 | } |
290 | 345 | ||
346 | /** | ||
347 | * tomoyo_same_aggregator - Check for duplicated "struct tomoyo_aggregator" entry. | ||
348 | * | ||
349 | * @a: Pointer to "struct tomoyo_acl_head". | ||
350 | * @b: Pointer to "struct tomoyo_acl_head". | ||
351 | * | ||
352 | * Returns true if @a == @b, false otherwise. | ||
353 | */ | ||
291 | static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a, | 354 | static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a, |
292 | const struct tomoyo_acl_head *b) | 355 | const struct tomoyo_acl_head *b) |
293 | { | 356 | { |
294 | const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head); | 357 | const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), |
295 | const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head); | 358 | head); |
359 | const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), | ||
360 | head); | ||
296 | return p1->original_name == p2->original_name && | 361 | return p1->original_name == p2->original_name && |
297 | p1->aggregated_name == p2->aggregated_name; | 362 | p1->aggregated_name == p2->aggregated_name; |
298 | } | 363 | } |
299 | 364 | ||
300 | /** | 365 | /** |
301 | * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list. | 366 | * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list. |
302 | * | 367 | * |
303 | * @original_name: The original program's name. | 368 | * @param: Pointer to "struct tomoyo_acl_param". |
304 | * @aggregated_name: The program name to use. | ||
305 | * @is_delete: True if it is a delete request. | ||
306 | * | 369 | * |
307 | * Returns 0 on success, negative value otherwise. | 370 | * Returns 0 on success, negative value otherwise. |
308 | * | 371 | * |
309 | * Caller holds tomoyo_read_lock(). | 372 | * Caller holds tomoyo_read_lock(). |
310 | */ | 373 | */ |
311 | static int tomoyo_update_aggregator_entry(const char *original_name, | 374 | int tomoyo_write_aggregator(struct tomoyo_acl_param *param) |
312 | const char *aggregated_name, | ||
313 | const bool is_delete) | ||
314 | { | 375 | { |
315 | struct tomoyo_aggregator e = { }; | 376 | struct tomoyo_aggregator e = { }; |
316 | int error = is_delete ? -ENOENT : -ENOMEM; | 377 | int error = param->is_delete ? -ENOENT : -ENOMEM; |
317 | 378 | const char *original_name = tomoyo_read_token(param); | |
318 | if (!tomoyo_correct_path(original_name) || | 379 | const char *aggregated_name = tomoyo_read_token(param); |
380 | if (!tomoyo_correct_word(original_name) || | ||
319 | !tomoyo_correct_path(aggregated_name)) | 381 | !tomoyo_correct_path(aggregated_name)) |
320 | return -EINVAL; | 382 | return -EINVAL; |
321 | e.original_name = tomoyo_get_name(original_name); | 383 | e.original_name = tomoyo_get_name(original_name); |
@@ -323,83 +385,181 @@ static int tomoyo_update_aggregator_entry(const char *original_name, | |||
323 | if (!e.original_name || !e.aggregated_name || | 385 | if (!e.original_name || !e.aggregated_name || |
324 | e.aggregated_name->is_patterned) /* No patterns allowed. */ | 386 | e.aggregated_name->is_patterned) /* No patterns allowed. */ |
325 | goto out; | 387 | goto out; |
326 | error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, | 388 | param->list = ¶m->ns->policy_list[TOMOYO_ID_AGGREGATOR]; |
327 | &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR], | 389 | error = tomoyo_update_policy(&e.head, sizeof(e), param, |
328 | tomoyo_same_aggregator); | 390 | tomoyo_same_aggregator); |
329 | out: | 391 | out: |
330 | tomoyo_put_name(e.original_name); | 392 | tomoyo_put_name(e.original_name); |
331 | tomoyo_put_name(e.aggregated_name); | 393 | tomoyo_put_name(e.aggregated_name); |
332 | return error; | 394 | return error; |
333 | } | 395 | } |
334 | 396 | ||
335 | /** | 397 | /** |
336 | * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list. | 398 | * tomoyo_find_namespace - Find specified namespace. |
337 | * | 399 | * |
338 | * @data: String to parse. | 400 | * @name: Name of namespace to find. |
339 | * @is_delete: True if it is a delete request. | 401 | * @len: Length of @name. |
340 | * | 402 | * |
341 | * Returns 0 on success, negative value otherwise. | 403 | * Returns pointer to "struct tomoyo_policy_namespace" if found, |
404 | * NULL otherwise. | ||
342 | * | 405 | * |
343 | * Caller holds tomoyo_read_lock(). | 406 | * Caller holds tomoyo_read_lock(). |
344 | */ | 407 | */ |
345 | int tomoyo_write_aggregator(char *data, const bool is_delete) | 408 | static struct tomoyo_policy_namespace *tomoyo_find_namespace |
409 | (const char *name, const unsigned int len) | ||
346 | { | 410 | { |
347 | char *cp = strchr(data, ' '); | 411 | struct tomoyo_policy_namespace *ns; |
412 | list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { | ||
413 | if (strncmp(name, ns->name, len) || | ||
414 | (name[len] && name[len] != ' ')) | ||
415 | continue; | ||
416 | return ns; | ||
417 | } | ||
418 | return NULL; | ||
419 | } | ||
348 | 420 | ||
349 | if (!cp) | 421 | /** |
350 | return -EINVAL; | 422 | * tomoyo_assign_namespace - Create a new namespace. |
351 | *cp++ = '\0'; | 423 | * |
352 | return tomoyo_update_aggregator_entry(data, cp, is_delete); | 424 | * @domainname: Name of namespace to create. |
425 | * | ||
426 | * Returns pointer to "struct tomoyo_policy_namespace" on success, | ||
427 | * NULL otherwise. | ||
428 | * | ||
429 | * Caller holds tomoyo_read_lock(). | ||
430 | */ | ||
431 | struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname) | ||
432 | { | ||
433 | struct tomoyo_policy_namespace *ptr; | ||
434 | struct tomoyo_policy_namespace *entry; | ||
435 | const char *cp = domainname; | ||
436 | unsigned int len = 0; | ||
437 | while (*cp && *cp++ != ' ') | ||
438 | len++; | ||
439 | ptr = tomoyo_find_namespace(domainname, len); | ||
440 | if (ptr) | ||
441 | return ptr; | ||
442 | if (len >= TOMOYO_EXEC_TMPSIZE - 10 || !tomoyo_domain_def(domainname)) | ||
443 | return NULL; | ||
444 | entry = kzalloc(sizeof(*entry) + len + 1, GFP_NOFS); | ||
445 | if (!entry) | ||
446 | return NULL; | ||
447 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | ||
448 | goto out; | ||
449 | ptr = tomoyo_find_namespace(domainname, len); | ||
450 | if (!ptr && tomoyo_memory_ok(entry)) { | ||
451 | char *name = (char *) (entry + 1); | ||
452 | ptr = entry; | ||
453 | memmove(name, domainname, len); | ||
454 | name[len] = '\0'; | ||
455 | entry->name = name; | ||
456 | tomoyo_init_policy_namespace(entry); | ||
457 | entry = NULL; | ||
458 | } | ||
459 | mutex_unlock(&tomoyo_policy_lock); | ||
460 | out: | ||
461 | kfree(entry); | ||
462 | return ptr; | ||
353 | } | 463 | } |
354 | 464 | ||
355 | /** | 465 | /** |
356 | * tomoyo_assign_domain - Create a domain. | 466 | * tomoyo_namespace_jump - Check for namespace jump. |
467 | * | ||
468 | * @domainname: Name of domain. | ||
469 | * | ||
470 | * Returns true if namespace differs, false otherwise. | ||
471 | */ | ||
472 | static bool tomoyo_namespace_jump(const char *domainname) | ||
473 | { | ||
474 | const char *namespace = tomoyo_current_namespace()->name; | ||
475 | const int len = strlen(namespace); | ||
476 | return strncmp(domainname, namespace, len) || | ||
477 | (domainname[len] && domainname[len] != ' '); | ||
478 | } | ||
479 | |||
480 | /** | ||
481 | * tomoyo_assign_domain - Create a domain or a namespace. | ||
357 | * | 482 | * |
358 | * @domainname: The name of domain. | 483 | * @domainname: The name of domain. |
359 | * @profile: Profile number to assign if the domain was newly created. | 484 | * @transit: True if transit to domain found or created. |
360 | * | 485 | * |
361 | * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise. | 486 | * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise. |
362 | * | 487 | * |
363 | * Caller holds tomoyo_read_lock(). | 488 | * Caller holds tomoyo_read_lock(). |
364 | */ | 489 | */ |
365 | struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, | 490 | struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, |
366 | const u8 profile) | 491 | const bool transit) |
367 | { | 492 | { |
368 | struct tomoyo_domain_info *entry; | 493 | struct tomoyo_domain_info e = { }; |
369 | struct tomoyo_domain_info *domain = NULL; | 494 | struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname); |
370 | const struct tomoyo_path_info *saved_domainname; | 495 | bool created = false; |
371 | bool found = false; | 496 | if (entry) { |
372 | 497 | if (transit) { | |
373 | if (!tomoyo_correct_domain(domainname)) | 498 | /* |
499 | * Since namespace is created at runtime, profiles may | ||
500 | * not be created by the moment the process transits to | ||
501 | * that domain. Do not perform domain transition if | ||
502 | * profile for that domain is not yet created. | ||
503 | */ | ||
504 | if (!entry->ns->profile_ptr[entry->profile]) | ||
505 | return NULL; | ||
506 | } | ||
507 | return entry; | ||
508 | } | ||
509 | /* Requested domain does not exist. */ | ||
510 | /* Don't create requested domain if domainname is invalid. */ | ||
511 | if (strlen(domainname) >= TOMOYO_EXEC_TMPSIZE - 10 || | ||
512 | !tomoyo_correct_domain(domainname)) | ||
513 | return NULL; | ||
514 | /* | ||
515 | * Since definition of profiles and acl_groups may differ across | ||
516 | * namespaces, do not inherit "use_profile" and "use_group" settings | ||
517 | * by automatically creating requested domain upon domain transition. | ||
518 | */ | ||
519 | if (transit && tomoyo_namespace_jump(domainname)) | ||
520 | return NULL; | ||
521 | e.ns = tomoyo_assign_namespace(domainname); | ||
522 | if (!e.ns) | ||
374 | return NULL; | 523 | return NULL; |
375 | saved_domainname = tomoyo_get_name(domainname); | 524 | /* |
376 | if (!saved_domainname) | 525 | * "use_profile" and "use_group" settings for automatically created |
526 | * domains are inherited from current domain. These are 0 for manually | ||
527 | * created domains. | ||
528 | */ | ||
529 | if (transit) { | ||
530 | const struct tomoyo_domain_info *domain = tomoyo_domain(); | ||
531 | e.profile = domain->profile; | ||
532 | e.group = domain->group; | ||
533 | } | ||
534 | e.domainname = tomoyo_get_name(domainname); | ||
535 | if (!e.domainname) | ||
377 | return NULL; | 536 | return NULL; |
378 | entry = kzalloc(sizeof(*entry), GFP_NOFS); | ||
379 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) | 537 | if (mutex_lock_interruptible(&tomoyo_policy_lock)) |
380 | goto out; | 538 | goto out; |
381 | list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { | 539 | entry = tomoyo_find_domain(domainname); |
382 | if (domain->is_deleted || | 540 | if (!entry) { |
383 | tomoyo_pathcmp(saved_domainname, domain->domainname)) | 541 | entry = tomoyo_commit_ok(&e, sizeof(e)); |
384 | continue; | 542 | if (entry) { |
385 | found = true; | 543 | INIT_LIST_HEAD(&entry->acl_info_list); |
386 | break; | 544 | list_add_tail_rcu(&entry->list, &tomoyo_domain_list); |
387 | } | 545 | created = true; |
388 | if (!found && tomoyo_memory_ok(entry)) { | 546 | } |
389 | INIT_LIST_HEAD(&entry->acl_info_list); | ||
390 | entry->domainname = saved_domainname; | ||
391 | saved_domainname = NULL; | ||
392 | entry->profile = profile; | ||
393 | list_add_tail_rcu(&entry->list, &tomoyo_domain_list); | ||
394 | domain = entry; | ||
395 | entry = NULL; | ||
396 | found = true; | ||
397 | } | 547 | } |
398 | mutex_unlock(&tomoyo_policy_lock); | 548 | mutex_unlock(&tomoyo_policy_lock); |
399 | out: | 549 | out: |
400 | tomoyo_put_name(saved_domainname); | 550 | tomoyo_put_name(e.domainname); |
401 | kfree(entry); | 551 | if (entry && transit) { |
402 | return found ? domain : NULL; | 552 | if (created) { |
553 | struct tomoyo_request_info r; | ||
554 | tomoyo_init_request_info(&r, entry, | ||
555 | TOMOYO_MAC_FILE_EXECUTE); | ||
556 | r.granted = false; | ||
557 | tomoyo_write_log(&r, "use_profile %u\n", | ||
558 | entry->profile); | ||
559 | tomoyo_write_log(&r, "use_group %u\n", entry->group); | ||
560 | } | ||
561 | } | ||
562 | return entry; | ||
403 | } | 563 | } |
404 | 564 | ||
405 | /** | 565 | /** |
@@ -413,22 +573,27 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, | |||
413 | */ | 573 | */ |
414 | int tomoyo_find_next_domain(struct linux_binprm *bprm) | 574 | int tomoyo_find_next_domain(struct linux_binprm *bprm) |
415 | { | 575 | { |
416 | struct tomoyo_request_info r; | ||
417 | char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); | ||
418 | struct tomoyo_domain_info *old_domain = tomoyo_domain(); | 576 | struct tomoyo_domain_info *old_domain = tomoyo_domain(); |
419 | struct tomoyo_domain_info *domain = NULL; | 577 | struct tomoyo_domain_info *domain = NULL; |
420 | const char *original_name = bprm->filename; | 578 | const char *original_name = bprm->filename; |
421 | u8 mode; | ||
422 | bool is_enforce; | ||
423 | int retval = -ENOMEM; | 579 | int retval = -ENOMEM; |
424 | bool need_kfree = false; | 580 | bool need_kfree = false; |
581 | bool reject_on_transition_failure = false; | ||
425 | struct tomoyo_path_info rn = { }; /* real name */ | 582 | struct tomoyo_path_info rn = { }; /* real name */ |
426 | 583 | struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS); | |
427 | mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); | 584 | if (!ee) |
428 | is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); | 585 | return -ENOMEM; |
429 | if (!tmp) | 586 | ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); |
430 | goto out; | 587 | if (!ee->tmp) { |
431 | 588 | kfree(ee); | |
589 | return -ENOMEM; | ||
590 | } | ||
591 | /* ee->dump->data is allocated by tomoyo_dump_page(). */ | ||
592 | tomoyo_init_request_info(&ee->r, NULL, TOMOYO_MAC_FILE_EXECUTE); | ||
593 | ee->r.ee = ee; | ||
594 | ee->bprm = bprm; | ||
595 | ee->r.obj = &ee->obj; | ||
596 | ee->obj.path1 = bprm->file->f_path; | ||
432 | retry: | 597 | retry: |
433 | if (need_kfree) { | 598 | if (need_kfree) { |
434 | kfree(rn.name); | 599 | kfree(rn.name); |
@@ -445,8 +610,10 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
445 | /* Check 'aggregator' directive. */ | 610 | /* Check 'aggregator' directive. */ |
446 | { | 611 | { |
447 | struct tomoyo_aggregator *ptr; | 612 | struct tomoyo_aggregator *ptr; |
448 | list_for_each_entry_rcu(ptr, &tomoyo_policy_list | 613 | struct list_head *list = |
449 | [TOMOYO_ID_AGGREGATOR], head.list) { | 614 | &old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR]; |
615 | /* Check 'aggregator' directive. */ | ||
616 | list_for_each_entry_rcu(ptr, list, head.list) { | ||
450 | if (ptr->head.is_deleted || | 617 | if (ptr->head.is_deleted || |
451 | !tomoyo_path_matches_pattern(&rn, | 618 | !tomoyo_path_matches_pattern(&rn, |
452 | ptr->original_name)) | 619 | ptr->original_name)) |
@@ -460,7 +627,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
460 | } | 627 | } |
461 | 628 | ||
462 | /* Check execute permission. */ | 629 | /* Check execute permission. */ |
463 | retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn); | 630 | retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, &rn); |
464 | if (retval == TOMOYO_RETRY_REQUEST) | 631 | if (retval == TOMOYO_RETRY_REQUEST) |
465 | goto retry; | 632 | goto retry; |
466 | if (retval < 0) | 633 | if (retval < 0) |
@@ -471,20 +638,30 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
471 | * wildcard) rather than the pathname passed to execve() | 638 | * wildcard) rather than the pathname passed to execve() |
472 | * (which never contains wildcard). | 639 | * (which never contains wildcard). |
473 | */ | 640 | */ |
474 | if (r.param.path.matched_path) { | 641 | if (ee->r.param.path.matched_path) { |
475 | if (need_kfree) | 642 | if (need_kfree) |
476 | kfree(rn.name); | 643 | kfree(rn.name); |
477 | need_kfree = false; | 644 | need_kfree = false; |
478 | /* This is OK because it is read only. */ | 645 | /* This is OK because it is read only. */ |
479 | rn = *r.param.path.matched_path; | 646 | rn = *ee->r.param.path.matched_path; |
480 | } | 647 | } |
481 | 648 | ||
482 | /* Calculate domain to transit to. */ | 649 | /* Calculate domain to transit to. */ |
483 | switch (tomoyo_transition_type(old_domain->domainname, &rn)) { | 650 | switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname, |
651 | &rn)) { | ||
652 | case TOMOYO_TRANSITION_CONTROL_RESET: | ||
653 | /* Transit to the root of specified namespace. */ | ||
654 | snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name); | ||
655 | /* | ||
656 | * Make do_execve() fail if domain transition across namespaces | ||
657 | * has failed. | ||
658 | */ | ||
659 | reject_on_transition_failure = true; | ||
660 | break; | ||
484 | case TOMOYO_TRANSITION_CONTROL_INITIALIZE: | 661 | case TOMOYO_TRANSITION_CONTROL_INITIALIZE: |
485 | /* Transit to the child of tomoyo_kernel_domain domain. */ | 662 | /* Transit to the child of current namespace's root. */ |
486 | snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " " | 663 | snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", |
487 | "%s", rn.name); | 664 | old_domain->ns->name, rn.name); |
488 | break; | 665 | break; |
489 | case TOMOYO_TRANSITION_CONTROL_KEEP: | 666 | case TOMOYO_TRANSITION_CONTROL_KEEP: |
490 | /* Keep current domain. */ | 667 | /* Keep current domain. */ |
@@ -502,33 +679,32 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
502 | domain = old_domain; | 679 | domain = old_domain; |
503 | } else { | 680 | } else { |
504 | /* Normal domain transition. */ | 681 | /* Normal domain transition. */ |
505 | snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", | 682 | snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", |
506 | old_domain->domainname->name, rn.name); | 683 | old_domain->domainname->name, rn.name); |
507 | } | 684 | } |
508 | break; | 685 | break; |
509 | } | 686 | } |
510 | if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10) | 687 | if (!domain) |
511 | goto done; | 688 | domain = tomoyo_assign_domain(ee->tmp, true); |
512 | domain = tomoyo_find_domain(tmp); | ||
513 | if (domain) | 689 | if (domain) |
514 | goto done; | 690 | retval = 0; |
515 | if (is_enforce) { | 691 | else if (reject_on_transition_failure) { |
516 | int error = tomoyo_supervisor(&r, "# wants to create domain\n" | 692 | printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n", |
517 | "%s\n", tmp); | 693 | ee->tmp); |
518 | if (error == TOMOYO_RETRY_REQUEST) | 694 | retval = -ENOMEM; |
519 | goto retry; | 695 | } else if (ee->r.mode == TOMOYO_CONFIG_ENFORCING) |
520 | if (error < 0) | 696 | retval = -ENOMEM; |
521 | goto done; | 697 | else { |
698 | retval = 0; | ||
699 | if (!old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED]) { | ||
700 | old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED] = true; | ||
701 | ee->r.granted = false; | ||
702 | tomoyo_write_log(&ee->r, "%s", tomoyo_dif | ||
703 | [TOMOYO_DIF_TRANSITION_FAILED]); | ||
704 | printk(KERN_WARNING | ||
705 | "ERROR: Domain '%s' not defined.\n", ee->tmp); | ||
706 | } | ||
522 | } | 707 | } |
523 | domain = tomoyo_assign_domain(tmp, old_domain->profile); | ||
524 | done: | ||
525 | if (domain) | ||
526 | goto out; | ||
527 | printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp); | ||
528 | if (is_enforce) | ||
529 | retval = -EPERM; | ||
530 | else | ||
531 | old_domain->transition_failed = true; | ||
532 | out: | 708 | out: |
533 | if (!domain) | 709 | if (!domain) |
534 | domain = old_domain; | 710 | domain = old_domain; |
@@ -537,6 +713,54 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) | |||
537 | bprm->cred->security = domain; | 713 | bprm->cred->security = domain; |
538 | if (need_kfree) | 714 | if (need_kfree) |
539 | kfree(rn.name); | 715 | kfree(rn.name); |
540 | kfree(tmp); | 716 | kfree(ee->tmp); |
717 | kfree(ee->dump.data); | ||
718 | kfree(ee); | ||
541 | return retval; | 719 | return retval; |
542 | } | 720 | } |
721 | |||
722 | /** | ||
723 | * tomoyo_dump_page - Dump a page to buffer. | ||
724 | * | ||
725 | * @bprm: Pointer to "struct linux_binprm". | ||
726 | * @pos: Location to dump. | ||
727 | * @dump: Poiner to "struct tomoyo_page_dump". | ||
728 | * | ||
729 | * Returns true on success, false otherwise. | ||
730 | */ | ||
731 | bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos, | ||
732 | struct tomoyo_page_dump *dump) | ||
733 | { | ||
734 | struct page *page; | ||
735 | /* dump->data is released by tomoyo_finish_execve(). */ | ||
736 | if (!dump->data) { | ||
737 | dump->data = kzalloc(PAGE_SIZE, GFP_NOFS); | ||
738 | if (!dump->data) | ||
739 | return false; | ||
740 | } | ||
741 | /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */ | ||
742 | #ifdef CONFIG_MMU | ||
743 | if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) | ||
744 | return false; | ||
745 | #else | ||
746 | page = bprm->page[pos / PAGE_SIZE]; | ||
747 | #endif | ||
748 | if (page != dump->page) { | ||
749 | const unsigned int offset = pos % PAGE_SIZE; | ||
750 | /* | ||
751 | * Maybe kmap()/kunmap() should be used here. | ||
752 | * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic(). | ||
753 | * So do I. | ||
754 | */ | ||
755 | char *kaddr = kmap_atomic(page, KM_USER0); | ||
756 | dump->page = page; | ||
757 | memcpy(dump->data + offset, kaddr + offset, | ||
758 | PAGE_SIZE - offset); | ||
759 | kunmap_atomic(kaddr, KM_USER0); | ||
760 | } | ||
761 | /* Same with put_arg_page(page) in fs/exec.c */ | ||
762 | #ifdef CONFIG_MMU | ||
763 | put_page(page); | ||
764 | #endif | ||
765 | return true; | ||
766 | } | ||