|
| 1 | +use std::borrow::Cow; |
1 | 2 | use std::io::Write;
|
2 | 3 | use std::os::fd::AsRawFd;
|
3 | 4 | use std::os::unix::process::CommandExt;
|
@@ -257,44 +258,97 @@ pub(crate) fn ensure_labeled(
|
257 | 258 | ) -> Result<SELinuxLabelState> {
|
258 | 259 | let r = has_security_selinux(root, path)?;
|
259 | 260 | if matches!(r, SELinuxLabelState::Unlabeled) {
|
260 |
| - let abspath = Utf8Path::new("/").join(&path); |
261 |
| - let label = require_label(policy, &abspath, metadata.mode())?; |
262 |
| - tracing::trace!("Setting label for {path} to {label}"); |
263 |
| - set_security_selinux_path(root, &path, label.as_bytes())?; |
| 261 | + relabel(root, metadata, path, None, policy)?; |
264 | 262 | }
|
265 | 263 | Ok(r)
|
266 | 264 | }
|
267 | 265 |
|
268 |
| -pub(crate) fn ensure_dir_labeled_recurse_policy( |
| 266 | +/// Given the policy, relabel the target file or directory. |
| 267 | +/// Optionally, an override for the path can be provided |
| 268 | +/// to set the label as if the target has that filename. |
| 269 | +pub(crate) fn relabel( |
269 | 270 | root: &Dir,
|
270 |
| - destname: impl AsRef<Utf8Path>, |
| 271 | + metadata: &Metadata, |
| 272 | + path: &Utf8Path, |
271 | 273 | as_path: Option<&Utf8Path>,
|
272 |
| - mode: rustix::fs::Mode, |
273 |
| - policy: Option<&ostree::SePolicy>, |
| 274 | + policy: &ostree::SePolicy, |
274 | 275 | ) -> Result<()> {
|
275 |
| - ensure_dir_labeled(root, &destname, as_path, mode, policy)?; |
276 |
| - let mut dest_path = destname.as_ref().to_path_buf(); |
| 276 | + assert!(!path.starts_with("/")); |
| 277 | + let as_path = as_path |
| 278 | + .map(Cow::Borrowed) |
| 279 | + .unwrap_or_else(|| Utf8Path::new("/").join(path).into()); |
| 280 | + let label = require_label(policy, &as_path, metadata.mode())?; |
| 281 | + tracing::trace!("Setting label for {path} to {label}"); |
| 282 | + set_security_selinux_path(root, &path, label.as_bytes()) |
| 283 | +} |
277 | 284 |
|
278 |
| - for ent in root.read_dir(destname.as_ref())? { |
| 285 | +pub(crate) fn relabel_recurse_inner( |
| 286 | + root: &Dir, |
| 287 | + path: &mut Utf8PathBuf, |
| 288 | + mut as_path: Option<&mut Utf8PathBuf>, |
| 289 | + policy: &ostree::SePolicy, |
| 290 | +) -> Result<()> { |
| 291 | + // Relabel this directory |
| 292 | + let self_meta = root.dir_metadata()?; |
| 293 | + relabel( |
| 294 | + root, |
| 295 | + &self_meta, |
| 296 | + path, |
| 297 | + as_path.as_ref().map(|p| p.as_path()), |
| 298 | + policy, |
| 299 | + )?; |
| 300 | + |
| 301 | + // Relabel all children |
| 302 | + for ent in root.read_dir(&path)? { |
279 | 303 | let ent = ent?;
|
280 | 304 | let metadata = ent.metadata()?;
|
281 | 305 | let name = ent.file_name();
|
282 | 306 | let name = name
|
283 | 307 | .to_str()
|
284 | 308 | .ok_or_else(|| anyhow::anyhow!("Invalid non-UTF-8 filename: {name:?}"))?;
|
285 |
| - dest_path.push(name); |
| 309 | + // Extend both copies of the path |
| 310 | + path.push(name); |
| 311 | + if let Some(p) = as_path.as_mut() { |
| 312 | + p.push(name); |
| 313 | + } |
286 | 314 |
|
287 | 315 | if metadata.is_dir() {
|
288 |
| - ensure_dir_labeled_recurse_policy(root, &dest_path, as_path, mode, policy)?; |
| 316 | + let as_path = as_path.as_deref_mut(); |
| 317 | + relabel_recurse_inner(root, path, as_path, policy)?; |
289 | 318 | } else {
|
290 |
| - ensure_dir_labeled(root, &dest_path, as_path, mode, policy)? |
| 319 | + let as_path = as_path.as_ref().map(|p| p.as_path()); |
| 320 | + relabel(root, &metadata, &path, as_path, policy)? |
| 321 | + } |
| 322 | + // Trim what we added to the path |
| 323 | + let r = path.pop(); |
| 324 | + assert!(r); |
| 325 | + if let Some(p) = as_path.as_mut() { |
| 326 | + let r = p.pop(); |
| 327 | + assert!(r); |
291 | 328 | }
|
292 |
| - dest_path.pop(); |
293 | 329 | }
|
294 | 330 |
|
295 | 331 | Ok(())
|
296 | 332 | }
|
297 | 333 |
|
| 334 | +/// Recursively relabel the target directory. |
| 335 | +pub(crate) fn relabel_recurse( |
| 336 | + root: &Dir, |
| 337 | + path: impl AsRef<Utf8Path>, |
| 338 | + as_path: Option<&Utf8Path>, |
| 339 | + policy: &ostree::SePolicy, |
| 340 | +) -> Result<()> { |
| 341 | + let mut path = path.as_ref().to_owned(); |
| 342 | + // This path must be relative, as we access via cap-std |
| 343 | + assert!(!path.starts_with("/")); |
| 344 | + let mut as_path = as_path.map(|v| v.to_owned()); |
| 345 | + // But the as_path must be absolute, if provided |
| 346 | + if let Some(as_path) = as_path.as_deref() { |
| 347 | + assert!(as_path.starts_with("/")); |
| 348 | + } |
| 349 | + relabel_recurse_inner(root, &mut path, as_path.as_mut(), policy) |
| 350 | +} |
| 351 | + |
298 | 352 | /// A wrapper for creating a directory, also optionally setting a SELinux label.
|
299 | 353 | /// The provided `skip` parameter is a device/inode that we will ignore (and not traverse).
|
300 | 354 | pub(crate) fn ensure_dir_labeled_recurse(
|
|
0 commit comments