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

Subversion リポジトリの参照

Contents of /branches/mod_tomoyo.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5673 - (show annotations) (download) (as text)
Tue Nov 29 08:08:15 2011 UTC (12 years, 6 months ago) by kumaneko
File MIME type: text/x-csrc
File size: 25140 byte(s)


1 /*
2 * mod_tomoyo.c - Apache module for TOMOYO Linux.
3 *
4 * About this module:
5 *
6 * This module allows Apache 2.x running on TOMOYO Linux kernels to process
7 * requests under different TOMOYO Linux's domains based on requested
8 * server's name (and optionally based on requested resource's pathname) by
9 * requesting TOMOYO Linux's domain transition before processing requests.
10 *
11 * Access restrictions are provided by TOMOYO Linux kernel's Mandatory Access
12 * Control functionality. Therefore, if you want Apache to process requests
13 * with limited set of permissions, you have to configure TOMOYO Linux's
14 * policy and assign "enforcing mode".
15 *
16 * Runtime dependency:
17 *
18 * TOMOYO Linux 2.5.0 (or later).
19 *
20 * How to build and install:
21 *
22 * Install packages needed for developing Apache modules and run below
23 * command. If your system has apxs2, use apxs2 rather than apxs.
24 *
25 * apxs -i -a -c mod_tomoyo.c
26 *
27 * How to configure:
28 *
29 * TOMOYO_TransitionMap directive is provided by this module.
30 * You may perform domain transition based on requested resource's pathname
31 * using this directive.
32 *
33 * This directive can appear in the server-wide configuration files
34 * (e.g., httpd.conf) outside <Directory> or <Location> containers.
35 *
36 * DocumentRoot /var/www/html/
37 * ServerName www.example.com
38 * TOMOYO_TransitionMap /etc/tomoyo/apache2_transition_table.conf
39 *
40 * <VirtualHost *:80>
41 * DocumentRoot /home/cat/html/
42 * ServerName cat.example.com
43 * TOMOYO_TransitionMap /home/cat/apache2_transition_table.conf
44 * </VirtualHost>
45 *
46 * <VirtualHost *:80>
47 * DocumentRoot /home/dog/html/
48 * ServerName dog.example.com
49 * TOMOYO_TransitionMap /home/dog/apache2_transition_table.conf
50 * </VirtualHost>
51 *
52 * This directive takes one parameter which specifies pathname to mapping
53 * table file. The mapping table file contains list of "pathname patterns"
54 * and "domainname" pairs, written in accordance with TOMOYO Linux's pathname
55 * representation rule and wildcard characters. For example,
56 *
57 * /var/www/cgi-bin/\* <kernel> //apache /www.example.com /cgi-programs
58 * /usr/share/horde/\{\*\}/\* <kernel> //apache /www.example.com /horde
59 * /var/www/html/\{\*\}/\* <kernel> //apache /www.example.com /static-files
60 *
61 * in /etc/tomoyo/apache2_transition_table.conf and
62 *
63 * /home/cat/html/\* <kernel> //apache /cat.example.com
64 * /home/cat/html/\{\*\}/\* <kernel> //apache /cat.example.com
65 *
66 * in /home/cat/apache2_transition_table.conf and
67 *
68 * /home/dog/html/\* <kernel> //apache /dog.example.com
69 * /home/dog/html/\{\*\}/\* <kernel> //apache /dog.example.com
70 *
71 * in /home/dog/apache2_transition_table.conf .
72 *
73 * You need to beforehand specify domainnames in the mapping table to
74 * /sys/kernel/security/tomoyo/domain_policy using "task manual_domain_transition" directive
75 * (e.g.
76 *
77 * <kernel> /usr/sbin/httpd
78 * task manual_domain_transition <kernel> //apache /www.example.com /cgi-programs
79 * task manual_domain_transition <kernel> //apache /www.example.com /horde
80 * task manual_domain_transition <kernel> //apache /www.example.com /static-files
81 * task manual_domain_transition <kernel> //apache /cat.example.com
82 * task manual_domain_transition <kernel> //apache /dog.example.com
83 *
84 * ).
85 *
86 * If the requested pathname did not match the pathname patterns listed in
87 * the mapping table file, the request will fail with internal error.
88 * Be sure to cover all possible pathnames you want to allow access.
89 *
90 * If you want to use this module for separating virtual hosts and not for
91 * separating permissions within a virtual host, you can specify like
92 *
93 * /var/www/cgi-bin/\* <kernel> //apache /www.example.com
94 * /usr/share/horde/\{\*\}/\* <kernel> //apache /www.example.com
95 * /var/www/html/\{\*\}/\* <kernel> //apache /www.example.com
96 *
97 * in /etc/tomoyo/apache2_transition_table.conf and
98 *
99 * /home/cat/html/\* <kernel> //apache /cat.example.com
100 * /home/cat/html/\{\*\}/\* <kernel> //apache /cat.example.com
101 *
102 * in /home/cat/apache2_transition_table.conf and
103 *
104 * /home/dog/html/\* <kernel> //apache /dog.example.com
105 * /home/dog/html/\{\*\}/\* <kernel> //apache /dog.example.com
106 *
107 * in /home/dog/apache2_transition_table.conf .
108 *
109 * Author:
110 *
111 * Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
112 *
113 * The idea to use one-time worker thread is borrowed from mod_selinux.c
114 * developed by KaiGai Kohei.
115 */
116 #include <stdio.h>
117 #include <string.h>
118 #include <unistd.h>
119 #include <fcntl.h>
120
121 #define s8 char
122 #define u8 unsigned char
123 #define bool _Bool
124 #define false 0
125 #define true 1
126
127 /**
128 * ccs_is_byte_range - Check whether the string is a \ooo style octal value.
129 *
130 * @str: Pointer to the string.
131 *
132 * Returns true if @str is a \ooo style octal value, false otherwise.
133 */
134 static inline bool ccs_is_byte_range(const char *str)
135 {
136 return *str >= '0' && *str++ <= '3' &&
137 *str >= '0' && *str++ <= '7' &&
138 *str >= '0' && *str <= '7';
139 }
140
141 /**
142 * ccs_is_decimal - Check whether the character is a decimal character.
143 *
144 * @c: The character to check.
145 *
146 * Returns true if @c is a decimal character, false otherwise.
147 */
148 static inline bool ccs_is_decimal(const char c)
149 {
150 return c >= '0' && c <= '9';
151 }
152
153 /**
154 * ccs_is_hexadecimal - Check whether the character is a hexadecimal character.
155 *
156 * @c: The character to check.
157 *
158 * Returns true if @c is a hexadecimal character, false otherwise.
159 */
160 static inline bool ccs_is_hexadecimal(const char c)
161 {
162 return (c >= '0' && c <= '9') ||
163 (c >= 'A' && c <= 'F') ||
164 (c >= 'a' && c <= 'f');
165 }
166
167 /**
168 * ccs_is_alphabet_char - Check whether the character is an alphabet.
169 *
170 * @c: The character to check.
171 *
172 * Returns true if @c is an alphabet character, false otherwise.
173 */
174 static inline bool ccs_is_alphabet_char(const char c)
175 {
176 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
177 }
178
179 /**
180 * ccs_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern.
181 *
182 * @filename: The start of string to check.
183 * @filename_end: The end of string to check.
184 * @pattern: The start of pattern to compare.
185 * @pattern_end: The end of pattern to compare.
186 *
187 * Returns true if @filename matches @pattern, false otherwise.
188 */
189 static bool ccs_file_matches_pattern2(const char *filename,
190 const char *filename_end,
191 const char *pattern,
192 const char *pattern_end)
193 {
194 while (filename < filename_end && pattern < pattern_end) {
195 char c;
196 if (*pattern != '\\') {
197 if (*filename++ != *pattern++)
198 return false;
199 continue;
200 }
201 c = *filename;
202 pattern++;
203 switch (*pattern) {
204 int i;
205 int j;
206 case '?':
207 if (c == '/') {
208 return false;
209 } else if (c == '\\') {
210 if (filename[1] == '\\')
211 filename++;
212 else if (ccs_is_byte_range(filename + 1))
213 filename += 3;
214 else
215 return false;
216 }
217 break;
218 case '\\':
219 if (c != '\\')
220 return false;
221 if (*++filename != '\\')
222 return false;
223 break;
224 case '+':
225 if (!ccs_is_decimal(c))
226 return false;
227 break;
228 case 'x':
229 if (!ccs_is_hexadecimal(c))
230 return false;
231 break;
232 case 'a':
233 if (!ccs_is_alphabet_char(c))
234 return false;
235 break;
236 case '0':
237 case '1':
238 case '2':
239 case '3':
240 if (c == '\\' && ccs_is_byte_range(filename + 1)
241 && !strncmp(filename + 1, pattern, 3)) {
242 filename += 3;
243 pattern += 2;
244 break;
245 }
246 return false; /* Not matched. */
247 case '*':
248 case '@':
249 for (i = 0; i <= filename_end - filename; i++) {
250 if (ccs_file_matches_pattern2(filename + i,
251 filename_end,
252 pattern + 1,
253 pattern_end))
254 return true;
255 c = filename[i];
256 if (c == '.' && *pattern == '@')
257 break;
258 if (c != '\\')
259 continue;
260 if (filename[i + 1] == '\\')
261 i++;
262 else if (ccs_is_byte_range(filename + i + 1))
263 i += 3;
264 else
265 break; /* Bad pattern. */
266 }
267 return false; /* Not matched. */
268 default:
269 j = 0;
270 c = *pattern;
271 if (c == '$') {
272 while (ccs_is_decimal(filename[j]))
273 j++;
274 } else if (c == 'X') {
275 while (ccs_is_hexadecimal(filename[j]))
276 j++;
277 } else if (c == 'A') {
278 while (ccs_is_alphabet_char(filename[j]))
279 j++;
280 }
281 for (i = 1; i <= j; i++) {
282 if (ccs_file_matches_pattern2(filename + i,
283 filename_end,
284 pattern + 1,
285 pattern_end))
286 return true;
287 }
288 return false; /* Not matched or bad pattern. */
289 }
290 filename++;
291 pattern++;
292 }
293 while (*pattern == '\\' &&
294 (*(pattern + 1) == '*' || *(pattern + 1) == '@'))
295 pattern += 2;
296 return filename == filename_end && pattern == pattern_end;
297 }
298
299 /**
300 * ccs_file_matches_pattern - Pattern matching without '/' character.
301 *
302 * @filename: The start of string to check.
303 * @filename_end: The end of string to check.
304 * @pattern: The start of pattern to compare.
305 * @pattern_end: The end of pattern to compare.
306 *
307 * Returns true if @filename matches @pattern, false otherwise.
308 */
309 static bool ccs_file_matches_pattern(const char *filename,
310 const char *filename_end,
311 const char *pattern,
312 const char *pattern_end)
313 {
314 const char *pattern_start = pattern;
315 bool first = true;
316 bool result;
317 while (pattern < pattern_end - 1) {
318 /* Split at "\-" pattern. */
319 if (*pattern++ != '\\' || *pattern++ != '-')
320 continue;
321 result = ccs_file_matches_pattern2(filename, filename_end,
322 pattern_start, pattern - 2);
323 if (first)
324 result = !result;
325 if (result)
326 return false;
327 first = false;
328 pattern_start = pattern;
329 }
330 result = ccs_file_matches_pattern2(filename, filename_end,
331 pattern_start, pattern_end);
332 return first ? result : !result;
333 }
334
335 /**
336 * ccs_path_matches_pattern2 - Do pathname pattern matching.
337 *
338 * @f: The start of string to check.
339 * @p: The start of pattern to compare.
340 *
341 * Returns true if @f matches @p, false otherwise.
342 */
343 static bool ccs_path_matches_pattern2(const char *f, const char *p)
344 {
345 const char *f_delimiter;
346 const char *p_delimiter;
347 while (*f && *p) {
348 f_delimiter = strchr(f, '/');
349 if (!f_delimiter)
350 f_delimiter = f + strlen(f);
351 p_delimiter = strchr(p, '/');
352 if (!p_delimiter)
353 p_delimiter = p + strlen(p);
354 if (*p == '\\' && *(p + 1) == '{')
355 goto recursive;
356 if (!ccs_file_matches_pattern(f, f_delimiter, p, p_delimiter))
357 return false;
358 f = f_delimiter;
359 if (*f)
360 f++;
361 p = p_delimiter;
362 if (*p)
363 p++;
364 }
365 /* Ignore trailing "\*" and "\@" in @pattern. */
366 while (*p == '\\' &&
367 (*(p + 1) == '*' || *(p + 1) == '@'))
368 p += 2;
369 return !*f && !*p;
370 recursive:
371 /*
372 * The "\{" pattern is permitted only after '/' character.
373 * This guarantees that below "*(p - 1)" is safe.
374 * Also, the "\}" pattern is permitted only before '/' character
375 * so that "\{" + "\}" pair will not break the "\-" operator.
376 */
377 if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' ||
378 *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\')
379 return false; /* Bad pattern. */
380 do {
381 /* Compare current component with pattern. */
382 if (!ccs_file_matches_pattern(f, f_delimiter, p + 2,
383 p_delimiter - 2))
384 break;
385 /* Proceed to next component. */
386 f = f_delimiter;
387 if (!*f)
388 break;
389 f++;
390 /* Continue comparison. */
391 if (ccs_path_matches_pattern2(f, p_delimiter + 1))
392 return true;
393 f_delimiter = strchr(f, '/');
394 } while (f_delimiter);
395 return false; /* Not matched. */
396 }
397
398 /**
399 * ccs_const_part_length - Evaluate the initial length without a pattern in a token.
400 *
401 * @filename: The string to evaluate.
402 *
403 * Returns the initial length without a pattern in @filename.
404 */
405 static int ccs_const_part_length(const char *filename)
406 {
407 char c;
408 int len = 0;
409 if (!filename)
410 return 0;
411 while (1) {
412 c = *filename++;
413 if (!c)
414 break;
415 if (c != '\\') {
416 len++;
417 continue;
418 }
419 c = *filename++;
420 switch (c) {
421 case '\\': /* "\\" */
422 len += 2;
423 continue;
424 case '0': /* "\ooo" */
425 case '1':
426 case '2':
427 case '3':
428 c = *filename++;
429 if (c < '0' || c > '7')
430 break;
431 c = *filename++;
432 if (c < '0' || c > '7')
433 break;
434 len += 4;
435 continue;
436 }
437 break;
438 }
439 return len;
440 }
441
442 /**
443 * ccs_path_matches_pattern - Check whether the given filename matches the given pattern.
444 *
445 * @filename: The filename to check.
446 * @pattern: The pattern to compare.
447 *
448 * Returns true if matches, false otherwise.
449 *
450 * The following patterns are available.
451 * \\ \ itself.
452 * \ooo Octal representation of a byte.
453 * \* Zero or more repetitions of characters other than '/'.
454 * \@ Zero or more repetitions of characters other than '/' or '.'.
455 * \? 1 byte character other than '/'.
456 * \$ One or more repetitions of decimal digits.
457 * \+ 1 decimal digit.
458 * \X One or more repetitions of hexadecimal digits.
459 * \x 1 hexadecimal digit.
460 * \A One or more repetitions of alphabet characters.
461 * \a 1 alphabet character.
462 *
463 * \- Subtraction operator.
464 *
465 * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/
466 * /dir/dir/dir/ ).
467 */
468 static bool ccs_path_matches_pattern(const char *filename, const char *pattern)
469 {
470 const char *f = filename;
471 const char *p = pattern;
472 const int len = ccs_const_part_length(pattern);
473 /* If @pattern doesn't contain pattern, I can use strcmp(). */
474 if (len == strlen(pattern))
475 return !strcmp(filename, pattern);
476 /* Compare the initial length without patterns. */
477 if (strncmp(f, p, len))
478 return false;
479 f += len;
480 p += len;
481 return ccs_path_matches_pattern2(f, p);
482 }
483
484 /**
485 * ccs_normalize_line - Format string.
486 *
487 * @line: The line to normalize.
488 *
489 * Leading and trailing whitespaces are removed.
490 * Multiple whitespaces are packed into single space.
491 *
492 * Returns nothing.
493 */
494 static void ccs_normalize_line(unsigned char *line)
495 {
496 unsigned char *sp = line;
497 unsigned char *dp = line;
498 _Bool first = true;
499 while (*sp && (*sp <= ' ' || 127 <= *sp))
500 sp++;
501 while (*sp) {
502 if (!first)
503 *dp++ = ' ';
504 first = false;
505 while (' ' < *sp && *sp < 127)
506 *dp++ = *sp++;
507 while (*sp && (*sp <= ' ' || 127 <= *sp))
508 sp++;
509 }
510 *dp = '\0';
511 }
512
513 /**
514 * ccs_make_byte - Make byte value from three octal characters.
515 *
516 * @c1: The first character.
517 * @c2: The second character.
518 * @c3: The third character.
519 *
520 * Returns byte value.
521 */
522 static u8 ccs_make_byte(const u8 c1, const u8 c2, const u8 c3)
523 {
524 return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0');
525 }
526
527 /**
528 * ccs_correct_word2 - Check whether the given string follows the naming rules.
529 *
530 * @string: The byte sequence to check. Not '\0'-terminated.
531 * @len: Length of @string.
532 *
533 * Returns true if @string follows the naming rules, false otherwise.
534 */
535 static bool ccs_correct_word2(const char *string, size_t len)
536 {
537 const char *const start = string;
538 bool in_repetition = false;
539 unsigned char c;
540 unsigned char d;
541 unsigned char e;
542 if (!len)
543 goto out;
544 while (len--) {
545 c = *string++;
546 if (c == '\\') {
547 if (!len--)
548 goto out;
549 c = *string++;
550 switch (c) {
551 case '\\': /* "\\" */
552 continue;
553 case '$': /* "\$" */
554 case '+': /* "\+" */
555 case '?': /* "\?" */
556 case '*': /* "\*" */
557 case '@': /* "\@" */
558 case 'x': /* "\x" */
559 case 'X': /* "\X" */
560 case 'a': /* "\a" */
561 case 'A': /* "\A" */
562 case '-': /* "\-" */
563 continue;
564 case '{': /* "/\{" */
565 if (string - 3 < start || *(string - 3) != '/')
566 break;
567 in_repetition = true;
568 continue;
569 case '}': /* "\}/" */
570 if (*string != '/')
571 break;
572 if (!in_repetition)
573 break;
574 in_repetition = false;
575 continue;
576 case '0': /* "\ooo" */
577 case '1':
578 case '2':
579 case '3':
580 if (!len-- || !len--)
581 break;
582 d = *string++;
583 e = *string++;
584 if (d < '0' || d > '7' || e < '0' || e > '7')
585 break;
586 c = ccs_make_byte(c, d, e);
587 if (c <= ' ' || c >= 127)
588 continue;
589 }
590 goto out;
591 } else if (in_repetition && c == '/') {
592 goto out;
593 } else if (c <= ' ' || c >= 127) {
594 goto out;
595 }
596 }
597 if (in_repetition)
598 goto out;
599 return true;
600 out:
601 return false;
602 }
603
604 /**
605 * ccs_correct_word - Check whether the given string follows the naming rules.
606 *
607 * @string: The string to check.
608 *
609 * Returns true if @string follows the naming rules, false otherwise.
610 */
611 static bool ccs_correct_word(const char *string)
612 {
613 return ccs_correct_word2(string, strlen(string));
614 }
615
616 /**
617 * ccs_correct_path - Check whether the given pathname follows the naming rules.
618 *
619 * @filename: The pathname to check.
620 *
621 * Returns true if @filename follows the naming rules, false otherwise.
622 */
623 static bool ccs_correct_path(const char *filename)
624 {
625 return *filename == '/' && ccs_correct_word(filename);
626 }
627
628 /**
629 * ccs_domain_def - Check whether the given token can be a domainname.
630 *
631 * @buffer: The token to check.
632 *
633 * Returns true if @buffer possibly be a domainname, false otherwise.
634 */
635 static bool ccs_domain_def(const char *buffer)
636 {
637 const char *cp;
638 int len;
639 if (*buffer != '<')
640 return false;
641 cp = strchr(buffer, ' ');
642 if (!cp)
643 len = strlen(buffer);
644 else
645 len = cp - buffer;
646 if (buffer[len - 1] != '>' || !ccs_correct_word2(buffer + 1, len - 2))
647 return false;
648 return true;
649 }
650
651 /**
652 * ccs_correct_domain - Check whether the given domainname follows the naming rules.
653 *
654 * @domainname: The domainname to check.
655 *
656 * Returns true if @domainname follows the naming rules, false otherwise.
657 */
658 static bool ccs_correct_domain(const char *domainname)
659 {
660 if (!domainname || !ccs_domain_def(domainname))
661 return false;
662 domainname = strchr(domainname, ' ');
663 if (!domainname++)
664 return true;
665 while (1) {
666 const char *cp = strchr(domainname, ' ');
667 if (!cp)
668 break;
669 if (*domainname != '/' ||
670 !ccs_correct_word2(domainname, cp - domainname))
671 return false;
672 domainname = cp + 1;
673 }
674 return ccs_correct_path(domainname);
675 }
676
677 #include "httpd.h"
678 #include "apr_strings.h"
679 #include "ap_listen.h"
680 #include "http_log.h"
681
682 module AP_MODULE_DECLARE_DATA ccs_module;
683
684 static int ccs_transition_fd = EOF;
685 static int ccs_open_error = 0;
686
687 struct ccs_map_entry {
688 const char *pathname;
689 const char *domainname;
690 };
691
692 struct ccs_map_table {
693 struct ccs_map_entry *entry;
694 int len;
695 };
696
697 static char *ccs_encode_string(const char *str)
698 {
699 char *cp = malloc(strlen(str) * 4 + 1);
700 char *cp0 = cp;
701 if (!cp)
702 return NULL;
703 while (*str) {
704 const unsigned char c = *str++;
705 if (c == '\\') {
706 *cp++ = '\\';
707 *cp++ = '\\';
708 } else if (c > ' ' && c < 127) {
709 *cp++ = c;
710 } else {
711 *cp++ = '\\';
712 *cp++ = (c >> 6) + '0';
713 *cp++ = ((c >> 3) & 7) + '0';
714 *cp++ = (c & 7) + '0';
715 }
716 }
717 *cp = '\0';
718 return cp0;
719 }
720
721 static _Bool ccs_set_context(request_rec *r)
722 {
723 struct ccs_map_table *ptr =
724 ap_get_module_config(r->server->module_config, &ccs_module);
725 int i;
726 /* Transit domain by requested pathname. */
727 const char *name = ccs_encode_string(r->filename);
728 if (!name) {
729 ap_log_rerror(APLOG_MARK, APLOG_ERR, EPERM, r, "mod_tomoyo: "
730 "Unable to set security context. "
731 "Out of memory.");
732 return 0;
733 }
734 for (i = 0; i < ptr->len; i++) {
735 int len;
736 if (!ccs_path_matches_pattern(name, ptr->entry[i].pathname))
737 continue;
738 free((void *) name);
739 name = ptr->entry[i].domainname;
740 len = strlen(name) + 1;
741 if (write(ccs_transition_fd, name, len) == len)
742 return 1;
743 ap_log_rerror(APLOG_MARK, APLOG_ERR, EPERM, r, "mod_tomoyo: "
744 "Unable to set security context. "
745 "Can't transit to %s", name);
746 return 0;
747 }
748 ap_log_rerror(APLOG_MARK, APLOG_ERR, EPERM, r, "mod_tomoyo: "
749 "Unable to set security context. "
750 "No matching entry for %s (%u entries for %s)", name,
751 ptr->len, r->hostname);
752 free((void *) name);
753 return 0;
754 }
755
756 static int __thread volatile am_worker = 0;
757
758 static void *APR_THREAD_FUNC ccs_worker_handler(apr_thread_t *thread,
759 void *data)
760 {
761 request_rec *r = (request_rec *) data;
762 int result = HTTP_INTERNAL_SERVER_ERROR;
763 am_worker = 1;
764 /* Set security context. */
765 if (ccs_set_context(r)) {
766 /*
767 * Invoke content handler again.
768 * Thread local variable am_worker prevents from
769 * being called infinitely.
770 */
771 result = ap_run_handler(r);
772 if (result == DECLINED)
773 result = HTTP_INTERNAL_SERVER_ERROR;
774 }
775 apr_thread_exit(thread, result);
776 return NULL;
777 }
778
779 static int ccs_handler(request_rec *r)
780 {
781 apr_threadattr_t *thread_attr = NULL;
782 apr_thread_t *thread = NULL;
783 apr_status_t rv;
784 apr_status_t thread_rv;
785 if (am_worker)
786 return DECLINED;
787 if (ccs_open_error) {
788 ap_log_rerror(APLOG_MARK, APLOG_ERR, ccs_open_error, r,
789 "mod_tomoyo: Unable to open "
790 "/sys/kernel/security/tomoyo/self_domain "
791 "for writing.");
792 return HTTP_INTERNAL_SERVER_ERROR;
793 }
794 if (ccs_transition_fd == EOF)
795 return DECLINED;
796 apr_threadattr_create(&thread_attr, r->pool);
797 apr_threadattr_detach_set(thread_attr, 0);
798 rv = apr_thread_create(&thread, thread_attr, ccs_worker_handler, r,
799 r->pool);
800 if (rv != APR_SUCCESS) {
801 ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, "mod_tomoyo: "
802 "Unable to launch a one-time worker thread.");
803 return HTTP_INTERNAL_SERVER_ERROR;
804 }
805 rv = apr_thread_join(&thread_rv, thread);
806 if (rv != APR_SUCCESS) {
807 ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, "mod_tomoyo: "
808 "Unable to join the one-time worker thread.");
809 r->connection->aborted = 1;
810 return HTTP_INTERNAL_SERVER_ERROR;
811 }
812 return thread_rv;
813 }
814
815 static void ccs_hooks(apr_pool_t *p)
816 {
817 ap_hook_handler(ccs_handler, NULL, NULL, APR_HOOK_REALLY_FIRST);
818 }
819
820 static void *ccs_create_server_config(apr_pool_t *p, server_rec *s)
821 {
822 void *ptr = apr_palloc(p, sizeof(struct ccs_map_table));
823 if (ptr)
824 memset(ptr, 0, sizeof(struct ccs_map_table));
825 /*
826 * We can share because /sys/kernel/security/tomoyo/self_domain
827 * interface has no data.
828 */
829 if (ccs_transition_fd == EOF) {
830 ccs_transition_fd =
831 open("/sys/kernel/security/tomoyo/self_domain",
832 O_WRONLY);
833 /*
834 * Some access control mechanisms might reject opening
835 * /sys/kernel/security/tomoyo/self_domain for writing.
836 * This failure is reported by ccs_parse_table() if
837 * TOMOYO_TransitionMap keyword is specified, by ccs_handler()
838 * otherwise.
839 */
840 if (ccs_transition_fd == EOF && errno != ENOENT)
841 ccs_open_error = errno;
842 }
843 /* Allocation failure is reported by ccs_parse_table(). */
844 return ptr;
845 }
846
847 static const char *ccs_parse_table(cmd_parms *parms, void *mconfig,
848 const char *args)
849 {
850 static const int buffer_len = 8192;
851 char *buffer = apr_palloc(parms->pool, buffer_len);
852 int line = 0;
853 FILE *fp = NULL;
854 struct ccs_map_table *ptr =
855 ap_get_module_config(parms->server->module_config,
856 &ccs_module);
857 if (!ptr || !buffer)
858 goto no_memory;
859 if (ccs_open_error)
860 goto no_interface;
861 fp = fopen(args, "r");
862 if (!fp)
863 goto no_file;
864 {
865 int c;
866 while ((c = fgetc(fp)) != EOF)
867 if (c == '\n')
868 line++;
869 if (!line) {
870 fclose(fp);
871 goto no_file;
872 }
873 }
874 ptr->entry = apr_palloc(parms->pool,
875 line * sizeof(struct ccs_map_entry));
876 if (!ptr->entry)
877 goto no_memory;
878 ptr->len = line;
879 line = 0;
880 rewind(fp);
881 memset(buffer, 0, buffer_len);
882 while (fgets(buffer, buffer_len - 1, fp)) {
883 char *cp = strchr(buffer, '\n');
884 if (line == ptr->len)
885 goto invalid_line;
886 if (!cp) {
887 fclose(fp);
888 snprintf(buffer, buffer_len - 1, "mod_tomoyo: "
889 "Line %u of %s : Too long.", line + 1, args);
890 return buffer;
891 }
892 ccs_normalize_line((unsigned char *) buffer);
893 cp = strchr(buffer, ' ');
894 if (!cp)
895 goto invalid_line;
896 *cp++ = '\0';
897 if (!ccs_correct_path(buffer)) {
898 fclose(fp);
899 snprintf(buffer, buffer_len - 1, "mod_tomoyo: "
900 "Line %u of %s : Bad pathname.", line + 1,
901 args);
902 return buffer;
903 }
904 if (!ccs_correct_domain(cp)) {
905 fclose(fp);
906 snprintf(buffer, buffer_len - 1, "mod_tomoyo: "
907 "Line %u of %s : Bad domainname.", line + 1,
908 args);
909 return buffer;
910 }
911 cp = apr_pstrdup(parms->pool, cp);
912 if (!cp)
913 goto no_memory;
914 ptr->entry[line].domainname = cp;
915 cp = apr_pstrdup(parms->pool, buffer);
916 if (!cp)
917 goto no_memory;
918 ptr->entry[line++].pathname = cp;
919 }
920 fclose(fp);
921 return NULL;
922 no_memory:
923 if (fp)
924 fclose(fp);
925 return "mod_tomoyo: Out of memory.";
926 no_interface:
927 snprintf(buffer, buffer_len - 1,
928 "mod_tomoyo: Unable to open "
929 "/sys/kernel/security/tomoyo/self_domain for writing. "
930 "(errno = %d)", ccs_open_error);
931 return buffer;
932 no_file:
933 snprintf(buffer, buffer_len - 1, "mod_tomoyo: %s : Can't read.", args);
934 return buffer;
935 invalid_line:
936 fclose(fp);
937 snprintf(buffer, buffer_len - 1, "mod_tomoyo: "
938 "Line %u of %s : Bad line.", line + 1, args);
939 return buffer;
940 }
941
942 static command_rec ccs_cmds[2] = {
943 AP_INIT_RAW_ARGS("TOMOYO_TransitionMap", ccs_parse_table, NULL,
944 RSRC_CONF, "Path to path/domain mapping table."),
945 { NULL }
946 };
947
948 module AP_MODULE_DECLARE_DATA ccs_module = {
949 STANDARD20_MODULE_STUFF, NULL, NULL,
950 ccs_create_server_config, NULL, ccs_cmds, ccs_hooks
951 };

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