diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/tomoyo/common.c | 200 | ||||
-rw-r--r-- | security/tomoyo/common.h | 4 |
2 files changed, 121 insertions, 83 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 3c8bd8ee0b95..e0d0354008b7 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c | |||
@@ -187,6 +187,8 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, | |||
187 | const s8 pattern_type, const s8 end_type, | 187 | const s8 pattern_type, const s8 end_type, |
188 | const char *function) | 188 | const char *function) |
189 | { | 189 | { |
190 | const char *const start = filename; | ||
191 | bool in_repetition = false; | ||
190 | bool contains_pattern = false; | 192 | bool contains_pattern = false; |
191 | unsigned char c; | 193 | unsigned char c; |
192 | unsigned char d; | 194 | unsigned char d; |
@@ -212,9 +214,13 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, | |||
212 | if (c == '/') | 214 | if (c == '/') |
213 | goto out; | 215 | goto out; |
214 | } | 216 | } |
215 | while ((c = *filename++) != '\0') { | 217 | while (1) { |
218 | c = *filename++; | ||
219 | if (!c) | ||
220 | break; | ||
216 | if (c == '\\') { | 221 | if (c == '\\') { |
217 | switch ((c = *filename++)) { | 222 | c = *filename++; |
223 | switch (c) { | ||
218 | case '\\': /* "\\" */ | 224 | case '\\': /* "\\" */ |
219 | continue; | 225 | continue; |
220 | case '$': /* "\$" */ | 226 | case '$': /* "\$" */ |
@@ -231,6 +237,22 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, | |||
231 | break; /* Must not contain pattern */ | 237 | break; /* Must not contain pattern */ |
232 | contains_pattern = true; | 238 | contains_pattern = true; |
233 | continue; | 239 | continue; |
240 | case '{': /* "/\{" */ | ||
241 | if (filename - 3 < start || | ||
242 | *(filename - 3) != '/') | ||
243 | break; | ||
244 | if (pattern_type == -1) | ||
245 | break; /* Must not contain pattern */ | ||
246 | contains_pattern = true; | ||
247 | in_repetition = true; | ||
248 | continue; | ||
249 | case '}': /* "\}/" */ | ||
250 | if (*filename != '/') | ||
251 | break; | ||
252 | if (!in_repetition) | ||
253 | break; | ||
254 | in_repetition = false; | ||
255 | continue; | ||
234 | case '0': /* "\ooo" */ | 256 | case '0': /* "\ooo" */ |
235 | case '1': | 257 | case '1': |
236 | case '2': | 258 | case '2': |
@@ -246,6 +268,8 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, | |||
246 | continue; /* pattern is not \000 */ | 268 | continue; /* pattern is not \000 */ |
247 | } | 269 | } |
248 | goto out; | 270 | goto out; |
271 | } else if (in_repetition && c == '/') { | ||
272 | goto out; | ||
249 | } else if (tomoyo_is_invalid(c)) { | 273 | } else if (tomoyo_is_invalid(c)) { |
250 | goto out; | 274 | goto out; |
251 | } | 275 | } |
@@ -254,6 +278,8 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, | |||
254 | if (!contains_pattern) | 278 | if (!contains_pattern) |
255 | goto out; | 279 | goto out; |
256 | } | 280 | } |
281 | if (in_repetition) | ||
282 | goto out; | ||
257 | return true; | 283 | return true; |
258 | out: | 284 | out: |
259 | printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function, | 285 | printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function, |
@@ -360,33 +386,6 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) | |||
360 | } | 386 | } |
361 | 387 | ||
362 | /** | 388 | /** |
363 | * tomoyo_path_depth - Evaluate the number of '/' in a string. | ||
364 | * | ||
365 | * @pathname: The string to evaluate. | ||
366 | * | ||
367 | * Returns path depth of the string. | ||
368 | * | ||
369 | * I score 2 for each of the '/' in the @pathname | ||
370 | * and score 1 if the @pathname ends with '/'. | ||
371 | */ | ||
372 | static int tomoyo_path_depth(const char *pathname) | ||
373 | { | ||
374 | int i = 0; | ||
375 | |||
376 | if (pathname) { | ||
377 | const char *ep = pathname + strlen(pathname); | ||
378 | if (pathname < ep--) { | ||
379 | if (*ep != '/') | ||
380 | i++; | ||
381 | while (pathname <= ep) | ||
382 | if (*ep-- == '/') | ||
383 | i += 2; | ||
384 | } | ||
385 | } | ||
386 | return i; | ||
387 | } | ||
388 | |||
389 | /** | ||
390 | * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. | 389 | * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. |
391 | * | 390 | * |
392 | * @filename: The string to evaluate. | 391 | * @filename: The string to evaluate. |
@@ -444,11 +443,10 @@ void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) | |||
444 | ptr->is_dir = len && (name[len - 1] == '/'); | 443 | ptr->is_dir = len && (name[len - 1] == '/'); |
445 | ptr->is_patterned = (ptr->const_len < len); | 444 | ptr->is_patterned = (ptr->const_len < len); |
446 | ptr->hash = full_name_hash(name, len); | 445 | ptr->hash = full_name_hash(name, len); |
447 | ptr->depth = tomoyo_path_depth(name); | ||
448 | } | 446 | } |
449 | 447 | ||
450 | /** | 448 | /** |
451 | * tomoyo_file_matches_to_pattern2 - Pattern matching without '/' character | 449 | * tomoyo_file_matches_pattern2 - Pattern matching without '/' character |
452 | * and "\-" pattern. | 450 | * and "\-" pattern. |
453 | * | 451 | * |
454 | * @filename: The start of string to check. | 452 | * @filename: The start of string to check. |
@@ -458,10 +456,10 @@ void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) | |||
458 | * | 456 | * |
459 | * Returns true if @filename matches @pattern, false otherwise. | 457 | * Returns true if @filename matches @pattern, false otherwise. |
460 | */ | 458 | */ |
461 | static bool tomoyo_file_matches_to_pattern2(const char *filename, | 459 | static bool tomoyo_file_matches_pattern2(const char *filename, |
462 | const char *filename_end, | 460 | const char *filename_end, |
463 | const char *pattern, | 461 | const char *pattern, |
464 | const char *pattern_end) | 462 | const char *pattern_end) |
465 | { | 463 | { |
466 | while (filename < filename_end && pattern < pattern_end) { | 464 | while (filename < filename_end && pattern < pattern_end) { |
467 | char c; | 465 | char c; |
@@ -519,7 +517,7 @@ static bool tomoyo_file_matches_to_pattern2(const char *filename, | |||
519 | case '*': | 517 | case '*': |
520 | case '@': | 518 | case '@': |
521 | for (i = 0; i <= filename_end - filename; i++) { | 519 | for (i = 0; i <= filename_end - filename; i++) { |
522 | if (tomoyo_file_matches_to_pattern2( | 520 | if (tomoyo_file_matches_pattern2( |
523 | filename + i, filename_end, | 521 | filename + i, filename_end, |
524 | pattern + 1, pattern_end)) | 522 | pattern + 1, pattern_end)) |
525 | return true; | 523 | return true; |
@@ -550,7 +548,7 @@ static bool tomoyo_file_matches_to_pattern2(const char *filename, | |||
550 | j++; | 548 | j++; |
551 | } | 549 | } |
552 | for (i = 1; i <= j; i++) { | 550 | for (i = 1; i <= j; i++) { |
553 | if (tomoyo_file_matches_to_pattern2( | 551 | if (tomoyo_file_matches_pattern2( |
554 | filename + i, filename_end, | 552 | filename + i, filename_end, |
555 | pattern + 1, pattern_end)) | 553 | pattern + 1, pattern_end)) |
556 | return true; | 554 | return true; |
@@ -567,7 +565,7 @@ static bool tomoyo_file_matches_to_pattern2(const char *filename, | |||
567 | } | 565 | } |
568 | 566 | ||
569 | /** | 567 | /** |
570 | * tomoyo_file_matches_to_pattern - Pattern matching without without '/' character. | 568 | * tomoyo_file_matches_pattern - Pattern matching without without '/' character. |
571 | * | 569 | * |
572 | * @filename: The start of string to check. | 570 | * @filename: The start of string to check. |
573 | * @filename_end: The end of string to check. | 571 | * @filename_end: The end of string to check. |
@@ -576,7 +574,7 @@ static bool tomoyo_file_matches_to_pattern2(const char *filename, | |||
576 | * | 574 | * |
577 | * Returns true if @filename matches @pattern, false otherwise. | 575 | * Returns true if @filename matches @pattern, false otherwise. |
578 | */ | 576 | */ |
579 | static bool tomoyo_file_matches_to_pattern(const char *filename, | 577 | static bool tomoyo_file_matches_pattern(const char *filename, |
580 | const char *filename_end, | 578 | const char *filename_end, |
581 | const char *pattern, | 579 | const char *pattern, |
582 | const char *pattern_end) | 580 | const char *pattern_end) |
@@ -589,10 +587,10 @@ static bool tomoyo_file_matches_to_pattern(const char *filename, | |||
589 | /* Split at "\-" pattern. */ | 587 | /* Split at "\-" pattern. */ |
590 | if (*pattern++ != '\\' || *pattern++ != '-') | 588 | if (*pattern++ != '\\' || *pattern++ != '-') |
591 | continue; | 589 | continue; |
592 | result = tomoyo_file_matches_to_pattern2(filename, | 590 | result = tomoyo_file_matches_pattern2(filename, |
593 | filename_end, | 591 | filename_end, |
594 | pattern_start, | 592 | pattern_start, |
595 | pattern - 2); | 593 | pattern - 2); |
596 | if (first) | 594 | if (first) |
597 | result = !result; | 595 | result = !result; |
598 | if (result) | 596 | if (result) |
@@ -600,13 +598,79 @@ static bool tomoyo_file_matches_to_pattern(const char *filename, | |||
600 | first = false; | 598 | first = false; |
601 | pattern_start = pattern; | 599 | pattern_start = pattern; |
602 | } | 600 | } |
603 | result = tomoyo_file_matches_to_pattern2(filename, filename_end, | 601 | result = tomoyo_file_matches_pattern2(filename, filename_end, |
604 | pattern_start, pattern_end); | 602 | pattern_start, pattern_end); |
605 | return first ? result : !result; | 603 | return first ? result : !result; |
606 | } | 604 | } |
607 | 605 | ||
608 | /** | 606 | /** |
607 | * tomoyo_path_matches_pattern2 - Do pathname pattern matching. | ||
608 | * | ||
609 | * @f: The start of string to check. | ||
610 | * @p: The start of pattern to compare. | ||
611 | * | ||
612 | * Returns true if @f matches @p, false otherwise. | ||
613 | */ | ||
614 | static bool tomoyo_path_matches_pattern2(const char *f, const char *p) | ||
615 | { | ||
616 | const char *f_delimiter; | ||
617 | const char *p_delimiter; | ||
618 | |||
619 | while (*f && *p) { | ||
620 | f_delimiter = strchr(f, '/'); | ||
621 | if (!f_delimiter) | ||
622 | f_delimiter = f + strlen(f); | ||
623 | p_delimiter = strchr(p, '/'); | ||
624 | if (!p_delimiter) | ||
625 | p_delimiter = p + strlen(p); | ||
626 | if (*p == '\\' && *(p + 1) == '{') | ||
627 | goto recursive; | ||
628 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p, | ||
629 | p_delimiter)) | ||
630 | return false; | ||
631 | f = f_delimiter; | ||
632 | if (*f) | ||
633 | f++; | ||
634 | p = p_delimiter; | ||
635 | if (*p) | ||
636 | p++; | ||
637 | } | ||
638 | /* Ignore trailing "\*" and "\@" in @pattern. */ | ||
639 | while (*p == '\\' && | ||
640 | (*(p + 1) == '*' || *(p + 1) == '@')) | ||
641 | p += 2; | ||
642 | return !*f && !*p; | ||
643 | recursive: | ||
644 | /* | ||
645 | * The "\{" pattern is permitted only after '/' character. | ||
646 | * This guarantees that below "*(p - 1)" is safe. | ||
647 | * Also, the "\}" pattern is permitted only before '/' character | ||
648 | * so that "\{" + "\}" pair will not break the "\-" operator. | ||
649 | */ | ||
650 | if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || | ||
651 | *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') | ||
652 | return false; /* Bad pattern. */ | ||
653 | do { | ||
654 | /* Compare current component with pattern. */ | ||
655 | if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2, | ||
656 | p_delimiter - 2)) | ||
657 | break; | ||
658 | /* Proceed to next component. */ | ||
659 | f = f_delimiter; | ||
660 | if (!*f) | ||
661 | break; | ||
662 | f++; | ||
663 | /* Continue comparison. */ | ||
664 | if (tomoyo_path_matches_pattern2(f, p_delimiter + 1)) | ||
665 | return true; | ||
666 | f_delimiter = strchr(f, '/'); | ||
667 | } while (f_delimiter); | ||
668 | return false; /* Not matched. */ | ||
669 | } | ||
670 | |||
671 | /** | ||
609 | * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern. | 672 | * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern. |
673 | * | ||
610 | * @filename: The filename to check. | 674 | * @filename: The filename to check. |
611 | * @pattern: The pattern to compare. | 675 | * @pattern: The pattern to compare. |
612 | * | 676 | * |
@@ -615,24 +679,24 @@ static bool tomoyo_file_matches_to_pattern(const char *filename, | |||
615 | * The following patterns are available. | 679 | * The following patterns are available. |
616 | * \\ \ itself. | 680 | * \\ \ itself. |
617 | * \ooo Octal representation of a byte. | 681 | * \ooo Octal representation of a byte. |
618 | * \* More than or equals to 0 character other than '/'. | 682 | * \* Zero or more repetitions of characters other than '/'. |
619 | * \@ More than or equals to 0 character other than '/' or '.'. | 683 | * \@ Zero or more repetitions of characters other than '/' or '.'. |
620 | * \? 1 byte character other than '/'. | 684 | * \? 1 byte character other than '/'. |
621 | * \$ More than or equals to 1 decimal digit. | 685 | * \$ One or more repetitions of decimal digits. |
622 | * \+ 1 decimal digit. | 686 | * \+ 1 decimal digit. |
623 | * \X More than or equals to 1 hexadecimal digit. | 687 | * \X One or more repetitions of hexadecimal digits. |
624 | * \x 1 hexadecimal digit. | 688 | * \x 1 hexadecimal digit. |
625 | * \A More than or equals to 1 alphabet character. | 689 | * \A One or more repetitions of alphabet characters. |
626 | * \a 1 alphabet character. | 690 | * \a 1 alphabet character. |
691 | * | ||
627 | * \- Subtraction operator. | 692 | * \- Subtraction operator. |
693 | * | ||
694 | * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | ||
695 | * /dir/dir/dir/ ). | ||
628 | */ | 696 | */ |
629 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, | 697 | bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, |
630 | const struct tomoyo_path_info *pattern) | 698 | const struct tomoyo_path_info *pattern) |
631 | { | 699 | { |
632 | /* | ||
633 | if (!filename || !pattern) | ||
634 | return false; | ||
635 | */ | ||
636 | const char *f = filename->name; | 700 | const char *f = filename->name; |
637 | const char *p = pattern->name; | 701 | const char *p = pattern->name; |
638 | const int len = pattern->const_len; | 702 | const int len = pattern->const_len; |
@@ -640,37 +704,15 @@ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, | |||
640 | /* If @pattern doesn't contain pattern, I can use strcmp(). */ | 704 | /* If @pattern doesn't contain pattern, I can use strcmp(). */ |
641 | if (!pattern->is_patterned) | 705 | if (!pattern->is_patterned) |
642 | return !tomoyo_pathcmp(filename, pattern); | 706 | return !tomoyo_pathcmp(filename, pattern); |
643 | /* Dont compare if the number of '/' differs. */ | 707 | /* Don't compare directory and non-directory. */ |
644 | if (filename->depth != pattern->depth) | 708 | if (filename->is_dir != pattern->is_dir) |
645 | return false; | 709 | return false; |
646 | /* Compare the initial length without patterns. */ | 710 | /* Compare the initial length without patterns. */ |
647 | if (strncmp(f, p, len)) | 711 | if (strncmp(f, p, len)) |
648 | return false; | 712 | return false; |
649 | f += len; | 713 | f += len; |
650 | p += len; | 714 | p += len; |
651 | /* Main loop. Compare each directory component. */ | 715 | return tomoyo_path_matches_pattern2(f, p); |
652 | while (*f && *p) { | ||
653 | const char *f_delimiter = strchr(f, '/'); | ||
654 | const char *p_delimiter = strchr(p, '/'); | ||
655 | if (!f_delimiter) | ||
656 | f_delimiter = f + strlen(f); | ||
657 | if (!p_delimiter) | ||
658 | p_delimiter = p + strlen(p); | ||
659 | if (!tomoyo_file_matches_to_pattern(f, f_delimiter, | ||
660 | p, p_delimiter)) | ||
661 | return false; | ||
662 | f = f_delimiter; | ||
663 | if (*f) | ||
664 | f++; | ||
665 | p = p_delimiter; | ||
666 | if (*p) | ||
667 | p++; | ||
668 | } | ||
669 | /* Ignore trailing "\*" and "\@" in @pattern. */ | ||
670 | while (*p == '\\' && | ||
671 | (*(p + 1) == '*' || *(p + 1) == '@')) | ||
672 | p += 2; | ||
673 | return !*f && !*p; | ||
674 | } | 716 | } |
675 | 717 | ||
676 | /** | 718 | /** |
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 31df541911f7..92169d29b2db 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h | |||
@@ -56,9 +56,6 @@ struct tomoyo_page_buffer { | |||
56 | * (5) "is_patterned" is a bool which is true if "name" contains wildcard | 56 | * (5) "is_patterned" is a bool which is true if "name" contains wildcard |
57 | * characters, false otherwise. This allows TOMOYO to use "hash" and | 57 | * characters, false otherwise. This allows TOMOYO to use "hash" and |
58 | * strcmp() for string comparison if "is_patterned" is false. | 58 | * strcmp() for string comparison if "is_patterned" is false. |
59 | * (6) "depth" is calculated using the number of "/" characters in "name". | ||
60 | * This allows TOMOYO to avoid comparing two pathnames which never match | ||
61 | * (e.g. whether "/var/www/html/index.html" matches "/tmp/sh-thd-\$"). | ||
62 | */ | 59 | */ |
63 | struct tomoyo_path_info { | 60 | struct tomoyo_path_info { |
64 | const char *name; | 61 | const char *name; |
@@ -66,7 +63,6 @@ struct tomoyo_path_info { | |||
66 | u16 const_len; /* = tomoyo_const_part_length(name) */ | 63 | u16 const_len; /* = tomoyo_const_part_length(name) */ |
67 | bool is_dir; /* = tomoyo_strendswith(name, "/") */ | 64 | bool is_dir; /* = tomoyo_strendswith(name, "/") */ |
68 | bool is_patterned; /* = tomoyo_path_contains_pattern(name) */ | 65 | bool is_patterned; /* = tomoyo_path_contains_pattern(name) */ |
69 | u16 depth; /* = tomoyo_path_depth(name) */ | ||
70 | }; | 66 | }; |
71 | 67 | ||
72 | /* | 68 | /* |