| 1103 | /// no untrusted process is running yet. |
| 1104 | #[cfg(unix)] |
| 1105 | fn chown_sandbox_home(root: &Path, uid: Option<Uid>, gid: Option<Gid>) -> Result<()> { |
| 1106 | use nix::unistd::chown; |
| 1107 | |
| 1108 | let meta = std::fs::symlink_metadata(root).into_diagnostic()?; |
| 1109 | if meta.file_type().is_symlink() { |
| 1110 | return Err(miette::miette!( |
| 1111 | "path '{}' is a symlink — refusing to chown (potential privilege escalation)", |
| 1112 | root.display() |
| 1113 | )); |
| 1114 | } |
| 1115 | |
| 1116 | chown(root, uid, gid).into_diagnostic()?; |
| 1117 | |
| 1118 | if meta.is_dir() |
| 1119 | && let Ok(entries) = std::fs::read_dir(root) |
| 1120 | { |
| 1121 | for entry in entries { |
| 1122 | let entry = entry.into_diagnostic()?; |
| 1123 | let path = entry.path(); |
| 1124 | if path |
| 1125 | .symlink_metadata() |
| 1126 | .is_ok_and(|m| m.file_type().is_symlink()) |
| 1127 | { |
| 1128 | debug!(path = %path.display(), "Skipping symlink during sandbox home chown"); |
| 1129 | continue; |
| 1130 | } |
| 1131 | chown_sandbox_home(&path, uid, gid)?; |
| 1132 | } |
| 1133 | } |
| 1134 | |
| 1135 | Ok(()) |
| 1136 | } |
| 1137 | |
| 1138 | /// Prepare filesystem for the sandboxed process. |
| 1139 | /// |