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

Subversion リポジトリの参照

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

Parent Directory Parent Directory | Revision Log Revision Log


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

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