From 1cb08a0a05260e4d3332f49569b1224da7be0c26 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Wed, 8 Sep 2021 14:00:14 +0200 Subject: [PATCH] move token_shadow to pbs_config workspace Also moved out crypt.rs (libcrypt bindings) to pbs_tools workspace. --- pbs-config/src/lib.rs | 1 + .../config => pbs-config/src}/token_shadow.rs | 22 ++++-- pbs-tools/src/crypt.rs | 68 +++++++++++++++++ pbs-tools/src/lib.rs | 1 + src/api2/access/user.rs | 2 +- src/auth.rs | 75 +------------------ src/config/mod.rs | 1 - src/server/auth.rs | 3 +- 8 files changed, 91 insertions(+), 82 deletions(-) rename {src/config => pbs-config/src}/token_shadow.rs (82%) create mode 100644 pbs-tools/src/crypt.rs diff --git a/pbs-config/src/lib.rs b/pbs-config/src/lib.rs index 76b10146..48ceb73d 100644 --- a/pbs-config/src/lib.rs +++ b/pbs-config/src/lib.rs @@ -7,6 +7,7 @@ pub mod remote; pub mod sync; pub mod tape_encryption_keys; pub mod tape_job; +pub mod token_shadow; pub mod verify; use anyhow::{format_err, Error}; diff --git a/src/config/token_shadow.rs b/pbs-config/src/token_shadow.rs similarity index 82% rename from src/config/token_shadow.rs rename to pbs-config/src/token_shadow.rs index 6a328c0f..6e466ce5 100644 --- a/src/config/token_shadow.rs +++ b/pbs-config/src/token_shadow.rs @@ -6,9 +6,9 @@ use serde_json::{from_value, Value}; use proxmox::tools::fs::CreateOptions; -use crate::api2::types::Authid; -use crate::auth; -use pbs_config::open_backup_lockfile; +use pbs_api_types::Authid; +//use crate::auth; +use crate::{open_backup_lockfile, BackupLockGuard}; const LOCK_FILE: &str = pbs_buildcfg::configdir!("/token.shadow.lock"); const CONF_FILE: &str = pbs_buildcfg::configdir!("/token.shadow"); @@ -21,6 +21,11 @@ pub struct ApiTokenSecret { pub secret: String, } +// Get exclusive lock +fn lock_config() -> Result { + open_backup_lockfile(LOCK_FILE, None, true) +} + fn read_file() -> Result, Error> { let json = proxmox::tools::fs::file_get_json(CONF_FILE, Some(Value::Null))?; @@ -33,7 +38,7 @@ fn read_file() -> Result, Error> { } fn write_file(data: HashMap) -> Result<(), Error> { - let backup_user = pbs_config::backup_user()?; + let backup_user = crate::backup_user()?; let options = CreateOptions::new() .perm(nix::sys::stat::Mode::from_bits_truncate(0o0640)) .owner(backup_user.uid) @@ -43,6 +48,7 @@ fn write_file(data: HashMap) -> Result<(), Error> { proxmox::tools::fs::replace_file(CONF_FILE, &json, options) } + /// Verifies that an entry for given tokenid / API token secret exists pub fn verify_secret(tokenid: &Authid, secret: &str) -> Result<(), Error> { if !tokenid.is_token() { @@ -52,7 +58,7 @@ pub fn verify_secret(tokenid: &Authid, secret: &str) -> Result<(), Error> { let data = read_file()?; match data.get(tokenid) { Some(hashed_secret) => { - auth::verify_crypt_pw(secret, &hashed_secret) + pbs_tools::crypt::verify_crypt_pw(secret, &hashed_secret) }, None => bail!("invalid API token"), } @@ -64,10 +70,10 @@ pub fn set_secret(tokenid: &Authid, secret: &str) -> Result<(), Error> { bail!("not an API token ID"); } - let _guard = open_backup_lockfile(LOCK_FILE, None, true)?; + let _guard = lock_config()?; let mut data = read_file()?; - let hashed_secret = auth::encrypt_pw(secret)?; + let hashed_secret = pbs_tools::crypt::encrypt_pw(secret)?; data.insert(tokenid.clone(), hashed_secret); write_file(data)?; @@ -80,7 +86,7 @@ pub fn delete_secret(tokenid: &Authid) -> Result<(), Error> { bail!("not an API token ID"); } - let _guard = open_backup_lockfile(LOCK_FILE, None, true)?; + let _guard = lock_config()?; let mut data = read_file()?; data.remove(tokenid); diff --git a/pbs-tools/src/crypt.rs b/pbs-tools/src/crypt.rs new file mode 100644 index 00000000..c47bfe24 --- /dev/null +++ b/pbs-tools/src/crypt.rs @@ -0,0 +1,68 @@ +use std::ffi::CStr; + +use anyhow::{bail, Error}; + +// from libcrypt1, 'lib/crypt.h.in' +const CRYPT_OUTPUT_SIZE: usize = 384; +const CRYPT_MAX_PASSPHRASE_SIZE: usize = 512; +const CRYPT_DATA_RESERVED_SIZE: usize = 767; +const CRYPT_DATA_INTERNAL_SIZE: usize = 30720; + +#[repr(C)] +struct crypt_data { + output: [libc::c_char; CRYPT_OUTPUT_SIZE], + setting: [libc::c_char; CRYPT_OUTPUT_SIZE], + input: [libc::c_char; CRYPT_MAX_PASSPHRASE_SIZE], + reserved: [libc::c_char; CRYPT_DATA_RESERVED_SIZE], + initialized: libc::c_char, + internal: [libc::c_char; CRYPT_DATA_INTERNAL_SIZE], +} + +pub fn crypt(password: &[u8], salt: &[u8]) -> Result { + #[link(name = "crypt")] + extern "C" { + #[link_name = "crypt_r"] + fn __crypt_r( + key: *const libc::c_char, + salt: *const libc::c_char, + data: *mut crypt_data, + ) -> *mut libc::c_char; + } + + let mut data: crypt_data = unsafe { std::mem::zeroed() }; + for (i, c) in salt.iter().take(data.setting.len() - 1).enumerate() { + data.setting[i] = *c as libc::c_char; + } + for (i, c) in password.iter().take(data.input.len() - 1).enumerate() { + data.input[i] = *c as libc::c_char; + } + + let res = unsafe { + let status = __crypt_r( + &data.input as *const _, + &data.setting as *const _, + &mut data as *mut _, + ); + if status.is_null() { + bail!("internal error: crypt_r returned null pointer"); + } + CStr::from_ptr(&data.output as *const _) + }; + Ok(String::from(res.to_str()?)) +} + +pub fn encrypt_pw(password: &str) -> Result { + + let salt = proxmox::sys::linux::random_data(8)?; + let salt = format!("$5${}$", base64::encode_config(&salt, base64::CRYPT)); + + crypt(password.as_bytes(), salt.as_bytes()) +} + +pub fn verify_crypt_pw(password: &str, enc_password: &str) -> Result<(), Error> { + let verify = crypt(password.as_bytes(), enc_password.as_bytes())?; + if verify != enc_password { + bail!("invalid credentials"); + } + Ok(()) +} diff --git a/pbs-tools/src/lib.rs b/pbs-tools/src/lib.rs index bb82f7d4..000591c3 100644 --- a/pbs-tools/src/lib.rs +++ b/pbs-tools/src/lib.rs @@ -6,6 +6,7 @@ pub mod broadcast_future; pub mod cert; pub mod cli; pub mod compression; +pub mod crypt; pub mod crypt_config; pub mod format; pub mod fd; diff --git a/src/api2/access/user.rs b/src/api2/access/user.rs index bb934093..6d5aa0ee 100644 --- a/src/api2/access/user.rs +++ b/src/api2/access/user.rs @@ -13,9 +13,9 @@ use pbs_api_types::{ PASSWORD_FORMAT, PROXMOX_CONFIG_DIGEST_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA, Authid, Tokenname, UserWithTokens, Userid, }; +use pbs_config::token_shadow; use crate::config::user; -use crate::config::token_shadow; use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY}; use crate::config::cached_user_info::CachedUserInfo; use pbs_config::open_backup_lockfile; diff --git a/src/auth.rs b/src/auth.rs index aee183ee..33ec0286 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -4,7 +4,6 @@ use std::process::{Command, Stdio}; use std::io::Write; -use std::ffi::CStr; use anyhow::{bail, format_err, Error}; use serde_json::json; @@ -19,7 +18,7 @@ pub trait ProxmoxAuthenticator { fn remove_password(&self, username: &UsernameRef) -> Result<(), Error>; } -pub struct PAM(); +struct PAM(); impl ProxmoxAuthenticator for PAM { @@ -70,73 +69,7 @@ impl ProxmoxAuthenticator for PAM { } } -pub struct PBS(); - -// from libcrypt1, 'lib/crypt.h.in' -const CRYPT_OUTPUT_SIZE: usize = 384; -const CRYPT_MAX_PASSPHRASE_SIZE: usize = 512; -const CRYPT_DATA_RESERVED_SIZE: usize = 767; -const CRYPT_DATA_INTERNAL_SIZE: usize = 30720; - -#[repr(C)] -struct crypt_data { - output: [libc::c_char; CRYPT_OUTPUT_SIZE], - setting: [libc::c_char; CRYPT_OUTPUT_SIZE], - input: [libc::c_char; CRYPT_MAX_PASSPHRASE_SIZE], - reserved: [libc::c_char; CRYPT_DATA_RESERVED_SIZE], - initialized: libc::c_char, - internal: [libc::c_char; CRYPT_DATA_INTERNAL_SIZE], -} - -pub fn crypt(password: &[u8], salt: &[u8]) -> Result { - #[link(name = "crypt")] - extern "C" { - #[link_name = "crypt_r"] - fn __crypt_r( - key: *const libc::c_char, - salt: *const libc::c_char, - data: *mut crypt_data, - ) -> *mut libc::c_char; - } - - let mut data: crypt_data = unsafe { std::mem::zeroed() }; - for (i, c) in salt.iter().take(data.setting.len() - 1).enumerate() { - data.setting[i] = *c as libc::c_char; - } - for (i, c) in password.iter().take(data.input.len() - 1).enumerate() { - data.input[i] = *c as libc::c_char; - } - - let res = unsafe { - let status = __crypt_r( - &data.input as *const _, - &data.setting as *const _, - &mut data as *mut _, - ); - if status.is_null() { - bail!("internal error: crypt_r returned null pointer"); - } - CStr::from_ptr(&data.output as *const _) - }; - Ok(String::from(res.to_str()?)) -} - - -pub fn encrypt_pw(password: &str) -> Result { - - let salt = proxmox::sys::linux::random_data(8)?; - let salt = format!("$5${}$", base64::encode_config(&salt, base64::CRYPT)); - - crypt(password.as_bytes(), salt.as_bytes()) -} - -pub fn verify_crypt_pw(password: &str, enc_password: &str) -> Result<(), Error> { - let verify = crypt(password.as_bytes(), enc_password.as_bytes())?; - if verify != enc_password { - bail!("invalid credentials"); - } - Ok(()) -} +struct PBS(); const SHADOW_CONFIG_FILENAME: &str = configdir!("/shadow.json"); @@ -146,13 +79,13 @@ impl ProxmoxAuthenticator for PBS { let data = proxmox::tools::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?; match data[username.as_str()].as_str() { None => bail!("no password set"), - Some(enc_password) => verify_crypt_pw(password, enc_password)?, + Some(enc_password) => pbs_tools::crypt::verify_crypt_pw(password, enc_password)?, } Ok(()) } fn store_password(&self, username: &UsernameRef, password: &str) -> Result<(), Error> { - let enc_password = encrypt_pw(password)?; + let enc_password = pbs_tools::crypt::encrypt_pw(password)?; let mut data = proxmox::tools::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?; data[username.as_str()] = enc_password.into(); diff --git a/src/config/mod.rs b/src/config/mod.rs index c2cbe6f3..7b3d3add 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -20,7 +20,6 @@ pub mod cached_user_info; pub mod datastore; pub mod node; pub mod tfa; -pub mod token_shadow; pub mod user; /// Check configuration directory permissions diff --git a/src/server/auth.rs b/src/server/auth.rs index 9fb5a204..b5b0e0bc 100644 --- a/src/server/auth.rs +++ b/src/server/auth.rs @@ -4,6 +4,7 @@ use anyhow::{format_err, Error}; use std::sync::Arc; use pbs_tools::ticket::{self, Ticket}; +use pbs_config::token_shadow; use crate::api2::types::{Authid, Userid}; use crate::auth_helpers::*; @@ -131,7 +132,7 @@ impl ApiAuth for UserApiAuth { .decode_utf8() .map_err(|_| format_err!("failed to decode API token header"))?; - crate::config::token_shadow::verify_secret(&tokenid, &tokensecret)?; + token_shadow::verify_secret(&tokenid, &tokensecret)?; Ok(tokenid) }