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

Subversion リポジトリの参照

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

Parent Directory Parent Directory | Revision Log Revision Log


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

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