| 4480 | } |
| 4481 | |
| 4482 | static bool win_private_directory_tree_secure(const wchar_t *directory_path) { |
| 4483 | if (!directory_path) { |
| 4484 | return false; |
| 4485 | } |
| 4486 | size_t length = wcslen(directory_path); |
| 4487 | bool drive_absolute = length >= 4 && |
| 4488 | ((directory_path[0] >= L'A' && directory_path[0] <= L'Z') || |
| 4489 | (directory_path[0] >= L'a' && directory_path[0] <= L'z')) && |
| 4490 | directory_path[1] == L':' && |
| 4491 | (directory_path[2] == L'\\' || directory_path[2] == L'/'); |
| 4492 | if (!drive_absolute) { |
| 4493 | /* Local current-user ACLs do not provide the intended guarantee for |
| 4494 | * UNC/device namespaces. Daemon cache logs must stay on a local |
| 4495 | * absolute drive path. */ |
| 4496 | return false; |
| 4497 | } |
| 4498 | wchar_t *path = wide_copy(directory_path); |
| 4499 | if (!path) { |
| 4500 | return false; |
| 4501 | } |
| 4502 | for (size_t i = 0; i < length; i++) { |
| 4503 | if (path[i] == L'/') { |
| 4504 | path[i] = L'\\'; |
| 4505 | } |
| 4506 | } |
| 4507 | win_security_t security; |
| 4508 | if (!win_security_init(&security)) { |
| 4509 | free(path); |
| 4510 | return false; |
| 4511 | } |
| 4512 | bool ok = true; |
| 4513 | size_t component_start = 3; |
| 4514 | for (size_t i = component_start; ok && i <= length; i++) { |
| 4515 | if (i < length && path[i] != L'\\') { |
| 4516 | continue; |
| 4517 | } |
| 4518 | if (i == component_start) { |
| 4519 | component_start = i + 1; |
| 4520 | continue; |
| 4521 | } |
| 4522 | wchar_t saved = path[i]; |
| 4523 | path[i] = L'\0'; |
| 4524 | const wchar_t *component = path + component_start; |
| 4525 | if (wcscmp(component, L".") == 0 || wcscmp(component, L"..") == 0) { |
| 4526 | ok = false; |
| 4527 | } else { |
| 4528 | DWORD attributes = GetFileAttributesW(path); |
| 4529 | if (attributes == INVALID_FILE_ATTRIBUTES) { |
| 4530 | DWORD error = GetLastError(); |
| 4531 | ok = (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) && |
| 4532 | CreateDirectoryW(path, &security.directory_attributes) != 0; |
| 4533 | } |
| 4534 | /* Ancestors are observe-only and must already be secure. The |
| 4535 | * final current-user directory is intentionally handled below by |
| 4536 | * win_runtime_directory_secure(), which may replace its DACL. */ |
| 4537 | if (ok && i < length) { |
| 4538 | ok = win_directory_component_secure(&security, path); |
| 4539 | if (!ok) { |
no test coverage detected