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

Subversion リポジトリの参照

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 722 - (show annotations) (download) (as text)
Thu Nov 22 04:09:54 2007 UTC (16 years, 6 months ago) by kumaneko
Original Path: trunk/1.5.x/ccs-patch/fs/tomoyo_domain.c
File MIME type: text/x-csrc
File size: 28490 byte(s)
Use struct list1_head
1 /*
2 * fs/tomoyo_domain.c
3 *
4 * Implementation of the Domain-Based Mandatory Access Control.
5 *
6 * Copyright (C) 2005-2007 NTT DATA CORPORATION
7 *
8 * Version: 1.5.2-pre 2007/11/19
9 *
10 * This file is applicable to both 2.4.30 and 2.6.11 and later.
11 * See README.ccs for ChangeLog.
12 *
13 */
14 /***** TOMOYO Linux start. *****/
15
16 #include <linux/ccs_common.h>
17 #include <linux/tomoyo.h>
18 #include <linux/realpath.h>
19 #include <linux/highmem.h>
20 #include <linux/binfmts.h>
21
22 #ifndef for_each_process
23 #define for_each_process for_each_task
24 #endif
25
26 /************************* VARIABLES *************************/
27
28 /* The initial domain. */
29 struct domain_info KERNEL_DOMAIN;
30
31 /* List of domains. */
32 LIST1_HEAD(domain_list);
33
34 /* /sbin/init started? */
35 extern int sbin_init_started;
36
37 #ifdef CONFIG_TOMOYO
38
39 /* Lock for appending domain's ACL. */
40 DEFINE_MUTEX(domain_acl_lock);
41
42 /************************* UTILITY FUNCTIONS *************************/
43
44 /***** The structure for program files to force domain reconstruction. *****/
45
46 struct domain_initializer_entry {
47 struct list1_head list;
48 const struct path_info *domainname; /* This may be NULL */
49 const struct path_info *program;
50 bool is_deleted;
51 bool is_not;
52 bool is_last_name;
53 };
54
55 /***** The structure for domains to not to transit domains. *****/
56
57 struct domain_keeper_entry {
58 struct list1_head list;
59 const struct path_info *domainname;
60 const struct path_info *program; /* This may be NULL */
61 bool is_deleted;
62 bool is_not;
63 bool is_last_name;
64 };
65
66 /***** The structure for program files that should be aggregated. *****/
67
68 struct aggregator_entry {
69 struct list1_head list;
70 const struct path_info *original_name;
71 const struct path_info *aggregated_name;
72 bool is_deleted;
73 };
74
75 /***** The structure for program files that should be aliased. *****/
76
77 struct alias_entry {
78 struct list1_head list;
79 const struct path_info *original_name;
80 const struct path_info *aliased_name;
81 bool is_deleted;
82 };
83
84 /************************* VARIABLES *************************/
85
86 /* Domain creation lock. */
87 static DEFINE_MUTEX(new_domain_assign_lock);
88
89 /************************* UTILITY FUNCTIONS *************************/
90
91 const char *GetLastName(const struct domain_info *domain)
92 {
93 const char *cp0 = domain->domainname->name, *cp1;
94 if ((cp1 = strrchr(cp0, ' ')) != NULL) return cp1 + 1;
95 return cp0;
96 }
97
98 int AddDomainACL(struct domain_info *domain, struct acl_info *acl)
99 {
100 list1_add_tail_mb(&acl->list, &domain->acl_info_list);
101 UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);
102 return 0;
103 }
104
105 int DelDomainACL(struct acl_info *ptr)
106 {
107 ptr->is_deleted = 1;
108 UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);
109 return 0;
110 }
111
112 /************************* DOMAIN INITIALIZER HANDLER *************************/
113
114 static LIST1_HEAD(domain_initializer_list);
115
116 static int AddDomainInitializerEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete)
117 {
118 struct domain_initializer_entry *new_entry, *ptr;
119 static DEFINE_MUTEX(lock);
120 const struct path_info *saved_program, *saved_domainname = NULL;
121 int error = -ENOMEM;
122 bool is_last_name = 0;
123 if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */
124 if (domainname) {
125 if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) {
126 is_last_name = 1;
127 } else if (!IsCorrectDomain(domainname, __FUNCTION__)) {
128 return -EINVAL;
129 }
130 if ((saved_domainname = SaveName(domainname)) == NULL) return -ENOMEM;
131 }
132 if ((saved_program = SaveName(program)) == NULL) return -ENOMEM;
133 mutex_lock(&lock);
134 list1_for_each_entry(ptr, &domain_initializer_list, list) {
135 if (ptr->is_not == is_not && ptr->domainname == saved_domainname && ptr->program == saved_program) {
136 ptr->is_deleted = is_delete;
137 error = 0;
138 goto out;
139 }
140 }
141 if (is_delete) {
142 error = -ENOENT;
143 goto out;
144 }
145 if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;
146 new_entry->domainname = saved_domainname;
147 new_entry->program = saved_program;
148 new_entry->is_not = is_not;
149 new_entry->is_last_name = is_last_name;
150 list1_add_tail_mb(&new_entry->list, &domain_initializer_list);
151 error = 0;
152 out:
153 mutex_unlock(&lock);
154 return error;
155 }
156
157 int ReadDomainInitializerPolicy(struct io_buffer *head)
158 {
159 struct list1_head *pos;
160 list1_for_each_cookie(pos, head->read_var2, &domain_initializer_list) {
161 struct domain_initializer_entry *ptr;
162 ptr = list1_entry(pos, struct domain_initializer_entry, list);
163 if (ptr->is_deleted) continue;
164 if (ptr->domainname) {
165 if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s from %s\n", ptr->is_not ? "no_" : "", ptr->program->name, ptr->domainname->name)) return -ENOMEM;
166 } else {
167 if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s\n", ptr->is_not ? "no_" : "", ptr->program->name)) return -ENOMEM;
168 }
169 }
170 return 0;
171 }
172
173 int AddDomainInitializerPolicy(char *data, const bool is_not, const bool is_delete)
174 {
175 char *cp = strstr(data, " from ");
176 if (cp) {
177 *cp = '\0';
178 return AddDomainInitializerEntry(cp + 6, data, is_not, is_delete);
179 } else {
180 return AddDomainInitializerEntry(NULL, data, is_not, is_delete);
181 }
182 }
183
184 static bool IsDomainInitializer(const struct path_info *domainname, const struct path_info *program, const struct path_info *last_name)
185 {
186 struct domain_initializer_entry *ptr;
187 bool flag = 0;
188 list1_for_each_entry(ptr, &domain_initializer_list, list) {
189 if (ptr->is_deleted) continue;
190 if (ptr->domainname) {
191 if (!ptr->is_last_name) {
192 if (ptr->domainname != domainname) continue;
193 } else {
194 if (pathcmp(ptr->domainname, last_name)) continue;
195 }
196 }
197 if (pathcmp(ptr->program, program)) continue;
198 if (ptr->is_not) return 0;
199 flag = 1;
200 }
201 return flag;
202 }
203
204 /************************* DOMAIN KEEPER HANDLER *************************/
205
206 static LIST1_HEAD(domain_keeper_list);
207
208 static int AddDomainKeeperEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete)
209 {
210 struct domain_keeper_entry *new_entry, *ptr;
211 const struct path_info *saved_domainname, *saved_program = NULL;
212 static DEFINE_MUTEX(lock);
213 int error = -ENOMEM;
214 bool is_last_name = 0;
215 if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) {
216 is_last_name = 1;
217 } else if (!IsCorrectDomain(domainname, __FUNCTION__)) {
218 return -EINVAL;
219 }
220 if (program) {
221 if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL;
222 if ((saved_program = SaveName(program)) == NULL) return -ENOMEM;
223 }
224 if ((saved_domainname = SaveName(domainname)) == NULL) return -ENOMEM;
225 mutex_lock(&lock);
226 list1_for_each_entry(ptr, &domain_keeper_list, list) {
227 if (ptr->is_not == is_not && ptr->domainname == saved_domainname && ptr->program == saved_program) {
228 ptr->is_deleted = is_delete;
229 error = 0;
230 goto out;
231 }
232 }
233 if (is_delete) {
234 error = -ENOENT;
235 goto out;
236 }
237 if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;
238 new_entry->domainname = saved_domainname;
239 new_entry->program = saved_program;
240 new_entry->is_not = is_not;
241 new_entry->is_last_name = is_last_name;
242 list1_add_tail_mb(&new_entry->list, &domain_keeper_list);
243 error = 0;
244 out:
245 mutex_unlock(&lock);
246 return error;
247 }
248
249 int AddDomainKeeperPolicy(char *data, const bool is_not, const bool is_delete)
250 {
251 char *cp = strstr(data, " from ");
252 if (cp) {
253 *cp = '\0';
254 return AddDomainKeeperEntry(cp + 6, data, is_not, is_delete);
255 } else {
256 return AddDomainKeeperEntry(data, NULL, is_not, is_delete);
257 }
258 }
259
260 int ReadDomainKeeperPolicy(struct io_buffer *head)
261 {
262 struct list1_head *pos;
263 list1_for_each_cookie(pos, head->read_var2, &domain_keeper_list) {
264 struct domain_keeper_entry *ptr;
265 ptr = list1_entry(pos, struct domain_keeper_entry, list);
266 if (ptr->is_deleted) continue;
267 if (ptr->program) {
268 if (io_printf(head, "%s" KEYWORD_KEEP_DOMAIN "%s from %s\n", ptr->is_not ? "no_" : "", ptr->program->name, ptr->domainname->name)) return -ENOMEM;
269 } else {
270 if (io_printf(head, "%s" KEYWORD_KEEP_DOMAIN "%s\n", ptr->is_not ? "no_" : "", ptr->domainname->name)) return -ENOMEM;
271 }
272 }
273 return 0;
274 }
275
276 static bool IsDomainKeeper(const struct path_info *domainname, const struct path_info *program, const struct path_info *last_name)
277 {
278 struct domain_keeper_entry *ptr;
279 bool flag = 0;
280 list1_for_each_entry(ptr, &domain_keeper_list, list) {
281 if (ptr->is_deleted) continue;
282 if (!ptr->is_last_name) {
283 if (ptr->domainname != domainname) continue;
284 } else {
285 if (pathcmp(ptr->domainname, last_name)) continue;
286 }
287 if (ptr->program && pathcmp(ptr->program, program)) continue;
288 if (ptr->is_not) return 0;
289 flag = 1;
290 }
291 return flag;
292 }
293
294 /************************* SYMBOLIC LINKED PROGRAM HANDLER *************************/
295
296 static LIST1_HEAD(alias_list);
297
298 static int AddAliasEntry(const char *original_name, const char *aliased_name, const bool is_delete)
299 {
300 struct alias_entry *new_entry, *ptr;
301 static DEFINE_MUTEX(lock);
302 const struct path_info *saved_original_name, *saved_aliased_name;
303 int error = -ENOMEM;
304 if (!IsCorrectPath(original_name, 1, -1, -1, __FUNCTION__) || !IsCorrectPath(aliased_name, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */
305 if ((saved_original_name = SaveName(original_name)) == NULL || (saved_aliased_name = SaveName(aliased_name)) == NULL) return -ENOMEM;
306 mutex_lock(&lock);
307 list1_for_each_entry(ptr, &alias_list, list) {
308 if (ptr->original_name == saved_original_name && ptr->aliased_name == saved_aliased_name) {
309 ptr->is_deleted = is_delete;
310 error = 0;
311 goto out;
312 }
313 }
314 if (is_delete) {
315 error = -ENOENT;
316 goto out;
317 }
318 if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;
319 new_entry->original_name = saved_original_name;
320 new_entry->aliased_name = saved_aliased_name;
321 list1_add_tail_mb(&new_entry->list, &alias_list);
322 error = 0;
323 out:
324 mutex_unlock(&lock);
325 return error;
326 }
327
328 int ReadAliasPolicy(struct io_buffer *head)
329 {
330 struct list1_head *pos;
331 list1_for_each_cookie(pos, head->read_var2, &alias_list) {
332 struct alias_entry *ptr;
333 ptr = list1_entry(pos, struct alias_entry, list);
334 if (ptr->is_deleted) continue;
335 if (io_printf(head, KEYWORD_ALIAS "%s %s\n", ptr->original_name->name, ptr->aliased_name->name)) return -ENOMEM;
336 }
337 return 0;
338 }
339
340 int AddAliasPolicy(char *data, const bool is_delete)
341 {
342 char *cp = strchr(data, ' ');
343 if (!cp) return -EINVAL;
344 *cp++ = '\0';
345 return AddAliasEntry(data, cp, is_delete);
346 }
347
348 /************************* DOMAIN AGGREGATOR HANDLER *************************/
349
350 static LIST1_HEAD(aggregator_list);
351
352 static int AddAggregatorEntry(const char *original_name, const char *aggregated_name, const bool is_delete)
353 {
354 struct aggregator_entry *new_entry, *ptr;
355 static DEFINE_MUTEX(lock);
356 const struct path_info *saved_original_name, *saved_aggregated_name;
357 int error = -ENOMEM;
358 if (!IsCorrectPath(original_name, 1, 0, -1, __FUNCTION__) || !IsCorrectPath(aggregated_name, 1, -1, -1, __FUNCTION__)) return -EINVAL;
359 if ((saved_original_name = SaveName(original_name)) == NULL || (saved_aggregated_name = SaveName(aggregated_name)) == NULL) return -ENOMEM;
360 mutex_lock(&lock);
361 list1_for_each_entry(ptr, &aggregator_list, list) {
362 if (ptr->original_name == saved_original_name && ptr->aggregated_name == saved_aggregated_name) {
363 ptr->is_deleted = is_delete;
364 error = 0;
365 goto out;
366 }
367 }
368 if (is_delete) {
369 error = -ENOENT;
370 goto out;
371 }
372 if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;
373 new_entry->original_name = saved_original_name;
374 new_entry->aggregated_name = saved_aggregated_name;
375 list1_add_tail_mb(&new_entry->list, &aggregator_list);
376 error = 0;
377 out:
378 mutex_unlock(&lock);
379 return error;
380 }
381
382 int ReadAggregatorPolicy(struct io_buffer *head)
383 {
384 struct list1_head *pos;
385 list1_for_each_cookie(pos, head->read_var2, &aggregator_list) {
386 struct aggregator_entry *ptr;
387 ptr = list1_entry(pos, struct aggregator_entry, list);
388 if (ptr->is_deleted) continue;
389 if (io_printf(head, KEYWORD_AGGREGATOR "%s %s\n", ptr->original_name->name, ptr->aggregated_name->name)) return -ENOMEM;
390 }
391 return 0;
392 }
393
394 int AddAggregatorPolicy(char *data, const bool is_delete)
395 {
396 char *cp = strchr(data, ' ');
397 if (!cp) return -EINVAL;
398 *cp++ = '\0';
399 return AddAggregatorEntry(data, cp, is_delete);
400 }
401
402 /************************* DOMAIN DELETION HANDLER *************************/
403
404 /* #define DEBUG_DOMAIN_UNDELETE */
405
406 int DeleteDomain(char *domainname0)
407 {
408 struct domain_info *domain;
409 struct path_info domainname;
410 domainname.name = domainname0;
411 fill_path_info(&domainname);
412 mutex_lock(&new_domain_assign_lock);
413 #ifdef DEBUG_DOMAIN_UNDELETE
414 printk("DeleteDomain %s\n", domainname0);
415 list1_for_each_entry(domain, &domain_list, list) {
416 if (pathcmp(domain->domainname, &domainname)) continue;
417 printk("List: %p %u\n", domain, domain->is_deleted);
418 }
419 #endif
420 /* Is there an active domain? */
421 list1_for_each_entry(domain, &domain_list, list) {
422 struct domain_info *domain2;
423 /* Never delete KERNEL_DOMAIN */
424 if (domain == &KERNEL_DOMAIN || domain->is_deleted || pathcmp(domain->domainname, &domainname)) continue;
425 /* Mark already deleted domains as non undeletable. */
426 list1_for_each_entry(domain2, &domain_list, list) {
427 if (!domain2->is_deleted || pathcmp(domain2->domainname, &domainname)) continue;
428 #ifdef DEBUG_DOMAIN_UNDELETE
429 if (domain2->is_deleted != 255) printk("Marked %p as non undeletable\n", domain2);
430 #endif
431 domain2->is_deleted = 255;
432 }
433 /* Delete and mark active domain as undeletable. */
434 domain->is_deleted = 1;
435 #ifdef DEBUG_DOMAIN_UNDELETE
436 printk("Marked %p as undeletable\n", domain);
437 #endif
438 break;
439 }
440 mutex_unlock(&new_domain_assign_lock);
441 return 0;
442 }
443
444 struct domain_info *UndeleteDomain(const char *domainname0)
445 {
446 struct domain_info *domain, *candidate_domain = NULL;
447 struct path_info domainname;
448 domainname.name = domainname0;
449 fill_path_info(&domainname);
450 mutex_lock(&new_domain_assign_lock);
451 #ifdef DEBUG_DOMAIN_UNDELETE
452 printk("UndeleteDomain %s\n", domainname0);
453 list1_for_each_entry(domain, &domain_list, list) {
454 if (pathcmp(domain->domainname, &domainname)) continue;
455 printk("List: %p %u\n", domain, domain->is_deleted);
456 }
457 #endif
458 list1_for_each_entry(domain, &domain_list, list) {
459 if (pathcmp(&domainname, domain->domainname)) continue;
460 if (!domain->is_deleted) {
461 /* This domain is active. I can't undelete. */
462 candidate_domain = NULL;
463 #ifdef DEBUG_DOMAIN_UNDELETE
464 printk("%p is active. I can't undelete.\n", domain);
465 #endif
466 break;
467 }
468 /* Is this domain undeletable? */
469 if (domain->is_deleted == 1) candidate_domain = domain;
470 }
471 if (candidate_domain) {
472 candidate_domain->is_deleted = 0;
473 #ifdef DEBUG_DOMAIN_UNDELETE
474 printk("%p was undeleted.\n", candidate_domain);
475 #endif
476 }
477 mutex_unlock(&new_domain_assign_lock);
478 return candidate_domain;
479 }
480
481 /************************* DOMAIN TRANSITION HANDLER *************************/
482
483 struct domain_info *FindOrAssignNewDomain(const char *domainname, const u8 profile)
484 {
485 struct domain_info *domain = NULL;
486 const struct path_info *saved_domainname;
487 mutex_lock(&new_domain_assign_lock);
488 if ((domain = FindDomain(domainname)) != NULL) goto out;
489 if (!IsCorrectDomain(domainname, __FUNCTION__)) goto out;
490 if ((saved_domainname = SaveName(domainname)) == NULL) goto out;
491 /* Can I reuse memory of deleted domain? */
492 list1_for_each_entry(domain, &domain_list, list) {
493 struct task_struct *p;
494 struct acl_info *ptr;
495 int flag;
496 if (!domain->is_deleted || domain->domainname != saved_domainname) continue;
497 flag = 0;
498 /***** CRITICAL SECTION START *****/
499 read_lock(&tasklist_lock);
500 for_each_process(p) {
501 if (p->domain_info == domain) { flag = 1; break; }
502 }
503 read_unlock(&tasklist_lock);
504 /***** CRITICAL SECTION END *****/
505 if (flag) continue;
506 #ifdef DEBUG_DOMAIN_UNDELETE
507 printk("Reusing %p %s\n", domain, domain->domainname->name);
508 #endif
509 list1_for_each_entry(ptr, &domain->acl_info_list, list) ptr->is_deleted = 1;
510 domain->profile = profile;
511 domain->quota_warned = 0;
512 mb(); /* Avoid out-of-order execution. */
513 domain->is_deleted = 0;
514 goto out;
515 }
516 /* No memory reusable. Create using new memory. */
517 if ((domain = alloc_element(sizeof(*domain))) != NULL) {
518 INIT_LIST1_HEAD(&domain->acl_info_list);
519 domain->domainname = saved_domainname;
520 domain->profile = profile;
521 list1_add_tail_mb(&domain->list, &domain_list);
522 }
523 out: ;
524 mutex_unlock(&new_domain_assign_lock);
525 return domain;
526 }
527
528 static int Escape(char *dest, const char *src, int dest_len)
529 {
530 while (*src) {
531 const unsigned char c = * (const unsigned char *) src;
532 if (c == '\\') {
533 dest_len -= 2;
534 if (dest_len <= 0) goto out;
535 *dest++ = '\\';
536 *dest++ = '\\';
537 } else if (c > ' ' && c < 127) {
538 if (--dest_len <= 0) goto out;
539 *dest++ = c;
540 } else {
541 dest_len -= 4;
542 if (dest_len <= 0) goto out;
543 *dest++ = '\\';
544 *dest++ = (c >> 6) + '0';
545 *dest++ = ((c >> 3) & 7) + '0';
546 *dest++ = (c & 7) + '0';
547 }
548 src++;
549 }
550 if (--dest_len <= 0) goto out;
551 *dest = '\0';
552 return 0;
553 out:
554 return -ENOMEM;
555 }
556
557 static char *get_argv0(struct linux_binprm *bprm)
558 {
559 char *arg_ptr = ccs_alloc(PAGE_SIZE); /* Initial buffer. */
560 int arg_len = 0;
561 unsigned long pos = bprm->p;
562 int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;
563 if (!bprm->argc || !arg_ptr) goto out;
564 while (1) {
565 struct page *page;
566 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
567 if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) goto out;
568 #else
569 page = bprm->page[i];
570 #endif
571 { /* Map and copy to kernel buffer and unmap. */
572 const char *kaddr = kmap(page);
573 if (!kaddr) { /* Mapping failed. */
574 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
575 put_page(page);
576 #endif
577 goto out;
578 }
579 memmove(arg_ptr + arg_len, kaddr + offset, PAGE_SIZE - offset);
580 kunmap(page);
581 }
582 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
583 put_page(page);
584 pos += PAGE_SIZE - offset;
585 #endif
586 arg_len += PAGE_SIZE - offset;
587 if (memchr(arg_ptr, '\0', arg_len)) break;
588 { /* Initial buffer was too small for argv[0]. Retry after expanding buffer. */
589 char *tmp_arg_ptr = ccs_alloc(arg_len + PAGE_SIZE);
590 if (!tmp_arg_ptr) goto out;
591 memmove(tmp_arg_ptr, arg_ptr, arg_len);
592 ccs_free(arg_ptr);
593 arg_ptr = tmp_arg_ptr;
594 }
595 i++;
596 offset = 0;
597 }
598 return arg_ptr;
599 out: /* Release initial buffer. */
600 ccs_free(arg_ptr);
601 return NULL;
602 }
603
604 static int FindNextDomain(struct linux_binprm *bprm, struct domain_info **next_domain, const u8 do_perm_check)
605 {
606 /* This function assumes that the size of buffer returned by realpath() = CCS_MAX_PATHNAME_LEN. */
607 struct domain_info *old_domain = current->domain_info, *domain = NULL;
608 const char *old_domain_name = old_domain->domainname->name;
609 const char *original_name = bprm->filename;
610 struct file *filp = bprm->file;
611 char *new_domain_name = NULL;
612 char *real_program_name = NULL, *symlink_program_name = NULL;
613 const bool is_enforce = CheckCCSEnforce(CCS_TOMOYO_MAC_FOR_FILE);
614 int retval;
615 struct path_info r, s, l;
616
617 {
618 /*
619 * Built-in initializers. This is needed because policies are not loaded until starting /sbin/init .
620 */
621 static int first = 1;
622 if (first) {
623 AddDomainInitializerEntry(NULL, "/sbin/hotplug", 0, 0);
624 AddDomainInitializerEntry(NULL, "/sbin/modprobe", 0, 0);
625 first = 0;
626 }
627 }
628
629 /* Get realpath of program. */
630 retval = -ENOENT; /* I hope realpath() won't fail with -ENOMEM. */
631 if ((real_program_name = realpath(original_name)) == NULL) goto out;
632 /* Get realpath of symbolic link. */
633 if ((symlink_program_name = realpath_nofollow(original_name)) == NULL) goto out;
634
635 r.name = real_program_name;
636 fill_path_info(&r);
637 s.name = symlink_program_name;
638 fill_path_info(&s);
639 if ((l.name = strrchr(old_domain_name, ' ')) != NULL) l.name++;
640 else l.name = old_domain_name;
641 fill_path_info(&l);
642
643 if (!do_perm_check) goto ok;
644
645 /* Check 'alias' directive. */
646 if (pathcmp(&r, &s)) {
647 struct alias_entry *ptr;
648 /* Is this program allowed to be called via symbolic links? */
649 list1_for_each_entry(ptr, &alias_list, list) {
650 if (ptr->is_deleted || pathcmp(&r, ptr->original_name) || pathcmp(&s, ptr->aliased_name)) continue;
651 memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);
652 strncpy(real_program_name, ptr->aliased_name->name, CCS_MAX_PATHNAME_LEN - 1);
653 fill_path_info(&r);
654 break;
655 }
656 }
657
658 /* Compare basename of real_program_name and argv[0] */
659 if (bprm->argc > 0 && CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ARGV0)) {
660 char *org_argv0 = get_argv0(bprm);
661 retval = -ENOMEM;
662 if (org_argv0) {
663 const int len = strlen(org_argv0);
664 char *printable_argv0 = ccs_alloc(len * 4 + 8);
665 if (printable_argv0 && Escape(printable_argv0, org_argv0, len * 4 + 8) == 0) {
666 const char *base_argv0, *base_filename;
667 if ((base_argv0 = strrchr(printable_argv0, '/')) == NULL) base_argv0 = printable_argv0; else base_argv0++;
668 if ((base_filename = strrchr(real_program_name, '/')) == NULL) base_filename = real_program_name; else base_filename++;
669 if (strcmp(base_argv0, base_filename)) retval = CheckArgv0Perm(&r, base_argv0);
670 else retval = 0;
671 }
672 ccs_free(printable_argv0);
673 ccs_free(org_argv0);
674 }
675 if (retval) goto out;
676 }
677
678 /* Check 'aggregator' directive. */
679 {
680 struct aggregator_entry *ptr;
681 /* Is this program allowed to be aggregated? */
682 list1_for_each_entry(ptr, &aggregator_list, list) {
683 if (ptr->is_deleted || !PathMatchesToPattern(&r, ptr->original_name)) continue;
684 memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);
685 strncpy(real_program_name, ptr->aggregated_name->name, CCS_MAX_PATHNAME_LEN - 1);
686 fill_path_info(&r);
687 break;
688 }
689 }
690
691 /* Check execute permission. */
692 if ((retval = CheckExecPerm(&r, filp)) < 0) goto out;
693
694 ok: ;
695 /* Allocate memory for calcurating domain name. */
696 retval = -ENOMEM;
697 if ((new_domain_name = ccs_alloc(CCS_MAX_PATHNAME_LEN + 16)) == NULL) goto out;
698
699 if (IsDomainInitializer(old_domain->domainname, &r, &l)) {
700 /* Transit to the child of KERNEL_DOMAIN domain. */
701 snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1, ROOT_NAME " " "%s", real_program_name);
702 } else if (old_domain == &KERNEL_DOMAIN && !sbin_init_started) {
703 /*
704 * Needn't to transit from kernel domain before starting /sbin/init .
705 * But transit from kernel domain if executing initializers, for they might start before /sbin/init .
706 */
707 domain = old_domain;
708 } else if (IsDomainKeeper(old_domain->domainname, &r, &l)) {
709 /* Keep current domain. */
710 domain = old_domain;
711 } else {
712 /* Normal domain transition. */
713 snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1, "%s %s", old_domain_name, real_program_name);
714 }
715 if (!domain && strlen(new_domain_name) < CCS_MAX_PATHNAME_LEN) {
716 if (is_enforce) {
717 domain = FindDomain(new_domain_name);
718 if (!domain) if (CheckSupervisor("#Need to create domain\n%s\n", new_domain_name) == 0) domain = FindOrAssignNewDomain(new_domain_name, current->domain_info->profile);
719 } else {
720 domain = FindOrAssignNewDomain(new_domain_name, current->domain_info->profile);
721 }
722 }
723 if (!domain) {
724 printk("TOMOYO-ERROR: Domain '%s' not defined.\n", new_domain_name);
725 if (is_enforce) retval = -EPERM;
726 } else {
727 retval = 0;
728 }
729 out: ;
730 ccs_free(new_domain_name);
731 ccs_free(real_program_name);
732 ccs_free(symlink_program_name);
733 *next_domain = domain ? domain : old_domain;
734 return retval;
735 }
736
737 static int CheckEnviron(struct linux_binprm *bprm)
738 {
739 char *arg_ptr = ccs_alloc(CCS_MAX_PATHNAME_LEN);
740 int arg_len = 0;
741 unsigned long pos = bprm->p;
742 int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;
743 int argv_count = bprm->argc;
744 int envp_count = bprm->envc;
745 //printk("start %d %d\n", argv_count, envp_count);
746 int error = -ENOMEM;
747 if (!arg_ptr) goto out;
748 if (!envp_count) {
749 error = 0;
750 goto out;
751 }
752 while (error == -ENOMEM) {
753 struct page *page;
754 const char *kaddr;
755 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
756 if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) goto out;
757 #else
758 page = bprm->page[i];
759 #endif
760 /* Map */
761 kaddr = kmap(page);
762 if (!kaddr) { /* Mapping failed. */
763 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
764 put_page(page);
765 #endif
766 goto out;
767 }
768 /* Read. */
769 while (argv_count && offset < PAGE_SIZE) {
770 if (!kaddr[offset++]) argv_count--;
771 }
772 if (argv_count) goto unmap_page;
773 while (offset < PAGE_SIZE) {
774 const unsigned char c = kaddr[offset++];
775 if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {
776 if (c == '=') {
777 arg_ptr[arg_len++] = '\0';
778 } else if (c == '\\') {
779 arg_ptr[arg_len++] = '\\';
780 arg_ptr[arg_len++] = '\\';
781 } else if (c > ' ' && c < 127) {
782 arg_ptr[arg_len++] = c;
783 } else {
784 arg_ptr[arg_len++] = '\\';
785 arg_ptr[arg_len++] = (c >> 6) + '0';
786 arg_ptr[arg_len++] = ((c >> 3) & 7) + '0';
787 arg_ptr[arg_len++] = (c & 7) + '0';
788 }
789 } else {
790 arg_ptr[arg_len] = '\0';
791 }
792 if (c) continue;
793 if (CheckEnvPerm(arg_ptr)) {
794 error = -EPERM;
795 break;
796 }
797 if (!--envp_count) {
798 error = 0;
799 break;
800 }
801 arg_len = 0;
802 }
803 unmap_page:
804 /* Unmap. */
805 kunmap(page);
806 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
807 put_page(page);
808 pos += PAGE_SIZE - offset;
809 #endif
810 i++;
811 offset = 0;
812 }
813 out:
814 ccs_free(arg_ptr);
815 if (error && !CheckCCSEnforce(CCS_TOMOYO_MAC_FOR_ENV)) error = 0;
816 return error;
817 }
818
819 static void UnEscape(unsigned char *dest)
820 {
821 unsigned char *src = dest;
822 unsigned char c, d, e;
823 while ((c = *src++) != '\0') {
824 if (c != '\\') {
825 *dest++ = c;
826 continue;
827 }
828 c = *src++;
829 if (c == '\\') {
830 *dest++ = c;
831 } else if (c >= '0' && c <= '3' &&
832 (d = *src++) >= '0' && d <= '7' &&
833 (e = *src++) >= '0' && e <= '7') {
834 *dest++ = ((c - '0') << 6) | ((d - '0') << 3) | (e - '0');
835 } else {
836 break;
837 }
838 }
839 *dest = '\0';
840 }
841
842 static int try_alt_exec(struct linux_binprm *bprm, char **alt_exec0)
843 {
844 struct file *filp;
845 int retval;
846 /* domainname must not be modified. */
847 char *domainname = (char *) current->domain_info->domainname->name;
848 char *alt_exec;
849 const char *alt_exec1 = GetAltExec();
850 if (!alt_exec1 || *alt_exec1 != '/') return -EINVAL;
851 retval = strlen(alt_exec1) + 1;
852 *alt_exec0 = alt_exec = ccs_alloc(retval);
853 if (!alt_exec) return -ENOMEM;
854 memmove(alt_exec, alt_exec1, retval);
855 UnEscape(alt_exec);
856 allow_write_access(bprm->file);
857 fput(bprm->file);
858 bprm->file = NULL;
859 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
860 retval = remove_arg_zero(bprm);
861 if (retval) return retval;
862 #else
863 remove_arg_zero(bprm);
864 #endif
865 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
866 retval = copy_strings_kernel(1, &bprm->interp, bprm);
867 #else
868 retval = copy_strings_kernel(1, &bprm->filename, bprm);
869 #endif
870 if (retval < 0) return retval;
871 bprm->argc++;
872 retval = copy_strings_kernel(1, &domainname, bprm);
873 if (retval < 0) return retval;
874 bprm->argc++;
875 retval = copy_strings_kernel(1, &alt_exec, bprm);
876 if (retval < 0) return retval;
877 bprm->argc++;
878 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
879 bprm->interp = alt_exec;
880 #endif
881 filp = open_exec(alt_exec);
882 if (IS_ERR(filp)) return PTR_ERR(filp);
883 bprm->file= filp;
884 bprm->filename = alt_exec;
885 return prepare_binprm(bprm);
886 }
887
888 #endif
889
890 int search_binary_handler_with_transition(struct linux_binprm *bprm, struct pt_regs *regs)
891 {
892 struct domain_info *next_domain = NULL, *prev_domain = current->domain_info;
893 int retval;
894 char *alt_exec = NULL; /* Keep valid until search_binary_handler() finishes. */
895 #if defined(CONFIG_SAKURA) || defined(CONFIG_TOMOYO)
896 extern void CCS_LoadPolicy(const char *filename);
897 CCS_LoadPolicy(bprm->filename);
898 #endif
899 #if defined(CONFIG_TOMOYO)
900 retval = FindNextDomain(bprm, &next_domain, 1);
901 if (retval == -EPERM && try_alt_exec(bprm, &alt_exec) >= 0) {
902 current->tomoyo_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
903 retval = FindNextDomain(bprm, &next_domain, 0);
904 current->tomoyo_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
905 }
906 #else
907 retval = 0; next_domain = prev_domain;
908 #endif
909 if (retval == 0) {
910 current->domain_info = next_domain;
911 #if defined(CONFIG_TOMOYO)
912 if (CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ENV)) retval = CheckEnviron(bprm);
913 #endif
914 current->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
915 if (!retval) retval = search_binary_handler(bprm, regs);
916 current->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
917 if (retval < 0) current->domain_info = prev_domain;
918 }
919 #if defined(CONFIG_SAKURA) || defined(CONFIG_TOMOYO)
920 ccs_free(alt_exec);
921 #endif
922 return retval;
923 }
924
925 /***** TOMOYO Linux end. *****/

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