diff --git a/server/daemon/src/main.rs b/server/daemon/src/main.rs index 01edf56eb..154aa129a 100644 --- a/server/daemon/src/main.rs +++ b/server/daemon/src/main.rs @@ -14,11 +14,11 @@ #[global_allocator] static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; -use std::fs::{metadata, Metadata}; +use std::fs::metadata; #[cfg(target_family = "unix")] use std::os::unix::fs::MetadataExt; use std::path::PathBuf; -use std::process::exit; +use std::process::ExitCode; use clap::{Args, Parser, Subcommand}; use kanidmd_core::config::{Configuration, ServerConfig}; @@ -84,53 +84,17 @@ impl KanidmdOpt { } } -fn read_file_metadata(path: &PathBuf) -> Metadata { - match metadata(path) { - Ok(m) => m, - Err(e) => { - eprintln!( - "Unable to read metadata for '{}' - {:?}", - path.to_str().unwrap_or("invalid file path"), - e - ); - std::process::exit(1); - } - } -} - -/// Gets the user details if we're running in unix-land -#[cfg(not(target_family = "windows"))] -fn get_user_details_unix() -> (u32, u32) { - let cuid = get_current_uid(); - let ceuid = get_effective_uid(); - let cgid = get_current_gid(); - let cegid = get_effective_gid(); - - if cuid == 0 || ceuid == 0 || cgid == 0 || cegid == 0 { - eprintln!("WARNING: This is running as uid == 0 (root) which may be a security risk."); - // eprintln!("ERROR: Refusing to run - this process must not operate as root."); - // std::process::exit(1); - } - - if cuid != ceuid || cgid != cegid { - eprintln!("{} != {} || {} != {}", cuid, ceuid, cgid, cegid); - eprintln!("ERROR: Refusing to run - uid and euid OR gid and egid must be consistent."); - std::process::exit(1); - } - (cuid, ceuid) -} - /// Get information on the windows username #[cfg(target_family = "windows")] fn get_user_details_windows() { - eprintln!( + debug!( "Running on windows, current username is: {:?}", whoami::username() ); } #[tokio::main(flavor = "multi_thread")] -async fn main() { +async fn main() -> ExitCode { tracing_forest::worker_task() .set_global(true) .set_tag(sketching::event_tagger) @@ -149,7 +113,25 @@ async fn main() { // Get info about who we are. #[cfg(target_family = "unix")] - let (cuid, ceuid) = get_user_details_unix(); + let (cuid, ceuid) = { + let cuid = get_current_uid(); + let ceuid = get_effective_uid(); + let cgid = get_current_gid(); + let cegid = get_effective_gid(); + + if cuid == 0 || ceuid == 0 || cgid == 0 || cegid == 0 { + warn!("This is running as uid == 0 (root) which may be a security risk."); + // eprintln!("ERROR: Refusing to run - this process must not operate as root."); + // std::process::exit(1); + } + + if cuid != ceuid || cgid != cegid { + error!("{} != {} || {} != {}", cuid, ceuid, cgid, cegid); + error!("Refusing to run - uid and euid OR gid and egid must be consistent."); + return ExitCode::FAILURE + } + (cuid, ceuid) + }; // Read cli args, determine if we should backup/restore let opt = KanidmdParser::parse(); @@ -157,28 +139,39 @@ async fn main() { // print the app version and bail if let KanidmdOpt::Version(_) = &opt.commands { kanidm_proto::utils::show_version("kanidmd"); - exit(0); + return ExitCode::SUCCESS }; let mut config = Configuration::new(); // Check the permissions are OK. #[cfg(target_family = "unix")] { - let cfg_meta = read_file_metadata(&(opt.commands.commonopt().config_path)); + let cfg_path = &opt.commands.commonopt().config_path; + let cfg_meta = match metadata(cfg_path) { + Ok(m) => m, + Err(e) => { + error!( + "Unable to read metadata for '{}' - {:?}", + cfg_path.to_str().unwrap_or("invalid file path"), + e + ); + return ExitCode::FAILURE + } + }; if !kanidm_lib_file_permissions::readonly(&cfg_meta) { - eprintln!("WARNING: permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...", + warn!("permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...", opt.commands.commonopt().config_path.to_str().unwrap_or("invalid file path")); } if cfg_meta.mode() & 0o007 != 0 { - eprintln!("WARNING: {} has 'everyone' permission bits in the mode. This could be a security risk ...", + warn!("WARNING: {} has 'everyone' permission bits in the mode. This could be a security risk ...", opt.commands.commonopt().config_path.to_str().unwrap_or("invalid file path") ); } if cfg_meta.uid() == cuid || cfg_meta.uid() == ceuid { - eprintln!("WARNING: {} owned by the current uid, which may allow file permission changes. This could be a security risk ...", + warn!("WARNING: {} owned by the current uid, which may allow file permission changes. This could be a security risk ...", opt.commands.commonopt().config_path.to_str().unwrap_or("invalid file path") ); } @@ -188,8 +181,8 @@ async fn main() { let sconfig = match ServerConfig::new(&(opt.commands.commonopt().config_path)) { Ok(c) => c, Err(e) => { - eprintln!("Config Parse failure {:?}", e); - std::process::exit(1); + error!("Config Parse failure {:?}", e); + return ExitCode::FAILURE } }; // Check the permissions of the files from the configuration. @@ -198,28 +191,38 @@ async fn main() { // We can't check the db_path permissions because it may not exist yet! if let Some(db_parent_path) = db_path.parent() { if !db_parent_path.exists() { - eprintln!( + warn!( "DB folder {} may not exist, server startup may FAIL!", db_parent_path.to_str().unwrap_or("invalid file path") ); } let db_par_path_buf = db_parent_path.to_path_buf(); - let i_meta = read_file_metadata(&db_par_path_buf); + let i_meta = match metadata(&db_par_path_buf) { + Ok(m) => m, + Err(e) => { + error!( + "Unable to read metadata for '{}' - {:?}", + &db_par_path_buf.to_str().unwrap_or("invalid file path"), + e + ); + return ExitCode::FAILURE + } + }; if !i_meta.is_dir() { - eprintln!( + error!( "ERROR: Refusing to run - DB folder {} may not be a directory", db_par_path_buf.to_str().unwrap_or("invalid file path") ); - std::process::exit(1); + return ExitCode::FAILURE } if kanidm_lib_file_permissions::readonly(&i_meta) { - eprintln!("WARNING: DB folder permissions on {} indicate it may not be RW. This could cause the server start up to fail!", db_par_path_buf.to_str().unwrap_or("invalid file path")); + warn!("WARNING: DB folder permissions on {} indicate it may not be RW. This could cause the server start up to fail!", db_par_path_buf.to_str().unwrap_or("invalid file path")); } #[cfg(not(target_os="windows"))] if i_meta.mode() & 0o007 != 0 { - eprintln!("WARNING: DB folder {} has 'everyone' permission bits in the mode. This could be a security risk ...", db_par_path_buf.to_str().unwrap_or("invalid file path")); + warn!("WARNING: DB folder {} has 'everyone' permission bits in the mode. This could be a security risk ...", db_par_path_buf.to_str().unwrap_or("invalid file path")); } } @@ -243,9 +246,9 @@ async fn main() { KanidmdOpt::Server(_sopt) | KanidmdOpt::ConfigTest(_sopt) => { let config_test = matches!(&opt.commands, KanidmdOpt::ConfigTest(_)); if config_test { - eprintln!("Running in server configuration test mode ..."); + info!("Running in server configuration test mode ..."); } else { - eprintln!("Running in server mode ..."); + info!("Running in server mode ..."); }; // configuration options that only relate to server mode @@ -253,24 +256,43 @@ async fn main() { if let Some(i_str) = &(sconfig.tls_chain) { let i_path = PathBuf::from(i_str.as_str()); - - let i_meta = read_file_metadata(&i_path); + let i_meta = match metadata(&i_path) { + Ok(m) => m, + Err(e) => { + error!( + "Unable to read metadata for '{}' - {:?}", + &i_path.to_str().unwrap_or("invalid file path"), + e + ); + return ExitCode::FAILURE + } + }; if !kanidm_lib_file_permissions::readonly(&i_meta) { - eprintln!("WARNING: permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...", i_str); + warn!("permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...", i_str); } } if let Some(i_str) = &(sconfig.tls_key) { let i_path = PathBuf::from(i_str.as_str()); - let i_meta = read_file_metadata(&i_path); - if !kanidm_lib_file_permissions::readonly(&i_meta) { - eprintln!("WARNING: permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...", i_str); - } - #[cfg(not(target_os="windows"))] - if i_meta.mode() & 0o007 != 0 { - eprintln!("WARNING: {} has 'everyone' permission bits in the mode. This could be a security risk ...", i_str); + let i_meta = match metadata(&i_path) { + Ok(m) => m, + Err(e) => { + error!( + "Unable to read metadata for '{}' - {:?}", + &i_path.to_str().unwrap_or("invalid file path"), + e + ); + return ExitCode::FAILURE } + }; + if !kanidm_lib_file_permissions::readonly(&i_meta) { + warn!("permissions on {} may not be secure. Should be readonly to running uid. This could be a security risk ...", i_str); + } + #[cfg(not(target_os="windows"))] + if i_meta.mode() & 0o007 != 0 { + warn!("WARNING: {} has 'everyone' permission bits in the mode. This could be a security risk ...", i_str); + } } let sctx = create_server_core(config, config_test).await; @@ -325,18 +347,18 @@ async fn main() { } } } - eprintln!("Signal received, shutting down"); + info!("Signal received, shutting down"); // Send a broadcast that we are done. sctx.shutdown().await; } Err(_) => { - eprintln!("Failed to start server core!"); + error!("Failed to start server core!"); // We may need to return an exit code here, but that may take some re-architecting // to ensure we drop everything cleanly. - return; + return ExitCode::FAILURE } } - eprintln!("Stopped 🛑 "); + info!("Stopped 🛑 "); } @@ -344,12 +366,12 @@ async fn main() { KanidmdOpt::Database { commands: DbCommands::Backup(bopt), } => { - eprintln!("Running in backup mode ..."); + info!("Running in backup mode ..."); let p = match bopt.path.to_str() { Some(p) => p, None => { - eprintln!("Invalid backup path"); - std::process::exit(1); + error!("Invalid backup path"); + return ExitCode::FAILURE } }; backup_server_core(&config, p); @@ -357,12 +379,12 @@ async fn main() { KanidmdOpt::Database { commands: DbCommands::Restore(ropt), } => { - eprintln!("Running in restore mode ..."); + info!("Running in restore mode ..."); let p = match ropt.path.to_str() { Some(p) => p, None => { - eprintln!("Invalid restore path"); - std::process::exit(1); + error!("Invalid restore path"); + return ExitCode::FAILURE } }; restore_server_core(&config, p).await; @@ -370,59 +392,59 @@ async fn main() { KanidmdOpt::Database { commands: DbCommands::Verify(_vopt), } => { - eprintln!("Running in db verification mode ..."); + info!("Running in db verification mode ..."); verify_server_core(&config).await; } KanidmdOpt::RecoverAccount(raopt) => { - eprintln!("Running account recovery ..."); + info!("Running account recovery ..."); recover_account_core(&config, &raopt.name).await; } KanidmdOpt::Database { commands: DbCommands::Reindex(_copt), } => { - eprintln!("Running in reindex mode ..."); + info!("Running in reindex mode ..."); reindex_server_core(&config).await; } KanidmdOpt::DbScan { commands: DbScanOpt::ListIndexes(_), } => { - eprintln!("👀 db scan - list indexes"); + info!("👀 db scan - list indexes"); dbscan_list_indexes_core(&config); } KanidmdOpt::DbScan { commands: DbScanOpt::ListId2Entry(_), } => { - eprintln!("👀 db scan - list id2entry"); + info!("👀 db scan - list id2entry"); dbscan_list_id2entry_core(&config); } KanidmdOpt::DbScan { commands: DbScanOpt::ListIndexAnalysis(_), } => { - eprintln!("👀 db scan - list index analysis"); + info!("👀 db scan - list index analysis"); dbscan_list_index_analysis_core(&config); } KanidmdOpt::DbScan { commands: DbScanOpt::ListIndex(dopt), } => { - eprintln!("👀 db scan - list index content - {}", dopt.index_name); + info!("👀 db scan - list index content - {}", dopt.index_name); dbscan_list_index_core(&config, dopt.index_name.as_str()); } KanidmdOpt::DbScan { commands: DbScanOpt::GetId2Entry(dopt), } => { - eprintln!("👀 db scan - get id2 entry - {}", dopt.id); + info!("👀 db scan - get id2 entry - {}", dopt.id); dbscan_get_id2entry_core(&config, dopt.id); } KanidmdOpt::DomainSettings { commands: DomainSettingsCmds::DomainChange(_dopt), } => { - eprintln!("Running in domain name change mode ... this may take a long time ..."); + info!("Running in domain name change mode ... this may take a long time ..."); domain_rename_core(&config).await; } KanidmdOpt::Database { commands: DbCommands::Vacuum(_copt), } => { - eprintln!("Running in vacuum mode ..."); + info!("Running in vacuum mode ..."); vacuum_server_core(&config); } KanidmdOpt::HealthCheck(sopt) => { @@ -478,15 +500,16 @@ async fn main() { format!("Failed to complete healthcheck: {:?}", error) } }; - eprintln!("CRITICAL: {error_message}"); - exit(1); + error!("CRITICAL: {error_message}"); + return ExitCode::FAILURE } }; debug!("Request: {req:?}"); - println!("OK") + info!("OK") } KanidmdOpt::Version(_) => {} } + ExitCode::SUCCESS }) - .await; + .await } diff --git a/unix_integration/src/cache_clear.rs b/unix_integration/src/cache_clear.rs index b113ffa73..ad390c0bb 100644 --- a/unix_integration/src/cache_clear.rs +++ b/unix_integration/src/cache_clear.rs @@ -13,6 +13,8 @@ #[macro_use] extern crate tracing; +use std::process::ExitCode; + use clap::Parser; use futures::executor::block_on; use kanidm_unix_common::client::call_daemon; @@ -23,14 +25,14 @@ use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; include!("./opt/cache_clear.rs"); #[tokio::main] -async fn main() { +async fn main() -> ExitCode { let opt = CacheClearOpt::parse(); if opt.debug { ::std::env::set_var("RUST_LOG", "kanidm=debug,kanidm_client=debug"); } if opt.version { println!("{}", kanidm_proto::utils::get_version("kanidm_cache_clear")); - std::process::exit(0); + return ExitCode::SUCCESS; } sketching::tracing_subscriber::fmt::init(); @@ -41,13 +43,13 @@ async fn main() { Ok(c) => c, Err(_e) => { error!("Failed to parse {}", DEFAULT_CONFIG_PATH); - std::process::exit(1); + return ExitCode::FAILURE; } }; if !opt.really { error!("Are you sure you want to proceed? If so use --really"); - return; + return ExitCode::SUCCESS; } let req = ClientRequest::ClearCache; @@ -62,5 +64,6 @@ async fn main() { Err(e) => { error!("Error -> {:?}", e); } - } + }; + ExitCode::SUCCESS } diff --git a/unix_integration/src/cache_invalidate.rs b/unix_integration/src/cache_invalidate.rs index 6190f8079..a5595a78b 100644 --- a/unix_integration/src/cache_invalidate.rs +++ b/unix_integration/src/cache_invalidate.rs @@ -13,6 +13,8 @@ #[macro_use] extern crate tracing; +use std::process::ExitCode; + use clap::Parser; use futures::executor::block_on; use kanidm_unix_common::client::call_daemon; @@ -23,7 +25,7 @@ use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; include!("./opt/cache_invalidate.rs"); #[tokio::main] -async fn main() { +async fn main() -> ExitCode { let opt = CacheInvalidateOpt::parse(); if opt.debug { ::std::env::set_var("RUST_LOG", "kanidm=debug,kanidm_client=debug"); @@ -33,7 +35,7 @@ async fn main() { "{}", kanidm_proto::utils::get_version("kanidm_cache_invalidate") ); - std::process::exit(0); + return ExitCode::SUCCESS; } sketching::tracing_subscriber::fmt::init(); @@ -44,7 +46,7 @@ async fn main() { Ok(c) => c, Err(_e) => { error!("Failed to parse {}", DEFAULT_CONFIG_PATH); - std::process::exit(1); + return ExitCode::FAILURE; } }; @@ -60,5 +62,6 @@ async fn main() { Err(e) => { error!("Error -> {:?}", e); } - } + }; + ExitCode::SUCCESS } diff --git a/unix_integration/src/ssh_authorizedkeys.rs b/unix_integration/src/ssh_authorizedkeys.rs index 2f13e0bf3..4d2e1989c 100644 --- a/unix_integration/src/ssh_authorizedkeys.rs +++ b/unix_integration/src/ssh_authorizedkeys.rs @@ -14,6 +14,7 @@ extern crate tracing; use std::path::PathBuf; +use std::process::ExitCode; use clap::Parser; use futures::executor::block_on; @@ -25,7 +26,7 @@ use kanidm_unix_common::unix_proto::{ClientRequest, ClientResponse}; include!("./opt/ssh_authorizedkeys.rs"); #[tokio::main] -async fn main() { +async fn main() -> ExitCode { let opt = SshAuthorizedOpt::parse(); if opt.debug { ::std::env::set_var("RUST_LOG", "kanidm=debug,kanidm_client=debug"); @@ -35,7 +36,7 @@ async fn main() { "{}", kanidm_proto::utils::get_version("kanidm_ssh_authorizedkeys") ); - std::process::exit(0); + return ExitCode::SUCCESS; } sketching::tracing_subscriber::fmt::init(); @@ -46,7 +47,7 @@ async fn main() { Ok(c) => c, Err(e) => { error!("Failed to parse {}: {:?}", DEFAULT_CONFIG_PATH, e); - std::process::exit(1); + return ExitCode::FAILURE; } }; @@ -61,7 +62,7 @@ async fn main() { "Failed to find unix socket at {}, quitting!", cfg.sock_path.as_str() ); - std::process::exit(1); + return ExitCode::FAILURE; } let req = ClientRequest::SshKey(opt.account_id); @@ -77,5 +78,6 @@ async fn main() { Err(e) => { error!("Error calling kanidm_unixd -> {:?}", e); } - } + }; + ExitCode::SUCCESS } diff --git a/unix_integration/src/tasks_daemon.rs b/unix_integration/src/tasks_daemon.rs index 4c97ad112..d1d9a9ab5 100644 --- a/unix_integration/src/tasks_daemon.rs +++ b/unix_integration/src/tasks_daemon.rs @@ -14,6 +14,7 @@ use std::ffi::CString; use std::os::unix::ffi::OsStrExt; use std::os::unix::fs::symlink; use std::path::Path; +use std::process::ExitCode; use std::time::Duration; use std::{fs, io}; @@ -217,18 +218,13 @@ async fn handle_tasks(stream: UnixStream, cfg: &KanidmUnixdConfig) { } #[tokio::main] -async fn main() { +async fn main() -> ExitCode { // let cuid = get_current_uid(); // let cgid = get_current_gid(); // We only need to check effective id let ceuid = get_effective_uid(); let cegid = get_effective_gid(); - if ceuid != 0 || cegid != 0 { - eprintln!("Refusing to run - this process *MUST* operate as root."); - std::process::exit(1); - } - tracing_forest::worker_task() .set_global(true) // Fall back to stderr @@ -241,12 +237,17 @@ async fn main() { ) }) .on(async { + if ceuid != 0 || cegid != 0 { + error!("Refusing to run - this process *MUST* operate as root."); + return ExitCode::FAILURE; + } + let unixd_path = Path::new(DEFAULT_CONFIG_PATH); let unixd_path_str = match unixd_path.to_str() { Some(cps) => cps, None => { error!("Unable to turn unixd_path to str"); - std::process::exit(1); + return ExitCode::FAILURE; } }; @@ -254,7 +255,7 @@ async fn main() { Ok(v) => v, Err(_) => { error!("Failed to parse {}", unixd_path_str); - std::process::exit(1); + return ExitCode::FAILURE; } }; @@ -284,6 +285,7 @@ async fn main() { }; server.await; + ExitCode::SUCCESS }) - .await; + .await } diff --git a/unix_integration/src/test_auth.rs b/unix_integration/src/test_auth.rs index 413b6a787..837f6e6fe 100644 --- a/unix_integration/src/test_auth.rs +++ b/unix_integration/src/test_auth.rs @@ -2,6 +2,8 @@ #[macro_use] extern crate tracing; +use std::process::ExitCode; + use clap::Parser; use futures::executor::block_on; use kanidm_unix_common::client::call_daemon; @@ -18,7 +20,7 @@ struct ClientOpt { } #[tokio::main] -async fn main() { +async fn main() -> ExitCode { let opt = ClientOpt::parse(); if opt.debug { ::std::env::set_var("RUST_LOG", "kanidm=debug,kanidm_client=debug"); @@ -27,11 +29,20 @@ async fn main() { debug!("Starting PAM auth tester tool ..."); - let cfg = KanidmUnixdConfig::new() + let Ok(cfg) = KanidmUnixdConfig::new() .read_options_from_optional_config(DEFAULT_CONFIG_PATH) - .unwrap_or_else(|_| panic!("Failed to parse {}", DEFAULT_CONFIG_PATH)); + else { + error!("Failed to parse {}", DEFAULT_CONFIG_PATH); + return ExitCode::FAILURE + }; - let password = rpassword::prompt_password("Enter Unix password: ").unwrap(); + let password = match rpassword::prompt_password("Enter Unix password: ") { + Ok(p) => p, + Err(e) => { + error!("Problem getting input password: {}", e); + return ExitCode::FAILURE; + } + }; let req = ClientRequest::PamAuthenticate(opt.account_id.clone(), password); let sereq = ClientRequest::PamAccountAllowed(opt.account_id); @@ -55,7 +66,7 @@ async fn main() { Err(e) => { error!("Error -> {:?}", e); } - } + }; match block_on(call_daemon(cfg.sock_path.as_str(), sereq)) { Ok(r) => match r { @@ -76,5 +87,6 @@ async fn main() { Err(e) => { error!("Error -> {:?}", e); } - } + }; + ExitCode::SUCCESS }