From 3a25914ec59953db583dee86a5d87bd75eeedc1d Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Fri, 23 Jun 2023 00:13:56 -0400 Subject: [PATCH] Added `create_folder` method to `MailStore` trait; Add code to set restrictive permissions on mail account folders; --- Cargo.toml | 6 ++++++ src/mailstore/filesystem.rs | 36 +++++++++++++++++++++++++++++++++++- src/mailstore/mod.rs | 15 +++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ccb1196..b41579c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] digest = "0.10" +libc = "0.2.146" rustls-pemfile = "1.0" sha2 = "0.10" time = "0.3" @@ -12,6 +13,11 @@ x509-parser = "0.15" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies.pw] +git = "https://codeberg.org/jeang3nie/pw_rs.git" +default-features = false +features = [ "libc" ] + [dependencies.rustls] version = "0.21" features = [ "dangerous_configuration" ] diff --git a/src/mailstore/filesystem.rs b/src/mailstore/filesystem.rs index f671572..470d124 100644 --- a/src/mailstore/filesystem.rs +++ b/src/mailstore/filesystem.rs @@ -1,4 +1,8 @@ -use std::io::BufReader; +use std::{ + ffi::CString, + io::BufReader, + os::{fd::AsRawFd, unix::prelude::OpenOptionsExt}, +}; use crate::prelude::Certificate; @@ -149,6 +153,21 @@ impl MailStore for Filesystem { Some(folder) } + fn create_folder(&self, user: &str, folder: &str) -> Result<(), io::Error> { + if self.has_mailuser(user) { + let user: Mailbox = user.parse().map_err(|e: ParseMailboxError| { + io::Error::new(io::ErrorKind::Other, e.to_string()) + })?; + let user: Mailuser = user.into(); + let mut path = self.path.clone(); + path.push(&user.host.to_string()); + path.push(&user.username); + path.push(folder); + fs::create_dir_all(&path)?; + } + Ok(()) + } + fn get_message(&self, user: &str, folder: &str, id: &str) -> Option { self.get_folder(user, folder) .and_then(|f| f.messages.get(id).cloned()) @@ -230,7 +249,22 @@ impl MailStore for Filesystem { .create(true) .write(true) .truncate(true) + .mode(0o2770) .open(path)?; + if let Some(pw) = pw::Passwd::getpw() + .map_err(|e: pw::Error| io::Error::new(io::ErrorKind::Other, e.to_string()))? + { + let groups = pw + .groups() + .map_err(|e: pw::Error| io::Error::new(io::ErrorKind::Other, e.to_string()))?; + if let Some(gr) = groups.iter().find(|g| g.name == mb.username) { + unsafe { + if libc::fchown(fd.as_raw_fd(), pw.uid, gr.gid) != 0 { + return Err(io::Error::last_os_error()); + } + } + } + } let mut writer = BufWriter::new(fd); writer.write_all(blurb.as_bytes())?; } diff --git a/src/mailstore/mod.rs b/src/mailstore/mod.rs index e146168..7b2b4b4 100644 --- a/src/mailstore/mod.rs +++ b/src/mailstore/mod.rs @@ -19,6 +19,8 @@ pub trait MailStore { fn has_mailuser(&self, mailuser: &str) -> bool; /// Retreives all of the messages for this user fn get_folder(&self, user: &str, folder: &str) -> Option; + /// Creates a folder for the specified user, if it doesn't already exist + fn create_folder(&self, user: &str, folder: &str) -> Result<(), Self::Error>; /// Checks whether this server has a message with the given title for this user and if so /// returns the message fn get_message(&self, user: &str, folder: &str, id: &str) -> Option; @@ -97,6 +99,19 @@ impl MailStore for Domain { .and_then(|u| u.folders.get(folder).cloned()) } + fn create_folder(&self, user: &str, folder: &str) -> Result<(), io::Error> { + self.users.get(user).cloned().and_then(|mut u| { + u.folders.insert( + folder.to_string(), + Folder { + name: folder.to_string(), + messages: HashMap::new(), + }, + ) + }); + Ok(()) + } + fn get_message(&self, user: &str, folder: &str, title: &str) -> Option { self.users .get(user)