| .. | .. |
|---|
| 91 | 91 | void tomoyo_convert_time(time64_t time64, struct tomoyo_time *stamp) |
|---|
| 92 | 92 | { |
|---|
| 93 | 93 | struct tm tm; |
|---|
| 94 | + |
|---|
| 94 | 95 | time64_to_tm(time64, 0, &tm); |
|---|
| 95 | 96 | stamp->sec = tm.tm_sec; |
|---|
| 96 | 97 | stamp->min = tm.tm_min; |
|---|
| .. | .. |
|---|
| 106 | 107 | * @string: String representation for permissions in foo/bar/buz format. |
|---|
| 107 | 108 | * @keyword: Keyword to find from @string/ |
|---|
| 108 | 109 | * |
|---|
| 109 | | - * Returns ture if @keyword was found in @string, false otherwise. |
|---|
| 110 | + * Returns true if @keyword was found in @string, false otherwise. |
|---|
| 110 | 111 | * |
|---|
| 111 | 112 | * This function assumes that strncmp(w1, w2, strlen(w1)) != 0 if w1 != w2. |
|---|
| 112 | 113 | */ |
|---|
| 113 | 114 | bool tomoyo_permstr(const char *string, const char *keyword) |
|---|
| 114 | 115 | { |
|---|
| 115 | 116 | const char *cp = strstr(string, keyword); |
|---|
| 117 | + |
|---|
| 116 | 118 | if (cp) |
|---|
| 117 | 119 | return cp == string || *(cp - 1) == '/'; |
|---|
| 118 | 120 | return false; |
|---|
| .. | .. |
|---|
| 132 | 134 | { |
|---|
| 133 | 135 | char *pos = param->data; |
|---|
| 134 | 136 | char *del = strchr(pos, ' '); |
|---|
| 137 | + |
|---|
| 135 | 138 | if (del) |
|---|
| 136 | 139 | *del++ = '\0'; |
|---|
| 137 | 140 | else |
|---|
| .. | .. |
|---|
| 139 | 142 | param->data = del; |
|---|
| 140 | 143 | return pos; |
|---|
| 141 | 144 | } |
|---|
| 145 | + |
|---|
| 146 | +static bool tomoyo_correct_path2(const char *filename, const size_t len); |
|---|
| 142 | 147 | |
|---|
| 143 | 148 | /** |
|---|
| 144 | 149 | * tomoyo_get_domainname - Read a domainname from a line. |
|---|
| .. | .. |
|---|
| 152 | 157 | { |
|---|
| 153 | 158 | char *start = param->data; |
|---|
| 154 | 159 | char *pos = start; |
|---|
| 160 | + |
|---|
| 155 | 161 | while (*pos) { |
|---|
| 156 | | - if (*pos++ != ' ' || *pos++ == '/') |
|---|
| 162 | + if (*pos++ != ' ' || |
|---|
| 163 | + tomoyo_correct_path2(pos, strchrnul(pos, ' ') - pos)) |
|---|
| 157 | 164 | continue; |
|---|
| 158 | | - pos -= 2; |
|---|
| 159 | | - *pos++ = '\0'; |
|---|
| 165 | + *(pos - 1) = '\0'; |
|---|
| 160 | 166 | break; |
|---|
| 161 | 167 | } |
|---|
| 162 | 168 | param->data = pos; |
|---|
| .. | .. |
|---|
| 181 | 187 | const char *cp = *str; |
|---|
| 182 | 188 | char *ep; |
|---|
| 183 | 189 | int base = 10; |
|---|
| 190 | + |
|---|
| 184 | 191 | if (*cp == '0') { |
|---|
| 185 | 192 | char c = *(cp + 1); |
|---|
| 193 | + |
|---|
| 186 | 194 | if (c == 'x' || c == 'X') { |
|---|
| 187 | 195 | base = 16; |
|---|
| 188 | 196 | cp += 2; |
|---|
| .. | .. |
|---|
| 240 | 248 | struct tomoyo_name_union *ptr) |
|---|
| 241 | 249 | { |
|---|
| 242 | 250 | char *filename; |
|---|
| 251 | + |
|---|
| 243 | 252 | if (param->data[0] == '@') { |
|---|
| 244 | 253 | param->data++; |
|---|
| 245 | 254 | ptr->group = tomoyo_get_group(param, TOMOYO_PATH_GROUP); |
|---|
| .. | .. |
|---|
| 266 | 275 | char *data; |
|---|
| 267 | 276 | u8 type; |
|---|
| 268 | 277 | unsigned long v; |
|---|
| 278 | + |
|---|
| 269 | 279 | memset(ptr, 0, sizeof(*ptr)); |
|---|
| 270 | 280 | if (param->data[0] == '@') { |
|---|
| 271 | 281 | param->data++; |
|---|
| .. | .. |
|---|
| 429 | 439 | unsigned char c; |
|---|
| 430 | 440 | unsigned char d; |
|---|
| 431 | 441 | unsigned char e; |
|---|
| 442 | + |
|---|
| 432 | 443 | if (!len) |
|---|
| 433 | 444 | goto out; |
|---|
| 434 | 445 | while (len--) { |
|---|
| .. | .. |
|---|
| 505 | 516 | } |
|---|
| 506 | 517 | |
|---|
| 507 | 518 | /** |
|---|
| 519 | + * tomoyo_correct_path2 - Check whether the given pathname follows the naming rules. |
|---|
| 520 | + * |
|---|
| 521 | + * @filename: The pathname to check. |
|---|
| 522 | + * @len: Length of @filename. |
|---|
| 523 | + * |
|---|
| 524 | + * Returns true if @filename follows the naming rules, false otherwise. |
|---|
| 525 | + */ |
|---|
| 526 | +static bool tomoyo_correct_path2(const char *filename, const size_t len) |
|---|
| 527 | +{ |
|---|
| 528 | + const char *cp1 = memchr(filename, '/', len); |
|---|
| 529 | + const char *cp2 = memchr(filename, '.', len); |
|---|
| 530 | + |
|---|
| 531 | + return cp1 && (!cp2 || (cp1 < cp2)) && tomoyo_correct_word2(filename, len); |
|---|
| 532 | +} |
|---|
| 533 | + |
|---|
| 534 | +/** |
|---|
| 508 | 535 | * tomoyo_correct_path - Validate a pathname. |
|---|
| 509 | 536 | * |
|---|
| 510 | 537 | * @filename: The pathname to check. |
|---|
| .. | .. |
|---|
| 514 | 541 | */ |
|---|
| 515 | 542 | bool tomoyo_correct_path(const char *filename) |
|---|
| 516 | 543 | { |
|---|
| 517 | | - return *filename == '/' && tomoyo_correct_word(filename); |
|---|
| 544 | + return tomoyo_correct_path2(filename, strlen(filename)); |
|---|
| 518 | 545 | } |
|---|
| 519 | 546 | |
|---|
| 520 | 547 | /** |
|---|
| .. | .. |
|---|
| 533 | 560 | return true; |
|---|
| 534 | 561 | while (1) { |
|---|
| 535 | 562 | const unsigned char *cp = strchr(domainname, ' '); |
|---|
| 563 | + |
|---|
| 536 | 564 | if (!cp) |
|---|
| 537 | 565 | break; |
|---|
| 538 | | - if (*domainname != '/' || |
|---|
| 539 | | - !tomoyo_correct_word2(domainname, cp - domainname)) |
|---|
| 566 | + if (!tomoyo_correct_path2(domainname, cp - domainname)) |
|---|
| 540 | 567 | return false; |
|---|
| 541 | 568 | domainname = cp + 1; |
|---|
| 542 | 569 | } |
|---|
| .. | .. |
|---|
| 554 | 581 | { |
|---|
| 555 | 582 | const unsigned char *cp; |
|---|
| 556 | 583 | int len; |
|---|
| 584 | + |
|---|
| 557 | 585 | if (*buffer != '<') |
|---|
| 558 | 586 | return false; |
|---|
| 559 | 587 | cp = strchr(buffer, ' '); |
|---|
| .. | .. |
|---|
| 583 | 611 | |
|---|
| 584 | 612 | name.name = domainname; |
|---|
| 585 | 613 | tomoyo_fill_path_info(&name); |
|---|
| 586 | | - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { |
|---|
| 614 | + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list, |
|---|
| 615 | + srcu_read_lock_held(&tomoyo_ss)) { |
|---|
| 587 | 616 | if (!domain->is_deleted && |
|---|
| 588 | 617 | !tomoyo_pathcmp(&name, domain->domainname)) |
|---|
| 589 | 618 | return domain; |
|---|
| .. | .. |
|---|
| 668 | 697 | { |
|---|
| 669 | 698 | while (filename < filename_end && pattern < pattern_end) { |
|---|
| 670 | 699 | char c; |
|---|
| 700 | + int i; |
|---|
| 701 | + int j; |
|---|
| 702 | + |
|---|
| 671 | 703 | if (*pattern != '\\') { |
|---|
| 672 | 704 | if (*filename++ != *pattern++) |
|---|
| 673 | 705 | return false; |
|---|
| .. | .. |
|---|
| 676 | 708 | c = *filename; |
|---|
| 677 | 709 | pattern++; |
|---|
| 678 | 710 | switch (*pattern) { |
|---|
| 679 | | - int i; |
|---|
| 680 | | - int j; |
|---|
| 681 | 711 | case '?': |
|---|
| 682 | 712 | if (c == '/') { |
|---|
| 683 | 713 | return false; |
|---|
| .. | .. |
|---|
| 985 | 1015 | struct tomoyo_domain_info *domain, const u8 index) |
|---|
| 986 | 1016 | { |
|---|
| 987 | 1017 | u8 profile; |
|---|
| 1018 | + |
|---|
| 988 | 1019 | memset(r, 0, sizeof(*r)); |
|---|
| 989 | 1020 | if (!domain) |
|---|
| 990 | 1021 | domain = tomoyo_domain(); |
|---|
| .. | .. |
|---|
| 1015 | 1046 | return false; |
|---|
| 1016 | 1047 | if (!domain) |
|---|
| 1017 | 1048 | return true; |
|---|
| 1018 | | - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { |
|---|
| 1049 | + if (READ_ONCE(domain->flags[TOMOYO_DIF_QUOTA_WARNED])) |
|---|
| 1050 | + return false; |
|---|
| 1051 | + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list, |
|---|
| 1052 | + srcu_read_lock_held(&tomoyo_ss)) { |
|---|
| 1019 | 1053 | u16 perm; |
|---|
| 1020 | | - u8 i; |
|---|
| 1054 | + |
|---|
| 1021 | 1055 | if (ptr->is_deleted) |
|---|
| 1022 | 1056 | continue; |
|---|
| 1057 | + /* |
|---|
| 1058 | + * Reading perm bitmap might race with tomoyo_merge_*() because |
|---|
| 1059 | + * caller does not hold tomoyo_policy_lock mutex. But exceeding |
|---|
| 1060 | + * max_learning_entry parameter by a few entries does not harm. |
|---|
| 1061 | + */ |
|---|
| 1023 | 1062 | switch (ptr->type) { |
|---|
| 1024 | 1063 | case TOMOYO_TYPE_PATH_ACL: |
|---|
| 1025 | | - perm = container_of(ptr, struct tomoyo_path_acl, head) |
|---|
| 1026 | | - ->perm; |
|---|
| 1064 | + perm = data_race(container_of(ptr, struct tomoyo_path_acl, head)->perm); |
|---|
| 1027 | 1065 | break; |
|---|
| 1028 | 1066 | case TOMOYO_TYPE_PATH2_ACL: |
|---|
| 1029 | | - perm = container_of(ptr, struct tomoyo_path2_acl, head) |
|---|
| 1030 | | - ->perm; |
|---|
| 1067 | + perm = data_race(container_of(ptr, struct tomoyo_path2_acl, head)->perm); |
|---|
| 1031 | 1068 | break; |
|---|
| 1032 | 1069 | case TOMOYO_TYPE_PATH_NUMBER_ACL: |
|---|
| 1033 | | - perm = container_of(ptr, struct tomoyo_path_number_acl, |
|---|
| 1034 | | - head)->perm; |
|---|
| 1070 | + perm = data_race(container_of(ptr, struct tomoyo_path_number_acl, head) |
|---|
| 1071 | + ->perm); |
|---|
| 1035 | 1072 | break; |
|---|
| 1036 | 1073 | case TOMOYO_TYPE_MKDEV_ACL: |
|---|
| 1037 | | - perm = container_of(ptr, struct tomoyo_mkdev_acl, |
|---|
| 1038 | | - head)->perm; |
|---|
| 1074 | + perm = data_race(container_of(ptr, struct tomoyo_mkdev_acl, head)->perm); |
|---|
| 1039 | 1075 | break; |
|---|
| 1040 | 1076 | case TOMOYO_TYPE_INET_ACL: |
|---|
| 1041 | | - perm = container_of(ptr, struct tomoyo_inet_acl, |
|---|
| 1042 | | - head)->perm; |
|---|
| 1077 | + perm = data_race(container_of(ptr, struct tomoyo_inet_acl, head)->perm); |
|---|
| 1043 | 1078 | break; |
|---|
| 1044 | 1079 | case TOMOYO_TYPE_UNIX_ACL: |
|---|
| 1045 | | - perm = container_of(ptr, struct tomoyo_unix_acl, |
|---|
| 1046 | | - head)->perm; |
|---|
| 1080 | + perm = data_race(container_of(ptr, struct tomoyo_unix_acl, head)->perm); |
|---|
| 1047 | 1081 | break; |
|---|
| 1048 | 1082 | case TOMOYO_TYPE_MANUAL_TASK_ACL: |
|---|
| 1049 | 1083 | perm = 0; |
|---|
| .. | .. |
|---|
| 1051 | 1085 | default: |
|---|
| 1052 | 1086 | perm = 1; |
|---|
| 1053 | 1087 | } |
|---|
| 1054 | | - for (i = 0; i < 16; i++) |
|---|
| 1055 | | - if (perm & (1 << i)) |
|---|
| 1056 | | - count++; |
|---|
| 1088 | + count += hweight16(perm); |
|---|
| 1057 | 1089 | } |
|---|
| 1058 | 1090 | if (count < tomoyo_profile(domain->ns, domain->profile)-> |
|---|
| 1059 | 1091 | pref[TOMOYO_PREF_MAX_LEARNING_ENTRY]) |
|---|
| 1060 | 1092 | return true; |
|---|
| 1061 | | - if (!domain->flags[TOMOYO_DIF_QUOTA_WARNED]) { |
|---|
| 1062 | | - domain->flags[TOMOYO_DIF_QUOTA_WARNED] = true; |
|---|
| 1063 | | - /* r->granted = false; */ |
|---|
| 1064 | | - tomoyo_write_log(r, "%s", tomoyo_dif[TOMOYO_DIF_QUOTA_WARNED]); |
|---|
| 1065 | | - printk(KERN_WARNING "WARNING: " |
|---|
| 1066 | | - "Domain '%s' has too many ACLs to hold. " |
|---|
| 1067 | | - "Stopped learning mode.\n", domain->domainname->name); |
|---|
| 1068 | | - } |
|---|
| 1093 | + WRITE_ONCE(domain->flags[TOMOYO_DIF_QUOTA_WARNED], true); |
|---|
| 1094 | + /* r->granted = false; */ |
|---|
| 1095 | + tomoyo_write_log(r, "%s", tomoyo_dif[TOMOYO_DIF_QUOTA_WARNED]); |
|---|
| 1096 | +#ifndef CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING |
|---|
| 1097 | + pr_warn("WARNING: Domain '%s' has too many ACLs to hold. Stopped learning mode.\n", |
|---|
| 1098 | + domain->domainname->name); |
|---|
| 1099 | +#endif |
|---|
| 1069 | 1100 | return false; |
|---|
| 1070 | 1101 | } |
|---|