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

Subversion リポジトリの参照

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

Parent Directory Parent Directory | Revision Log Revision Log


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

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