current work version
Dieser Commit ist enthalten in:
Ursprung
a828ccfffc
Commit
0080f592c0
18 geänderte Dateien mit 375 neuen und 268 gelöschten Zeilen
|
@ -4,5 +4,26 @@ RUST_LOG="TRACE"
|
|||
SSL_CERT_DIR="/etc/ca-certificates/extracted/cadir/"
|
||||
SSL_CERT_FILE="/etc/ca-certificates/extracted/tls-ca-bundle.pem"
|
||||
|
||||
[target]
|
||||
[target.'cfg(debug_assertions)']
|
||||
[profile.dev]
|
||||
opt-level = 3
|
||||
debug = true
|
||||
strip = "none"
|
||||
debug-assertions = true
|
||||
overflow-checks = true
|
||||
lto = false
|
||||
panic = "abort"
|
||||
incremental = true
|
||||
codegen-units = 16
|
||||
rpath = false
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
debug = false
|
||||
strip = "debuginfo"
|
||||
debug-assertions = false
|
||||
overflow-checks = true
|
||||
lto = true
|
||||
panic = 'abort'
|
||||
incremental = true
|
||||
codegen-units = 16
|
||||
rpath = false
|
||||
|
|
|
@ -10,3 +10,6 @@ end_of_line = lf
|
|||
charset = utf-8
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
|
|
@ -52,3 +52,11 @@ repos:
|
|||
types:
|
||||
- rust
|
||||
pass_filenames: false
|
||||
- id: tomlq
|
||||
name: toml Format
|
||||
description: Formats Toml files
|
||||
entry: echo tomlq . -ti
|
||||
language: system
|
||||
types:
|
||||
- toml
|
||||
exclude: '\.lock$'
|
||||
|
|
47
Cargo.lock
generiert
47
Cargo.lock
generiert
|
@ -190,17 +190,6 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-scoped"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4042078ea593edffc452eef14e99fdb2b120caa4ad9618bcdeabc4a023b98740"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"pin-project",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-signal"
|
||||
version = "0.2.11"
|
||||
|
@ -244,9 +233,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
|||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
|
@ -1096,9 +1085,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.173"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libsystemd"
|
||||
|
@ -1529,16 +1518,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "racme"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"acme2-eab",
|
||||
"async-scoped",
|
||||
"caps",
|
||||
"clap",
|
||||
"data-encoding",
|
||||
|
@ -1722,9 +1710,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.27"
|
||||
version = "0.23.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
|
||||
checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
|
@ -1953,12 +1941,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
|
@ -2266,9 +2251,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.29"
|
||||
version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
|
||||
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2724,18 +2709,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.25"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
||||
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.25"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
||||
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
22
Cargo.toml
22
Cargo.toml
|
@ -12,9 +12,18 @@ unstable = ["capabilities"]
|
|||
derive-new = "0.7.0"
|
||||
env_logger = "0.11"
|
||||
lazy_static = "1.5"
|
||||
schemars = { version = "0.9.0", default-features = false, features = ["derive", "std", "preserve_order"] }
|
||||
serde_json = { version = "1.0.140", default-features = false, features = ["std"] }
|
||||
toml = "0.8"
|
||||
|
||||
[dependencies.schemars]
|
||||
version = "0.9.0"
|
||||
default-features = false
|
||||
features = ["derive", "std", "preserve_order"]
|
||||
|
||||
[dependencies.serde_json]
|
||||
version = "1.0.140"
|
||||
default-features = false
|
||||
features = ["std"]
|
||||
|
||||
[dependencies.caps]
|
||||
version = "0.5.5"
|
||||
default-features = false
|
||||
|
@ -23,7 +32,7 @@ optional = true
|
|||
[dependencies.libc]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
features = ["const-extern-fn", "std", "extra_traits"]
|
||||
features = ["const-extern-fn", "std"]
|
||||
|
||||
[dependencies.data-encoding]
|
||||
version = "2.9"
|
||||
|
@ -52,11 +61,6 @@ version = "0.1"
|
|||
default-features = false
|
||||
features = ["fs"]
|
||||
|
||||
[dependencies.async-scoped]
|
||||
version = "0.9"
|
||||
default-features = false
|
||||
features = ["use-tokio"]
|
||||
|
||||
[dependencies.libsystemd]
|
||||
version = "0.7"
|
||||
default-features = false
|
||||
|
@ -82,7 +86,7 @@ features = ["derive"]
|
|||
[dependencies.tokio]
|
||||
version = "1.45"
|
||||
default-features = false
|
||||
features = ["rt", "sync", "time", "net"]
|
||||
features = ["rt", "sync", "time", "net", "macros"]
|
||||
|
||||
[dependencies.reqwest]
|
||||
version = "0.12"
|
||||
|
|
10
Makefile
10
Makefile
|
@ -1,10 +0,0 @@
|
|||
POLYFILL=polyfill-glibc
|
||||
TARGET_GLIBC=2.36
|
||||
SOURCEFILES=$(wildcard src/**/*.rs)
|
||||
|
||||
target/release/racme: Cargo.toml Cargo.lock $(SOURCEFILES)
|
||||
cargo build --release
|
||||
|
||||
racme: target/release/racme
|
||||
$(POLYFILL) --target-glibc=$(TARGET_GLIBC) --rename-dynamic-symbols=polyfill-glibc-renames.txt --output=racme $^
|
||||
strip racme
|
|
@ -1 +0,0 @@
|
|||
pidfd_getpid@GLIBC_2.39 pidfd_getpid
|
|
@ -8,13 +8,16 @@ use lazy_static::lazy_static;
|
|||
lazy_static! {
|
||||
pub static ref FILE_MODE: OpenOptions = OpenOptions::new().create(false).read(true).write(false).truncate(false).to_owned();
|
||||
pub static ref FILE_MODE_WRITE: OpenOptions = OpenOptions::new().create_new(true).write(true).to_owned();
|
||||
pub static ref FILE_MODE_OVERWRITE: OpenOptions = OpenOptions::new().create_new(false).create(true).truncate(true).write(true).to_owned();
|
||||
}
|
||||
lazy_static! {
|
||||
pub static ref LETS_ENCRYPT: String = String::from("letsencrypt");
|
||||
pub static ref LETS_ENCRYPT_STAGING: String = String::from("letsencrypt-staging");
|
||||
}
|
||||
|
||||
pub fn with_mode_write(mode: u32) -> OpenOptions {
|
||||
FILE_MODE.clone().mode(mode).to_owned()
|
||||
}
|
||||
pub const MODE_SECRETS: u32 = 0o600;
|
||||
pub const MODE_PRIVATE: u32 = 0o640;
|
||||
pub const MODE_PUBLIC: u32 = 0o644;
|
||||
|
||||
pub const POOL_SIZE: usize = 1;
|
||||
|
||||
|
|
22
src/main.rs
22
src/main.rs
|
@ -12,8 +12,6 @@ pub(crate) mod process;
|
|||
pub(crate) mod types;
|
||||
pub(crate) mod utils;
|
||||
|
||||
#[cfg(feature = "capabilities")]
|
||||
use crate::utils::check_permissions;
|
||||
use crate::{
|
||||
consts::*,
|
||||
prelude::*,
|
||||
|
@ -30,9 +28,9 @@ use crate::{
|
|||
SubCommand,
|
||||
},
|
||||
},
|
||||
utils::check_permissions,
|
||||
};
|
||||
use acme2_eab::Directory;
|
||||
use async_scoped::TokioScope;
|
||||
use clap::Parser;
|
||||
use env_logger::init as log_init;
|
||||
use libsystemd::daemon;
|
||||
|
@ -112,7 +110,7 @@ async fn load_privkey(path: PathBuf) -> Result<PKey<Private>, Error> {
|
|||
|
||||
async fn racme(flags: Arguments) -> Result<(), Error> {
|
||||
let client = default_client()?;
|
||||
let dns_manager = Manager::new();
|
||||
let mut dns_manager = Manager::new();
|
||||
let systemd_access = daemon::booted();
|
||||
let mainconfig = {
|
||||
let file = match FILE_MODE.open(flags.config).await {
|
||||
|
@ -123,6 +121,9 @@ async fn racme(flags: Arguments) -> Result<(), Error> {
|
|||
};
|
||||
GeneralConfig::from_file(file).await
|
||||
};
|
||||
for (zone, builder) in mainconfig.dns.iter() {
|
||||
dns_manager.add_builder(zone.clone(), builder.clone()).await;
|
||||
}
|
||||
trace!("Parsed Config: {mainconfig:?}");
|
||||
let files = {
|
||||
let rd = match read_dir(mainconfig.sites_path.clone()).await {
|
||||
|
@ -171,27 +172,23 @@ async fn racme(flags: Arguments) -> Result<(), Error> {
|
|||
}
|
||||
let challengepath = mainconfig.http_challenge_path.and_then(|path| PathBuf::from_str(path.as_str()).ok());
|
||||
|
||||
unsafe {
|
||||
TokioScope::scope_and_collect(|scope| {
|
||||
for site in siteconfigs {
|
||||
if let Some(account) = accounts.get(&site.ca) {
|
||||
scope.spawn(process::site(ProcessorArgs::new(
|
||||
process::site(ProcessorArgs::new(
|
||||
site,
|
||||
Arc::clone(account),
|
||||
account.clone(),
|
||||
&reload_services,
|
||||
&restart_services,
|
||||
certs.clone(),
|
||||
challengepath.clone(),
|
||||
dns_manager.clone(),
|
||||
client.clone(),
|
||||
)));
|
||||
))
|
||||
.await;
|
||||
} else {
|
||||
error!("Could not process site {} because of previous errors", site.name)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
.await;
|
||||
|
||||
if systemd_access {
|
||||
process::services(restart_services.into_inner(), reload_services.into_inner()).await;
|
||||
|
@ -242,7 +239,6 @@ async fn schema_generator() -> Result<(), Error> {
|
|||
fn main() {
|
||||
log_init();
|
||||
let args = Arguments::parse();
|
||||
#[cfg(feature = "capabilities")]
|
||||
if args.subcommands.is_none() && !check_permissions() {
|
||||
error!(
|
||||
"This program needs the capability to change the ownership and the permissions of files. this can be done via adding the capability via `capsh --caps=\"cap_chown+ep cap_fowner+ep\" --shell=racme -- racme.toml`, systemd service setting AmbientCapabilities or running as root(not recommended)"
|
||||
|
|
|
@ -1,27 +1,29 @@
|
|||
#[cfg(feature = "capabilities")]
|
||||
use std::os::{
|
||||
fd::AsFd,
|
||||
unix::fs::fchown,
|
||||
};
|
||||
use std::{
|
||||
collections::{
|
||||
HashMap,
|
||||
HashSet,
|
||||
},
|
||||
fs::Permissions,
|
||||
os::unix::fs::PermissionsExt as _,
|
||||
os::{
|
||||
fd::AsFd,
|
||||
unix::fs::{
|
||||
PermissionsExt as _,
|
||||
fchown,
|
||||
},
|
||||
},
|
||||
path::PathBuf,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
#[cfg(feature = "capabilities")]
|
||||
use crate::utils::get_uid_gid;
|
||||
use crate::{
|
||||
consts::{
|
||||
ATTEMPTS,
|
||||
FILE_MODE,
|
||||
FILE_MODE_OVERWRITE,
|
||||
MODE_PRIVATE,
|
||||
MODE_PUBLIC,
|
||||
MODE_SECRETS,
|
||||
WAIT_TIME,
|
||||
with_mode_write,
|
||||
},
|
||||
load_privkey,
|
||||
prelude::*,
|
||||
|
@ -38,6 +40,7 @@ use crate::{
|
|||
},
|
||||
utils::{
|
||||
gen_key,
|
||||
get_uid_gid,
|
||||
prefix_emails,
|
||||
},
|
||||
};
|
||||
|
@ -54,7 +57,6 @@ use acme2_eab::{
|
|||
OrderBuilder,
|
||||
OrderStatus,
|
||||
};
|
||||
use async_scoped::TokioScope;
|
||||
use log::*;
|
||||
use openssl::{
|
||||
hash::MessageDigest,
|
||||
|
@ -82,6 +84,7 @@ use tokio::{
|
|||
AsyncReadExt,
|
||||
AsyncWriteExt,
|
||||
},
|
||||
join,
|
||||
};
|
||||
use zbus_systemd::systemd1;
|
||||
|
||||
|
@ -146,7 +149,7 @@ pub async fn accounts(
|
|||
}
|
||||
} else {
|
||||
info!("creating new key for the account {}", name.clone());
|
||||
accountkeyfile = match with_mode_write(0o600).open(accountkey).await {
|
||||
accountkeyfile = match FILE_MODE_OVERWRITE.clone().mode(MODE_SECRETS).open(accountkey).await {
|
||||
Ok(file) => Some(file),
|
||||
Err(error) => {
|
||||
error!("Failed to open the file for the accountkey: {error}");
|
||||
|
@ -218,7 +221,6 @@ pub async fn site(args: ProcessorArgs<'_>) {
|
|||
};
|
||||
cert_renew = true;
|
||||
}
|
||||
#[cfg(feature = "capabilities")]
|
||||
let (uid, gid) = get_uid_gid(args.owner(), args.group());
|
||||
let mut private_key;
|
||||
// Private key block
|
||||
|
@ -245,7 +247,7 @@ pub async fn site(args: ProcessorArgs<'_>) {
|
|||
}
|
||||
if write_pkey {
|
||||
let pkey = private_key.private_key_to_pem_pkcs8().unwrap();
|
||||
let mut file = match_error!(with_mode_write(0o640).open(private_key_file.clone()).await=>Err(error)->"Failed to write new private key: {error}");
|
||||
let mut file = match_error!(FILE_MODE_OVERWRITE.clone().mode(MODE_PRIVATE).open(private_key_file.clone()).await=>Err(error)->"Failed to write new private key: {error}");
|
||||
#[cfg(feature = "capabilities")]
|
||||
if let Err(error) = fchown(file.as_fd(), uid, gid) {
|
||||
error!("Failed to change owner of the new privatekey: {error}");
|
||||
|
@ -289,13 +291,11 @@ pub async fn site(args: ProcessorArgs<'_>) {
|
|||
builder.set_identifiers(args.san().iter().map(|s| s.to_owned().into()).collect::<Vec<Identifier>>());
|
||||
order = match_error!(builder.build().await=>Err(error)-> "Failed order the certificate: {error}");
|
||||
let authorizations = match_error!(order.authorizations().await=>Err(error)-> "Failed to get the authorizations: {error}");
|
||||
let (_, result) = tokio::join! {
|
||||
unsafe {
|
||||
TokioScope::scope_and_collect(|scope|{
|
||||
let (_, result) = join! {
|
||||
async {
|
||||
for authorization in authorizations {
|
||||
scope.spawn(auth(authorization, args.challenge_dir(), args.dns_manager()));
|
||||
auth(authorization, args.challenge_dir(), args.dns_manager()).await;
|
||||
}
|
||||
})
|
||||
},
|
||||
order.wait_ready(WAIT_TIME, ATTEMPTS),
|
||||
};
|
||||
|
@ -338,17 +338,13 @@ pub async fn site(args: ProcessorArgs<'_>) {
|
|||
Ok(Some(certs)) => certs,
|
||||
};
|
||||
debug!("Received {} certificates.", certs.len());
|
||||
let mut pubkey_file =
|
||||
match_error!(with_mode_write(0o644).open(pubkey_filename).await=>Err(error)-> "Failed to open the file for the publickey: {error}");
|
||||
let mut pubkey_file = match_error!(FILE_MODE_OVERWRITE.clone().mode(MODE_PUBLIC).open(pubkey_filename).await=>Err(error)-> "Failed to open the file for the publickey: {error}");
|
||||
match_error!(pubkey_file.write_all(&certs[0].to_pem().unwrap()).await=>Err(error)-> "Failed to write the publickey: {error}");
|
||||
#[cfg(feature = "capabilities")]
|
||||
if let Err(error) = fchown(pubkey_file.as_fd(), uid, gid) {
|
||||
error!("Failed to change owner of the new publickey: {error}");
|
||||
return;
|
||||
}
|
||||
let mut fullchain =
|
||||
match_error!(with_mode_write(0o644).open(directory.join("fullchain.pem")).await=>Err(error)-> "failed to open the fullchain.pem: {error}");
|
||||
#[cfg(feature = "capabilities")]
|
||||
let mut fullchain = match_error!(FILE_MODE_OVERWRITE.clone().mode(MODE_PUBLIC).open(directory.join("fullchain.pem")).await=>Err(error)-> "failed to open the fullchain.pem: {error}");
|
||||
if let Err(error) = fchown(fullchain.as_fd(), uid, gid) {
|
||||
error!("Failed to change owner of the new file with the complete chain: {error}");
|
||||
return;
|
||||
|
@ -356,8 +352,8 @@ pub async fn site(args: ProcessorArgs<'_>) {
|
|||
for cert in certs.clone() {
|
||||
let _ = fullchain.write_all(&cert.to_pem().unwrap()).await;
|
||||
}
|
||||
let mut bundle = match_error!(with_mode_write(0o640).open(directory.join("bundle.pem")).await=>Err(error)-> "failed to open the bundle.pem: {error}");
|
||||
#[cfg(feature = "capabilities")]
|
||||
let mut bundle = match_error!(FILE_MODE_OVERWRITE.clone().mode(MODE_PRIVATE).open(directory.join("bundle.pem")).await=>Err(error)-> "failed to open the bundle.pem: {error}");
|
||||
|
||||
if let Err(error) = fchown(bundle.as_fd(), uid, gid) {
|
||||
error!("Failed to change owner of the new bundle: {error}");
|
||||
return;
|
||||
|
@ -377,7 +373,7 @@ pub async fn site(args: ProcessorArgs<'_>) {
|
|||
}
|
||||
}
|
||||
|
||||
async fn dns_auth(mut dns_challenge: Challenge, manager: Manager) -> types::Result<()> {
|
||||
async fn dns_auth(mut dns_challenge: Challenge, manager: Manager, domain: String) -> types::Result<()> {
|
||||
let token = match dns_challenge.token {
|
||||
Some(ref token) => token.clone(),
|
||||
None => {
|
||||
|
@ -389,13 +385,13 @@ async fn dns_auth(mut dns_challenge: Challenge, manager: Manager) -> types::Resu
|
|||
Ok(None) => return Error::err("Failed to get key authoriration: No Authorization returned"),
|
||||
Err(error) => return Error::err(format!("Failed to get key_authorization: {error}")),
|
||||
};
|
||||
if let Some(_guard) = manager.set_record(token.clone(), value).await {
|
||||
if let Some(mut guard) = manager.set_record(format!("_acme-challenge.{domain}"), value).await {
|
||||
dns_challenge = match dns_challenge.validate().await {
|
||||
Ok(challenge) => challenge,
|
||||
Err(error) => return Error::err(format!("Failed to send the request for validation: {error}")),
|
||||
};
|
||||
let challenge_result = dns_challenge.wait_done(WAIT_TIME, ATTEMPTS).await;
|
||||
drop(_guard);
|
||||
guard.unset_token().await;
|
||||
match challenge_result {
|
||||
Ok(_) => {
|
||||
info!("Challenge: {token} was correctly validated");
|
||||
|
@ -410,8 +406,9 @@ async fn dns_auth(mut dns_challenge: Challenge, manager: Manager) -> types::Resu
|
|||
|
||||
|
||||
pub async fn auth(auth: Authorization, challenge_dir: Option<PathBuf>, manager: Manager) {
|
||||
trace!("processing Authorization: {}:{}", auth.identifier.r#type, auth.identifier.value.clone());
|
||||
if let Some(dns_challenge) = auth.get_challenge("dns-01") {
|
||||
match dns_auth(dns_challenge, manager).await {
|
||||
match dns_auth(dns_challenge, manager, auth.identifier.value.clone()).await {
|
||||
Ok(()) => return,
|
||||
Err(error) => {
|
||||
error!("Failed to authenticate via DNS: {error}");
|
||||
|
@ -426,8 +423,7 @@ pub async fn auth(auth: Authorization, challenge_dir: Option<PathBuf>, manager:
|
|||
if let Some(directory) = challenge_dir {
|
||||
match_error!(create_dir_all(directory.clone()).await=>Err(error)-> "Failed to ensure the directory exists: {error}");
|
||||
let filename = directory.join(challenge.token.clone().unwrap());
|
||||
let mut challengefile =
|
||||
match_error!(with_mode_write(0o644).open(filename.clone()).await=>Err(error)-> "Failed to open the file for the http-challenge: {error}");
|
||||
let mut challengefile = match_error!(FILE_MODE_OVERWRITE.clone().mode(MODE_PUBLIC).open(filename.clone()).await=>Err(error)-> "Failed to open the file for the http-challenge: {error}");
|
||||
match_error!(challengefile.set_permissions(Permissions::from_mode(0o644)).await=>Err(error)-> "Failed to give the file the nessesary permissions: {error}");
|
||||
match_error!(
|
||||
challengefile.write_all(challenge.key_authorization().unwrap().unwrap().as_bytes()).await=>Err(error)->
|
||||
|
|
|
@ -33,10 +33,15 @@ pub struct GeneralConfig {
|
|||
pub sites_path: String,
|
||||
#[serde(default = "GeneralConfig::default_challenge")]
|
||||
pub http_challenge_path: Option<String>,
|
||||
|
||||
/// This contains the domains(Keys) and the DNS-Servers(values) that are responsible for it.
|
||||
#[serde(default = "GeneralConfig::default_dns")]
|
||||
pub dns: HashMap<String, DnsBuilder>,
|
||||
#[serde(default = "GeneralConfig::default_certificates")]
|
||||
pub certificates_path: String,
|
||||
|
||||
/// The Key of this table describe an nickname for an CA.
|
||||
/// Letsencrypt Prod and Staging are builtin configured, so they doesn't have to be configured.
|
||||
#[serde(default = "GeneralConfig::default_cas")]
|
||||
pub ca: HashMap<String, CA>,
|
||||
}
|
||||
|
@ -166,11 +171,9 @@ pub struct SiteConfig {
|
|||
|
||||
/// Owner of the Certificate and private key
|
||||
#[serde(default)]
|
||||
#[cfg(feature = "capabilities")]
|
||||
pub owner: String,
|
||||
pub owner: Option<String>,
|
||||
|
||||
/// Group of the Certificate and private key
|
||||
#[serde(default)]
|
||||
#[cfg(feature = "capabilities")]
|
||||
pub group: String,
|
||||
pub group: Option<String>,
|
||||
}
|
||||
|
|
|
@ -4,9 +4,10 @@ use serde::Deserialize;
|
|||
|
||||
use crate::{
|
||||
macros::DefDer,
|
||||
types::traits::{
|
||||
DnsHandler,
|
||||
DnsToken,
|
||||
types::{
|
||||
dns::Dns,
|
||||
structs::DnsToken,
|
||||
traits::DnsHandler,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -15,22 +16,26 @@ use crate::{
|
|||
#[serde(deny_unknown_fields)]
|
||||
pub struct DNSUpdateClientOptions {}
|
||||
|
||||
impl DNSUpdateClientOptions {
|
||||
pub fn build(self, _zone: String) -> Dns {
|
||||
Dns::DNSUpdate(DnsUpdateHandler {})
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
pub(super) struct DnsUpdateHandler {}
|
||||
pub struct DnsUpdateHandler {}
|
||||
|
||||
impl DnsHandler for DnsUpdateHandler {
|
||||
async fn set_record(&self, _domain: String, _content: String) -> crate::types::BoxedResult<dyn DnsToken> {
|
||||
Ok(Box::pin(DnsUpdateToken {}))
|
||||
async fn set_record(&self, _domain: String, _content: String) -> crate::types::Result<DnsToken> {
|
||||
Ok(DnsToken::new_dns_update())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
struct DnsUpdateToken {}
|
||||
pub struct DnsUpdateToken {}
|
||||
|
||||
impl Drop for DnsUpdateToken {
|
||||
fn drop(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
impl DnsUpdateToken {
|
||||
pub async fn remove(&mut self) {}
|
||||
}
|
||||
impl DnsToken for DnsUpdateToken {}
|
||||
|
||||
unsafe impl Send for DnsUpdateToken {}
|
||||
|
|
|
@ -14,11 +14,11 @@ use crate::{
|
|||
PdnsHandler,
|
||||
},
|
||||
},
|
||||
structs::Error,
|
||||
traits::{
|
||||
DnsHandler,
|
||||
structs::{
|
||||
DnsToken,
|
||||
Error,
|
||||
},
|
||||
traits::DnsHandler,
|
||||
},
|
||||
};
|
||||
use log::*;
|
||||
|
@ -27,7 +27,6 @@ use schemars::JsonSchema;
|
|||
use serde::Deserialize;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
};
|
||||
use tokio::sync::Mutex;
|
||||
|
@ -42,7 +41,7 @@ impl Manager {
|
|||
})))
|
||||
}
|
||||
|
||||
pub async fn set_record(&self, domain: String, value: String) -> Option<Pin<Box<dyn DnsToken + '_>>> {
|
||||
pub async fn set_record(&self, domain: String, value: String) -> Option<DnsToken> {
|
||||
let mut tld = domain.clone();
|
||||
if !tld.ends_with('.') {
|
||||
tld.push('.');
|
||||
|
@ -50,8 +49,8 @@ impl Manager {
|
|||
let (mut best_match_domain, mut best_match_length) = ("", 0);
|
||||
let guard = self.0.lock().await;
|
||||
for domain in guard.servers.keys() {
|
||||
if domain.ends_with(&tld) {
|
||||
let matched = domain.rmatches(&tld).last().unwrap();
|
||||
trace!("Checking {domain} < {tld}");
|
||||
if let Some(matched) = tld.rmatches(domain).last() {
|
||||
if matched.len() > best_match_length {
|
||||
best_match_domain = matched;
|
||||
best_match_length = matched.len();
|
||||
|
@ -59,6 +58,7 @@ impl Manager {
|
|||
}
|
||||
}
|
||||
if best_match_length == 0 {
|
||||
trace!("No matching Domain Found: {domain} TXT {value}");
|
||||
return None;
|
||||
}
|
||||
let handler = guard.servers.get(best_match_domain).unwrap();
|
||||
|
@ -70,6 +70,21 @@ impl Manager {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn add_builder(&mut self, zone: String, builder: DnsBuilder) {
|
||||
let mut fixed_zone = zone.clone();
|
||||
if !fixed_zone.ends_with('.') {
|
||||
fixed_zone.push('.');
|
||||
}
|
||||
self.0.lock().await.servers.insert(
|
||||
fixed_zone,
|
||||
match builder {
|
||||
DnsBuilder::PowerDNS(pdns_client_options) => pdns_client_options.build(zone),
|
||||
DnsBuilder::DNSUpdate(dnsupdate_client_options) => dnsupdate_client_options.build(zone),
|
||||
DnsBuilder::None => Dns::None,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Manager {}
|
||||
|
@ -92,14 +107,14 @@ pub enum DnsBuilder {
|
|||
}
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
enum Dns {
|
||||
pub enum Dns {
|
||||
PowerDNS(PdnsHandler),
|
||||
DNSUpdate(DnsUpdateHandler),
|
||||
None,
|
||||
}
|
||||
|
||||
impl Dns {
|
||||
pub async fn set_record(&self, domain: String, content: String) -> crate::types::BoxedResult<dyn DnsToken> {
|
||||
pub async fn set_record(&self, domain: String, content: String) -> crate::types::Result<DnsToken> {
|
||||
match self {
|
||||
Dns::PowerDNS(pdns_handler) => pdns_handler.set_record(domain, content).await,
|
||||
Dns::DNSUpdate(dns_update_handler) => dns_update_handler.set_record(domain, content).await,
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use std::{
|
||||
pin::Pin,
|
||||
time::{
|
||||
use std::time::{
|
||||
SystemTime,
|
||||
UNIX_EPOCH,
|
||||
},
|
||||
};
|
||||
|
||||
use derive_new::new;
|
||||
|
@ -12,22 +9,24 @@ use macro_rules_attribute::macro_rules_derive;
|
|||
use reqwest::{
|
||||
RequestBuilder,
|
||||
StatusCode,
|
||||
Url,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{
|
||||
Deserialize,
|
||||
Serialize,
|
||||
};
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
use crate::{
|
||||
default_client,
|
||||
macros::DefDer,
|
||||
types::{
|
||||
structs::Error,
|
||||
traits::{
|
||||
DnsHandler,
|
||||
dns::Dns,
|
||||
structs::{
|
||||
DnsToken,
|
||||
Error,
|
||||
},
|
||||
traits::DnsHandler,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -48,8 +47,20 @@ impl PdnsClientOptions {
|
|||
}
|
||||
}
|
||||
|
||||
impl PdnsClientOptions {
|
||||
pub fn build(self, zone: String) -> Dns {
|
||||
Dns::PowerDNS(PdnsHandler {
|
||||
client: default_client().unwrap(),
|
||||
server: self.server,
|
||||
api_key: self.api_key,
|
||||
server_id: self.server_id,
|
||||
zone,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
pub(super) struct PdnsHandler {
|
||||
pub struct PdnsHandler {
|
||||
client: reqwest::Client,
|
||||
server: String,
|
||||
api_key: String,
|
||||
|
@ -57,24 +68,49 @@ pub(super) struct PdnsHandler {
|
|||
zone: String,
|
||||
}
|
||||
|
||||
fn fix_url(baseurl: Url, path: String) -> Result<Url, ()> {
|
||||
let mut copy = baseurl.clone();
|
||||
copy.set_scheme("https")?;
|
||||
copy.set_port(None)?;
|
||||
copy.set_path(&path);
|
||||
copy.set_query(None);
|
||||
copy.set_fragment(None);
|
||||
copy.set_username("")?;
|
||||
copy.set_password(None)?;
|
||||
trace!("Resulting URL: {copy}");
|
||||
Ok(copy)
|
||||
}
|
||||
|
||||
|
||||
impl DnsHandler for PdnsHandler {
|
||||
async fn set_record(&self, domain: String, content: String) -> crate::types::BoxedResult<dyn DnsToken> {
|
||||
let base_request = self
|
||||
.client
|
||||
.patch(format!("https://{}/api/v1/servers/{}/zones/{}", self.server, self.server_id, self.zone))
|
||||
.header("Content-Type", "application/json")
|
||||
.header("X-API-Key", self.api_key.clone());
|
||||
async fn set_record(&self, mut domain: String, content: String) -> crate::types::Result<DnsToken> {
|
||||
trace!("Original URL: {}", self.server);
|
||||
let baseurl = match reqwest::Url::parse(&self.server) {
|
||||
Ok(url) => {
|
||||
match fix_url(url, format!("/api/v1/servers/{}/zones/{}", self.server_id, self.zone)) {
|
||||
Ok(url) => url,
|
||||
Err(_) => return Error::err("Failed to parse the Server URL of the DNS Server"),
|
||||
}
|
||||
},
|
||||
Err(error) => return Error::err(format!("Failed to parse the server url: {error}")),
|
||||
};
|
||||
let base_request = self.client.patch(baseurl).header("Content-Type", "application/json").header("X-API-Key", self.api_key.clone());
|
||||
|
||||
if !domain.ends_with('.') {
|
||||
domain.push('.');
|
||||
}
|
||||
|
||||
match base_request
|
||||
.try_clone()
|
||||
.unwrap()
|
||||
.json(&RecordUpdate::new(vec![
|
||||
RRSet::new(
|
||||
domain,
|
||||
domain.clone(),
|
||||
"TXT",
|
||||
ChangeType::Replace {
|
||||
records: vec![Record::new(content)],
|
||||
records: vec![Record::new(format!("\"{content}\""))],
|
||||
comments: vec![Comment::new("ACME entry", "")],
|
||||
ttl: 0,
|
||||
},
|
||||
),
|
||||
]))
|
||||
|
@ -83,49 +119,53 @@ impl DnsHandler for PdnsHandler {
|
|||
{
|
||||
Ok(_resp) if _resp.status() == StatusCode::NO_CONTENT => {},
|
||||
Ok(resp) => {
|
||||
let err = match resp.json::<PdnsError>().await {
|
||||
Ok(error) => error.error,
|
||||
Err(error) => format!("{error}"),
|
||||
let response_text = match resp.text().await {
|
||||
Ok(resp) => resp,
|
||||
Err(error) => return Error::err(format!("Failed to get the errormessage: {error}")),
|
||||
};
|
||||
let err = match serde_json::from_slice::<PdnsError>(response_text.clone().as_bytes()) {
|
||||
Ok(error) => {
|
||||
trace!("The Server Error has {} suberrors", error.errors.len());
|
||||
for suberror in error.errors {
|
||||
trace!("SubError: {suberror}")
|
||||
}
|
||||
error.error
|
||||
},
|
||||
Err(error) => {
|
||||
trace!("http Response: {response_text:?}");
|
||||
format!("(Parsing error) {error}")
|
||||
},
|
||||
};
|
||||
return Error::err(format!("Failed to set the record: {err}"));
|
||||
},
|
||||
Err(error) => return Error::err(format!("Failed to send the request to update the record: {error}")),
|
||||
}
|
||||
|
||||
Ok(Box::pin(PdnsToken {
|
||||
builder: base_request.try_clone().unwrap().json(&0),
|
||||
}))
|
||||
Ok(DnsToken::new_pdns(base_request.try_clone().unwrap().json(&RecordUpdate::new(vec![RRSet::new(domain, "TXT", ChangeType::Delete)]))))
|
||||
}
|
||||
}
|
||||
|
||||
/// Token that deletes the Record when its no longer needed
|
||||
#[derive(Debug)]
|
||||
pub(super) struct PdnsToken {
|
||||
builder: RequestBuilder,
|
||||
#[derive(Debug, new)]
|
||||
pub struct PdnsToken {
|
||||
pub(crate) builder: RequestBuilder,
|
||||
}
|
||||
|
||||
impl PdnsToken {
|
||||
fn new(builder: RequestBuilder) -> Pin<Box<Self>> {
|
||||
Box::pin(Self {
|
||||
builder,
|
||||
})
|
||||
impl Clone for PdnsToken {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
builder: self.builder.try_clone().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PdnsToken {
|
||||
fn drop(&mut self) {
|
||||
let handle = match Handle::try_current() {
|
||||
Ok(handle) => handle,
|
||||
Err(error) => {
|
||||
error!("Failed to aquire the handle from tokio. Cleanup of the Token Failed: {error}");
|
||||
return;
|
||||
},
|
||||
};
|
||||
match handle.block_on(self.builder.try_clone().unwrap().send()) {
|
||||
impl PdnsToken {
|
||||
pub async fn remove(&mut self) {
|
||||
match self.builder.try_clone().unwrap().send().await {
|
||||
Ok(response) => {
|
||||
if response.status() != StatusCode::NO_CONTENT {
|
||||
let status = response.status();
|
||||
match handle.block_on(response.json::<PdnsError>()) {
|
||||
match response.json::<PdnsError>().await {
|
||||
Ok(error) => error!("Failed to delete the Record({status}): {error}"),
|
||||
Err(error) => error!("Failed to parse the Error Response({status}): {error}"),
|
||||
}
|
||||
|
@ -137,7 +177,6 @@ impl Drop for PdnsToken {
|
|||
}
|
||||
|
||||
unsafe impl Send for PdnsToken {}
|
||||
impl DnsToken for PdnsToken {}
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
#[derive(Deserialize)]
|
||||
|
@ -183,6 +222,7 @@ enum ChangeType {
|
|||
Replace {
|
||||
records: Vec<Record>,
|
||||
comments: Vec<Comment>,
|
||||
ttl: u16,
|
||||
},
|
||||
Delete,
|
||||
}
|
||||
|
@ -201,5 +241,5 @@ struct RRSet {
|
|||
#[macro_rules_derive(DefDer)]
|
||||
#[derive(Serialize, new)]
|
||||
struct RecordUpdate {
|
||||
rrset: Vec<RRSet>,
|
||||
rrsets: Vec<RRSet>,
|
||||
}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
use std::{
|
||||
collections::HashSet,
|
||||
pin::Pin,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::types::traits::DnsToken;
|
||||
|
||||
pub mod config;
|
||||
pub mod cryptography;
|
||||
|
@ -20,6 +16,3 @@ pub type VString = Vec<String>;
|
|||
pub type SafeSet<T> = Mutex<HashSet<T>>;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, structs::Error>;
|
||||
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type BoxedResult<T: DnsToken> = std::result::Result<Pin<Box<T>>, structs::Error>;
|
||||
|
|
|
@ -16,7 +16,10 @@ use clap::{
|
|||
};
|
||||
use derive_new::new;
|
||||
use macro_rules_attribute::macro_rules_derive;
|
||||
use reqwest::Client;
|
||||
use reqwest::{
|
||||
Client,
|
||||
RequestBuilder,
|
||||
};
|
||||
use tokio::sync::MutexGuard;
|
||||
|
||||
use crate::{
|
||||
|
@ -28,7 +31,11 @@ use crate::{
|
|||
Algorithm,
|
||||
Strength,
|
||||
},
|
||||
dns::Manager,
|
||||
dns::{
|
||||
Manager,
|
||||
dnsupdate::DnsUpdateToken,
|
||||
pdns::PdnsToken,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -86,11 +93,9 @@ impl<'a: 'b, 'b> ProcessorArgs<'a> {
|
|||
|
||||
attr_function!(pub challenge_dir => Option<PathBuf>);
|
||||
|
||||
#[cfg(feature = "capabilities")]
|
||||
attr_function!(pub owner site => String);
|
||||
attr_function!(pub owner site => Option<String>);
|
||||
|
||||
#[cfg(feature = "capabilities")]
|
||||
attr_function!(pub group site => String);
|
||||
attr_function!(pub group site => Option<String>);
|
||||
|
||||
attr_function!(pub client => Client);
|
||||
|
||||
|
@ -155,3 +160,33 @@ impl Error {
|
|||
Err(Self::new(message.into()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[macro_rules_derive(DefDer)]
|
||||
pub enum DnsToken {
|
||||
None,
|
||||
Pdns(Box<PdnsToken>),
|
||||
DnsUpdate(DnsUpdateToken),
|
||||
}
|
||||
|
||||
impl DnsToken {
|
||||
pub fn new_pdns(builder: RequestBuilder) -> Self {
|
||||
Self::Pdns(Box::new(PdnsToken::new(builder)))
|
||||
}
|
||||
|
||||
pub fn new_dns_update() -> Self {
|
||||
Self::DnsUpdate(DnsUpdateToken {})
|
||||
}
|
||||
|
||||
pub fn new_none() -> Self {
|
||||
Self::None
|
||||
}
|
||||
|
||||
pub async fn unset_token(&mut self) {
|
||||
match self {
|
||||
DnsToken::None => {},
|
||||
DnsToken::Pdns(pdns_token) => pdns_token.remove().await,
|
||||
DnsToken::DnsUpdate(dns_update_token) => dns_update_token.remove().await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use crate::consts::{
|
||||
|
||||
use crate::{
|
||||
consts::{
|
||||
BRAINPOOL_MIDDLE,
|
||||
BRAINPOOL_STRONG,
|
||||
BRAINPOOL_WEAK,
|
||||
SECP_MIDDLE,
|
||||
SECP_STRONG,
|
||||
SECP_WEAK,
|
||||
};
|
||||
|
||||
|
||||
use crate::{
|
||||
},
|
||||
types,
|
||||
types::{
|
||||
cryptography::{
|
||||
Algorithm,
|
||||
Strength,
|
||||
},
|
||||
structs::San,
|
||||
structs::{
|
||||
DnsToken,
|
||||
San,
|
||||
},
|
||||
},
|
||||
};
|
||||
use log::*;
|
||||
|
@ -100,7 +102,7 @@ pub trait MatchX509 {
|
|||
impl MatchX509 for X509 {
|
||||
fn days_left(&self, days: u32) -> bool {
|
||||
let future_date = Asn1Time::days_from_now(days).unwrap();
|
||||
self.not_after().compare(&future_date).is_ok_and(|order| order.is_le())
|
||||
self.not_after().compare(&future_date).is_ok_and(|order| order.is_gt())
|
||||
}
|
||||
|
||||
fn match_san<T: IntoIterator<Item = San>>(&self, names: T) -> bool {
|
||||
|
@ -120,9 +122,6 @@ impl MatchX509 for X509 {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(drop_bounds)]
|
||||
pub trait DnsToken: std::fmt::Debug + Send + Drop {}
|
||||
|
||||
pub trait DnsHandler: std::fmt::Debug + Send {
|
||||
async fn set_record(&self, domain: String, content: String) -> types::BoxedResult<dyn DnsToken>;
|
||||
async fn set_record(&self, domain: String, content: String) -> types::Result<DnsToken>;
|
||||
}
|
||||
|
|
76
src/utils.rs
76
src/utils.rs
|
@ -109,10 +109,16 @@ pub fn string_to_cn(name: String) -> X509Name {
|
|||
builder.build()
|
||||
}
|
||||
|
||||
#[cfg(feature = "capabilities")]
|
||||
pub fn check_permissions() -> bool {
|
||||
let caps = has_cap(None, CAPABILITY_SET, caps::Capability::CAP_CHOWN).unwrap_or(false) &&
|
||||
has_cap(None, CAPABILITY_SET, caps::Capability::CAP_FOWNER).unwrap_or(false);
|
||||
#[allow(unused_mut)]
|
||||
let mut perms: u8 = 0;
|
||||
#[cfg(feature = "capabilities")]
|
||||
{
|
||||
if has_cap(None, CAPABILITY_SET, caps::Capability::CAP_CHOWN).unwrap_or(false) &&
|
||||
has_cap(None, CAPABILITY_SET, caps::Capability::CAP_FOWNER).unwrap_or(false)
|
||||
{
|
||||
perms |= 1 << 0
|
||||
}
|
||||
if cfg!(debug_assertions) {
|
||||
for set in [
|
||||
CapSet::Ambient,
|
||||
|
@ -131,53 +137,59 @@ pub fn check_permissions() -> bool {
|
|||
}
|
||||
}
|
||||
}
|
||||
caps
|
||||
}
|
||||
{
|
||||
if unsafe { libc::geteuid() } == 0 {
|
||||
perms |= 1 << 1;
|
||||
}
|
||||
}
|
||||
perms > 0
|
||||
}
|
||||
|
||||
pub fn get_uid_gid(username: String, group: String) -> (Option<u32>, Option<u32>) {
|
||||
let uid = if username.contains('\0') {
|
||||
error!("Invalid Username");
|
||||
fn clean_name(name: Option<String>) -> Option<CString> {
|
||||
if let Some(name) = name {
|
||||
if name.contains('\0') {
|
||||
error!("Invalid name: {name}");
|
||||
return None;
|
||||
}
|
||||
match CString::new(name) {
|
||||
Ok(cname) => Some(cname),
|
||||
Err(error) => {
|
||||
error!("Failed to convert the Username to an uid: {error}");
|
||||
None
|
||||
},
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_uid_gid(username: Option<String>, group: Option<String>) -> (Option<u32>, Option<u32>) {
|
||||
let uid = if let Some(username) = clean_name(username) {
|
||||
unsafe {
|
||||
match CString::new(username) {
|
||||
Ok(cstr) => {
|
||||
let passwd = libc::getpwnam(cstr.as_ptr());
|
||||
let passwd = libc::getpwnam(username.as_ptr());
|
||||
if !passwd.is_null() {
|
||||
Some(passwd.read().pw_uid)
|
||||
} else {
|
||||
warn!("Failed to get user: User does not exist");
|
||||
None
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
error!("Failed to convert the Username to an uid: {error}");
|
||||
}
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
let gid = if group.contains('\0') {
|
||||
error!("Invalid Group name");
|
||||
None
|
||||
} else {
|
||||
let gid = if let Some(group) = clean_name(group) {
|
||||
unsafe {
|
||||
match CString::new(group) {
|
||||
Ok(cstr) => {
|
||||
let passwd = libc::getgrnam(cstr.as_ptr());
|
||||
if !passwd.is_null() {
|
||||
Some(passwd.read().gr_gid)
|
||||
let group = libc::getgrnam(group.as_ptr());
|
||||
if !group.is_null() {
|
||||
Some(group.read().gr_gid)
|
||||
} else {
|
||||
warn!("Failed to get user: User does not exist");
|
||||
warn!("Failed to get group: Group does not exist");
|
||||
None
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
error!("Failed to convert the Group name to an uid: {error}");
|
||||
}
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
(uid, gid)
|
||||
}
|
||||
|
|
Laden …
Tabelle hinzufügen
In neuem Issue referenzieren