aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/tomoyo/common.c200
-rw-r--r--security/tomoyo/common.h4
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 */
372static 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 */
461static bool tomoyo_file_matches_to_pattern2(const char *filename, 459static 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 */
579static bool tomoyo_file_matches_to_pattern(const char *filename, 577static 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 */
614static 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 */
629bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, 697bool 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 */
63struct tomoyo_path_info { 60struct 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/*