オープンソース・ソフトウェアの開発とダウンロード

Subversion リポジトリの参照

Contents of /trunk/1.8.x/ccs-patch/security/ccsecurity/domain.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3924 - (show annotations) (download) (as text)
Thu Aug 26 11:01:19 2010 UTC (13 years, 8 months ago) by kumaneko
File MIME type: text/x-csrc
File size: 29661 byte(s)
Replace task.state[] with auto_domain_transition .
1 /*
2 * security/ccsecurity/domain.c
3 *
4 * Copyright (C) 2005-2010 NTT DATA CORPORATION
5 *
6 * Version: 1.8.0-pre 2010/08/01
7 *
8 * This file is applicable to both 2.4.30 and 2.6.11 and later.
9 * See README.ccs for ChangeLog.
10 *
11 */
12
13 #include <linux/slab.h>
14 #include <linux/highmem.h>
15 #include <linux/version.h>
16 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
17 #include <linux/namei.h>
18 #include <linux/mount.h>
19 #endif
20 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
21 #include <linux/fs_struct.h>
22 #endif
23 #include "internal.h"
24
25 /* Variables definitions.*/
26
27 /* The global domain. */
28 struct ccs_domain_info ccs_acl_group[CCS_MAX_ACL_GROUPS];
29
30 /* The initial domain. */
31 struct ccs_domain_info ccs_kernel_domain;
32
33 /* The list for "struct ccs_domain_info". */
34 LIST_HEAD(ccs_domain_list);
35
36 struct list_head ccs_policy_list[CCS_MAX_POLICY];
37 struct list_head ccs_group_list[CCS_MAX_GROUP];
38 struct list_head ccs_shared_list[CCS_MAX_LIST];
39
40 /**
41 * ccs_update_policy - Update an entry for exception policy.
42 *
43 * @new_entry: Pointer to "struct ccs_acl_info".
44 * @size: Size of @new_entry in bytes.
45 * @is_delete: True if it is a delete request.
46 * @list: Pointer to "struct list_head".
47 * @check_duplicate: Callback function to find duplicated entry.
48 *
49 * Returns 0 on success, negative value otherwise.
50 *
51 * Caller holds ccs_read_lock().
52 */
53 int ccs_update_policy(struct ccs_acl_head *new_entry, const int size,
54 bool is_delete, struct list_head *list,
55 bool (*check_duplicate) (const struct ccs_acl_head *,
56 const struct ccs_acl_head *))
57 {
58 int error = is_delete ? -ENOENT : -ENOMEM;
59 struct ccs_acl_head *entry;
60 if (mutex_lock_interruptible(&ccs_policy_lock))
61 return -ENOMEM;
62 list_for_each_entry_rcu(entry, list, list) {
63 if (!check_duplicate(entry, new_entry))
64 continue;
65 entry->is_deleted = is_delete;
66 error = 0;
67 break;
68 }
69 if (error && !is_delete) {
70 entry = ccs_commit_ok(new_entry, size);
71 if (entry) {
72 list_add_tail_rcu(&entry->list, list);
73 error = 0;
74 }
75 }
76 mutex_unlock(&ccs_policy_lock);
77 return error;
78 }
79
80 /**
81 * ccs_update_domain - Update an entry for domain policy.
82 *
83 * @new_entry: Pointer to "struct ccs_acl_info".
84 * @size: Size of @new_entry in bytes.
85 * @is_delete: True if it is a delete request.
86 * @domain: Pointer to "struct ccs_domain_info".
87 * @check_duplicate: Callback function to find duplicated entry.
88 * @merge_duplicate: Callback function to merge duplicated entry.
89 *
90 * Returns 0 on success, negative value otherwise.
91 *
92 * Caller holds ccs_read_lock().
93 */
94 int ccs_update_domain(struct ccs_acl_info *new_entry, const int size,
95 bool is_delete, struct ccs_domain_info *domain,
96 bool (*check_duplicate) (const struct ccs_acl_info *,
97 const struct ccs_acl_info *),
98 bool (*merge_duplicate) (struct ccs_acl_info *,
99 struct ccs_acl_info *,
100 const bool))
101 {
102 int error = is_delete ? -ENOENT : -ENOMEM;
103 struct ccs_acl_info *entry;
104 const u8 type = new_entry->type;
105 const u8 i = type == CCS_TYPE_AUTO_EXECUTE_HANDLER ||
106 type == CCS_TYPE_DENIED_EXECUTE_HANDLER ||
107 type == CCS_TYPE_AUTO_TASK_ACL;
108 if (mutex_lock_interruptible(&ccs_policy_lock))
109 return error;
110 list_for_each_entry_rcu(entry, &domain->acl_info_list[i], list) {
111 if (!check_duplicate(entry, new_entry))
112 continue;
113 if (merge_duplicate)
114 entry->is_deleted = merge_duplicate(entry, new_entry,
115 is_delete);
116 else
117 entry->is_deleted = is_delete;
118 error = 0;
119 break;
120 }
121 if (error && !is_delete) {
122 entry = ccs_commit_ok(new_entry, size);
123 if (entry) {
124 if (entry->cond)
125 atomic_inc(&entry->cond->head.users);
126 list_add_tail_rcu(&entry->list,
127 &domain->acl_info_list[i]);
128 error = 0;
129 }
130 }
131 mutex_unlock(&ccs_policy_lock);
132 return error;
133 }
134
135 void ccs_check_acl(struct ccs_request_info *r,
136 bool (*check_entry) (struct ccs_request_info *,
137 const struct ccs_acl_info *))
138 {
139 const struct ccs_domain_info *domain = ccs_current_domain();
140 struct ccs_acl_info *ptr;
141 bool retried = false;
142 const u8 i = !check_entry;
143 retry:
144 list_for_each_entry_rcu(ptr, &domain->acl_info_list[i], list) {
145 if (ptr->is_deleted)
146 continue;
147 if (ptr->type != r->param_type)
148 continue;
149 if (check_entry && !check_entry(r, ptr))
150 continue;
151 if (!ccs_condition(r, ptr->cond))
152 continue;
153 r->matched_acl = ptr;
154 r->granted = true;
155 return;
156 }
157 if (!retried) {
158 retried = true;
159 domain = &ccs_acl_group[domain->group];
160 goto retry;
161 }
162 r->granted = false;
163 }
164
165 static bool ccs_same_transition_control(const struct ccs_acl_head *a,
166 const struct ccs_acl_head *b)
167 {
168 const struct ccs_transition_control *p1 = container_of(a, typeof(*p1),
169 head);
170 const struct ccs_transition_control *p2 = container_of(b, typeof(*p2),
171 head);
172 return p1->type == p2->type && p1->is_last_name == p2->is_last_name
173 && p1->domainname == p2->domainname
174 && p1->program == p2->program;
175 }
176
177 /**
178 * ccs_update_transition_control_entry - Update "struct ccs_transition_control" list.
179 *
180 * @domainname: The name of domain. Maybe NULL.
181 * @program: The name of program. Maybe NULL.
182 * @type: Type of transition.
183 * @is_delete: True if it is a delete request.
184 *
185 * Returns 0 on success, negative value otherwise.
186 */
187 static int ccs_update_transition_control_entry(const char *domainname,
188 const char *program,
189 const u8 type,
190 const bool is_delete)
191 {
192 struct ccs_transition_control e = { .type = type };
193 int error = is_delete ? -ENOENT : -ENOMEM;
194 if (program && strcmp(program, "any")) {
195 if (!ccs_correct_path(program))
196 return -EINVAL;
197 e.program = ccs_get_name(program);
198 if (!e.program)
199 goto out;
200 }
201 if (domainname && strcmp(domainname, "any")) {
202 if (!ccs_correct_domain(domainname)) {
203 if (!ccs_correct_path(domainname))
204 goto out;
205 e.is_last_name = true;
206 }
207 e.domainname = ccs_get_name(domainname);
208 if (!e.domainname)
209 goto out;
210 }
211 error = ccs_update_policy(&e.head, sizeof(e), is_delete,
212 &ccs_policy_list[CCS_ID_TRANSITION_CONTROL],
213 ccs_same_transition_control);
214 out:
215 ccs_put_name(e.domainname);
216 ccs_put_name(e.program);
217 return error;
218 }
219
220 /**
221 * ccs_write_transition_control - Write "struct ccs_transition_control" list.
222 *
223 * @data: String to parse.
224 * @is_delete: True if it is a delete request.
225 * @type: Type of this entry.
226 *
227 * Returns 0 on success, negative value otherwise.
228 */
229 int ccs_write_transition_control(char *data, const bool is_delete,
230 const u8 type)
231 {
232 char *domainname = strstr(data, " from ");
233 if (domainname) {
234 *domainname = '\0';
235 domainname += 6;
236 } else if (type == CCS_TRANSITION_CONTROL_NO_KEEP ||
237 type == CCS_TRANSITION_CONTROL_KEEP) {
238 domainname = data;
239 data = NULL;
240 }
241 return ccs_update_transition_control_entry(domainname, data, type,
242 is_delete);
243 }
244
245 /**
246 * ccs_transition_type - Get domain transition type.
247 *
248 * @domainname: The name of domain.
249 * @program: The name of program.
250 *
251 * Returns CCS_TRANSITION_CONTROL_INITIALIZE if executing @program
252 * reinitializes domain transition, CCS_TRANSITION_CONTROL_KEEP if executing
253 * @program suppresses domain transition, others otherwise.
254 *
255 * Caller holds ccs_read_lock().
256 */
257 static u8 ccs_transition_type(const struct ccs_path_info *domainname,
258 const struct ccs_path_info *program)
259 {
260 const struct ccs_transition_control *ptr;
261 const char *last_name = ccs_last_word(domainname->name);
262 u8 type;
263 for (type = 0; type < CCS_MAX_TRANSITION_TYPE; type++) {
264 next:
265 list_for_each_entry_rcu(ptr, &ccs_policy_list
266 [CCS_ID_TRANSITION_CONTROL],
267 head.list) {
268 if (ptr->head.is_deleted || ptr->type != type)
269 continue;
270 if (ptr->domainname) {
271 if (!ptr->is_last_name) {
272 if (ptr->domainname != domainname)
273 continue;
274 } else {
275 /*
276 * Use direct strcmp() since this is
277 * unlikely used.
278 */
279 if (strcmp(ptr->domainname->name,
280 last_name))
281 continue;
282 }
283 }
284 if (ptr->program && ccs_pathcmp(ptr->program, program))
285 continue;
286 if (type == CCS_TRANSITION_CONTROL_NO_INITIALIZE) {
287 /*
288 * Do not check for initialize_domain if
289 * no_initialize_domain matched.
290 */
291 type = CCS_TRANSITION_CONTROL_NO_KEEP;
292 goto next;
293 }
294 goto done;
295 }
296 }
297 done:
298 return type;
299 }
300
301 static bool ccs_same_aggregator(const struct ccs_acl_head *a,
302 const struct ccs_acl_head *b)
303 {
304 const struct ccs_aggregator *p1 = container_of(a, typeof(*p1), head);
305 const struct ccs_aggregator *p2 = container_of(b, typeof(*p2), head);
306 return p1->original_name == p2->original_name &&
307 p1->aggregated_name == p2->aggregated_name;
308 }
309
310 /**
311 * ccs_update_aggregator_entry - Update "struct ccs_aggregator" list.
312 *
313 * @original_name: The original program's name.
314 * @aggregated_name: The aggregated program's name.
315 * @is_delete: True if it is a delete request.
316 *
317 * Returns 0 on success, negative value otherwise.
318 */
319 static int ccs_update_aggregator_entry(const char *original_name,
320 const char *aggregated_name,
321 const bool is_delete)
322 {
323 struct ccs_aggregator e = { };
324 int error = is_delete ? -ENOENT : -ENOMEM;
325 if (!ccs_correct_word(original_name) ||
326 !ccs_correct_path(aggregated_name))
327 return -EINVAL;
328 e.original_name = ccs_get_name(original_name);
329 e.aggregated_name = ccs_get_name(aggregated_name);
330 if (!e.original_name || !e.aggregated_name ||
331 e.aggregated_name->is_patterned) /* No patterns allowed. */
332 goto out;
333 error = ccs_update_policy(&e.head, sizeof(e), is_delete,
334 &ccs_policy_list[CCS_ID_AGGREGATOR],
335 ccs_same_aggregator);
336 out:
337 ccs_put_name(e.original_name);
338 ccs_put_name(e.aggregated_name);
339 return error;
340 }
341
342 /**
343 * ccs_write_aggregator - Write "struct ccs_aggregator" list.
344 *
345 * @data: String to parse.
346 * @is_delete: True if it is a delete request.
347 *
348 * Returns 0 on success, negative value otherwise.
349 */
350 int ccs_write_aggregator(char *data, const bool is_delete)
351 {
352 char *w[2];
353 if (!ccs_tokenize(data, w, sizeof(w)) || !w[1][0])
354 return -EINVAL;
355 return ccs_update_aggregator_entry(w[0], w[1], is_delete);
356 }
357
358 /* Domain create/delete handler. */
359
360 /**
361 * ccs_delete_domain - Delete a domain.
362 *
363 * @domainname: The name of domain.
364 *
365 * Returns 0.
366 */
367 int ccs_delete_domain(char *domainname)
368 {
369 struct ccs_domain_info *domain;
370 struct ccs_path_info name;
371 name.name = domainname;
372 ccs_fill_path_info(&name);
373 if (mutex_lock_interruptible(&ccs_policy_lock))
374 return 0;
375 /* Is there an active domain? */
376 list_for_each_entry_rcu(domain, &ccs_domain_list, list) {
377 /* Never delete ccs_kernel_domain */
378 if (domain == &ccs_kernel_domain)
379 continue;
380 if (domain->is_deleted ||
381 ccs_pathcmp(domain->domainname, &name))
382 continue;
383 domain->is_deleted = true;
384 break;
385 }
386 mutex_unlock(&ccs_policy_lock);
387 return 0;
388 }
389
390 /**
391 * ccs_assign_domain - Create a domain.
392 *
393 * @domainname: The name of domain.
394 * @profile: Profile number to assign if the domain was newly created.
395 * @group: Group number to assign if the domain was newly created.
396 * @transit: True if transit to domain found or created.
397 *
398 * Returns pointer to "struct ccs_domain_info" on success, NULL otherwise.
399 *
400 * Caller holds ccs_read_lock().
401 */
402 struct ccs_domain_info *ccs_assign_domain(const char *domainname,
403 const u8 profile, const u8 group,
404 const bool transit)
405 {
406 struct ccs_domain_info e = { };
407 struct ccs_domain_info *entry = ccs_find_domain(domainname);
408 bool created = false;
409 if (entry)
410 goto out;
411 if (strlen(domainname) >= CCS_EXEC_TMPSIZE - 10 ||
412 !ccs_correct_domain(domainname))
413 return NULL;
414 e.profile = profile;
415 e.group = group;
416 e.domainname = ccs_get_name(domainname);
417 if (!e.domainname)
418 return NULL;
419 if (mutex_lock_interruptible(&ccs_policy_lock))
420 goto out;
421 entry = ccs_find_domain(domainname);
422 if (!entry) {
423 entry = ccs_commit_ok(&e, sizeof(e));
424 if (entry) {
425 INIT_LIST_HEAD(&entry->acl_info_list[0]);
426 INIT_LIST_HEAD(&entry->acl_info_list[1]);
427 list_add_tail_rcu(&entry->list, &ccs_domain_list);
428 created = true;
429 }
430 }
431 mutex_unlock(&ccs_policy_lock);
432 out:
433 ccs_put_name(e.domainname);
434 if (entry && transit) {
435 current->ccs_domain_info = entry;
436 if (created) {
437 struct ccs_request_info r;
438 ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE);
439 r.granted = false;
440 ccs_write_log(&r, "use_profile %u\n", r.profile);
441 }
442 }
443 return entry;
444 }
445
446 /**
447 * ccs_find_next_domain - Find a domain.
448 *
449 * @ee: Pointer to "struct ccs_execve".
450 *
451 * Returns 0 on success, negative value otherwise.
452 *
453 * Caller holds ccs_read_lock().
454 */
455 static int ccs_find_next_domain(struct ccs_execve *ee)
456 {
457 struct ccs_request_info *r = &ee->r;
458 const struct ccs_path_info *handler = ee->handler;
459 struct ccs_domain_info *domain = NULL;
460 struct ccs_domain_info * const old_domain = ccs_current_domain();
461 struct linux_binprm *bprm = ee->bprm;
462 struct task_struct *task = current;
463 struct ccs_path_info rn = { }; /* real name */
464 int retval;
465 bool need_kfree = false;
466 retry:
467 r->matched_acl = NULL;
468 if (need_kfree) {
469 kfree(rn.name);
470 need_kfree = false;
471 }
472
473 /* Get symlink's pathname of program. */
474 retval = ccs_symlink_path(bprm->filename, &rn);
475 if (retval < 0)
476 goto out;
477 need_kfree = true;
478
479 if (handler) {
480 if (ccs_pathcmp(&rn, handler)) {
481 /* Failed to verify execute handler. */
482 static u8 counter = 20;
483 if (counter) {
484 counter--;
485 printk(KERN_WARNING "Failed to verify: %s\n",
486 handler->name);
487 }
488 goto out;
489 }
490 } else {
491 struct ccs_aggregator *ptr;
492 /* Check 'aggregator' directive. */
493 list_for_each_entry_rcu(ptr,
494 &ccs_policy_list[CCS_ID_AGGREGATOR],
495 head.list) {
496 if (ptr->head.is_deleted ||
497 !ccs_path_matches_pattern(&rn, ptr->original_name))
498 continue;
499 kfree(rn.name);
500 need_kfree = false;
501 /* This is OK because it is read only. */
502 rn = *ptr->aggregated_name;
503 break;
504 }
505
506 /* Check execute permission. */
507 retval = ccs_path_permission(r, CCS_TYPE_EXECUTE, &rn);
508 if (retval == CCS_RETRY_REQUEST)
509 goto retry;
510 if (retval < 0)
511 goto out;
512 /*
513 * To be able to specify domainnames with wildcards, use the
514 * pathname specified in the policy (which may contain
515 * wildcard) rather than the pathname passed to execve()
516 * (which never contains wildcard).
517 */
518 if (r->param.path.matched_path) {
519 if (need_kfree)
520 kfree(rn.name);
521 need_kfree = false;
522 /* This is OK because it is read only. */
523 rn = *r->param.path.matched_path;
524 }
525 }
526
527 /* Calculate domain to transit to. */
528 switch (ccs_transition_type(old_domain->domainname, &rn)) {
529 case CCS_TRANSITION_CONTROL_INITIALIZE:
530 /* Transit to the child of ccs_kernel_domain domain. */
531 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, CCS_ROOT_NAME " " "%s",
532 rn.name);
533 break;
534 case CCS_TRANSITION_CONTROL_KEEP:
535 /* Keep current domain. */
536 domain = old_domain;
537 break;
538 default:
539 if (old_domain == &ccs_kernel_domain && !ccs_policy_loaded) {
540 /*
541 * Needn't to transit from kernel domain before
542 * starting /sbin/init. But transit from kernel domain
543 * if executing initializers because they might start
544 * before /sbin/init.
545 */
546 domain = old_domain;
547 } else {
548 /* Normal domain transition. */
549 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s",
550 old_domain->domainname->name, rn.name);
551 }
552 break;
553 }
554 /*
555 * Tell GC that I started execve().
556 * Also, tell open_exec() to check read permission.
557 */
558 task->ccs_flags |= CCS_TASK_IS_IN_EXECVE;
559 /*
560 * Make task->ccs_flags visible to GC before changing
561 * task->ccs_domain_info .
562 */
563 smp_mb();
564 /*
565 * Proceed to the next domain in order to allow reaching via PID.
566 * It will be reverted if execve() failed. Reverting is not good.
567 * But it is better than being unable to reach via PID in interactive
568 * enforcing mode.
569 */
570 if (!domain)
571 domain = ccs_assign_domain(ee->tmp, r->profile,
572 old_domain->group, true);
573 if (domain)
574 retval = 0;
575 else if (r->mode == CCS_CONFIG_ENFORCING)
576 retval = -ENOMEM;
577 else {
578 retval = 0;
579 if (!old_domain->flags[CCS_DIF_TRANSITION_FAILED]) {
580 old_domain->flags[CCS_DIF_TRANSITION_FAILED] = true;
581 r->granted = false;
582 ccs_write_log(r, CCS_KEYWORD_TRANSITION_FAILED "\n");
583 printk(KERN_WARNING
584 "ERROR: Domain '%s' not defined.\n", ee->tmp);
585 }
586 }
587 out:
588 if (need_kfree)
589 kfree(rn.name);
590 return retval;
591 }
592
593 /**
594 * ccs_environ - Check permission for environment variable names.
595 *
596 * @ee: Pointer to "struct ccs_execve".
597 *
598 * Returns 0 on success, negative value otherwise.
599 */
600 static int ccs_environ(struct ccs_execve *ee)
601 {
602 struct ccs_request_info *r = &ee->r;
603 struct linux_binprm *bprm = ee->bprm;
604 /* env_page->data is allocated by ccs_dump_page(). */
605 struct ccs_page_dump env_page = { };
606 char *arg_ptr; /* Size is CCS_EXEC_TMPSIZE bytes */
607 int arg_len = 0;
608 unsigned long pos = bprm->p;
609 int offset = pos % PAGE_SIZE;
610 int argv_count = bprm->argc;
611 int envp_count = bprm->envc;
612 /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */
613 int error = -ENOMEM;
614 ee->r.type = CCS_MAC_ENVIRON;
615 ee->r.mode = ccs_get_mode(ccs_current_domain()->profile,
616 CCS_MAC_ENVIRON);
617 if (!r->mode || !envp_count)
618 return 0;
619 arg_ptr = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS);
620 if (!arg_ptr)
621 goto out;
622 while (error == -ENOMEM) {
623 if (!ccs_dump_page(bprm, pos, &env_page))
624 goto out;
625 pos += PAGE_SIZE - offset;
626 /* Read. */
627 while (argv_count && offset < PAGE_SIZE) {
628 if (!env_page.data[offset++])
629 argv_count--;
630 }
631 if (argv_count) {
632 offset = 0;
633 continue;
634 }
635 while (offset < PAGE_SIZE) {
636 const unsigned char c = env_page.data[offset++];
637 if (c && arg_len < CCS_EXEC_TMPSIZE - 10) {
638 if (c == '=') {
639 arg_ptr[arg_len++] = '\0';
640 } else if (c == '\\') {
641 arg_ptr[arg_len++] = '\\';
642 arg_ptr[arg_len++] = '\\';
643 } else if (c > ' ' && c < 127) {
644 arg_ptr[arg_len++] = c;
645 } else {
646 arg_ptr[arg_len++] = '\\';
647 arg_ptr[arg_len++] = (c >> 6) + '0';
648 arg_ptr[arg_len++]
649 = ((c >> 3) & 7) + '0';
650 arg_ptr[arg_len++] = (c & 7) + '0';
651 }
652 } else {
653 arg_ptr[arg_len] = '\0';
654 }
655 if (c)
656 continue;
657 if (ccs_env_perm(r, arg_ptr)) {
658 error = -EPERM;
659 break;
660 }
661 if (!--envp_count) {
662 error = 0;
663 break;
664 }
665 arg_len = 0;
666 }
667 offset = 0;
668 }
669 out:
670 if (r->mode != 3)
671 error = 0;
672 kfree(env_page.data);
673 kfree(arg_ptr);
674 return error;
675 }
676
677 /**
678 * ccs_unescape - Unescape escaped string.
679 *
680 * @dest: String to unescape.
681 *
682 * Returns nothing.
683 */
684 static void ccs_unescape(unsigned char *dest)
685 {
686 unsigned char *src = dest;
687 unsigned char c;
688 unsigned char d;
689 unsigned char e;
690 while (1) {
691 c = *src++;
692 if (!c)
693 break;
694 if (c != '\\') {
695 *dest++ = c;
696 continue;
697 }
698 c = *src++;
699 if (c == '\\') {
700 *dest++ = c;
701 continue;
702 }
703 if (c < '0' || c > '3')
704 break;
705 d = *src++;
706 if (d < '0' || d > '7')
707 break;
708 e = *src++;
709 if (e < '0' || e > '7')
710 break;
711 *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0');
712 }
713 *dest = '\0';
714 }
715
716 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
717 static int ccs_copy_argv(const char *arg, struct linux_binprm *bprm)
718 {
719 const int ret = copy_strings_kernel(1, &arg, bprm);
720 if (ret >= 0)
721 bprm->argc++;
722 return ret;
723 }
724 #else
725 static int ccs_copy_argv(char *arg, struct linux_binprm *bprm)
726 {
727 const int ret = copy_strings_kernel(1, &arg, bprm);
728 if (ret >= 0)
729 bprm->argc++;
730 return ret;
731 }
732 #endif
733
734 /**
735 * ccs_try_alt_exec - Try to start execute handler.
736 *
737 * @ee: Pointer to "struct ccs_execve".
738 *
739 * Returns 0 on success, negative value otherwise.
740 */
741 static int ccs_try_alt_exec(struct ccs_execve *ee)
742 {
743 /*
744 * Contents of modified bprm.
745 * The envp[] in original bprm is moved to argv[] so that
746 * the alternatively executed program won't be affected by
747 * some dangerous environment variables like LD_PRELOAD.
748 *
749 * modified bprm->argc
750 * = original bprm->argc + original bprm->envc + 7
751 * modified bprm->envc
752 * = 0
753 *
754 * modified bprm->argv[0]
755 * = the program's name specified by *_execute_handler
756 * modified bprm->argv[1]
757 * = ccs_current_domain()->domainname->name
758 * modified bprm->argv[2]
759 * = the current process's name
760 * modified bprm->argv[3]
761 * = the current process's information (e.g. uid/gid).
762 * modified bprm->argv[4]
763 * = original bprm->filename
764 * modified bprm->argv[5]
765 * = original bprm->argc in string expression
766 * modified bprm->argv[6]
767 * = original bprm->envc in string expression
768 * modified bprm->argv[7]
769 * = original bprm->argv[0]
770 * ...
771 * modified bprm->argv[bprm->argc + 6]
772 * = original bprm->argv[bprm->argc - 1]
773 * modified bprm->argv[bprm->argc + 7]
774 * = original bprm->envp[0]
775 * ...
776 * modified bprm->argv[bprm->envc + bprm->argc + 6]
777 * = original bprm->envp[bprm->envc - 1]
778 */
779 struct linux_binprm *bprm = ee->bprm;
780 struct file *filp;
781 int retval;
782 const int original_argc = bprm->argc;
783 const int original_envc = bprm->envc;
784 struct task_struct *task = current;
785
786 /* Close the requested program's dentry. */
787 ee->obj.path1.dentry = NULL;
788 ee->obj.path1.mnt = NULL;
789 ee->obj.validate_done = false;
790 allow_write_access(bprm->file);
791 fput(bprm->file);
792 bprm->file = NULL;
793
794 /* Invalidate page dump cache. */
795 ee->dump.page = NULL;
796
797 /* Move envp[] to argv[] */
798 bprm->argc += bprm->envc;
799 bprm->envc = 0;
800
801 /* Set argv[6] */
802 {
803 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_envc);
804 retval = ccs_copy_argv(ee->tmp, bprm);
805 if (retval < 0)
806 goto out;
807 }
808
809 /* Set argv[5] */
810 {
811 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_argc);
812 retval = ccs_copy_argv(ee->tmp, bprm);
813 if (retval < 0)
814 goto out;
815 }
816
817 /* Set argv[4] */
818 {
819 retval = ccs_copy_argv(bprm->filename, bprm);
820 if (retval < 0)
821 goto out;
822 }
823
824 /* Set argv[3] */
825 {
826 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1,
827 "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d "
828 "sgid=%d fsuid=%d fsgid=%d",
829 (pid_t) ccsecurity_exports.sys_getpid(),
830 current_uid(), current_gid(), current_euid(),
831 current_egid(), current_suid(), current_sgid(),
832 current_fsuid(), current_fsgid());
833 retval = ccs_copy_argv(ee->tmp, bprm);
834 if (retval < 0)
835 goto out;
836 }
837
838 /* Set argv[2] */
839 {
840 char *exe = (char *) ccs_get_exe();
841 if (exe) {
842 retval = ccs_copy_argv(exe, bprm);
843 kfree(exe);
844 } else {
845 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
846 retval = ccs_copy_argv("<unknown>", bprm);
847 #else
848 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "<unknown>");
849 retval = ccs_copy_argv(ee->tmp, bprm);
850 #endif
851 }
852 if (retval < 0)
853 goto out;
854 }
855
856 /* Set argv[1] */
857 {
858 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
859 retval = ccs_copy_argv(ccs_current_domain()->domainname->name,
860 bprm);
861 #else
862 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s",
863 ccs_current_domain()->domainname->name);
864 retval = ccs_copy_argv(ee->tmp, bprm);
865 #endif
866 if (retval < 0)
867 goto out;
868 }
869
870 /* Set argv[0] */
871 {
872 struct path root;
873 char *cp;
874 int root_len;
875 int handler_len;
876 get_fs_root(current->fs, &root);
877 cp = ccs_realpath_from_path(&root);
878 path_put(&root);
879 if (!cp) {
880 retval = -ENOMEM;
881 goto out;
882 }
883 root_len = strlen(cp);
884 retval = strncmp(ee->handler->name, cp, root_len);
885 root_len--;
886 kfree(cp);
887 if (retval) {
888 retval = -ENOENT;
889 goto out;
890 }
891 handler_len = ee->handler->total_len + 1;
892 cp = kmalloc(handler_len, CCS_GFP_FLAGS);
893 if (!cp) {
894 retval = -ENOMEM;
895 goto out;
896 }
897 ee->handler_path = cp;
898 /* Adjust root directory for open_exec(). */
899 memmove(cp, ee->handler->name + root_len,
900 handler_len - root_len);
901 ccs_unescape(cp);
902 retval = -ENOENT;
903 if (!*cp || *cp != '/')
904 goto out;
905 retval = ccs_copy_argv(cp, bprm);
906 if (retval < 0)
907 goto out;
908 }
909 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
910 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24)
911 bprm->argv_len = bprm->exec - bprm->p;
912 #endif
913 #endif
914
915 /*
916 * OK, now restart the process with execute handler program's dentry.
917 */
918 filp = open_exec(ee->handler_path);
919 if (IS_ERR(filp)) {
920 retval = PTR_ERR(filp);
921 goto out;
922 }
923 ee->obj.path1.dentry = filp->f_dentry;
924 ee->obj.path1.mnt = filp->f_vfsmnt;
925 bprm->file = filp;
926 bprm->filename = ee->handler_path;
927 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
928 bprm->interp = bprm->filename;
929 #endif
930 retval = prepare_binprm(bprm);
931 if (retval < 0)
932 goto out;
933 task->ccs_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
934 retval = ccs_find_next_domain(ee);
935 task->ccs_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
936 out:
937 return retval;
938 }
939
940 /**
941 * ccs_find_execute_handler - Find an execute handler.
942 *
943 * @ee: Pointer to "struct ccs_execve".
944 * @type: Type of execute handler.
945 *
946 * Returns true if found, false otherwise.
947 *
948 * Caller holds ccs_read_lock().
949 */
950 static bool ccs_find_execute_handler(struct ccs_execve *ee, const u8 type)
951 {
952 struct ccs_request_info *r = &ee->r;
953 /*
954 * To avoid infinite execute handler loop, don't use execute handler
955 * if the current process is marked as execute handler .
956 */
957 if (current->ccs_flags & CCS_TASK_IS_EXECUTE_HANDLER)
958 return false;
959 r->param_type = type;
960 ccs_check_acl(r, NULL);
961 if (!r->granted)
962 return false;
963 ee->handler = container_of(r->matched_acl, struct ccs_handler_acl,
964 head)->handler;
965 return true;
966 }
967
968 #ifdef CONFIG_MMU
969 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
970 #define CCS_BPRM_MMU
971 #elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 3
972 #define CCS_BPRM_MMU
973 #elif defined(AX_MAJOR) && AX_MAJOR == 3 && defined(AX_MINOR) && AX_MINOR >= 2
974 #define CCS_BPRM_MMU
975 #endif
976 #endif
977
978 /**
979 * ccs_dump_page - Dump a page to buffer.
980 *
981 * @bprm: Pointer to "struct linux_binprm".
982 * @pos: Location to dump.
983 * @dump: Poiner to "struct ccs_page_dump".
984 *
985 * Returns true on success, false otherwise.
986 */
987 bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos,
988 struct ccs_page_dump *dump)
989 {
990 struct page *page;
991 /* dump->data is released by ccs_finish_execve(). */
992 if (!dump->data) {
993 dump->data = kzalloc(PAGE_SIZE, CCS_GFP_FLAGS);
994 if (!dump->data)
995 return false;
996 }
997 /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
998 #ifdef CCS_BPRM_MMU
999 if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
1000 return false;
1001 #else
1002 page = bprm->page[pos / PAGE_SIZE];
1003 #endif
1004 if (page != dump->page) {
1005 const unsigned int offset = pos % PAGE_SIZE;
1006 /*
1007 * Maybe kmap()/kunmap() should be used here.
1008 * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
1009 * So do I.
1010 */
1011 char *kaddr = kmap_atomic(page, KM_USER0);
1012 dump->page = page;
1013 memcpy(dump->data + offset, kaddr + offset,
1014 PAGE_SIZE - offset);
1015 kunmap_atomic(kaddr, KM_USER0);
1016 }
1017 /* Same with put_arg_page(page) in fs/exec.c */
1018 #ifdef CCS_BPRM_MMU
1019 put_page(page);
1020 #endif
1021 return true;
1022 }
1023
1024 /**
1025 * ccs_start_execve - Prepare for execve() operation.
1026 *
1027 * @bprm: Pointer to "struct linux_binprm".
1028 * @eep: Pointer to "struct ccs_execve *".
1029 *
1030 * Returns 0 on success, negative value otherwise.
1031 */
1032 static int ccs_start_execve(struct linux_binprm *bprm,
1033 struct ccs_execve **eep)
1034 {
1035 int retval;
1036 struct task_struct *task = current;
1037 struct ccs_execve *ee;
1038 *eep = NULL;
1039 ee = kzalloc(sizeof(*ee), CCS_GFP_FLAGS);
1040 if (!ee)
1041 return -ENOMEM;
1042 ee->tmp = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS);
1043 if (!ee->tmp) {
1044 kfree(ee);
1045 return -ENOMEM;
1046 }
1047 ee->reader_idx = ccs_read_lock();
1048 /* ee->dump->data is allocated by ccs_dump_page(). */
1049 ee->previous_domain = task->ccs_domain_info;
1050 /* Clear manager flag. */
1051 task->ccs_flags &= ~CCS_TASK_IS_MANAGER;
1052 *eep = ee;
1053 ccs_init_request_info(&ee->r, CCS_MAC_FILE_EXECUTE);
1054 ee->r.ee = ee;
1055 ee->bprm = bprm;
1056 ee->r.obj = &ee->obj;
1057 ee->obj.path1.dentry = bprm->file->f_dentry;
1058 ee->obj.path1.mnt = bprm->file->f_vfsmnt;
1059 /*
1060 * No need to call ccs_environ() for execute handler because envp[] is
1061 * moved to argv[].
1062 */
1063 if (ccs_find_execute_handler(ee, CCS_TYPE_AUTO_EXECUTE_HANDLER))
1064 return ccs_try_alt_exec(ee);
1065 retval = ccs_find_next_domain(ee);
1066 if (retval == -EPERM) {
1067 if (ccs_find_execute_handler(ee,
1068 CCS_TYPE_DENIED_EXECUTE_HANDLER))
1069 return ccs_try_alt_exec(ee);
1070 }
1071 if (!retval)
1072 retval = ccs_environ(ee);
1073 return retval;
1074 }
1075
1076 /**
1077 * ccs_finish_execve - Clean up execve() operation.
1078 *
1079 * @retval: Return code of an execve() operation.
1080 * @ee: Pointer to "struct ccs_execve".
1081 *
1082 * Caller holds ccs_read_lock().
1083 */
1084 static void ccs_finish_execve(int retval, struct ccs_execve *ee)
1085 {
1086 struct task_struct *task = current;
1087 if (!ee)
1088 return;
1089 if (retval < 0) {
1090 task->ccs_domain_info = ee->previous_domain;
1091 /*
1092 * Make task->ccs_domain_info visible to GC before changing
1093 * task->ccs_flags .
1094 */
1095 smp_mb();
1096 } else {
1097 /* Mark the current process as execute handler. */
1098 if (ee->handler)
1099 task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER;
1100 /* Mark the current process as normal process. */
1101 else
1102 task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER;
1103 }
1104 /* Tell GC that I finished execve(). */
1105 task->ccs_flags &= ~CCS_TASK_IS_IN_EXECVE;
1106 ccs_read_unlock(ee->reader_idx);
1107 kfree(ee->handler_path);
1108 kfree(ee->tmp);
1109 kfree(ee->dump.data);
1110 kfree(ee);
1111 }
1112
1113 static int __ccs_search_binary_handler(struct linux_binprm *bprm,
1114 struct pt_regs *regs)
1115 {
1116 struct ccs_execve *ee;
1117 int retval;
1118 if (!ccs_policy_loaded)
1119 ccsecurity_exports.load_policy(bprm->filename);
1120 retval = ccs_start_execve(bprm, &ee);
1121 if (!retval)
1122 retval = search_binary_handler(bprm, regs);
1123 ccs_finish_execve(retval, ee);
1124 return retval;
1125 }
1126
1127 void __init ccs_domain_init(void)
1128 {
1129 ccsecurity_ops.search_binary_handler = __ccs_search_binary_handler;
1130 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26