diff options
Diffstat (limited to 'security/tomoyo/common.c')
| -rw-r--r-- | security/tomoyo/common.c | 200 |
1 files changed, 121 insertions, 79 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 | /** |
