{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.
pull/48/head
Jake McGinty 2021-04-09 15:00:53 +09:00
parent ee890ccaa7
commit 72dc14c49c
4 changed files with 52 additions and 12 deletions

View File

@ -1,4 +1,5 @@
use crate::Error; use crate::Error;
use colored::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use shared::{ensure_dirs_exist, Cidr, IoErrorContext, Peer, CLIENT_DATA_PATH}; use shared::{ensure_dirs_exist, Cidr, IoErrorContext, Peer, CLIENT_DATA_PATH};
use std::{ use std::{
@ -23,12 +24,18 @@ pub enum Contents {
impl DataStore { impl DataStore {
pub(self) fn open_with_path<P: AsRef<Path>>(path: P, create: bool) -> Result<Self, Error> { pub(self) fn open_with_path<P: AsRef<Path>>(path: P, create: bool) -> Result<Self, Error> {
let path = path.as_ref();
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.read(true) .read(true)
.write(true) .write(true)
.create(create) .create(create)
.open(&path) .open(path)
.with_path(&path)?; .with_path(path)?;
if shared::chmod(&file, 0o600)? {
println!("{} updated permissions for {} to 0600.", "[!]".yellow(), path.display());
}
let mut json = String::new(); let mut json = String::new();
file.read_to_string(&mut json).with_path(path)?; file.read_to_string(&mut json).with_path(path)?;
let contents = serde_json::from_str(&json).unwrap_or_else(|_| Contents::V1 { let contents = serde_json::from_str(&json).unwrap_or_else(|_| Contents::V1 {

View File

@ -114,6 +114,7 @@ pub struct ConfigFile {
impl ConfigFile { impl ConfigFile {
pub fn write_to_path<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> { pub fn write_to_path<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
let mut invitation_file = File::create(&path).with_path(&path)?; let mut invitation_file = File::create(&path).with_path(&path)?;
shared::chmod(&invitation_file, 0o600)?;
invitation_file invitation_file
.write_all(toml::to_string(self).unwrap().as_bytes()) .write_all(toml::to_string(self).unwrap().as_bytes())
.with_path(path)?; .with_path(path)?;
@ -121,6 +122,11 @@ impl ConfigFile {
} }
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Error> { pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
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)?)?) Ok(toml::from_slice(&std::fs::read(&path).with_path(path)?)?)
} }
} }

View File

@ -1,4 +1,5 @@
use crate::{ensure_dirs_exist, Error, IoErrorContext, CLIENT_CONFIG_PATH}; use crate::{ensure_dirs_exist, Error, IoErrorContext, CLIENT_CONFIG_PATH};
use colored::*;
use indoc::writedoc; use indoc::writedoc;
use ipnetwork::IpNetwork; use ipnetwork::IpNetwork;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -58,12 +59,16 @@ impl InterfaceConfig {
comments: bool, comments: bool,
mode: Option<u32>, mode: Option<u32>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let path = path.as_ref();
let mut target_file = OpenOptions::new() let mut target_file = OpenOptions::new()
.create_new(true) .create_new(true)
.write(true) .write(true)
.open(&path) .open(path)
.with_path(&path)?; .with_path(path)?;
if let Some(val) = mode { 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 metadata = target_file.metadata()?;
let mut permissions = metadata.permissions(); let mut permissions = metadata.permissions();
permissions.set_mode(val); permissions.set_mode(val);
@ -106,7 +111,12 @@ impl InterfaceConfig {
} }
pub fn from_interface(interface: &InterfaceName) -> Result<Self, Error> { pub fn from_interface(interface: &InterfaceName) -> Result<Self, Error> {
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<PathBuf, Error> { fn build_config_file_path(interface: &InterfaceName) -> Result<PathBuf, Error> {

View File

@ -1,3 +1,4 @@
use colored::*;
use ipnetwork::IpNetwork; use ipnetwork::IpNetwork;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use prompts::hostname_validator; use prompts::hostname_validator;
@ -362,21 +363,37 @@ pub static WG_DIR: &str = "/etc/wireguard";
pub fn ensure_dirs_exist(dirs: &[&Path]) -> Result<(), Error> { pub fn ensure_dirs_exist(dirs: &[&Path]) -> Result<(), Error> {
for dir in dirs { for dir in dirs {
match fs::create_dir(dir) { 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 => { Err(e) if e.kind() != io::ErrorKind::AlreadyExists => {
return Err(e.into()); 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(()) 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<bool, Error> {
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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;