| 1384 | } |
| 1385 | |
| 1386 | static bool posix_directory_parent_secure(int directory_fd) { |
| 1387 | struct stat status; |
| 1388 | if (directory_fd < 0 || fstat(directory_fd, &status) != 0 || !S_ISDIR(status.st_mode) || |
| 1389 | !posix_directory_owner_trusted(status.st_uid) || |
| 1390 | !cbm_macos_extended_acl_fd_is_deny_only(directory_fd)) { |
| 1391 | return false; |
| 1392 | } |
| 1393 | /* #1537: this ANCESTOR check refused any group-write bit, which is the same |
| 1394 | * rule #1535 removed on the activation side — and the sibling that decision |
| 1395 | * covers but that never got changed. A group-writable ~ or ~/.cache is |
| 1396 | * ordinary (WSL2 ships 0775, so do several distro skeletons and any site |
| 1397 | * with a shared primary group), and refusing it here made the daemon |
| 1398 | * unusable with no way for the reader to see why. |
| 1399 | * |
| 1400 | * WORLD-writable is still refused: any local user could swap a path |
| 1401 | * component. Group-writable is admitted for ancestors only — the private |
| 1402 | * directory itself is chmod'd to 0700 and verified after this walk, so the |
| 1403 | * thing that actually holds data stays owner-private either way. */ |
| 1404 | if ((status.st_mode & 0002) != 0) { |
| 1405 | return status.st_uid == (uid_t)0 && (status.st_mode & S_ISVTX) != 0; |
| 1406 | } |
| 1407 | if ((status.st_mode & 0020) != 0) { |
| 1408 | char mode_text[16]; |
| 1409 | (void)snprintf(mode_text, sizeof(mode_text), "%04o", (unsigned)(status.st_mode & 07777)); |
| 1410 | cbm_log_warn("daemon.private_dir_group_writable_ancestor", "mode", mode_text); |
| 1411 | } |
| 1412 | return true; |
| 1413 | } |
| 1414 | |
| 1415 | /* Validate a path transition only through the two already-open directory |
| 1416 | * handles. A group/other-writable parent is unsafe unless it is the standard |
no test coverage detected