feat(cfg): turn groupnames into gids at parse time
This commit is contained in:
7
assets/config.toml
Normal file
7
assets/config.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# example file to show/test configuration
|
||||||
|
|
||||||
|
[unix]
|
||||||
|
groups = [ "wheel", 0 ]
|
||||||
|
|
||||||
|
# [server]
|
||||||
|
# listen = "127.0.0.1:8080"
|
||||||
40
src/conf.rs
40
src/conf.rs
@@ -1,40 +1,40 @@
|
|||||||
|
//! Configuration file structure. (TODO, docs)
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{self, Read, Seek},
|
io::{self, Read, Seek},
|
||||||
|
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
|
||||||
};
|
};
|
||||||
|
|
||||||
use libc::gid_t;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
use crate::serdes::Group;
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum Either<T, U> {
|
|
||||||
Left(T),
|
|
||||||
Right(U),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Debug, U: Debug> Debug for Either<T, U> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Left(l) => l.fmt(f),
|
|
||||||
Self::Right(r) => r.fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Group = Either<String, gid_t>;
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Unix {
|
pub struct Unix {
|
||||||
groups: Vec<Group>,
|
pub groups: Vec<Group>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct Server {
|
||||||
|
pub listen: SocketAddr,
|
||||||
|
}
|
||||||
|
impl Default for Server {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
listen: SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 8080)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
unix: Unix,
|
pub unix: Unix,
|
||||||
|
pub server: Server,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ pub mod args;
|
|||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod conf;
|
pub mod conf;
|
||||||
pub mod ext;
|
pub mod ext;
|
||||||
|
pub mod serdes;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
|||||||
103
src/serdes/inner_helpers.rs
Normal file
103
src/serdes/inner_helpers.rs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
use libc::gid_t;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, de::Error};
|
||||||
|
|
||||||
|
/// Private module until its worth it to promote from size to a crate module.
|
||||||
|
mod __libc_wrappers {
|
||||||
|
use std::{ffi::CStr, mem::MaybeUninit};
|
||||||
|
|
||||||
|
use libc::c_char;
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum GetgrnamError {
|
||||||
|
#[error("no matching group record found")]
|
||||||
|
NoMatchingRecord,
|
||||||
|
/// ace variant for unexpected errors, error handling is shallow here
|
||||||
|
#[error("unexpected getgrnam_r error")]
|
||||||
|
Unexpected,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_group_by_name(cname: &CStr) -> Result<libc::gid_t, GetgrnamError> {
|
||||||
|
use GetgrnamError as E;
|
||||||
|
|
||||||
|
let name = cname.as_ptr();
|
||||||
|
let mut grp = MaybeUninit::uninit();
|
||||||
|
let mut buf = [c_char::from(0); 1024];
|
||||||
|
let mut result: MaybeUninit<*mut libc::group> = MaybeUninit::uninit();
|
||||||
|
let ret = unsafe {
|
||||||
|
libc::getgrnam_r(
|
||||||
|
name,
|
||||||
|
grp.as_mut_ptr(),
|
||||||
|
buf.as_mut_ptr(),
|
||||||
|
buf.len(),
|
||||||
|
result.as_mut_ptr(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: Pointers inside `grp` point may point to the buffer `buf` so they cannot be
|
||||||
|
// moved outside of this function.
|
||||||
|
|
||||||
|
// > On success, getgrnam_r() [..] return zero, and set `*result` to `grp`.
|
||||||
|
//
|
||||||
|
// > If no matching group record was found, these functions return 0 and store NULL in
|
||||||
|
// `*result`.
|
||||||
|
//
|
||||||
|
// > In case of error, an error number is returned, and NULL stored in `*result`.
|
||||||
|
//
|
||||||
|
// SAFETY: Either way, `result` is initialized to a nullable pointer.
|
||||||
|
let result_ptr = unsafe { result.assume_init() };
|
||||||
|
if ret == 0 && result_ptr == grp.as_mut_ptr() {
|
||||||
|
let grp_gid = unsafe { grp.assume_init() }.gr_gid;
|
||||||
|
Ok(grp_gid)
|
||||||
|
} else if ret == 0 && result_ptr.is_null() {
|
||||||
|
Err(E::NoMatchingRecord)
|
||||||
|
} else {
|
||||||
|
// ret should be != 0 and result NULL, but any other case is also unexpected and I'm
|
||||||
|
// not doing much FFI error handling rn
|
||||||
|
Err(E::Unexpected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum EitherTyp {
|
||||||
|
Gid(gid_t),
|
||||||
|
Groupname(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize either a gid number or a groupname into a [`gid_t`]
|
||||||
|
#[allow(clippy::missing_errors_doc)]
|
||||||
|
pub fn deserialize_group<'de, D>(d: D) -> Result<gid_t, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let either = EitherTyp::deserialize(d)?;
|
||||||
|
|
||||||
|
match either {
|
||||||
|
EitherTyp::Gid(gid) => Ok(gid),
|
||||||
|
EitherTyp::Groupname(groupname) => {
|
||||||
|
use __libc_wrappers::GetgrnamError as E;
|
||||||
|
use serde::de::Unexpected;
|
||||||
|
|
||||||
|
let cname = CString::new(groupname).map_err(|nul_err| {
|
||||||
|
D::Error::invalid_value(
|
||||||
|
Unexpected::Bytes(&nul_err.into_vec()),
|
||||||
|
&"a string without null bytes",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let gid = __libc_wrappers::get_group_by_name(&cname).map_err(|err| match err {
|
||||||
|
E::NoMatchingRecord => D::Error::invalid_value(
|
||||||
|
Unexpected::Other("non-existent group"),
|
||||||
|
&"existent group",
|
||||||
|
),
|
||||||
|
E::Unexpected => D::Error::custom("caught an unexpected getgrnam error"),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(gid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
16
src/serdes/mod.rs
Normal file
16
src/serdes/mod.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use libc::gid_t;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub mod inner_helpers;
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Group(#[serde(deserialize_with = "inner_helpers::deserialize_group")] pub gid_t);
|
||||||
|
|
||||||
|
impl Debug for Group {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Debug::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user