ALPHA: wire is a tool to deploy nixos systems wire.althaea.zone/
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

use OpenOptions instead of manually setting permissions (#481)

authored by

marshmallow and committed by
GitHub
7af3e66c cbd2d863

+54 -19
+1
CHANGELOG.md
··· 26 26 - Fixed garnix docs links in documentation. 27 27 - Forces `bash` instead of remote user's potentially unsupported shell. This bug 28 28 was causing strange and hard to diagnose issues. 29 + - Fixed a possible time-of-check to time-of-use bug while setting key permissions. 29 30 30 31 ## [v1.2.0] - 2026-03-18 31 32
+1 -1
Cargo.toml
··· 27 27 im = { version = "15.1.0", features = ["serde"] } 28 28 anyhow = "1.0.100" 29 29 prost = "0.14.1" 30 - nix = { version = "0.31.0", features = ["user", "poll", "term"] } 30 + nix = { version = "0.31.0", features = ["user", "poll", "term", "fs"] } 31 31 miette = { version = "7.6.0", features = ["fancy"] } 32 32 thiserror = "2.0.17" 33 33 sha2 = "0.11.0"
+52 -18
crates/key_agent/src/main.rs
··· 2 2 // Copyright 2024-2025 wire Contributors 3 3 4 4 #![deny(clippy::pedantic)] 5 + use anyhow::Context; 5 6 use base64::Engine; 6 7 use base64::prelude::BASE64_STANDARD; 7 8 use futures_util::stream::StreamExt; 9 + use nix::sys::stat::fchmod; 10 + use nix::unistd::fchown; 8 11 use nix::unistd::{Group, User}; 9 12 use prost::Message; 10 13 use prost::bytes::Bytes; 11 14 use sha2::{Digest, Sha256}; 12 - use std::os::unix::fs::PermissionsExt; 13 - use std::os::unix::fs::chown; 15 + use std::os::fd::AsFd; 14 16 use std::path::{Path, PathBuf}; 15 - use tokio::fs::File; 17 + use tokio::fs::OpenOptions; 16 18 use tokio::io::AsyncWriteExt; 17 19 use tokio_util::codec::{FramedRead, LengthDelimitedCodec}; 18 20 use wire_key_agent::keys::KeySpec; ··· 63 65 } 64 66 65 67 let path = PathBuf::from(&spec.destination); 66 - create_path(&path)?; 68 + create_path(&path).context("creating directory for key")?; 67 69 68 - let mut file = File::create(path).await?; 69 - let mut permissions = file.metadata().await?.permissions(); 70 + let mut file = OpenOptions::new() 71 + .write(true) 72 + .create(true) 73 + .truncate(true) 74 + // only applies if the file is created 75 + .mode(spec.unix_mode) 76 + .custom_flags(nix::libc::O_NOFOLLOW) 77 + .open(&path) 78 + .await 79 + .context("opening file")?; 70 80 71 - permissions.set_mode(spec.unix_mode); 72 - file.set_permissions(permissions).await?; 81 + // enforce permission on existing files 82 + let mode = nix::sys::stat::Mode::from_bits(spec.unix_mode) 83 + .with_context(|| format!("failed to create unix mode: {:o}", spec.unix_mode))?; 73 84 74 - let user = User::from_name(&spec.user)?; 75 - let group = Group::from_name(&spec.group)?; 85 + fchmod(file.as_fd(), mode) 86 + .with_context(|| format!("setting permissions of fd to {:o}", spec.unix_mode))?; 76 87 77 - chown( 78 - spec.destination, 79 - // Default uid/gid to 0. This is then wrapped around an Option again for 80 - // the function. 81 - Some(user.map_or(0, |user| user.uid.into())), 82 - Some(group.map_or(0, |group| group.gid.into())), 83 - )?; 88 + // Default uid/gid to 0. This is then wrapped around an Option again for 89 + // the function. 90 + let user = Some( 91 + User::from_name(&spec.user) 92 + .context("obtaining user")? 93 + .map_or_else( 94 + || { 95 + println!("warning: defaulting uid to `0`"); 96 + 97 + 0.into() 98 + }, 99 + |user| user.uid, 100 + ), 101 + ); 102 + let group = Some( 103 + Group::from_name(&spec.group) 104 + .context("obtaining group")? 105 + .map_or_else( 106 + || { 107 + println!("warning: defaulting gid to `0`"); 108 + 109 + 0.into() 110 + }, 111 + |group| group.gid, 112 + ), 113 + ); 114 + 115 + // set permission on new files 116 + fchown(&file, user, group) 117 + .with_context(|| format!("setting ownership of fd to {user:?}, {group:?}"))?; 84 118 85 - file.write_all(&key_bytes).await?; 119 + file.write_all(&key_bytes).await.context("writing to fd")?; 86 120 87 121 // last key, goobye 88 122 if spec.last {