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

Subversion リポジトリの参照

Contents of /branches/ccs-patch/security/ccsecurity/domain.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1007 - (show annotations) (download) (as text)
Thu Feb 28 06:42:01 2008 UTC (16 years, 3 months ago) by kumaneko
Original Path: trunk/1.6.x/ccs-patch/fs/tomoyo_domain.c
File MIME type: text/x-csrc
File size: 31313 byte(s)


1 /*
2 * fs/tomoyo_domain.c
3 *
4 * Implementation of the Domain-Based Mandatory Access Control.
5 *
6 * Copyright (C) 2005-2008 NTT DATA CORPORATION
7 *
8 * Version: 1.6.0-pre 2008/02/28
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 bool 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 if (domain) list1_add_tail_mb(&acl->list, &domain->acl_info_list);
101 else acl->type &= ~ACL_DELETED;
102 UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);
103 return 0;
104 }
105
106 int DelDomainACL(struct acl_info *acl)
107 {
108 if (acl) acl->type |= ACL_DELETED;
109 UpdateCounter(CCS_UPDATES_COUNTER_DOMAIN_POLICY);
110 return 0;
111 }
112
113 /************************* DOMAIN INITIALIZER HANDLER *************************/
114
115 static LIST1_HEAD(domain_initializer_list);
116
117 static int AddDomainInitializerEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete)
118 {
119 struct domain_initializer_entry *new_entry, *ptr;
120 static DEFINE_MUTEX(lock);
121 const struct path_info *saved_program, *saved_domainname = NULL;
122 int error = -ENOMEM;
123 bool is_last_name = 0;
124 if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */
125 if (domainname) {
126 if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) {
127 is_last_name = 1;
128 } else if (!IsCorrectDomain(domainname, __FUNCTION__)) {
129 return -EINVAL;
130 }
131 if ((saved_domainname = SaveName(domainname)) == NULL) return -ENOMEM;
132 }
133 if ((saved_program = SaveName(program)) == NULL) return -ENOMEM;
134 mutex_lock(&lock);
135 list1_for_each_entry(ptr, &domain_initializer_list, list) {
136 if (ptr->is_not == is_not && ptr->domainname == saved_domainname && ptr->program == saved_program) {
137 ptr->is_deleted = is_delete;
138 error = 0;
139 goto out;
140 }
141 }
142 if (is_delete) {
143 error = -ENOENT;
144 goto out;
145 }
146 if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;
147 new_entry->domainname = saved_domainname;
148 new_entry->program = saved_program;
149 new_entry->is_not = is_not;
150 new_entry->is_last_name = is_last_name;
151 list1_add_tail_mb(&new_entry->list, &domain_initializer_list);
152 error = 0;
153 out:
154 mutex_unlock(&lock);
155 return error;
156 }
157
158 int ReadDomainInitializerPolicy(struct io_buffer *head)
159 {
160 struct list1_head *pos;
161 list1_for_each_cookie(pos, head->read_var2, &domain_initializer_list) {
162 struct domain_initializer_entry *ptr;
163 ptr = list1_entry(pos, struct domain_initializer_entry, list);
164 if (ptr->is_deleted) continue;
165 if (ptr->domainname) {
166 if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s from %s\n", ptr->is_not ? "no_" : "", ptr->program->name, ptr->domainname->name)) return -ENOMEM;
167 } else {
168 if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s\n", ptr->is_not ? "no_" : "", ptr->program->name)) return -ENOMEM;
169 }
170 }
171 return 0;
172 }
173
174 int AddDomainInitializerPolicy(char *data, const bool is_not, const bool is_delete)
175 {
176 char *cp = strstr(data, " from ");
177 if (cp) {
178 *cp = '\0';
179 return AddDomainInitializerEntry(cp + 6, data, is_not, is_delete);
180 } else {
181 return AddDomainInitializerEntry(NULL, data, is_not, is_delete);
182 }
183 }
184
185 static bool IsDomainInitializer(const struct path_info *domainname, const struct path_info *program, const struct path_info *last_name)
186 {
187 struct domain_initializer_entry *ptr;
188 bool flag = 0;
189 list1_for_each_entry(ptr, &domain_initializer_list, list) {
190 if (ptr->is_deleted) continue;
191 if (ptr->domainname) {
192 if (!ptr->is_last_name) {
193 if (ptr->domainname != domainname) continue;
194 } else {
195 if (pathcmp(ptr->domainname, last_name)) continue;
196 }
197 }
198 if (pathcmp(ptr->program, program)) continue;
199 if (ptr->is_not) return 0;
200 flag = 1;
201 }
202 return flag;
203 }
204
205 /************************* DOMAIN KEEPER HANDLER *************************/
206
207 static LIST1_HEAD(domain_keeper_list);
208
209 static int AddDomainKeeperEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete)
210 {
211 struct domain_keeper_entry *new_entry, *ptr;
212 const struct path_info *saved_domainname, *saved_program = NULL;
213 static DEFINE_MUTEX(lock);
214 int error = -ENOMEM;
215 bool is_last_name = 0;
216 if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) {
217 is_last_name = 1;
218 } else if (!IsCorrectDomain(domainname, __FUNCTION__)) {
219 return -EINVAL;
220 }
221 if (program) {
222 if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL;
223 if ((saved_program = SaveName(program)) == NULL) return -ENOMEM;
224 }
225 if ((saved_domainname = SaveName(domainname)) == NULL) return -ENOMEM;
226 mutex_lock(&lock);
227 list1_for_each_entry(ptr, &domain_keeper_list, list) {
228 if (ptr->is_not == is_not && ptr->domainname == saved_domainname && ptr->program == saved_program) {
229 ptr->is_deleted = is_delete;
230 error = 0;
231 goto out;
232 }
233 }
234 if (is_delete) {
235 error = -ENOENT;
236 goto out;
237 }
238 if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;
239 new_entry->domainname = saved_domainname;
240 new_entry->program = saved_program;
241 new_entry->is_not = is_not;
242 new_entry->is_last_name = is_last_name;
243 list1_add_tail_mb(&new_entry->list, &domain_keeper_list);
244 error = 0;
245 out:
246 mutex_unlock(&lock);
247 return error;
248 }
249
250 int AddDomainKeeperPolicy(char *data, const bool is_not, const bool is_delete)
251 {
252 char *cp = strstr(data, " from ");
253 if (cp) {
254 *cp = '\0';
255 return AddDomainKeeperEntry(cp + 6, data, is_not, is_delete);
256 } else {
257 return AddDomainKeeperEntry(data, NULL, is_not, is_delete);
258 }
259 }
260
261 int ReadDomainKeeperPolicy(struct io_buffer *head)
262 {
263 struct list1_head *pos;
264 list1_for_each_cookie(pos, head->read_var2, &domain_keeper_list) {
265 struct domain_keeper_entry *ptr;
266 ptr = list1_entry(pos, struct domain_keeper_entry, list);
267 if (ptr->is_deleted) continue;
268 if (ptr->program) {
269 if (io_printf(head, "%s" KEYWORD_KEEP_DOMAIN "%s from %s\n", ptr->is_not ? "no_" : "", ptr->program->name, ptr->domainname->name)) return -ENOMEM;
270 } else {
271 if (io_printf(head, "%s" KEYWORD_KEEP_DOMAIN "%s\n", ptr->is_not ? "no_" : "", ptr->domainname->name)) return -ENOMEM;
272 }
273 }
274 return 0;
275 }
276
277 static bool IsDomainKeeper(const struct path_info *domainname, const struct path_info *program, const struct path_info *last_name)
278 {
279 struct domain_keeper_entry *ptr;
280 bool flag = 0;
281 list1_for_each_entry(ptr, &domain_keeper_list, list) {
282 if (ptr->is_deleted) continue;
283 if (!ptr->is_last_name) {
284 if (ptr->domainname != domainname) continue;
285 } else {
286 if (pathcmp(ptr->domainname, last_name)) continue;
287 }
288 if (ptr->program && pathcmp(ptr->program, program)) continue;
289 if (ptr->is_not) return 0;
290 flag = 1;
291 }
292 return flag;
293 }
294
295 /************************* SYMBOLIC LINKED PROGRAM HANDLER *************************/
296
297 static LIST1_HEAD(alias_list);
298
299 static int AddAliasEntry(const char *original_name, const char *aliased_name, const bool is_delete)
300 {
301 struct alias_entry *new_entry, *ptr;
302 static DEFINE_MUTEX(lock);
303 const struct path_info *saved_original_name, *saved_aliased_name;
304 int error = -ENOMEM;
305 if (!IsCorrectPath(original_name, 1, -1, -1, __FUNCTION__) || !IsCorrectPath(aliased_name, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */
306 if ((saved_original_name = SaveName(original_name)) == NULL || (saved_aliased_name = SaveName(aliased_name)) == NULL) return -ENOMEM;
307 mutex_lock(&lock);
308 list1_for_each_entry(ptr, &alias_list, list) {
309 if (ptr->original_name == saved_original_name && ptr->aliased_name == saved_aliased_name) {
310 ptr->is_deleted = is_delete;
311 error = 0;
312 goto out;
313 }
314 }
315 if (is_delete) {
316 error = -ENOENT;
317 goto out;
318 }
319 if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;
320 new_entry->original_name = saved_original_name;
321 new_entry->aliased_name = saved_aliased_name;
322 list1_add_tail_mb(&new_entry->list, &alias_list);
323 error = 0;
324 out:
325 mutex_unlock(&lock);
326 return error;
327 }
328
329 int ReadAliasPolicy(struct io_buffer *head)
330 {
331 struct list1_head *pos;
332 list1_for_each_cookie(pos, head->read_var2, &alias_list) {
333 struct alias_entry *ptr;
334 ptr = list1_entry(pos, struct alias_entry, list);
335 if (ptr->is_deleted) continue;
336 if (io_printf(head, KEYWORD_ALIAS "%s %s\n", ptr->original_name->name, ptr->aliased_name->name)) return -ENOMEM;
337 }
338 return 0;
339 }
340
341 int AddAliasPolicy(char *data, const bool is_delete)
342 {
343 char *cp = strchr(data, ' ');
344 if (!cp) return -EINVAL;
345 *cp++ = '\0';
346 return AddAliasEntry(data, cp, is_delete);
347 }
348
349 /************************* DOMAIN AGGREGATOR HANDLER *************************/
350
351 static LIST1_HEAD(aggregator_list);
352
353 static int AddAggregatorEntry(const char *original_name, const char *aggregated_name, const bool is_delete)
354 {
355 struct aggregator_entry *new_entry, *ptr;
356 static DEFINE_MUTEX(lock);
357 const struct path_info *saved_original_name, *saved_aggregated_name;
358 int error = -ENOMEM;
359 if (!IsCorrectPath(original_name, 1, 0, -1, __FUNCTION__) || !IsCorrectPath(aggregated_name, 1, -1, -1, __FUNCTION__)) return -EINVAL;
360 if ((saved_original_name = SaveName(original_name)) == NULL || (saved_aggregated_name = SaveName(aggregated_name)) == NULL) return -ENOMEM;
361 mutex_lock(&lock);
362 list1_for_each_entry(ptr, &aggregator_list, list) {
363 if (ptr->original_name == saved_original_name && ptr->aggregated_name == saved_aggregated_name) {
364 ptr->is_deleted = is_delete;
365 error = 0;
366 goto out;
367 }
368 }
369 if (is_delete) {
370 error = -ENOENT;
371 goto out;
372 }
373 if ((new_entry = alloc_element(sizeof(*new_entry))) == NULL) goto out;
374 new_entry->original_name = saved_original_name;
375 new_entry->aggregated_name = saved_aggregated_name;
376 list1_add_tail_mb(&new_entry->list, &aggregator_list);
377 error = 0;
378 out:
379 mutex_unlock(&lock);
380 return error;
381 }
382
383 int ReadAggregatorPolicy(struct io_buffer *head)
384 {
385 struct list1_head *pos;
386 list1_for_each_cookie(pos, head->read_var2, &aggregator_list) {
387 struct aggregator_entry *ptr;
388 ptr = list1_entry(pos, struct aggregator_entry, list);
389 if (ptr->is_deleted) continue;
390 if (io_printf(head, KEYWORD_AGGREGATOR "%s %s\n", ptr->original_name->name, ptr->aggregated_name->name)) return -ENOMEM;
391 }
392 return 0;
393 }
394
395 int AddAggregatorPolicy(char *data, const bool is_delete)
396 {
397 char *cp = strchr(data, ' ');
398 if (!cp) return -EINVAL;
399 *cp++ = '\0';
400 return AddAggregatorEntry(data, cp, is_delete);
401 }
402
403 /************************* DOMAIN DELETION HANDLER *************************/
404
405 /* #define DEBUG_DOMAIN_UNDELETE */
406
407 int DeleteDomain(char *domainname0)
408 {
409 struct domain_info *domain;
410 struct path_info domainname;
411 domainname.name = domainname0;
412 fill_path_info(&domainname);
413 mutex_lock(&new_domain_assign_lock);
414 #ifdef DEBUG_DOMAIN_UNDELETE
415 printk("DeleteDomain %s\n", domainname0);
416 list1_for_each_entry(domain, &domain_list, list) {
417 if (pathcmp(domain->domainname, &domainname)) continue;
418 printk("List: %p %u\n", domain, domain->is_deleted);
419 }
420 #endif
421 /* Is there an active domain? */
422 list1_for_each_entry(domain, &domain_list, list) {
423 struct domain_info *domain2;
424 /* Never delete KERNEL_DOMAIN */
425 if (domain == &KERNEL_DOMAIN || domain->is_deleted || pathcmp(domain->domainname, &domainname)) continue;
426 /* Mark already deleted domains as non undeletable. */
427 list1_for_each_entry(domain2, &domain_list, list) {
428 if (!domain2->is_deleted || pathcmp(domain2->domainname, &domainname)) continue;
429 #ifdef DEBUG_DOMAIN_UNDELETE
430 if (domain2->is_deleted != 255) printk("Marked %p as non undeletable\n", domain2);
431 #endif
432 domain2->is_deleted = 255;
433 }
434 /* Delete and mark active domain as undeletable. */
435 domain->is_deleted = 1;
436 #ifdef DEBUG_DOMAIN_UNDELETE
437 printk("Marked %p as undeletable\n", domain);
438 #endif
439 break;
440 }
441 mutex_unlock(&new_domain_assign_lock);
442 return 0;
443 }
444
445 struct domain_info *UndeleteDomain(const char *domainname0)
446 {
447 struct domain_info *domain, *candidate_domain = NULL;
448 struct path_info domainname;
449 domainname.name = domainname0;
450 fill_path_info(&domainname);
451 mutex_lock(&new_domain_assign_lock);
452 #ifdef DEBUG_DOMAIN_UNDELETE
453 printk("UndeleteDomain %s\n", domainname0);
454 list1_for_each_entry(domain, &domain_list, list) {
455 if (pathcmp(domain->domainname, &domainname)) continue;
456 printk("List: %p %u\n", domain, domain->is_deleted);
457 }
458 #endif
459 list1_for_each_entry(domain, &domain_list, list) {
460 if (pathcmp(&domainname, domain->domainname)) continue;
461 if (!domain->is_deleted) {
462 /* This domain is active. I can't undelete. */
463 candidate_domain = NULL;
464 #ifdef DEBUG_DOMAIN_UNDELETE
465 printk("%p is active. I can't undelete.\n", domain);
466 #endif
467 break;
468 }
469 /* Is this domain undeletable? */
470 if (domain->is_deleted == 1) candidate_domain = domain;
471 }
472 if (candidate_domain) {
473 candidate_domain->is_deleted = 0;
474 #ifdef DEBUG_DOMAIN_UNDELETE
475 printk("%p was undeleted.\n", candidate_domain);
476 #endif
477 }
478 mutex_unlock(&new_domain_assign_lock);
479 return candidate_domain;
480 }
481
482 /************************* DOMAIN TRANSITION HANDLER *************************/
483
484 struct domain_info *FindOrAssignNewDomain(const char *domainname, const u8 profile)
485 {
486 struct domain_info *domain = NULL;
487 const struct path_info *saved_domainname;
488 mutex_lock(&new_domain_assign_lock);
489 if ((domain = FindDomain(domainname)) != NULL) goto out;
490 if (!IsCorrectDomain(domainname, __FUNCTION__)) goto out;
491 if ((saved_domainname = SaveName(domainname)) == NULL) goto out;
492 /* Can I reuse memory of deleted domain? */
493 list1_for_each_entry(domain, &domain_list, list) {
494 struct task_struct *p;
495 struct acl_info *ptr;
496 bool flag;
497 if (!domain->is_deleted || domain->domainname != saved_domainname) continue;
498 flag = 0;
499 /***** CRITICAL SECTION START *****/
500 read_lock(&tasklist_lock);
501 for_each_process(p) {
502 if (p->domain_info == domain) { flag = 1; break; }
503 }
504 read_unlock(&tasklist_lock);
505 /***** CRITICAL SECTION END *****/
506 if (flag) continue;
507 #ifdef DEBUG_DOMAIN_UNDELETE
508 printk("Reusing %p %s\n", domain, domain->domainname->name);
509 #endif
510 list1_for_each_entry(ptr, &domain->acl_info_list, list) {
511 ptr->type |= ACL_DELETED;
512 }
513 domain->flags = 0;
514 domain->profile = profile;
515 domain->quota_warned = 0;
516 mb(); /* Avoid out-of-order execution. */
517 domain->is_deleted = 0;
518 goto out;
519 }
520 /* No memory reusable. Create using new memory. */
521 if ((domain = alloc_element(sizeof(*domain))) != NULL) {
522 INIT_LIST1_HEAD(&domain->acl_info_list);
523 domain->domainname = saved_domainname;
524 domain->profile = profile;
525 list1_add_tail_mb(&domain->list, &domain_list);
526 }
527 out: ;
528 mutex_unlock(&new_domain_assign_lock);
529 return domain;
530 }
531
532 static int Escape(char *dest, const char *src, int dest_len)
533 {
534 while (*src) {
535 const unsigned char c = * (const unsigned char *) src;
536 if (c == '\\') {
537 dest_len -= 2;
538 if (dest_len <= 0) goto out;
539 *dest++ = '\\';
540 *dest++ = '\\';
541 } else if (c > ' ' && c < 127) {
542 if (--dest_len <= 0) goto out;
543 *dest++ = c;
544 } else {
545 dest_len -= 4;
546 if (dest_len <= 0) goto out;
547 *dest++ = '\\';
548 *dest++ = (c >> 6) + '0';
549 *dest++ = ((c >> 3) & 7) + '0';
550 *dest++ = (c & 7) + '0';
551 }
552 src++;
553 }
554 if (--dest_len <= 0) goto out;
555 *dest = '\0';
556 return 0;
557 out:
558 return -ENOMEM;
559 }
560
561 static char *get_argv0(struct linux_binprm *bprm)
562 {
563 char *arg_ptr = ccs_alloc(PAGE_SIZE); /* Initial buffer. */
564 int arg_len = 0;
565 unsigned long pos = bprm->p;
566 int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;
567 if (!bprm->argc || !arg_ptr) goto out;
568 while (1) {
569 struct page *page;
570 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
571 if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) goto out;
572 #else
573 page = bprm->page[i];
574 #endif
575 { /* Map and copy to kernel buffer and unmap. */
576 const char *kaddr = kmap(page);
577 if (!kaddr) { /* Mapping failed. */
578 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
579 put_page(page);
580 #endif
581 goto out;
582 }
583 memmove(arg_ptr + arg_len, kaddr + offset, PAGE_SIZE - offset);
584 kunmap(page);
585 }
586 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
587 put_page(page);
588 pos += PAGE_SIZE - offset;
589 #endif
590 arg_len += PAGE_SIZE - offset;
591 if (memchr(arg_ptr, '\0', arg_len)) break;
592 { /* Initial buffer was too small for argv[0]. Retry after expanding buffer. */
593 char *tmp_arg_ptr = ccs_alloc(arg_len + PAGE_SIZE);
594 if (!tmp_arg_ptr) goto out;
595 memmove(tmp_arg_ptr, arg_ptr, arg_len);
596 ccs_free(arg_ptr);
597 arg_ptr = tmp_arg_ptr;
598 }
599 i++;
600 offset = 0;
601 }
602 return arg_ptr;
603 out: /* Release initial buffer. */
604 ccs_free(arg_ptr);
605 return NULL;
606 }
607
608 static int FindNextDomain(struct linux_binprm *bprm, struct domain_info **next_domain, const u8 do_perm_check)
609 {
610 /* This function assumes that the size of buffer returned by realpath() = CCS_MAX_PATHNAME_LEN. */
611 struct domain_info *old_domain = current->domain_info, *domain = NULL;
612 const char *old_domain_name = old_domain->domainname->name;
613 const char *original_name = bprm->filename;
614 char *new_domain_name = NULL;
615 char *real_program_name = NULL, *symlink_program_name = NULL;
616 const bool is_enforce = (CheckCCSFlags(CCS_TOMOYO_MAC_FOR_FILE) == 3);
617 int retval;
618 struct path_info r, s, l;
619
620 {
621 /*
622 * Built-in initializers. This is needed because policies are not loaded until starting /sbin/init .
623 */
624 static bool first = 1;
625 if (first) {
626 AddDomainInitializerEntry(NULL, "/sbin/hotplug", 0, 0);
627 AddDomainInitializerEntry(NULL, "/sbin/modprobe", 0, 0);
628 first = 0;
629 }
630 }
631
632 /* Get realpath of program. */
633 retval = -ENOENT; /* I hope realpath() won't fail with -ENOMEM. */
634 if ((real_program_name = realpath(original_name)) == NULL) goto out;
635 /* Get realpath of symbolic link. */
636 if ((symlink_program_name = realpath_nofollow(original_name)) == NULL) goto out;
637
638 r.name = real_program_name;
639 fill_path_info(&r);
640 s.name = symlink_program_name;
641 fill_path_info(&s);
642 if ((l.name = strrchr(old_domain_name, ' ')) != NULL) l.name++;
643 else l.name = old_domain_name;
644 fill_path_info(&l);
645
646 if (!do_perm_check) goto ok;
647
648 /* Check 'alias' directive. */
649 if (pathcmp(&r, &s)) {
650 struct alias_entry *ptr;
651 /* Is this program allowed to be called via symbolic links? */
652 list1_for_each_entry(ptr, &alias_list, list) {
653 if (ptr->is_deleted || pathcmp(&r, ptr->original_name) || pathcmp(&s, ptr->aliased_name)) continue;
654 memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);
655 strncpy(real_program_name, ptr->aliased_name->name, CCS_MAX_PATHNAME_LEN - 1);
656 fill_path_info(&r);
657 break;
658 }
659 }
660
661 /* Compare basename of real_program_name and argv[0] */
662 if (bprm->argc > 0 && CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ARGV0)) {
663 char *org_argv0 = get_argv0(bprm);
664 retval = -ENOMEM;
665 if (org_argv0) {
666 const int len = strlen(org_argv0);
667 char *printable_argv0 = ccs_alloc(len * 4 + 8);
668 if (printable_argv0 && Escape(printable_argv0, org_argv0, len * 4 + 8) == 0) {
669 const char *base_argv0, *base_filename;
670 if ((base_argv0 = strrchr(printable_argv0, '/')) == NULL) base_argv0 = printable_argv0; else base_argv0++;
671 if ((base_filename = strrchr(real_program_name, '/')) == NULL) base_filename = real_program_name; else base_filename++;
672 if (strcmp(base_argv0, base_filename)) retval = CheckArgv0Perm(&r, base_argv0);
673 else retval = 0;
674 }
675 ccs_free(printable_argv0);
676 ccs_free(org_argv0);
677 }
678 if (retval) goto out;
679 }
680
681 /* Check 'aggregator' directive. */
682 {
683 struct aggregator_entry *ptr;
684 /* Is this program allowed to be aggregated? */
685 list1_for_each_entry(ptr, &aggregator_list, list) {
686 if (ptr->is_deleted || !PathMatchesToPattern(&r, ptr->original_name)) continue;
687 memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);
688 strncpy(real_program_name, ptr->aggregated_name->name, CCS_MAX_PATHNAME_LEN - 1);
689 fill_path_info(&r);
690 break;
691 }
692 }
693
694 /* Check execute permission. */
695 if ((retval = CheckExecPerm(&r, bprm)) < 0) goto out;
696
697 ok: ;
698 /* Allocate memory for calcurating domain name. */
699 retval = -ENOMEM;
700 if ((new_domain_name = ccs_alloc(CCS_MAX_PATHNAME_LEN + 16)) == NULL) goto out;
701
702 if (IsDomainInitializer(old_domain->domainname, &r, &l)) {
703 /* Transit to the child of KERNEL_DOMAIN domain. */
704 snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1, ROOT_NAME " " "%s", real_program_name);
705 } else if (old_domain == &KERNEL_DOMAIN && !sbin_init_started) {
706 /*
707 * Needn't to transit from kernel domain before starting /sbin/init .
708 * But transit from kernel domain if executing initializers, for they might start before /sbin/init .
709 */
710 domain = old_domain;
711 } else if (IsDomainKeeper(old_domain->domainname, &r, &l)) {
712 /* Keep current domain. */
713 domain = old_domain;
714 } else {
715 /* Normal domain transition. */
716 snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1, "%s %s", old_domain_name, real_program_name);
717 }
718 if (!domain && strlen(new_domain_name) < CCS_MAX_PATHNAME_LEN) {
719 if (is_enforce) {
720 domain = FindDomain(new_domain_name);
721 if (!domain) if (CheckSupervisor("#Need to create domain\n%s\n", new_domain_name) == 0) domain = FindOrAssignNewDomain(new_domain_name, current->domain_info->profile);
722 } else {
723 domain = FindOrAssignNewDomain(new_domain_name, current->domain_info->profile);
724 }
725 }
726 if (!domain) {
727 printk("TOMOYO-ERROR: Domain '%s' not defined.\n", new_domain_name);
728 if (is_enforce) retval = -EPERM;
729 } else {
730 retval = 0;
731 }
732 out: ;
733 ccs_free(new_domain_name);
734 ccs_free(real_program_name);
735 ccs_free(symlink_program_name);
736 *next_domain = domain ? domain : old_domain;
737 return retval;
738 }
739
740 static int CheckEnviron(struct linux_binprm *bprm)
741 {
742 const u8 profile = current->domain_info->profile;
743 const u8 mode = CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ENV);
744 char *arg_ptr;
745 int arg_len = 0;
746 unsigned long pos = bprm->p;
747 int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;
748 int argv_count = bprm->argc;
749 int envp_count = bprm->envc;
750 //printk("start %d %d\n", argv_count, envp_count);
751 int error = -ENOMEM;
752 if (!mode || !envp_count) return 0;
753 arg_ptr = ccs_alloc(CCS_MAX_PATHNAME_LEN);
754 if (!arg_ptr) goto out;
755 while (error == -ENOMEM) {
756 struct page *page;
757 const char *kaddr;
758 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
759 if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) goto out;
760 pos += PAGE_SIZE - offset;
761 #else
762 page = bprm->page[i];
763 #endif
764 /* Map */
765 kaddr = kmap(page);
766 if (!kaddr) { /* Mapping failed. */
767 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
768 put_page(page);
769 #endif
770 goto out;
771 }
772 /* Read. */
773 while (argv_count && offset < PAGE_SIZE) {
774 if (!kaddr[offset++]) argv_count--;
775 }
776 if (argv_count) goto unmap_page;
777 while (offset < PAGE_SIZE) {
778 const unsigned char c = kaddr[offset++];
779 if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {
780 if (c == '=') {
781 arg_ptr[arg_len++] = '\0';
782 } else if (c == '\\') {
783 arg_ptr[arg_len++] = '\\';
784 arg_ptr[arg_len++] = '\\';
785 } else if (c > ' ' && c < 127) {
786 arg_ptr[arg_len++] = c;
787 } else {
788 arg_ptr[arg_len++] = '\\';
789 arg_ptr[arg_len++] = (c >> 6) + '0';
790 arg_ptr[arg_len++] = ((c >> 3) & 7) + '0';
791 arg_ptr[arg_len++] = (c & 7) + '0';
792 }
793 } else {
794 arg_ptr[arg_len] = '\0';
795 }
796 if (c) continue;
797 if (CheckEnvPerm(arg_ptr, profile, mode)) {
798 error = -EPERM;
799 break;
800 }
801 if (!--envp_count) {
802 error = 0;
803 break;
804 }
805 arg_len = 0;
806 }
807 unmap_page:
808 /* Unmap. */
809 kunmap(page);
810 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
811 put_page(page);
812 #endif
813 i++;
814 offset = 0;
815 }
816 out:
817 ccs_free(arg_ptr);
818 if (error && mode != 3) error = 0;
819 return error;
820 }
821
822 static void UnEscape(unsigned char *dest)
823 {
824 unsigned char *src = dest;
825 unsigned char c, d, e;
826 while ((c = *src++) != '\0') {
827 if (c != '\\') {
828 *dest++ = c;
829 continue;
830 }
831 c = *src++;
832 if (c == '\\') {
833 *dest++ = c;
834 } else if (c >= '0' && c <= '3' &&
835 (d = *src++) >= '0' && d <= '7' &&
836 (e = *src++) >= '0' && e <= '7') {
837 *dest++ = ((c - '0') << 6) | ((d - '0') << 3) | (e - '0');
838 } else {
839 break;
840 }
841 }
842 *dest = '\0';
843 }
844
845 static int try_alt_exec(struct linux_binprm *bprm, char **work)
846 {
847 /*
848 * Contents of modified bprm.
849 * The envp[] in original bprm is moved to argv[] so that
850 * the alternatively executed program won't be affected by
851 * some dangerous environment variables like LD_PRELOAD .
852 *
853 * modified bprm->argc
854 * = original bprm->argc + original bprm->envc + 7
855 * modified bprm->envc
856 * = 0
857 *
858 * modified bprm->argv[0]
859 * = the program's name specified by alt_exec
860 * modified bprm->argv[1]
861 * = current->domain_info->domainname->name
862 * modified bprm->argv[2]
863 * = the current process's name
864 * modified bprm->argv[3]
865 * = the current process's information (e.g. uid/gid).
866 * modified bprm->argv[4]
867 * = original bprm->filename
868 * modified bprm->argv[5]
869 * = original bprm->argc in string expression
870 * modified bprm->argv[6]
871 * = original bprm->envc in string expression
872 * modified bprm->argv[7]
873 * = original bprm->argv[0]
874 * ...
875 * modified bprm->argv[bprm->argc + 6]
876 * = original bprm->argv[bprm->argc - 1]
877 * modified bprm->argv[bprm->argc + 7]
878 * = original bprm->envp[0]
879 * ...
880 * modified bprm->argv[bprm->envc + bprm->argc + 6]
881 * = original bprm->envp[bprm->envc - 1]
882 */
883 struct file *filp;
884 int retval;
885 const int original_argc = bprm->argc;
886 const int original_envc = bprm->envc;
887 struct task_struct *task = current;
888 static const int buffer_len = PAGE_SIZE;
889 char *buffer = NULL;
890 char *alt_exec;
891 const char *alt_exec1 = GetAltExec();
892 if (!alt_exec1 || *alt_exec1 != '/') return -EINVAL;
893 retval = strlen(alt_exec1) + 1;
894 alt_exec = ccs_alloc(retval);
895 if (!alt_exec) return -ENOMEM;
896 *work = alt_exec;
897 memmove(alt_exec, alt_exec1, retval);
898 UnEscape(alt_exec);
899
900 /* Close the rejected program's dentry. */
901 allow_write_access(bprm->file);
902 fput(bprm->file);
903 bprm->file = NULL;
904
905 /* Allocate buffer. */
906 buffer = ccs_alloc(buffer_len);
907 if (!buffer) return -ENOMEM;
908
909 /* Move envp[] to argv[] */
910 bprm->argc += bprm->envc;
911 bprm->envc = 0;
912
913 /* Set argv[6] */
914 {
915 snprintf(buffer, buffer_len - 1, "%d", original_envc);
916 retval = copy_strings_kernel(1, &buffer, bprm);
917 if (retval < 0) goto out;
918 bprm->argc++;
919 }
920
921 /* Set argv[5] */
922 {
923 snprintf(buffer, buffer_len - 1, "%d", original_argc);
924 retval = copy_strings_kernel(1, &buffer, bprm);
925 if (retval < 0) goto out;
926 bprm->argc++;
927 }
928
929 /* Set argv[4] */
930 {
931 retval = copy_strings_kernel(1, &bprm->filename, bprm);
932 if (retval < 0) goto out;
933 bprm->argc++;
934 }
935
936 /* Set argv[3] */
937 {
938 const u32 tomoyo_flags = task->tomoyo_flags;
939 snprintf(buffer, buffer_len - 1, "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d sgid=%d fsuid=%d fsgid=%d state[0]=%u state[1]=%u state[2]=%u", task->pid, task->uid, task->gid, task->euid, task->egid, task->suid, task->sgid, task->fsuid, task->fsgid, (u8) (tomoyo_flags >> 24), (u8) (tomoyo_flags >> 16), (u8) (tomoyo_flags >> 8));
940 retval = copy_strings_kernel(1, &buffer, bprm);
941 if (retval < 0) goto out;
942 bprm->argc++;
943 }
944
945 /* Set argv[2] */
946 {
947 char *exe = (char *) GetEXE();
948 if (exe) {
949 retval = copy_strings_kernel(1, &exe, bprm);
950 ccs_free(exe);
951 } else {
952 snprintf(buffer, buffer_len - 1, "<unknown>");
953 retval = copy_strings_kernel(1, &buffer, bprm);
954 }
955 if (retval < 0) goto out;
956 bprm->argc++;
957 }
958
959 /* Set argv[1] */
960 {
961 strncpy(buffer, task->domain_info->domainname->name, buffer_len - 1);
962 retval = copy_strings_kernel(1, &buffer, bprm);
963 if (retval < 0) goto out;
964 bprm->argc++;
965 }
966
967 /* Set argv[0] */
968 {
969 retval = copy_strings_kernel(1, &alt_exec, bprm);
970 if (retval < 0) goto out;
971 bprm->argc++;
972 }
973 #if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,23) || LINUX_VERSION_CODE == KERNEL_VERSION(2,6,24)
974 bprm->argv_len = bprm->exec - bprm->p;
975 #endif
976
977 /* OK, now restart the process with the alternative program's dentry. */
978 filp = open_exec(alt_exec);
979 if (IS_ERR(filp)) {
980 retval = PTR_ERR(filp);
981 goto out;
982 }
983 bprm->file= filp;
984 bprm->filename = alt_exec;
985 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
986 bprm->interp = alt_exec;
987 #endif
988 retval = 0;
989 out:
990 /* Free buffer. */
991 ccs_free(buffer);
992 return retval;
993 }
994
995 #endif
996
997 int search_binary_handler_with_transition(struct linux_binprm *bprm, struct pt_regs *regs)
998 {
999 struct domain_info *next_domain = NULL, *prev_domain = current->domain_info;
1000 int retval;
1001 char *work = NULL; /* Keep valid until search_binary_handler() finishes. */
1002 #if defined(CONFIG_SAKURA) || defined(CONFIG_TOMOYO)
1003 extern void CCS_LoadPolicy(const char *filename);
1004 CCS_LoadPolicy(bprm->filename);
1005 #endif
1006 #if defined(CONFIG_TOMOYO)
1007 retval = FindNextDomain(bprm, &next_domain, 1);
1008 if (retval == -EPERM && try_alt_exec(bprm, &work) == 0 && prepare_binprm(bprm) >= 0) {
1009 current->tomoyo_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1010 retval = FindNextDomain(bprm, &next_domain, 0);
1011 current->tomoyo_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1012 }
1013 if (retval == 0) {
1014 current->domain_info = next_domain;
1015 retval = CheckEnviron(bprm);
1016 current->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
1017 if (!retval) retval = search_binary_handler(bprm, regs);
1018 current->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
1019 if (retval < 0) current->domain_info = prev_domain;
1020 }
1021 ccs_free(work);
1022 return retval;
1023 #else
1024 return search_binary_handler(bprm, regs);
1025 #endif
1026 }
1027
1028 /***** TOMOYO Linux end. *****/

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