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

Subversion リポジトリの参照

Contents of /branches/ccs-patch/fs/tomoyo_domain.c

Parent Directory Parent Directory | Revision Log Revision Log


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

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