From 72dc14c49cd9c3569e73fc228180b49e6e4c11c1 Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Fri, 9 Apr 2021 15:00:53 +0900 Subject: [PATCH] {client,server}: enforce permissions on directories and files This may become a warning rather than an action later, but for now let's make sure older installations that had incorrect permissions are taken care of. --- client/src/data_store.rs | 11 +++++++++-- server/src/main.rs | 6 ++++++ shared/src/interface_config.rs | 16 +++++++++++++--- shared/src/lib.rs | 31 ++++++++++++++++++++++++------- 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/client/src/data_store.rs b/client/src/data_store.rs index b405a91..ea46a08 100644 --- a/client/src/data_store.rs +++ b/client/src/data_store.rs @@ -1,4 +1,5 @@ use crate::Error; +use colored::*; use serde::{Deserialize, Serialize}; use shared::{ensure_dirs_exist, Cidr, IoErrorContext, Peer, CLIENT_DATA_PATH}; use std::{ @@ -23,12 +24,18 @@ pub enum Contents { impl DataStore { pub(self) fn open_with_path>(path: P, create: bool) -> Result { + let path = path.as_ref(); let mut file = OpenOptions::new() .read(true) .write(true) .create(create) - .open(&path) - .with_path(&path)?; + .open(path) + .with_path(path)?; + + if shared::chmod(&file, 0o600)? { + println!("{} updated permissions for {} to 0600.", "[!]".yellow(), path.display()); + } + let mut json = String::new(); file.read_to_string(&mut json).with_path(path)?; let contents = serde_json::from_str(&json).unwrap_or_else(|_| Contents::V1 { diff --git a/server/src/main.rs b/server/src/main.rs index 899dcba..ddddd3a 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -114,6 +114,7 @@ pub struct ConfigFile { impl ConfigFile { pub fn write_to_path>(&self, path: P) -> Result<(), Error> { let mut invitation_file = File::create(&path).with_path(&path)?; + shared::chmod(&invitation_file, 0o600)?; invitation_file .write_all(toml::to_string(self).unwrap().as_bytes()) .with_path(path)?; @@ -121,6 +122,11 @@ impl ConfigFile { } pub fn from_file>(path: P) -> Result { + let path = path.as_ref(); + let file = File::open(path).with_path(path)?; + if shared::chmod(&file, 0o600)? { + println!("{} updated permissions for {} to 0600.", "[!]".yellow(), path.display()); + } Ok(toml::from_slice(&std::fs::read(&path).with_path(path)?)?) } } diff --git a/shared/src/interface_config.rs b/shared/src/interface_config.rs index f7b2426..d0aaa2a 100644 --- a/shared/src/interface_config.rs +++ b/shared/src/interface_config.rs @@ -1,4 +1,5 @@ use crate::{ensure_dirs_exist, Error, IoErrorContext, CLIENT_CONFIG_PATH}; +use colored::*; use indoc::writedoc; use ipnetwork::IpNetwork; use serde::{Deserialize, Serialize}; @@ -58,12 +59,16 @@ impl InterfaceConfig { comments: bool, mode: Option, ) -> Result<(), Error> { + let path = path.as_ref(); let mut target_file = OpenOptions::new() .create_new(true) .write(true) - .open(&path) - .with_path(&path)?; + .open(path) + .with_path(path)?; if let Some(val) = mode { + if crate::chmod(&target_file, 0o600)? { + println!("{} updated permissions for {} to 0600.", "[!]".yellow(), path.display()); + } let metadata = target_file.metadata()?; let mut permissions = metadata.permissions(); permissions.set_mode(val); @@ -106,7 +111,12 @@ impl InterfaceConfig { } pub fn from_interface(interface: &InterfaceName) -> Result { - Self::from_file(Self::build_config_file_path(interface)?) + let path = Self::build_config_file_path(interface)?; + let file = File::open(&path).with_path(&path)?; + if crate::chmod(&file, 0o600)? { + println!("{} updated permissions for {} to 0600.", "[!]".yellow(), path.display()); + } + Self::from_file(path) } fn build_config_file_path(interface: &InterfaceName) -> Result { diff --git a/shared/src/lib.rs b/shared/src/lib.rs index e7a5039..9289dab 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -1,3 +1,4 @@ +use colored::*; use ipnetwork::IpNetwork; use lazy_static::lazy_static; use prompts::hostname_validator; @@ -362,21 +363,37 @@ pub static WG_DIR: &str = "/etc/wireguard"; pub fn ensure_dirs_exist(dirs: &[&Path]) -> Result<(), Error> { for dir in dirs { match fs::create_dir(dir) { - Ok(()) => { - let target_file = File::open(dir).with_path(dir)?; - let metadata = target_file.metadata().with_path(dir)?; - let mut permissions = metadata.permissions(); - permissions.set_mode(0o700); - }, Err(e) if e.kind() != io::ErrorKind::AlreadyExists => { return Err(e.into()); }, - _ => {}, + _ => { + let target_file = File::open(dir).with_path(dir)?; + if chmod(&target_file, 0o700)? { + println!("{} updated permissions for {} to 0700.", "[!]".yellow(), dir.display()); + } + }, } } Ok(()) } +/// Updates the permissions of a file or directory. Returns `Ok(true)` if +/// permissions had to be changed, `Ok(false)` if permissions were already +/// correct. +pub fn chmod(file: &File, mode: u32) -> Result { + let metadata = file.metadata()?; + let mut permissions = metadata.permissions(); + let updated = if permissions.mode() != mode { + permissions.set_mode(mode); + file.set_permissions(permissions)?; + true + } else { + false + }; + + Ok(updated) +} + #[cfg(test)] mod tests { use super::*;