//! Listening on a Unix socket: the directory at 0700, a stale socket removed, the socket at 0600 //! (M5a spec, section 7). The same steps as `brokerd`'s; no crate depends on another role's. use std::fs::{DirBuilder, Permissions}; use std::io; use std::os::unix::fs::{DirBuilderExt, PermissionsExt}; use std::os::unix::net::UnixListener; use std::path::Path; /// Bind one socket: prepare its directory at 0700, make it private, remove any stale socket, /// bind, then make the socket itself private at 0600. The error names the path. pub fn listen(socket: &Path) -> Result { // A socket needs a directory of its own: step 2 makes that directory 0700, which must never // be `/` or a shared directory reached through a symbolic link. let dir = match socket.parent() { Some(parent) if parent.parent().is_some() && !parent.as_os_str().is_empty() => { parent.to_path_buf() } _ => { return Err(format!( "{}: a socket needs a directory of its own, not / or none", socket.display() )); } }; // 1. The directory, at 0700. DirBuilder::new() .recursive(true) .mode(0o700) .create(&dir) .map_err(|e| format!("{}: {e}", dir.display()))?; // `set_permissions` follows a symbolic link, so a link would make the directory it points to // private instead: refuse it. let kind = std::fs::symlink_metadata(&dir) .map_err(|e| format!("{}: {e}", dir.display()))? .file_type(); if !kind.is_dir() { return Err(format!( "{}: is a symbolic link or not a directory; memoryd will not change its mode", dir.display() )); } // 2. Make it private, always, even when it was already there at 0755. std::fs::set_permissions(&dir, Permissions::from_mode(0o700)) .map_err(|e| format!("{}: {e}", dir.display()))?; // 3. A stale socket is removed; a missing one is fine. match std::fs::remove_file(socket) { Ok(()) => {} Err(e) if e.kind() == io::ErrorKind::NotFound => {} Err(e) => return Err(format!("{}: {e}", socket.display())), } // 4. Bind, then 5. make the socket private. let listener = UnixListener::bind(socket).map_err(|e| format!("{}: {e}", socket.display()))?; std::fs::set_permissions(socket, Permissions::from_mode(0o600)) .map_err(|e| format!("{}: {e}", socket.display()))?; Ok(listener) }