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

Subversion リポジトリの参照

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2943 - (show annotations) (download) (as text)
Mon Aug 24 04:58:42 2009 UTC (14 years, 9 months ago) by kumaneko
Original Path: branches/ccs-patch/security/ccsecurity/domain.c
File MIME type: text/x-csrc
File size: 34259 byte(s)


1 /*
2 * security/ccsecurity/domain.c
3 *
4 * Copyright (C) 2005-2009 NTT DATA CORPORATION
5 *
6 * Version: 1.7.0-pre 2009/08/24
7 *
8 * This file is applicable to both 2.4.30 and 2.6.11 and later.
9 * See README.ccs for ChangeLog.
10 *
11 */
12
13 #include <linux/slab.h>
14 #include <linux/highmem.h>
15 #include <linux/version.h>
16 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
17 #include <linux/namei.h>
18 #include <linux/mount.h>
19 #endif
20 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
21 #include <linux/fs_struct.h>
22 #endif
23 #include "internal.h"
24
25 /* For compatibility with older kernels. */
26 #ifndef for_each_process
27 #define for_each_process for_each_task
28 #endif
29
30 /* Variables definitions.*/
31
32 /* The initial domain. */
33 struct ccs_domain_info ccs_kernel_domain;
34
35 /* The list for "struct ccs_domain_info". */
36 LIST_HEAD(ccs_domain_list);
37
38 /**
39 * ccs_audit_execute_handler_log - Audit execute_handler log.
40 *
41 * @ee: Pointer to "struct ccs_execve_entry".
42 * @is_default: True if it is "execute_handler" log.
43 *
44 * Returns 0 on success, negative value otherwise.
45 */
46 static int ccs_audit_execute_handler_log(struct ccs_execve_entry *ee,
47 const bool is_default)
48 {
49 struct ccs_request_info *r = &ee->r;
50 const char *handler = ee->handler->name;
51 r->mode = ccs_get_mode(r->profile, CCS_MAC_FILE_EXECUTE);
52 return ccs_write_audit_log(true, r, "%s %s\n",
53 is_default ? CCS_KEYWORD_EXECUTE_HANDLER :
54 CCS_KEYWORD_DENIED_EXECUTE_HANDLER, handler);
55 }
56
57 /**
58 * ccs_audit_domain_creation_log - Audit domain creation log.
59 *
60 * @domain: Pointer to "struct ccs_domain_info".
61 *
62 * Returns 0 on success, negative value otherwise.
63 */
64 static int ccs_audit_domain_creation_log(struct ccs_domain_info *domain)
65 {
66 int error;
67 struct ccs_request_info r;
68 ccs_init_request_info(&r, domain, CCS_MAC_FILE_EXECUTE);
69 error = ccs_write_audit_log(false, &r, "use_profile %u\n", r.profile);
70 return error;
71 }
72
73 /* The list for "struct ccs_domain_initializer_entry". */
74 LIST_HEAD(ccs_domain_initializer_list);
75
76 /**
77 * ccs_update_domain_initializer_entry - Update "struct ccs_domain_initializer_entry" list.
78 *
79 * @domainname: The name of domain. May be NULL.
80 * @program: The name of program.
81 * @is_not: True if it is "no_initialize_domain" entry.
82 * @is_delete: True if it is a delete request.
83 *
84 * Returns 0 on success, negative value otherwise.
85 */
86 static int ccs_update_domain_initializer_entry(const char *domainname,
87 const char *program,
88 const bool is_not,
89 const bool is_delete)
90 {
91 struct ccs_domain_initializer_entry *entry = NULL;
92 struct ccs_domain_initializer_entry *ptr;
93 struct ccs_domain_initializer_entry e = { .is_not = is_not };
94 int error = is_delete ? -ENOENT : -ENOMEM;
95 if (!ccs_is_correct_path(program, 1, -1, -1))
96 return -EINVAL; /* No patterns allowed. */
97 if (domainname) {
98 if (!ccs_is_domain_def(domainname) &&
99 ccs_is_correct_path(domainname, 1, -1, -1))
100 e.is_last_name = true;
101 else if (!ccs_is_correct_domain(domainname))
102 return -EINVAL;
103 e.domainname = ccs_get_name(domainname);
104 if (!e.domainname)
105 goto out;
106 }
107 e.program = ccs_get_name(program);
108 if (!e.program)
109 goto out;
110 if (!is_delete)
111 entry = kmalloc(sizeof(e), GFP_KERNEL);
112 mutex_lock(&ccs_policy_lock);
113 list_for_each_entry_rcu(ptr, &ccs_domain_initializer_list, list) {
114 if (ccs_memcmp(ptr, &e, offsetof(typeof(e), is_not),
115 sizeof(e)))
116 continue;
117 ptr->is_deleted = is_delete;
118 error = 0;
119 break;
120 }
121 if (!is_delete && error && ccs_commit_ok(entry, &e, sizeof(e))) {
122 list_add_tail_rcu(&entry->list, &ccs_domain_initializer_list);
123 entry = NULL;
124 error = 0;
125 }
126 mutex_unlock(&ccs_policy_lock);
127 out:
128 ccs_put_name(e.domainname);
129 ccs_put_name(e.program);
130 kfree(entry);
131 return error;
132 }
133
134 /**
135 * ccs_read_domain_initializer_policy - Read "struct ccs_domain_initializer_entry" list.
136 *
137 * @head: Pointer to "struct ccs_io_buffer".
138 *
139 * Returns true on success, false otherwise.
140 *
141 * Caller holds ccs_read_lock().
142 */
143 bool ccs_read_domain_initializer_policy(struct ccs_io_buffer *head)
144 {
145 struct list_head *pos;
146 bool done = true;
147 ccs_assert_read_lock();
148 list_for_each_cookie(pos, head->read_var2,
149 &ccs_domain_initializer_list) {
150 const char *no;
151 const char *from = "";
152 const char *domain = "";
153 struct ccs_domain_initializer_entry *ptr;
154 ptr = list_entry(pos, struct ccs_domain_initializer_entry,
155 list);
156 if (ptr->is_deleted)
157 continue;
158 no = ptr->is_not ? "no_" : "";
159 if (ptr->domainname) {
160 from = " from ";
161 domain = ptr->domainname->name;
162 }
163 done = ccs_io_printf(head, "%s" CCS_KEYWORD_INITIALIZE_DOMAIN
164 "%s%s%s\n", no, ptr->program->name, from,
165 domain);
166 if (!done)
167 break;
168 }
169 return done;
170 }
171
172 /**
173 * ccs_write_domain_initializer_policy - Write "struct ccs_domain_initializer_entry" list.
174 *
175 * @data: String to parse.
176 * @is_not: True if it is "no_initialize_domain" entry.
177 * @is_delete: True if it is a delete request.
178 *
179 * Returns 0 on success, negative value otherwise.
180 */
181 int ccs_write_domain_initializer_policy(char *data, const bool is_not,
182 const bool is_delete)
183 {
184 char *cp = strstr(data, " from ");
185 if (cp) {
186 *cp = '\0';
187 return ccs_update_domain_initializer_entry(cp + 6, data,
188 is_not, is_delete);
189 }
190 return ccs_update_domain_initializer_entry(NULL, data, is_not,
191 is_delete);
192 }
193
194 /**
195 * ccs_is_domain_initializer - Check whether the given program causes domainname reinitialization.
196 *
197 * @domainname: The name of domain.
198 * @program: The name of program.
199 * @last_name: The last component of @domainname.
200 *
201 * Returns true if executing @program reinitializes domain transition,
202 * false otherwise.
203 *
204 * Caller holds ccs_read_lock().
205 */
206 static bool ccs_is_domain_initializer(const struct ccs_path_info *domainname,
207 const struct ccs_path_info *program,
208 const struct ccs_path_info *last_name)
209 {
210 struct ccs_domain_initializer_entry *ptr;
211 bool flag = false;
212 ccs_assert_read_lock();
213 list_for_each_entry_rcu(ptr, &ccs_domain_initializer_list, list) {
214 if (ptr->is_deleted)
215 continue;
216 if (ptr->domainname) {
217 if (!ptr->is_last_name) {
218 if (ptr->domainname != domainname)
219 continue;
220 } else {
221 if (ccs_pathcmp(ptr->domainname, last_name))
222 continue;
223 }
224 }
225 if (ccs_pathcmp(ptr->program, program))
226 continue;
227 if (ptr->is_not) {
228 flag = false;
229 break;
230 }
231 flag = true;
232 }
233 return flag;
234 }
235
236 /* The list for "struct ccs_domain_keeper_entry". */
237 LIST_HEAD(ccs_domain_keeper_list);
238
239 /**
240 * ccs_update_domain_keeper_entry - Update "struct ccs_domain_keeper_entry" list.
241 *
242 * @domainname: The name of domain.
243 * @program: The name of program. May be NULL.
244 * @is_not: True if it is "no_keep_domain" entry.
245 * @is_delete: True if it is a delete request.
246 *
247 * Returns 0 on success, negative value otherwise.
248 */
249 static int ccs_update_domain_keeper_entry(const char *domainname,
250 const char *program,
251 const bool is_not,
252 const bool is_delete)
253 {
254 struct ccs_domain_keeper_entry *entry = NULL;
255 struct ccs_domain_keeper_entry *ptr;
256 struct ccs_domain_keeper_entry e = { .is_not = is_not };
257 int error = is_delete ? -ENOENT : -ENOMEM;
258 if (!ccs_is_domain_def(domainname) &&
259 ccs_is_correct_path(domainname, 1, -1, -1))
260 e.is_last_name = true;
261 else if (!ccs_is_correct_domain(domainname))
262 return -EINVAL;
263 if (program) {
264 if (!ccs_is_correct_path(program, 1, -1, -1))
265 return -EINVAL;
266 e.program = ccs_get_name(program);
267 if (!e.program)
268 goto out;
269 }
270 e.domainname = ccs_get_name(domainname);
271 if (!e.domainname)
272 goto out;
273 if (!is_delete)
274 entry = kmalloc(sizeof(e), GFP_KERNEL);
275 mutex_lock(&ccs_policy_lock);
276 list_for_each_entry_rcu(ptr, &ccs_domain_keeper_list, list) {
277 if (ccs_memcmp(ptr, &e, offsetof(typeof(e), is_not),
278 sizeof(e)))
279 continue;
280 ptr->is_deleted = is_delete;
281 error = 0;
282 break;
283 }
284 if (!is_delete && error && ccs_commit_ok(entry, &e, sizeof(e))) {
285 list_add_tail_rcu(&entry->list, &ccs_domain_keeper_list);
286 entry = NULL;
287 error = 0;
288 }
289 mutex_unlock(&ccs_policy_lock);
290 out:
291 ccs_put_name(e.domainname);
292 ccs_put_name(e.program);
293 kfree(entry);
294 return error;
295 }
296
297 /**
298 * ccs_write_domain_keeper_policy - Write "struct ccs_domain_keeper_entry" list.
299 *
300 * @data: String to parse.
301 * @is_not: True if it is "no_keep_domain" entry.
302 * @is_delete: True if it is a delete request.
303 *
304 */
305 int ccs_write_domain_keeper_policy(char *data, const bool is_not,
306 const bool is_delete)
307 {
308 char *cp = strstr(data, " from ");
309 if (cp) {
310 *cp = '\0';
311 return ccs_update_domain_keeper_entry(cp + 6, data,
312 is_not, is_delete);
313 }
314 return ccs_update_domain_keeper_entry(data, NULL, is_not, is_delete);
315 }
316
317 /**
318 * ccs_read_domain_keeper_policy - Read "struct ccs_domain_keeper_entry" list.
319 *
320 * @head: Pointer to "struct ccs_io_buffer".
321 *
322 * Returns true on success, false otherwise.
323 *
324 * Caller holds ccs_read_lock().
325 */
326 bool ccs_read_domain_keeper_policy(struct ccs_io_buffer *head)
327 {
328 struct list_head *pos;
329 bool done = true;
330 ccs_assert_read_lock();
331 list_for_each_cookie(pos, head->read_var2,
332 &ccs_domain_keeper_list) {
333 struct ccs_domain_keeper_entry *ptr;
334 const char *no;
335 const char *from = "";
336 const char *program = "";
337 ptr = list_entry(pos, struct ccs_domain_keeper_entry, list);
338 if (ptr->is_deleted)
339 continue;
340 no = ptr->is_not ? "no_" : "";
341 if (ptr->program) {
342 from = " from ";
343 program = ptr->program->name;
344 }
345 done = ccs_io_printf(head, "%s" CCS_KEYWORD_KEEP_DOMAIN
346 "%s%s%s\n", no, program, from,
347 ptr->domainname->name);
348 if (!done)
349 break;
350 }
351 return done;
352 }
353
354 /**
355 * ccs_is_domain_keeper - Check whether the given program causes domain transition suppression.
356 *
357 * @domainname: The name of domain.
358 * @program: The name of program.
359 * @last_name: The last component of @domainname.
360 *
361 * Returns true if executing @program supresses domain transition,
362 * false otherwise.
363 *
364 * Caller holds ccs_read_lock().
365 */
366 static bool ccs_is_domain_keeper(const struct ccs_path_info *domainname,
367 const struct ccs_path_info *program,
368 const struct ccs_path_info *last_name)
369 {
370 struct ccs_domain_keeper_entry *ptr;
371 bool flag = false;
372 ccs_assert_read_lock();
373 list_for_each_entry_rcu(ptr, &ccs_domain_keeper_list, list) {
374 if (ptr->is_deleted)
375 continue;
376 if (!ptr->is_last_name) {
377 if (ptr->domainname != domainname)
378 continue;
379 } else {
380 if (ccs_pathcmp(ptr->domainname, last_name))
381 continue;
382 }
383 if (ptr->program && ccs_pathcmp(ptr->program, program))
384 continue;
385 if (ptr->is_not) {
386 flag = false;
387 break;
388 }
389 flag = true;
390 }
391 return flag;
392 }
393
394 /* The list for "struct ccs_aggregator_entry". */
395 LIST_HEAD(ccs_aggregator_list);
396
397 /**
398 * ccs_update_aggregator_entry - Update "struct ccs_aggregator_entry" list.
399 *
400 * @original_name: The original program's name.
401 * @aggregated_name: The aggregated program's name.
402 * @is_delete: True if it is a delete request.
403 *
404 * Returns 0 on success, negative value otherwise.
405 */
406 static int ccs_update_aggregator_entry(const char *original_name,
407 const char *aggregated_name,
408 const bool is_delete)
409 {
410 struct ccs_aggregator_entry *entry = NULL;
411 struct ccs_aggregator_entry *ptr;
412 struct ccs_aggregator_entry e = { };
413 int error = is_delete ? -ENOENT : -ENOMEM;
414 if (!ccs_is_correct_path(original_name, 1, 0, -1) ||
415 !ccs_is_correct_path(aggregated_name, 1, -1, -1))
416 return -EINVAL;
417 e.original_name = ccs_get_name(original_name);
418 e.aggregated_name = ccs_get_name(aggregated_name);
419 if (!e.original_name || !e.aggregated_name)
420 goto out;
421 if (!is_delete)
422 entry = kmalloc(sizeof(e), GFP_KERNEL);
423 mutex_lock(&ccs_policy_lock);
424 list_for_each_entry_rcu(ptr, &ccs_aggregator_list, list) {
425 if (ccs_memcmp(ptr, &e, offsetof(typeof(e), original_name),
426 sizeof(e)))
427 continue;
428 ptr->is_deleted = is_delete;
429 error = 0;
430 break;
431 }
432 if (!is_delete && error && ccs_commit_ok(entry, &e, sizeof(e))) {
433 list_add_tail_rcu(&entry->list, &ccs_aggregator_list);
434 entry = NULL;
435 error = 0;
436 }
437 mutex_unlock(&ccs_policy_lock);
438 out:
439 ccs_put_name(e.original_name);
440 ccs_put_name(e.aggregated_name);
441 kfree(entry);
442 return error;
443 }
444
445 /**
446 * ccs_read_aggregator_policy - Read "struct ccs_aggregator_entry" list.
447 *
448 * @head: Pointer to "struct ccs_io_buffer".
449 *
450 * Returns true on success, false otherwise.
451 *
452 * Caller holds ccs_read_lock().
453 */
454 bool ccs_read_aggregator_policy(struct ccs_io_buffer *head)
455 {
456 struct list_head *pos;
457 bool done = true;
458 ccs_assert_read_lock();
459 list_for_each_cookie(pos, head->read_var2, &ccs_aggregator_list) {
460 struct ccs_aggregator_entry *ptr;
461 ptr = list_entry(pos, struct ccs_aggregator_entry, list);
462 if (ptr->is_deleted)
463 continue;
464 done = ccs_io_printf(head, CCS_KEYWORD_AGGREGATOR "%s %s\n",
465 ptr->original_name->name,
466 ptr->aggregated_name->name);
467 if (!done)
468 break;
469 }
470 return done;
471 }
472
473 /**
474 * ccs_write_aggregator_policy - Write "struct ccs_aggregator_entry" list.
475 *
476 * @data: String to parse.
477 * @is_delete: True if it is a delete request.
478 *
479 * Returns 0 on success, negative value otherwise.
480 */
481 int ccs_write_aggregator_policy(char *data, const bool is_delete)
482 {
483 char *w[2];
484 if (!ccs_tokenize(data, w, sizeof(w)) || !w[1][0])
485 return -EINVAL;
486 return ccs_update_aggregator_entry(w[0], w[1], is_delete);
487 }
488
489 /* Domain create/delete handler. */
490
491 /**
492 * ccs_delete_domain - Delete a domain.
493 *
494 * @domainname: The name of domain.
495 *
496 * Returns 0.
497 */
498 int ccs_delete_domain(char *domainname)
499 {
500 struct ccs_domain_info *domain;
501 struct ccs_path_info name;
502 name.name = domainname;
503 ccs_fill_path_info(&name);
504 mutex_lock(&ccs_policy_lock);
505 /* Is there an active domain? */
506 list_for_each_entry_rcu(domain, &ccs_domain_list, list) {
507 /* Never delete ccs_kernel_domain */
508 if (domain == &ccs_kernel_domain)
509 continue;
510 if (domain->is_deleted ||
511 ccs_pathcmp(domain->domainname, &name))
512 continue;
513 domain->is_deleted = true;
514 break;
515 }
516 mutex_unlock(&ccs_policy_lock);
517 return 0;
518 }
519
520 /**
521 * ccs_find_or_assign_new_domain - Create a domain.
522 *
523 * @domainname: The name of domain.
524 * @profile: Profile number to assign if the domain was newly created.
525 *
526 * Returns pointer to "struct ccs_domain_info" on success, NULL otherwise.
527 */
528 struct ccs_domain_info *ccs_find_or_assign_new_domain(const char *domainname,
529 const u8 profile)
530 {
531 struct ccs_domain_info *entry;
532 struct ccs_domain_info *domain;
533 const struct ccs_path_info *saved_domainname;
534 bool found = false;
535
536 if (!ccs_is_correct_domain(domainname))
537 return NULL;
538 saved_domainname = ccs_get_name(domainname);
539 if (!saved_domainname)
540 return NULL;
541 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
542 mutex_lock(&ccs_policy_lock);
543 list_for_each_entry_rcu(domain, &ccs_domain_list, list) {
544 if (domain->is_deleted ||
545 ccs_pathcmp(saved_domainname, domain->domainname))
546 continue;
547 found = true;
548 break;
549 }
550 if (!found && ccs_memory_ok(entry, sizeof(*entry))) {
551 INIT_LIST_HEAD(&entry->acl_info_list);
552 entry->domainname = saved_domainname;
553 saved_domainname = NULL;
554 entry->profile = profile;
555 list_add_tail_rcu(&entry->list, &ccs_domain_list);
556 domain = entry;
557 entry = NULL;
558 found = true;
559 }
560 mutex_unlock(&ccs_policy_lock);
561 ccs_put_name(saved_domainname);
562 kfree(entry);
563 return found ? domain : NULL;
564 }
565
566 /**
567 * ccs_find_next_domain - Find a domain.
568 *
569 * @ee: Pointer to "struct ccs_execve_entry".
570 *
571 * Returns 0 on success, negative value otherwise.
572 *
573 * Caller holds ccs_read_lock().
574 */
575 static int ccs_find_next_domain(struct ccs_execve_entry *ee)
576 {
577 struct ccs_request_info *r = &ee->r;
578 const struct ccs_path_info *handler = ee->handler;
579 struct ccs_domain_info *domain = NULL;
580 const char *old_domain_name = r->domain->domainname->name;
581 struct linux_binprm *bprm = ee->bprm;
582 const u32 ccs_flags = current->ccs_flags;
583 struct ccs_path_info rn = { }; /* real name */
584 struct ccs_path_info ln; /* last name */
585 int retval;
586 bool need_kfree = false;
587 ccs_assert_read_lock();
588 ln.name = ccs_last_word(old_domain_name);
589 ccs_fill_path_info(&ln);
590 retry:
591 current->ccs_flags = ccs_flags;
592 r->cond = NULL;
593 if (need_kfree) {
594 kfree(rn.name);
595 need_kfree = false;
596 }
597
598 /* Get symlink's pathname of program. */
599 retval = ccs_symlink_path(bprm->filename, &rn);
600 if (retval < 0)
601 goto out;
602 need_kfree = true;
603
604 if (handler) {
605 if (ccs_pathcmp(&rn, handler)) {
606 /* Failed to verify execute handler. */
607 static u8 counter = 20;
608 if (counter) {
609 counter--;
610 printk(KERN_WARNING "Failed to verify: %s\n",
611 handler->name);
612 }
613 goto out;
614 }
615 } else {
616 struct ccs_aggregator_entry *ptr;
617 /* Check 'aggregator' directive. */
618 list_for_each_entry_rcu(ptr, &ccs_aggregator_list, list) {
619 if (ptr->is_deleted ||
620 !ccs_path_matches_pattern(&rn, ptr->original_name))
621 continue;
622 kfree(rn.name);
623 need_kfree = false;
624 /* This is OK because it is read only. */
625 rn = *ptr->aggregated_name;
626 break;
627 }
628
629 /* Check execute permission. */
630 retval = ccs_exec_perm(r, &rn);
631 if (retval == 1)
632 goto retry;
633 if (retval < 0)
634 goto out;
635 }
636
637 /* Calculate domain to transit to. */
638 if (ccs_is_domain_initializer(r->domain->domainname, &rn, &ln)) {
639 /* Transit to the child of ccs_kernel_domain domain. */
640 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, ROOT_NAME " " "%s",
641 rn.name);
642 } else if (r->domain == &ccs_kernel_domain && !ccs_policy_loaded) {
643 /*
644 * Needn't to transit from kernel domain before starting
645 * /sbin/init. But transit from kernel domain if executing
646 * initializers because they might start before /sbin/init.
647 */
648 domain = r->domain;
649 } else if (ccs_is_domain_keeper(r->domain->domainname, &rn, &ln)) {
650 /* Keep current domain. */
651 domain = r->domain;
652 } else {
653 /* Normal domain transition. */
654 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s",
655 old_domain_name, rn.name);
656 }
657 if (domain || strlen(ee->tmp) >= CCS_EXEC_TMPSIZE - 10)
658 goto done;
659 domain = ccs_find_domain(ee->tmp);
660 if (domain)
661 goto done;
662 if (r->mode == CCS_MAC_MODE_ENFORCING) {
663 int error = ccs_supervisor(r, "# wants to create domain\n"
664 "%s\n", ee->tmp);
665 if (error == 1)
666 goto retry;
667 if (error < 0)
668 goto done;
669 }
670 domain = ccs_find_or_assign_new_domain(ee->tmp, r->profile);
671 if (domain)
672 ccs_audit_domain_creation_log(r->domain);
673 done:
674 if (!domain) {
675 printk(KERN_WARNING "ERROR: Domain '%s' not defined.\n",
676 ee->tmp);
677 if (r->mode == CCS_MAC_MODE_ENFORCING)
678 retval = -EPERM;
679 else {
680 retval = 0;
681 r->domain->domain_transition_failed = true;
682 }
683 } else {
684 retval = 0;
685 }
686 out:
687 if (domain)
688 r->domain = domain;
689 if (need_kfree)
690 kfree(rn.name);
691 return retval;
692 }
693
694 /**
695 * ccs_environ - Check permission for environment variable names.
696 *
697 * @ee: Pointer to "struct ccs_execve_entry".
698 *
699 * Returns 0 on success, negative value otherwise.
700 */
701 static int ccs_environ(struct ccs_execve_entry *ee)
702 {
703 struct ccs_request_info *r = &ee->r;
704 struct linux_binprm *bprm = ee->bprm;
705 char *arg_ptr = ee->tmp;
706 int arg_len = 0;
707 unsigned long pos = bprm->p;
708 int offset = pos % PAGE_SIZE;
709 int argv_count = bprm->argc;
710 int envp_count = bprm->envc;
711 /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */
712 int error = -ENOMEM;
713 if (!r->mode || !envp_count)
714 return 0;
715 while (error == -ENOMEM) {
716 if (!ccs_dump_page(bprm, pos, &ee->dump))
717 goto out;
718 pos += PAGE_SIZE - offset;
719 /* Read. */
720 while (argv_count && offset < PAGE_SIZE) {
721 const char *kaddr = ee->dump.data;
722 if (!kaddr[offset++])
723 argv_count--;
724 }
725 if (argv_count) {
726 offset = 0;
727 continue;
728 }
729 while (offset < PAGE_SIZE) {
730 const char *kaddr = ee->dump.data;
731 const unsigned char c = kaddr[offset++];
732 if (c && arg_len < CCS_EXEC_TMPSIZE - 10) {
733 if (c == '=') {
734 arg_ptr[arg_len++] = '\0';
735 } else if (c == '\\') {
736 arg_ptr[arg_len++] = '\\';
737 arg_ptr[arg_len++] = '\\';
738 } else if (c > ' ' && c < 127) {
739 arg_ptr[arg_len++] = c;
740 } else {
741 arg_ptr[arg_len++] = '\\';
742 arg_ptr[arg_len++] = (c >> 6) + '0';
743 arg_ptr[arg_len++]
744 = ((c >> 3) & 7) + '0';
745 arg_ptr[arg_len++] = (c & 7) + '0';
746 }
747 } else {
748 arg_ptr[arg_len] = '\0';
749 }
750 if (c)
751 continue;
752 if (ccs_env_perm(r, arg_ptr)) {
753 error = -EPERM;
754 break;
755 }
756 if (!--envp_count) {
757 error = 0;
758 break;
759 }
760 arg_len = 0;
761 }
762 offset = 0;
763 }
764 out:
765 if (r->mode != 3)
766 error = 0;
767 return error;
768 }
769
770 /**
771 * ccs_unescape - Unescape escaped string.
772 *
773 * @dest: String to unescape.
774 *
775 * Returns nothing.
776 */
777 static void ccs_unescape(unsigned char *dest)
778 {
779 unsigned char *src = dest;
780 unsigned char c;
781 unsigned char d;
782 unsigned char e;
783 while (1) {
784 c = *src++;
785 if (!c)
786 break;
787 if (c != '\\') {
788 *dest++ = c;
789 continue;
790 }
791 c = *src++;
792 if (c == '\\') {
793 *dest++ = c;
794 continue;
795 }
796 if (c < '0' || c > '3')
797 break;
798 d = *src++;
799 if (d < '0' || d > '7')
800 break;
801 e = *src++;
802 if (e < '0' || e > '7')
803 break;
804 *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0');
805 }
806 *dest = '\0';
807 }
808
809 /**
810 * ccs_root_depth - Get number of directories to strip.
811 *
812 * @dentry: Pointer to "struct dentry".
813 * @vfsmnt: Pointer to "struct vfsmount".
814 *
815 * Returns number of directories to strip.
816 */
817 static inline int ccs_root_depth(struct dentry *dentry, struct vfsmount *vfsmnt)
818 {
819 int depth = 0;
820 ccs_realpath_lock();
821 for (;;) {
822 if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
823 /* Global root? */
824 if (vfsmnt->mnt_parent == vfsmnt)
825 break;
826 dentry = vfsmnt->mnt_mountpoint;
827 vfsmnt = vfsmnt->mnt_parent;
828 continue;
829 }
830 dentry = dentry->d_parent;
831 depth++;
832 }
833 ccs_realpath_unlock();
834 return depth;
835 }
836
837 /**
838 * ccs_get_root_depth - return the depth of root directory.
839 *
840 * Returns number of directories to strip.
841 */
842 static int ccs_get_root_depth(void)
843 {
844 int depth;
845 struct dentry *dentry;
846 struct vfsmount *vfsmnt;
847 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
848 struct path root;
849 #endif
850 read_lock(&current->fs->lock);
851 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
852 root = current->fs->root;
853 path_get(&current->fs->root);
854 dentry = root.dentry;
855 vfsmnt = root.mnt;
856 #else
857 dentry = dget(current->fs->root);
858 vfsmnt = mntget(current->fs->rootmnt);
859 #endif
860 read_unlock(&current->fs->lock);
861 depth = ccs_root_depth(dentry, vfsmnt);
862 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
863 path_put(&root);
864 #else
865 dput(dentry);
866 mntput(vfsmnt);
867 #endif
868 return depth;
869 }
870
871 static LIST_HEAD(ccs_execve_list);
872 static DEFINE_SPINLOCK(ccs_execve_list_lock);
873
874 /**
875 * ccs_allocate_execve_entry - Allocate memory for execve().
876 *
877 * Returns pointer to "struct ccs_execve_entry" on success, NULL otherwise.
878 */
879 static struct ccs_execve_entry *ccs_allocate_execve_entry(void)
880 {
881 struct ccs_execve_entry *ee = kzalloc(sizeof(*ee), GFP_KERNEL);
882 if (!ee)
883 return NULL;
884 ee->tmp = kzalloc(CCS_EXEC_TMPSIZE, GFP_KERNEL);
885 if (!ee->tmp) {
886 kfree(ee);
887 return NULL;
888 }
889 ee->reader_idx = ccs_read_lock();
890 /* ee->dump->data is allocated by ccs_dump_page(). */
891 ee->task = current;
892 spin_lock(&ccs_execve_list_lock);
893 list_add(&ee->list, &ccs_execve_list);
894 spin_unlock(&ccs_execve_list_lock);
895 return ee;
896 }
897
898 /**
899 * ccs_find_execve_entry - Find ccs_execve_entry of current process.
900 *
901 * Returns pointer to "struct ccs_execve_entry" on success, NULL otherwise.
902 */
903 static struct ccs_execve_entry *ccs_find_execve_entry(void)
904 {
905 struct task_struct *task = current;
906 struct ccs_execve_entry *ee = NULL;
907 struct ccs_execve_entry *p;
908 spin_lock(&ccs_execve_list_lock);
909 list_for_each_entry(p, &ccs_execve_list, list) {
910 if (p->task != task)
911 continue;
912 ee = p;
913 break;
914 }
915 spin_unlock(&ccs_execve_list_lock);
916 return ee;
917 }
918
919 /**
920 * ccs_free_execve_entry - Free memory for execve().
921 *
922 * @ee: Pointer to "struct ccs_execve_entry".
923 */
924 static void ccs_free_execve_entry(struct ccs_execve_entry *ee)
925 {
926 if (!ee)
927 return;
928 spin_lock(&ccs_execve_list_lock);
929 list_del(&ee->list);
930 spin_unlock(&ccs_execve_list_lock);
931 kfree(ee->handler_path);
932 kfree(ee->tmp);
933 kfree(ee->dump.data);
934 ccs_read_unlock(ee->reader_idx);
935 kfree(ee);
936 }
937
938 /**
939 * ccs_try_alt_exec - Try to start execute handler.
940 *
941 * @ee: Pointer to "struct ccs_execve_entry".
942 *
943 * Returns 0 on success, negative value otherwise.
944 */
945 static int ccs_try_alt_exec(struct ccs_execve_entry *ee)
946 {
947 /*
948 * Contents of modified bprm.
949 * The envp[] in original bprm is moved to argv[] so that
950 * the alternatively executed program won't be affected by
951 * some dangerous environment variables like LD_PRELOAD.
952 *
953 * modified bprm->argc
954 * = original bprm->argc + original bprm->envc + 7
955 * modified bprm->envc
956 * = 0
957 *
958 * modified bprm->argv[0]
959 * = the program's name specified by execute_handler
960 * modified bprm->argv[1]
961 * = ccs_current_domain()->domainname->name
962 * modified bprm->argv[2]
963 * = the current process's name
964 * modified bprm->argv[3]
965 * = the current process's information (e.g. uid/gid).
966 * modified bprm->argv[4]
967 * = original bprm->filename
968 * modified bprm->argv[5]
969 * = original bprm->argc in string expression
970 * modified bprm->argv[6]
971 * = original bprm->envc in string expression
972 * modified bprm->argv[7]
973 * = original bprm->argv[0]
974 * ...
975 * modified bprm->argv[bprm->argc + 6]
976 * = original bprm->argv[bprm->argc - 1]
977 * modified bprm->argv[bprm->argc + 7]
978 * = original bprm->envp[0]
979 * ...
980 * modified bprm->argv[bprm->envc + bprm->argc + 6]
981 * = original bprm->envp[bprm->envc - 1]
982 */
983 struct linux_binprm *bprm = ee->bprm;
984 struct file *filp;
985 int retval;
986 const int original_argc = bprm->argc;
987 const int original_envc = bprm->envc;
988 struct task_struct *task = current;
989
990 /* Close the requested program's dentry. */
991 allow_write_access(bprm->file);
992 fput(bprm->file);
993 bprm->file = NULL;
994
995 /* Invalidate page dump cache. */
996 ee->dump.page = NULL;
997
998 /* Move envp[] to argv[] */
999 bprm->argc += bprm->envc;
1000 bprm->envc = 0;
1001
1002 /* Set argv[6] */
1003 {
1004 char *cp = ee->tmp;
1005 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_envc);
1006 retval = copy_strings_kernel(1, &cp, bprm);
1007 if (retval < 0)
1008 goto out;
1009 bprm->argc++;
1010 }
1011
1012 /* Set argv[5] */
1013 {
1014 char *cp = ee->tmp;
1015 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_argc);
1016 retval = copy_strings_kernel(1, &cp, bprm);
1017 if (retval < 0)
1018 goto out;
1019 bprm->argc++;
1020 }
1021
1022 /* Set argv[4] */
1023 {
1024 retval = copy_strings_kernel(1, &bprm->filename, bprm);
1025 if (retval < 0)
1026 goto out;
1027 bprm->argc++;
1028 }
1029
1030 /* Set argv[3] */
1031 {
1032 char *cp = ee->tmp;
1033 const u32 ccs_flags = task->ccs_flags;
1034 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1,
1035 "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d "
1036 "sgid=%d fsuid=%d fsgid=%d state[0]=%u "
1037 "state[1]=%u state[2]=%u",
1038 (pid_t) sys_getpid(), current_uid(), current_gid(),
1039 current_euid(), current_egid(), current_suid(),
1040 current_sgid(), current_fsuid(), current_fsgid(),
1041 (u8) (ccs_flags >> 24), (u8) (ccs_flags >> 16),
1042 (u8) (ccs_flags >> 8));
1043 retval = copy_strings_kernel(1, &cp, bprm);
1044 if (retval < 0)
1045 goto out;
1046 bprm->argc++;
1047 }
1048
1049 /* Set argv[2] */
1050 {
1051 char *exe = (char *) ccs_get_exe();
1052 if (exe) {
1053 retval = copy_strings_kernel(1, &exe, bprm);
1054 kfree(exe);
1055 } else {
1056 exe = ee->tmp;
1057 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "<unknown>");
1058 retval = copy_strings_kernel(1, &exe, bprm);
1059 }
1060 if (retval < 0)
1061 goto out;
1062 bprm->argc++;
1063 }
1064
1065 /* Set argv[1] */
1066 {
1067 char *cp = ee->tmp;
1068 snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s",
1069 ccs_current_domain()->domainname->name);
1070 retval = copy_strings_kernel(1, &cp, bprm);
1071 if (retval < 0)
1072 goto out;
1073 bprm->argc++;
1074 }
1075
1076 /* Set argv[0] */
1077 {
1078 int depth = ccs_get_root_depth();
1079 int len = ee->handler->total_len + 1;
1080 char *cp = kmalloc(len, GFP_KERNEL);
1081 if (!cp) {
1082 retval = ENOMEM;
1083 goto out;
1084 }
1085 ee->handler_path = cp;
1086 memmove(cp, ee->handler->name, len);
1087 ccs_unescape(cp);
1088 retval = -ENOENT;
1089 if (!*cp || *cp != '/')
1090 goto out;
1091 /* Adjust root directory for open_exec(). */
1092 while (depth) {
1093 cp = strchr(cp + 1, '/');
1094 if (!cp)
1095 goto out;
1096 depth--;
1097 }
1098 memmove(ee->handler_path, cp, strlen(cp) + 1);
1099 cp = ee->handler_path;
1100 retval = copy_strings_kernel(1, &cp, bprm);
1101 if (retval < 0)
1102 goto out;
1103 bprm->argc++;
1104 }
1105 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
1106 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24)
1107 bprm->argv_len = bprm->exec - bprm->p;
1108 #endif
1109 #endif
1110
1111 /* OK, now restart the process with execute handler program's dentry. */
1112 filp = open_exec(ee->handler_path);
1113 if (IS_ERR(filp)) {
1114 retval = PTR_ERR(filp);
1115 goto out;
1116 }
1117 bprm->file = filp;
1118 bprm->filename = ee->handler_path;
1119 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
1120 bprm->interp = bprm->filename;
1121 #endif
1122 retval = prepare_binprm(bprm);
1123 if (retval < 0)
1124 goto out;
1125 task->ccs_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1126 retval = ccs_find_next_domain(ee);
1127 task->ccs_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR;
1128 out:
1129 return retval;
1130 }
1131
1132 /**
1133 * ccs_find_execute_handler - Find an execute handler.
1134 *
1135 * @ee: Pointer to "struct ccs_execve_entry".
1136 * @type: Type of execute handler.
1137 *
1138 * Returns true if found, false otherwise.
1139 *
1140 * Caller holds ccs_read_lock().
1141 */
1142 static bool ccs_find_execute_handler(struct ccs_execve_entry *ee,
1143 const u8 type)
1144 {
1145 struct task_struct *task = current;
1146 const struct ccs_domain_info *domain = ccs_current_domain();
1147 struct ccs_acl_info *ptr;
1148 bool found = false;
1149 ccs_assert_read_lock();
1150 /*
1151 * Don't use execute handler if the current process is
1152 * marked as execute handler to avoid infinite execute handler loop.
1153 */
1154 if (task->ccs_flags & CCS_TASK_IS_EXECUTE_HANDLER)
1155 return false;
1156 list_for_each_entry(ptr, &domain->acl_info_list, list) {
1157 struct ccs_execute_handler_record *acl;
1158 if (ptr->type != type)
1159 continue;
1160 acl = container_of(ptr, struct ccs_execute_handler_record,
1161 head);
1162 ee->handler = acl->handler;
1163 found = true;
1164 break;
1165 }
1166 return found;
1167 }
1168
1169 /**
1170 * ccs_dump_page - Dump a page to buffer.
1171 *
1172 * @bprm: Pointer to "struct linux_binprm".
1173 * @pos: Location to dump.
1174 * @dump: Poiner to "struct ccs_page_dump".
1175 *
1176 * Returns true on success, false otherwise.
1177 */
1178 bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos,
1179 struct ccs_page_dump *dump)
1180 {
1181 struct page *page;
1182 /* dump->data is released by ccs_free_execve_entry(). */
1183 if (!dump->data) {
1184 dump->data = kzalloc(PAGE_SIZE, GFP_KERNEL);
1185 if (!dump->data)
1186 return false;
1187 }
1188 /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
1189 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)
1190 if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
1191 return false;
1192 #elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR == 3 && defined(CONFIG_MMU)
1193 if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
1194 return false;
1195 #else
1196 page = bprm->page[pos / PAGE_SIZE];
1197 #endif
1198 if (page != dump->page) {
1199 const unsigned int offset = pos % PAGE_SIZE;
1200 /*
1201 * Maybe kmap()/kunmap() should be used here.
1202 * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
1203 * So do I.
1204 */
1205 char *kaddr = kmap_atomic(page, KM_USER0);
1206 dump->page = page;
1207 memcpy(dump->data + offset, kaddr + offset, PAGE_SIZE - offset);
1208 kunmap_atomic(kaddr, KM_USER0);
1209 }
1210 /* Same with put_arg_page(page) in fs/exec.c */
1211 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU)
1212 put_page(page);
1213 #elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR == 3 && defined(CONFIG_MMU)
1214 put_page(page);
1215 #endif
1216 return true;
1217 }
1218
1219 /**
1220 * ccs_fetch_next_domain - Fetch next_domain from the list.
1221 *
1222 * Returns pointer to "struct ccs_domain_info" which will be used if execve()
1223 * succeeds. This function does not return NULL.
1224 */
1225 struct ccs_domain_info *ccs_fetch_next_domain(void)
1226 {
1227 struct ccs_execve_entry *ee = ccs_find_execve_entry();
1228 struct ccs_domain_info *next_domain = NULL;
1229 if (ee)
1230 next_domain = ee->r.domain;
1231 if (!next_domain)
1232 next_domain = ccs_current_domain();
1233 return next_domain;
1234 }
1235
1236 /**
1237 * ccs_start_execve - Prepare for execve() operation.
1238 *
1239 * @bprm: Pointer to "struct linux_binprm".
1240 *
1241 * Returns 0 on success, negative value otherwise.
1242 */
1243 int ccs_start_execve(struct linux_binprm *bprm)
1244 {
1245 int retval;
1246 struct task_struct *task = current;
1247 struct ccs_execve_entry *ee = ccs_allocate_execve_entry();
1248 if (!ccs_policy_loaded)
1249 ccs_load_policy(bprm->filename);
1250 if (!ee)
1251 return -ENOMEM;
1252 ccs_init_request_info(&ee->r, NULL, CCS_MAC_FILE_EXECUTE);
1253 ee->r.ee = ee;
1254 ee->bprm = bprm;
1255 ee->r.obj = &ee->obj;
1256 ee->obj.path1.dentry = bprm->file->f_dentry;
1257 ee->obj.path1.mnt = bprm->file->f_vfsmnt;
1258 /* Clear manager flag. */
1259 task->ccs_flags &= ~CCS_TASK_IS_POLICY_MANAGER;
1260 if (ccs_find_execute_handler(ee, CCS_TYPE_EXECUTE_HANDLER)) {
1261 retval = ccs_try_alt_exec(ee);
1262 if (!retval)
1263 ccs_audit_execute_handler_log(ee, true);
1264 goto ok;
1265 }
1266 retval = ccs_find_next_domain(ee);
1267 if (retval != -EPERM)
1268 goto ok;
1269 if (ccs_find_execute_handler(ee, CCS_TYPE_DENIED_EXECUTE_HANDLER)) {
1270 retval = ccs_try_alt_exec(ee);
1271 if (!retval)
1272 ccs_audit_execute_handler_log(ee, false);
1273 }
1274 ok:
1275 if (retval < 0)
1276 goto out;
1277 ee->r.mode = ccs_get_mode(ee->r.profile, CCS_MAC_ENVIRON);
1278 retval = ccs_environ(ee);
1279 if (retval < 0)
1280 goto out;
1281 task->ccs_flags |= CCS_CHECK_READ_FOR_OPEN_EXEC;
1282 retval = 0;
1283 out:
1284 if (retval)
1285 ccs_finish_execve(retval);
1286 return retval;
1287 }
1288
1289 /**
1290 * ccs_finish_execve - Clean up execve() operation.
1291 *
1292 * @retval: Return code of an execve() operation.
1293 *
1294 * Caller holds ccs_read_lock().
1295 */
1296 void ccs_finish_execve(int retval)
1297 {
1298 struct task_struct *task = current;
1299 struct ccs_execve_entry *ee = ccs_find_execve_entry();
1300 ccs_assert_read_lock();
1301 task->ccs_flags &= ~CCS_CHECK_READ_FOR_OPEN_EXEC;
1302 if (!ee)
1303 return;
1304 if (retval < 0)
1305 goto out;
1306 /* Proceed to next domain if execution suceeded. */
1307 task->ccs_domain_info = ee->r.domain;
1308 /* Mark the current process as execute handler. */
1309 if (ee->handler)
1310 task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER;
1311 /* Mark the current process as normal process. */
1312 else
1313 task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER;
1314 out:
1315 ccs_free_execve_entry(ee);
1316 }

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