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

Subversion リポジトリの参照

Contents of /trunk/1.6.x/ccs-patch/fs/tomoyo_domain.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1032 - (show annotations) (download) (as text)
Tue Mar 11 08:25:41 2008 UTC (16 years, 2 months ago) by kumaneko
File MIME type: text/x-csrc
File size: 33528 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/11
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 bool get_argv0(struct linux_binprm *bprm, struct ccs_page_buffer *tmp)
538 {
539 char *arg_ptr = tmp->buffer;
540 int arg_len = 0;
541 unsigned long pos = bprm->p;
542 int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;
543 bool done = false;
544 if (!bprm->argc) goto out;
545 while (1) {
546 struct page *page;
547 const char *kaddr;
548 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
549 if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) goto out;
550 pos += PAGE_SIZE - offset;
551 #else
552 page = bprm->page[i];
553 #endif
554 /* Map. */
555 kaddr = kmap(page);
556 if (!kaddr) { /* Mapping failed. */
557 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
558 put_page(page);
559 #endif
560 goto out;
561 }
562 /* Read. */
563 while (offset < PAGE_SIZE) {
564 const unsigned char c = kaddr[offset++];
565 if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {
566 if (c == '\\') {
567 arg_ptr[arg_len++] = '\\';
568 arg_ptr[arg_len++] = '\\';
569 } else if (c == '/') {
570 arg_len = 0;
571 } else if (c > ' ' && c < 127) {
572 arg_ptr[arg_len++] = c;
573 } else {
574 arg_ptr[arg_len++] = '\\';
575 arg_ptr[arg_len++] = (c >> 6) + '0';
576 arg_ptr[arg_len++] = ((c >> 3) & 7) + '0';
577 arg_ptr[arg_len++] = (c & 7) + '0';
578 }
579 } else {
580 arg_ptr[arg_len] = '\0';
581 done = true;
582 break;
583 }
584 }
585 /* Unmap. */
586 kunmap(page);
587 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
588 put_page(page);
589 #endif
590 i++;
591 offset = 0;
592 if (done) break;
593 }
594 return true;
595 out:
596 return false;
597 }
598
599 static int FindNextDomain(struct linux_binprm *bprm, struct domain_info **next_domain, const struct path_info *path_to_verify, struct ccs_page_buffer *tmp)
600 {
601 /* This function assumes that the size of buffer returned by realpath() = CCS_MAX_PATHNAME_LEN. */
602 struct domain_info *old_domain = current->domain_info, *domain = NULL;
603 const char *old_domain_name = old_domain->domainname->name;
604 const char *original_name = bprm->filename;
605 char *new_domain_name = NULL;
606 char *real_program_name = NULL, *symlink_program_name = NULL;
607 const bool is_enforce = (CheckCCSFlags(CCS_TOMOYO_MAC_FOR_FILE) == 3);
608 int retval;
609 struct path_info r, s, l;
610
611 {
612 /*
613 * Built-in initializers. This is needed because policies are not loaded until starting /sbin/init .
614 */
615 static bool first = true;
616 if (first) {
617 AddDomainInitializerEntry(NULL, "/sbin/hotplug", 0, 0);
618 AddDomainInitializerEntry(NULL, "/sbin/modprobe", 0, 0);
619 first = false;
620 }
621 }
622
623 /* Get realpath of program. */
624 retval = -ENOENT; /* I hope realpath() won't fail with -ENOMEM. */
625 if ((real_program_name = realpath(original_name)) == NULL) goto out;
626 /* Get realpath of symbolic link. */
627 if ((symlink_program_name = realpath_nofollow(original_name)) == NULL) goto out;
628
629 r.name = real_program_name;
630 fill_path_info(&r);
631 s.name = symlink_program_name;
632 fill_path_info(&s);
633 if ((l.name = strrchr(old_domain_name, ' ')) != NULL) l.name++;
634 else l.name = old_domain_name;
635 fill_path_info(&l);
636
637 if (path_to_verify) {
638 if (pathcmp(&r, path_to_verify)) {
639 static u8 counter = 20;
640 if (counter) {
641 counter--;
642 printk("Failed to verify: %s\n", path_to_verify->name);
643 }
644 goto out;
645 }
646 goto ok;
647 }
648
649 /* Check 'alias' directive. */
650 if (pathcmp(&r, &s)) {
651 struct alias_entry *ptr;
652 /* Is this program allowed to be called via symbolic links? */
653 list1_for_each_entry(ptr, &alias_list, list) {
654 if (ptr->is_deleted || pathcmp(&r, ptr->original_name) || pathcmp(&s, ptr->aliased_name)) continue;
655 memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);
656 strncpy(real_program_name, ptr->aliased_name->name, CCS_MAX_PATHNAME_LEN - 1);
657 fill_path_info(&r);
658 break;
659 }
660 }
661
662 /* Compare basename of real_program_name and argv[0] */
663 if (bprm->argc > 0 && CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ARGV0)) {
664 char *base_argv0 = tmp->buffer;
665 const char *base_filename;
666 retval = -ENOMEM;
667 if (!get_argv0(bprm, tmp)) goto out;
668 if ((base_filename = strrchr(real_program_name, '/')) == NULL) base_filename = real_program_name; else base_filename++;
669 if (strcmp(base_argv0, base_filename)) {
670 retval = CheckArgv0Perm(&r, base_argv0);
671 if (retval) goto out;
672 }
673 }
674
675 /* Check 'aggregator' directive. */
676 {
677 struct aggregator_entry *ptr;
678 /* Is this program allowed to be aggregated? */
679 list1_for_each_entry(ptr, &aggregator_list, list) {
680 if (ptr->is_deleted || !PathMatchesToPattern(&r, ptr->original_name)) continue;
681 memset(real_program_name, 0, CCS_MAX_PATHNAME_LEN);
682 strncpy(real_program_name, ptr->aggregated_name->name, CCS_MAX_PATHNAME_LEN - 1);
683 fill_path_info(&r);
684 break;
685 }
686 }
687
688 /* Check execute permission. */
689 if ((retval = CheckExecPerm(&r, bprm, tmp)) < 0) goto out;
690
691 ok: ;
692 new_domain_name = tmp->buffer;
693 if (IsDomainInitializer(old_domain->domainname, &r, &l)) {
694 /* Transit to the child of KERNEL_DOMAIN domain. */
695 snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1, ROOT_NAME " " "%s", real_program_name);
696 } else if (old_domain == &KERNEL_DOMAIN && !sbin_init_started) {
697 /*
698 * Needn't to transit from kernel domain before starting /sbin/init .
699 * But transit from kernel domain if executing initializers, for they might start before /sbin/init .
700 */
701 domain = old_domain;
702 } else if (IsDomainKeeper(old_domain->domainname, &r, &l)) {
703 /* Keep current domain. */
704 domain = old_domain;
705 } else {
706 /* Normal domain transition. */
707 snprintf(new_domain_name, CCS_MAX_PATHNAME_LEN + 1, "%s %s", old_domain_name, real_program_name);
708 }
709 if (!domain && strlen(new_domain_name) < CCS_MAX_PATHNAME_LEN) {
710 if (is_enforce) {
711 domain = FindDomain(new_domain_name);
712 if (!domain) if (CheckSupervisor("#Need to create domain\n%s\n", new_domain_name) == 0) domain = FindOrAssignNewDomain(new_domain_name, current->domain_info->profile);
713 } else {
714 domain = FindOrAssignNewDomain(new_domain_name, current->domain_info->profile);
715 }
716 }
717 if (!domain) {
718 printk("TOMOYO-ERROR: Domain '%s' not defined.\n", new_domain_name);
719 if (is_enforce) retval = -EPERM;
720 } else {
721 retval = 0;
722 }
723 out: ;
724 ccs_free(real_program_name);
725 ccs_free(symlink_program_name);
726 *next_domain = domain ? domain : old_domain;
727 return retval;
728 }
729
730 static int CheckEnviron(struct linux_binprm *bprm, struct ccs_page_buffer *tmp)
731 {
732 const u8 profile = current->domain_info->profile;
733 const u8 mode = CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ENV);
734 char *arg_ptr = tmp->buffer;
735 int arg_len = 0;
736 unsigned long pos = bprm->p;
737 int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE;
738 int argv_count = bprm->argc;
739 int envp_count = bprm->envc;
740 //printk("start %d %d\n", argv_count, envp_count);
741 int error = -ENOMEM;
742 if (!mode || !envp_count) return 0;
743 while (error == -ENOMEM) {
744 struct page *page;
745 const char *kaddr;
746 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
747 if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) goto out;
748 pos += PAGE_SIZE - offset;
749 #else
750 page = bprm->page[i];
751 #endif
752 /* Map. */
753 kaddr = kmap(page);
754 if (!kaddr) { /* Mapping failed. */
755 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
756 put_page(page);
757 #endif
758 goto out;
759 }
760 /* Read. */
761 while (argv_count && offset < PAGE_SIZE) {
762 if (!kaddr[offset++]) argv_count--;
763 }
764 if (argv_count) goto unmap_page;
765 while (offset < PAGE_SIZE) {
766 const unsigned char c = kaddr[offset++];
767 if (c && arg_len < CCS_MAX_PATHNAME_LEN - 10) {
768 if (c == '=') {
769 arg_ptr[arg_len++] = '\0';
770 } else if (c == '\\') {
771 arg_ptr[arg_len++] = '\\';
772 arg_ptr[arg_len++] = '\\';
773 } else if (c > ' ' && c < 127) {
774 arg_ptr[arg_len++] = c;
775 } else {
776 arg_ptr[arg_len++] = '\\';
777 arg_ptr[arg_len++] = (c >> 6) + '0';
778 arg_ptr[arg_len++] = ((c >> 3) & 7) + '0';
779 arg_ptr[arg_len++] = (c & 7) + '0';
780 }
781 } else {
782 arg_ptr[arg_len] = '\0';
783 }
784 if (c) continue;
785 if (CheckEnvPerm(arg_ptr, profile, mode)) {
786 error = -EPERM;
787 break;
788 }
789 if (!--envp_count) {
790 error = 0;
791 break;
792 }
793 arg_len = 0;
794 }
795 unmap_page:
796 /* Unmap. */
797 kunmap(page);
798 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU)
799 put_page(page);
800 #endif
801 i++;
802 offset = 0;
803 }
804 out:
805 if (error && mode != 3) error = 0;
806 return error;
807 }
808
809 static void UnEscape(unsigned char *dest)
810 {
811 unsigned char *src = dest;
812 unsigned char c, d, e;
813 while ((c = *src++) != '\0') {
814 if (c != '\\') {
815 *dest++ = c;
816 continue;
817 }
818 c = *src++;
819 if (c == '\\') {
820 *dest++ = c;
821 } else if (c >= '0' && c <= '3' &&
822 (d = *src++) >= '0' && d <= '7' &&
823 (e = *src++) >= '0' && e <= '7') {
824 *dest++ = ((c - '0') << 6) | ((d - '0') << 3) | (e - '0');
825 } else {
826 break;
827 }
828 }
829 *dest = '\0';
830 }
831
832 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
833 #include <linux/namei.h>
834 #include <linux/mount.h>
835 #endif
836
837 /*
838 * GetRootDepth - return the depth of root directory.
839 */
840 static int GetRootDepth(void)
841 {
842 int depth = 0;
843 struct dentry *dentry;
844 struct vfsmount *vfsmnt;
845 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
846 struct path root;
847 #else
848 struct dentry *dentry0;
849 struct vfsmount *vfsmnt0;
850 #endif
851 read_lock(&current->fs->lock);
852 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
853 root = current->fs->root;
854 path_get(&current->fs->root);
855 dentry = root.dentry;
856 vfsmnt = root.mnt;
857 #else
858 dentry0 = dentry = dget(current->fs->root);
859 vfsmnt0 = vfsmnt = mntget(current->fs->rootmnt);
860 #endif
861 read_unlock(&current->fs->lock);
862 /***** CRITICAL SECTION START *****/
863 spin_lock(&dcache_lock);
864 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
865 spin_lock(&vfsmount_lock);
866 #endif
867 for (;;) {
868 if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
869 /* Global root? */
870 if (vfsmnt->mnt_parent == vfsmnt) break;
871 dentry = vfsmnt->mnt_mountpoint;
872 vfsmnt = vfsmnt->mnt_parent;
873 continue;
874 }
875 dentry = dentry->d_parent;
876 depth++;
877 }
878 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
879 spin_unlock(&vfsmount_lock);
880 #endif
881 spin_unlock(&dcache_lock);
882 /***** CRITICAL SECTION END *****/
883 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
884 path_put(&root);
885 #else
886 dput(dentry0);
887 mntput(vfsmnt0);
888 #endif
889 return depth;
890 }
891
892 static int try_alt_exec(struct linux_binprm *bprm, const struct path_info *filename, char **work, struct domain_info **next_domain, struct ccs_page_buffer *tmp)
893 {
894 /*
895 * Contents of modified bprm.
896 * The envp[] in original bprm is moved to argv[] so that
897 * the alternatively executed program won't be affected by
898 * some dangerous environment variables like LD_PRELOAD .
899 *
900 * modified bprm->argc
901 * = original bprm->argc + original bprm->envc + 7
902 * modified bprm->envc
903 * = 0
904 *
905 * modified bprm->argv[0]
906 * = the program's name specified by execute_handler
907 * modified bprm->argv[1]
908 * = current->domain_info->domainname->name
909 * modified bprm->argv[2]
910 * = the current process's name
911 * modified bprm->argv[3]
912 * = the current process's information (e.g. uid/gid).
913 * modified bprm->argv[4]
914 * = original bprm->filename
915 * modified bprm->argv[5]
916 * = original bprm->argc in string expression
917 * modified bprm->argv[6]
918 * = original bprm->envc in string expression
919 * modified bprm->argv[7]
920 * = original bprm->argv[0]
921 * ...
922 * modified bprm->argv[bprm->argc + 6]
923 * = original bprm->argv[bprm->argc - 1]
924 * modified bprm->argv[bprm->argc + 7]
925 * = original bprm->envp[0]
926 * ...
927 * modified bprm->argv[bprm->envc + bprm->argc + 6]
928 * = original bprm->envp[bprm->envc - 1]
929 */
930 struct file *filp;
931 int retval;
932 const int original_argc = bprm->argc;
933 const int original_envc = bprm->envc;
934 struct task_struct *task = current;
935 char *buffer = tmp->buffer;
936 /* Allocate memory for execute handler's pathname. */
937 char *execute_handler = ccs_alloc(sizeof(struct ccs_page_buffer));
938 *work = execute_handler;
939 if (!execute_handler) return -ENOMEM;
940 strncpy(execute_handler, filename->name, sizeof(struct ccs_page_buffer) - 1);
941 UnEscape(execute_handler);
942
943 /* Close the requested program's dentry. */
944 allow_write_access(bprm->file);
945 fput(bprm->file);
946 bprm->file = NULL;
947
948 { /* Adjust root directory for open_exec(). */
949 int depth = GetRootDepth();
950 char *cp = execute_handler;
951 if (!*cp || *cp != '/') return -ENOENT;
952 while (depth) {
953 cp = strchr(cp + 1, '/');
954 if (!cp) return -ENOENT;
955 depth--;
956 }
957 memmove(execute_handler, cp, strlen(cp) + 1);
958 }
959
960 /* Move envp[] to argv[] */
961 bprm->argc += bprm->envc;
962 bprm->envc = 0;
963
964 /* Set argv[6] */
965 {
966 snprintf(buffer, sizeof(struct ccs_page_buffer) - 1, "%d", original_envc);
967 retval = copy_strings_kernel(1, &buffer, bprm);
968 if (retval < 0) goto out;
969 bprm->argc++;
970 }
971
972 /* Set argv[5] */
973 {
974 snprintf(buffer, sizeof(struct ccs_page_buffer) - 1, "%d", original_argc);
975 retval = copy_strings_kernel(1, &buffer, bprm);
976 if (retval < 0) goto out;
977 bprm->argc++;
978 }
979
980 /* Set argv[4] */
981 {
982 retval = copy_strings_kernel(1, &bprm->filename, bprm);
983 if (retval < 0) goto out;
984 bprm->argc++;
985 }
986
987 /* Set argv[3] */
988 {
989 const u32 tomoyo_flags = task->tomoyo_flags;
990 snprintf(buffer, sizeof(struct ccs_page_buffer) - 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));
991 retval = copy_strings_kernel(1, &buffer, bprm);
992 if (retval < 0) goto out;
993 bprm->argc++;
994 }
995
996 /* Set argv[2] */
997 {
998 char *exe = (char *) GetEXE();
999 if (exe) {
1000 retval = copy_strings_kernel(1, &exe, bprm);
1001 ccs_free(exe);
1002 } else {
1003 snprintf(buffer, sizeof(struct ccs_page_buffer) - 1, "<unknown>");
1004 retval = copy_strings_kernel(1, &buffer, bprm);
1005 }
1006 if (retval < 0) goto out;
1007 bprm->argc++;
1008 }
1009
1010 /* Set argv[1] */
1011 {
1012 strncpy(buffer, task->domain_info->domainname->name, sizeof(struct ccs_page_buffer) - 1);
1013 retval = copy_strings_kernel(1, &buffer, bprm);
1014 if (retval < 0) goto out;
1015 bprm->argc++;
1016 }
1017
1018 /* Set argv[0] */
1019 {
1020 retval = copy_strings_kernel(1, &execute_handler, bprm);
1021 if (retval < 0) goto out;
1022 bprm->argc++;
1023 }
1024 #if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,23) || LINUX_VERSION_CODE == KERNEL_VERSION(2,6,24)
1025 bprm->argv_len = bprm->exec - bprm->p;
1026 #endif
1027
1028 /* OK, now restart the process with execute handler program's dentry. */
1029 filp = open_exec(execute_handler);
1030 if (IS_ERR(filp)) {
1031 retval = PTR_ERR(filp);
1032 goto out;
1033 }
1034 bprm->file= filp;
1035 bprm->filename = execute_handler;
1036 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
1037 bprm->interp = execute_handler;
1038 #endif
1039 retval = prepare_binprm(bprm);
1040 if (retval < 0) goto out;
1041 task->tomoyo_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1042 retval = FindNextDomain(bprm, next_domain, filename, tmp);
1043 task->tomoyo_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1044 out:
1045 return retval;
1046 }
1047
1048 static const struct path_info *FindExecuteHandler(bool is_preferred_handler)
1049 {
1050 const struct domain_info *domain = current->domain_info;
1051 struct acl_info *ptr;
1052 const u8 type = is_preferred_handler ? TYPE_PREFERRED_EXECUTE_HANDLER : TYPE_DEFAULT_EXECUTE_HANDLER;
1053 list1_for_each_entry(ptr, &domain->acl_info_list, list) {
1054 struct execute_handler_record *acl;
1055 if (ptr->type != type) continue;
1056 acl = container_of(ptr, struct execute_handler_record, head);
1057 return acl->handler;
1058 }
1059 return NULL;
1060 }
1061
1062 int search_binary_handler_with_transition(struct linux_binprm *bprm, struct pt_regs *regs)
1063 {
1064 struct task_struct *task = current;
1065 struct domain_info *next_domain = NULL, *prev_domain = task->domain_info;
1066 const struct path_info *handler;
1067 int retval;
1068 char *work = NULL; /* Keep valid until search_binary_handler() finishes. */
1069 struct ccs_page_buffer *buf = ccs_alloc(sizeof(struct ccs_page_buffer));
1070 CCS_LoadPolicy(bprm->filename);
1071 if (!buf) return -ENOMEM;
1072 //printk("rootdepth=%d\n", GetRootDepth());
1073 handler = FindExecuteHandler(true);
1074 if (handler) {
1075 retval = try_alt_exec(bprm, handler, &work, &next_domain, buf);
1076 } else if ((retval = FindNextDomain(bprm, &next_domain, NULL, buf)) == -EPERM) {
1077 handler = FindExecuteHandler(false);
1078 if (handler) retval = try_alt_exec(bprm, handler, &work, &next_domain, buf);
1079 }
1080 if (retval) goto out;
1081 task->domain_info = next_domain;
1082 retval = CheckEnviron(bprm, buf);
1083 if (retval) goto out;
1084 task->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
1085 retval = search_binary_handler(bprm, regs);
1086 task->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
1087 out:
1088 if (retval < 0) task->domain_info = prev_domain;
1089 ccs_free(work);
1090 ccs_free(buf);
1091 return retval;
1092 }
1093
1094 #else
1095
1096 int search_binary_handler_with_transition(struct linux_binprm *bprm, struct pt_regs *regs)
1097 {
1098 #ifdef CONFIG_SAKURA
1099 CCS_LoadPolicy(bprm->filename);
1100 #endif
1101 return search_binary_handler(bprm, regs);
1102 }
1103
1104 #endif
1105
1106 /***** TOMOYO Linux end. *****/

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