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

Subversion リポジトリの参照

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1031 - (show annotations) (download) (as text)
Tue Mar 11 04:38:56 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: 33473 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 #endif
848 read_lock(&current->fs->lock);
849 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
850 root = current->fs->root;
851 path_get(&current->fs->root);
852 dentry = root.dentry;
853 vfsmnt = root.mnt;
854 #else
855 dentry = dget(current->fs->root);
856 vfsmnt = mntget(current->fs->rootmnt);
857 #endif
858 read_unlock(&current->fs->lock);
859 /***** CRITICAL SECTION START *****/
860 spin_lock(&dcache_lock);
861 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
862 spin_lock(&vfsmount_lock);
863 #endif
864 for (;;) {
865 if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
866 /* Global root? */
867 if (vfsmnt->mnt_parent == vfsmnt) break;
868 dentry = vfsmnt->mnt_mountpoint;
869 vfsmnt = vfsmnt->mnt_parent;
870 continue;
871 }
872 dentry = dentry->d_parent;
873 depth++;
874 }
875 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
876 spin_unlock(&vfsmount_lock);
877 #endif
878 spin_unlock(&dcache_lock);
879 /***** CRITICAL SECTION END *****/
880 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
881 path_put(&root);
882 #else
883 dput(dentry);
884 mntput(vfsmnt);
885 #endif
886 return depth;
887 }
888
889 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)
890 {
891 /*
892 * Contents of modified bprm.
893 * The envp[] in original bprm is moved to argv[] so that
894 * the alternatively executed program won't be affected by
895 * some dangerous environment variables like LD_PRELOAD .
896 *
897 * modified bprm->argc
898 * = original bprm->argc + original bprm->envc + 7
899 * modified bprm->envc
900 * = 0
901 *
902 * modified bprm->argv[0]
903 * = the program's name specified by execute_handler
904 * modified bprm->argv[1]
905 * = current->domain_info->domainname->name
906 * modified bprm->argv[2]
907 * = the current process's name
908 * modified bprm->argv[3]
909 * = the current process's information (e.g. uid/gid).
910 * modified bprm->argv[4]
911 * = original bprm->filename
912 * modified bprm->argv[5]
913 * = original bprm->argc in string expression
914 * modified bprm->argv[6]
915 * = original bprm->envc in string expression
916 * modified bprm->argv[7]
917 * = original bprm->argv[0]
918 * ...
919 * modified bprm->argv[bprm->argc + 6]
920 * = original bprm->argv[bprm->argc - 1]
921 * modified bprm->argv[bprm->argc + 7]
922 * = original bprm->envp[0]
923 * ...
924 * modified bprm->argv[bprm->envc + bprm->argc + 6]
925 * = original bprm->envp[bprm->envc - 1]
926 */
927 struct file *filp;
928 int retval;
929 const int original_argc = bprm->argc;
930 const int original_envc = bprm->envc;
931 struct task_struct *task = current;
932 char *buffer = tmp->buffer;
933 /* Allocate memory for execute handler's pathname. */
934 char *execute_handler = ccs_alloc(sizeof(struct ccs_page_buffer));
935 *work = execute_handler;
936 if (!execute_handler) return -ENOMEM;
937 strncpy(execute_handler, filename->name, sizeof(struct ccs_page_buffer) - 1);
938 UnEscape(execute_handler);
939
940 /* Close the requested program's dentry. */
941 allow_write_access(bprm->file);
942 fput(bprm->file);
943 bprm->file = NULL;
944
945 { /* Adjust root directory for open_exec(). */
946 int depth = GetRootDepth();
947 char *cp = execute_handler;
948 if (!*cp || *cp != '/') return -ENOENT;
949 while (depth) {
950 cp = strchr(cp + 1, '/');
951 if (!cp) return -ENOENT;
952 depth--;
953 }
954 memmove(execute_handler, cp, strlen(cp) + 1);
955 }
956
957 /* Move envp[] to argv[] */
958 bprm->argc += bprm->envc;
959 bprm->envc = 0;
960
961 /* Set argv[6] */
962 {
963 snprintf(buffer, sizeof(struct ccs_page_buffer) - 1, "%d", original_envc);
964 retval = copy_strings_kernel(1, &buffer, bprm);
965 if (retval < 0) goto out;
966 bprm->argc++;
967 }
968
969 /* Set argv[5] */
970 {
971 snprintf(buffer, sizeof(struct ccs_page_buffer) - 1, "%d", original_argc);
972 retval = copy_strings_kernel(1, &buffer, bprm);
973 if (retval < 0) goto out;
974 bprm->argc++;
975 }
976
977 /* Set argv[4] */
978 {
979 retval = copy_strings_kernel(1, &bprm->filename, bprm);
980 if (retval < 0) goto out;
981 bprm->argc++;
982 }
983
984 /* Set argv[3] */
985 {
986 const u32 tomoyo_flags = task->tomoyo_flags;
987 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));
988 retval = copy_strings_kernel(1, &buffer, bprm);
989 if (retval < 0) goto out;
990 bprm->argc++;
991 }
992
993 /* Set argv[2] */
994 {
995 char *exe = (char *) GetEXE();
996 if (exe) {
997 retval = copy_strings_kernel(1, &exe, bprm);
998 ccs_free(exe);
999 } else {
1000 snprintf(buffer, sizeof(struct ccs_page_buffer) - 1, "<unknown>");
1001 retval = copy_strings_kernel(1, &buffer, bprm);
1002 }
1003 if (retval < 0) goto out;
1004 bprm->argc++;
1005 }
1006
1007 /* Set argv[1] */
1008 {
1009 strncpy(buffer, task->domain_info->domainname->name, sizeof(struct ccs_page_buffer) - 1);
1010 retval = copy_strings_kernel(1, &buffer, bprm);
1011 if (retval < 0) goto out;
1012 bprm->argc++;
1013 }
1014
1015 /* Set argv[0] */
1016 {
1017 retval = copy_strings_kernel(1, &execute_handler, bprm);
1018 if (retval < 0) goto out;
1019 bprm->argc++;
1020 }
1021 #if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,23) || LINUX_VERSION_CODE == KERNEL_VERSION(2,6,24)
1022 bprm->argv_len = bprm->exec - bprm->p;
1023 #endif
1024
1025 /* OK, now restart the process with execute handler program's dentry. */
1026 filp = open_exec(execute_handler);
1027 if (IS_ERR(filp)) {
1028 retval = PTR_ERR(filp);
1029 goto out;
1030 }
1031 bprm->file= filp;
1032 bprm->filename = execute_handler;
1033 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
1034 bprm->interp = execute_handler;
1035 #endif
1036 retval = prepare_binprm(bprm);
1037 if (retval < 0) goto out;
1038 task->tomoyo_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1039 retval = FindNextDomain(bprm, next_domain, filename, tmp);
1040 task->tomoyo_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1041 out:
1042 return retval;
1043 }
1044
1045 static const struct path_info *FindExecuteHandler(bool is_preferred_handler)
1046 {
1047 const struct domain_info *domain = current->domain_info;
1048 struct acl_info *ptr;
1049 const u8 type = is_preferred_handler ? TYPE_PREFERRED_EXECUTE_HANDLER : TYPE_DEFAULT_EXECUTE_HANDLER;
1050 list1_for_each_entry(ptr, &domain->acl_info_list, list) {
1051 struct execute_handler_record *acl;
1052 if (ptr->type != type) continue;
1053 acl = container_of(ptr, struct execute_handler_record, head);
1054 return acl->handler;
1055 }
1056 return NULL;
1057 }
1058
1059 int search_binary_handler_with_transition(struct linux_binprm *bprm, struct pt_regs *regs)
1060 {
1061 struct task_struct *task = current;
1062 struct domain_info *next_domain = NULL, *prev_domain = task->domain_info;
1063 const struct path_info *handler;
1064 int retval;
1065 char *work = NULL; /* Keep valid until search_binary_handler() finishes. */
1066 struct ccs_page_buffer *buf = ccs_alloc(sizeof(struct ccs_page_buffer));
1067 CCS_LoadPolicy(bprm->filename);
1068 if (!buf) return -ENOMEM;
1069 //printk("rootdepth=%d\n", GetRootDepth());
1070 handler = FindExecuteHandler(true);
1071 if (handler) {
1072 retval = try_alt_exec(bprm, handler, &work, &next_domain, buf);
1073 } else if ((retval = FindNextDomain(bprm, &next_domain, NULL, buf)) == -EPERM) {
1074 handler = FindExecuteHandler(false);
1075 if (handler) retval = try_alt_exec(bprm, handler, &work, &next_domain, buf);
1076 }
1077 if (retval) goto out;
1078 task->domain_info = next_domain;
1079 retval = CheckEnviron(bprm, buf);
1080 if (retval) goto out;
1081 task->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
1082 retval = search_binary_handler(bprm, regs);
1083 task->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC;
1084 out:
1085 if (retval < 0) task->domain_info = prev_domain;
1086 ccs_free(work);
1087 ccs_free(buf);
1088 return retval;
1089 }
1090
1091 #else
1092
1093 int search_binary_handler_with_transition(struct linux_binprm *bprm, struct pt_regs *regs)
1094 {
1095 #ifdef CONFIG_SAKURA
1096 CCS_LoadPolicy(bprm->filename);
1097 #endif
1098 return search_binary_handler(bprm, regs);
1099 }
1100
1101 #endif
1102
1103 /***** TOMOYO Linux end. *****/

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