| 1424 | } |
| 1425 | |
| 1426 | static int private_directory_tree_open(const char *directory_path) { |
| 1427 | if (!directory_path || !directory_path[0] || O_DIRECTORY == 0 || O_NOFOLLOW == 0) { |
| 1428 | return -1; |
| 1429 | } |
| 1430 | char *path = private_log_directory_path_copy(directory_path); |
| 1431 | if (!path) { |
| 1432 | return -1; |
| 1433 | } |
| 1434 | bool absolute = path[0] == '/'; |
| 1435 | int current_fd = open(absolute ? "/" : ".", O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); |
| 1436 | bool ok = current_fd >= 0 && fd_set_cloexec(current_fd); |
| 1437 | char *cursor = path; |
| 1438 | while (ok && *cursor == '/') { |
| 1439 | cursor++; |
| 1440 | } |
| 1441 | bool visited = false; |
| 1442 | while (ok && *cursor) { |
| 1443 | char *component = cursor; |
| 1444 | while (*cursor && *cursor != '/') { |
| 1445 | cursor++; |
| 1446 | } |
| 1447 | char saved = *cursor; |
| 1448 | *cursor = '\0'; |
| 1449 | if (strcmp(component, ".") == 0) { |
| 1450 | /* Relative paths may contain a harmless explicit current-dir |
| 1451 | * component. Parent traversal is never valid for private logs. */ |
| 1452 | } else if (strcmp(component, "..") == 0 || !component[0]) { |
| 1453 | ok = false; |
| 1454 | } else { |
| 1455 | ok = posix_directory_parent_secure(current_fd); |
| 1456 | if (!ok) { |
| 1457 | /* #1537: this branch used to leave the detail empty, so the |
| 1458 | * caller fell back to printing errno — which NOTHING here sets. |
| 1459 | * A reporter was handed "errno 2" (ENOENT) for a permission |
| 1460 | * refusal and went looking for a missing file that existed. |
| 1461 | * An unset errno is not a diagnosis; name the component. */ |
| 1462 | ipc_validation_detail_set("%s: ancestor '%s' is not a usable private-directory " |
| 1463 | "parent (must be owned by you, not world-writable, and " |
| 1464 | "carry no allow-ACL)", |
| 1465 | directory_path, component); |
| 1466 | } |
| 1467 | bool created = ok && mkdirat(current_fd, component, 0700) == 0; |
| 1468 | if (!created && errno != EEXIST) { |
| 1469 | ok = false; |
| 1470 | } |
| 1471 | int next_fd = |
| 1472 | ok ? openat(current_fd, component, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW) |
| 1473 | : -1; |
| 1474 | struct stat status; |
| 1475 | ok = next_fd >= 0 && fd_set_cloexec(next_fd) && fstat(next_fd, &status) == 0 && |
| 1476 | S_ISDIR(status.st_mode) && posix_directory_transition_secure(current_fd, next_fd); |
| 1477 | if (ok && created) { |
| 1478 | ok = status.st_uid == geteuid() && fchmod(next_fd, 0700) == 0 && |
| 1479 | cbm_macos_extended_acl_fd_clear(next_fd) && |
| 1480 | cbm_macos_extended_acl_fd_is_empty(next_fd); |
| 1481 | } |
| 1482 | if (ok) { |
| 1483 | (void)close(current_fd); |
no test coverage detected