Fix permissions setting for new account folder creation in the
`Filesystem` storage backend
This commit is contained in:
parent
c6675cb024
commit
64b5051341
3 changed files with 62 additions and 17 deletions
|
@ -2,7 +2,7 @@ use {
|
||||||
super::*,
|
super::*,
|
||||||
crate::{
|
crate::{
|
||||||
message::Parser as MessageParser,
|
message::Parser as MessageParser,
|
||||||
prelude::{ClientCertificateStore, Certificate, ParseMailboxError},
|
prelude::{Certificate, ClientCertificateStore},
|
||||||
},
|
},
|
||||||
rustls_pemfile::{read_one, Item},
|
rustls_pemfile::{read_one, Item},
|
||||||
std::{
|
std::{
|
||||||
|
@ -10,11 +10,13 @@ use {
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::{self, BufReader, BufWriter, Write},
|
io::{self, BufReader, BufWriter, Write},
|
||||||
iter,
|
iter,
|
||||||
os::{fd::AsRawFd, unix::{fs::DirBuilderExt, prelude::OpenOptionsExt}},
|
os::unix::fs::DirBuilderExt,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
mod error;
|
mod error;
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
|
|
||||||
pub trait MultiDomain: MailStore {
|
pub trait MultiDomain: MailStore {
|
||||||
|
@ -262,26 +264,27 @@ impl MailStore for Filesystem {
|
||||||
let mut path = self.path.clone();
|
let mut path = self.path.clone();
|
||||||
path.push(&mb.host.to_string());
|
path.push(&mb.host.to_string());
|
||||||
path.push(&mb.username);
|
path.push(&mb.username);
|
||||||
fs::create_dir_all(&path)?;
|
fs::DirBuilder::new()
|
||||||
|
.recursive(true)
|
||||||
|
.create(&path)?;
|
||||||
|
let p = CString::new(path.to_str().ok_or(Error::Utf8)?.to_string())?;
|
||||||
|
if let Some(pw) = pw::Passwd::getpw()? {
|
||||||
|
let groups = pw.groups()?;
|
||||||
|
if let Some(gr) = groups.iter().find(|g| g.name == mb.username) {
|
||||||
|
chown(p.clone(), pw.uid, gr.gid)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We have to explicitly call `chown` after creating the directory,
|
||||||
|
// rather than setting permissions during creation, as the umask
|
||||||
|
// might squash some of the bits we're specifically trying to set.
|
||||||
|
chmod(p, 0o2770)?;
|
||||||
if let Some(ref blurb) = mb.blurb {
|
if let Some(ref blurb) = mb.blurb {
|
||||||
path.push("blurb");
|
path.push("blurb");
|
||||||
let fd = File::options()
|
let fd = File::options()
|
||||||
.create(true)
|
.create(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
.mode(0o2770)
|
|
||||||
.open(&path)?;
|
.open(&path)?;
|
||||||
if let Some(pw) = pw::Passwd::getpw()?
|
|
||||||
{
|
|
||||||
let groups = pw.groups()?;
|
|
||||||
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().into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut writer = BufWriter::new(fd);
|
let mut writer = BufWriter::new(fd);
|
||||||
writer.write_all(blurb.as_bytes())?;
|
writer.write_all(blurb.as_bytes())?;
|
||||||
}
|
}
|
||||||
|
@ -421,6 +424,8 @@ impl ClientCertificateStore for Filesystem {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::os::unix::prelude::PermissionsExt;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn store() -> Filesystem {
|
fn store() -> Filesystem {
|
||||||
|
@ -514,7 +519,29 @@ mod tests {
|
||||||
.add_user("rob@misfin.example.org Rob Zombie")
|
.add_user("rob@misfin.example.org Rob Zombie")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(store().has_mailuser("rob@misfin.example.org"));
|
assert!(store().has_mailuser("rob@misfin.example.org"));
|
||||||
|
let permissions = fs::metadata("test/mailstore/misfin.example.org/rob")
|
||||||
|
.unwrap()
|
||||||
|
.permissions();
|
||||||
|
assert_eq!(permissions.mode(), 0o42770);
|
||||||
assert!(store().remove_user("rob@misfin.example.org"));
|
assert!(store().remove_user("rob@misfin.example.org"));
|
||||||
assert!(!store().has_mailuser("rob@misfin.example.org"));
|
assert!(!store().has_mailuser("rob@misfin.example.org"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn chown(path: CString, uid: u32, gid: u32) -> Result<(), io::Error> {
|
||||||
|
unsafe {
|
||||||
|
if libc::chown(path.as_ptr(), uid, gid) != 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chmod(path: CString, mode: u32) -> Result<(), io::Error> {
|
||||||
|
unsafe {
|
||||||
|
if libc::chmod(path.as_ptr(), mode) != 0 {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
use {
|
use {
|
||||||
crate::prelude::ParseMailboxError,
|
crate::prelude::ParseMailboxError,
|
||||||
std::{fmt, io},
|
std::{ffi::NulError, fmt, io, str::Utf8Error},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
MailBox(ParseMailboxError),
|
MailBox(ParseMailboxError),
|
||||||
|
FFi(NulError),
|
||||||
Permissions(pw::Error),
|
Permissions(pw::Error),
|
||||||
|
Utf8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Io(e) => write!(f, "Filesystem error: {e}"),
|
Self::Io(e) => write!(f, "Filesystem error: {e}"),
|
||||||
|
Self::FFi(e) => write!(f, "Filesystem error: {e}"),
|
||||||
Self::MailBox(e) => write!(f, "Filesystem error: {e}"),
|
Self::MailBox(e) => write!(f, "Filesystem error: {e}"),
|
||||||
Self::Permissions(e) => write!(f, "Filesystem error: {e}"),
|
Self::Permissions(e) => write!(f, "Filesystem error: {e}"),
|
||||||
|
Self::Utf8 => write!(f, "Filesystem error: Utf8 failure"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,8 +28,10 @@ impl std::error::Error for Error {
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
match self {
|
match self {
|
||||||
Self::Io(e) => Some(e),
|
Self::Io(e) => Some(e),
|
||||||
|
Self::FFi(e) => Some(e),
|
||||||
Self::MailBox(e) => Some(e),
|
Self::MailBox(e) => Some(e),
|
||||||
Self::Permissions(e) => Some(e),
|
Self::Permissions(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +42,12 @@ impl From<io::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<NulError> for Error {
|
||||||
|
fn from(value: NulError) -> Self {
|
||||||
|
Self::FFi(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ParseMailboxError> for Error {
|
impl From<ParseMailboxError> for Error {
|
||||||
fn from(value: ParseMailboxError) -> Self {
|
fn from(value: ParseMailboxError) -> Self {
|
||||||
Self::MailBox(value)
|
Self::MailBox(value)
|
||||||
|
@ -47,3 +59,9 @@ impl From<pw::Error> for Error {
|
||||||
Self::Permissions(value)
|
Self::Permissions(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Utf8Error> for Error {
|
||||||
|
fn from(_value: Utf8Error) -> Self {
|
||||||
|
Self::Utf8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ pub use super::{
|
||||||
gemtext::{GemtextNode, Parser as GemtextParser},
|
gemtext::{GemtextNode, Parser as GemtextParser},
|
||||||
host::{Error as ParseHostError, Host},
|
host::{Error as ParseHostError, Host},
|
||||||
mailbox::{Error as ParseMailboxError, Mailbox},
|
mailbox::{Error as ParseMailboxError, Mailbox},
|
||||||
|
mailstore::{Account, Domain, Filesystem, FilesystemError, Folder, MailStore},
|
||||||
mailuser::Mailuser,
|
mailuser::Mailuser,
|
||||||
mailstore::{Account, Domain, Folder, Filesystem, FilesystemError, MailStore},
|
|
||||||
message::{Error as ParseMessageError, Link, Message, Recipients},
|
message::{Error as ParseMessageError, Link, Message, Recipients},
|
||||||
//receiver,
|
//receiver,
|
||||||
request::{Error as ParseRequestError, Request},
|
request::{Error as ParseRequestError, Request},
|
||||||
|
|
Loading…
Add table
Reference in a new issue