]> git.proxmox.com Git - proxmox-backup.git/commitdiff
move token_shadow to pbs_config workspace
authorDietmar Maurer <dietmar@proxmox.com>
Wed, 8 Sep 2021 12:00:14 +0000 (14:00 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Wed, 8 Sep 2021 12:00:14 +0000 (14:00 +0200)
Also moved out crypt.rs (libcrypt bindings) to pbs_tools workspace.

pbs-config/src/lib.rs
pbs-config/src/token_shadow.rs [new file with mode: 0644]
pbs-tools/src/crypt.rs [new file with mode: 0644]
pbs-tools/src/lib.rs
src/api2/access/user.rs
src/auth.rs
src/config/mod.rs
src/config/token_shadow.rs [deleted file]
src/server/auth.rs

index 76b10146fcfeec0ac5e99487556f94fb0c5f5040..48ceb73dd1992b6ec3304c9371a4f7aee1800042 100644 (file)
@@ -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/pbs-config/src/token_shadow.rs b/pbs-config/src/token_shadow.rs
new file mode 100644 (file)
index 0000000..6e466ce
--- /dev/null
@@ -0,0 +1,96 @@
+use std::collections::HashMap;
+
+use anyhow::{bail, format_err, Error};
+use serde::{Serialize, Deserialize};
+use serde_json::{from_value, Value};
+
+use proxmox::tools::fs::CreateOptions;
+
+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");
+
+#[derive(Serialize, Deserialize)]
+#[serde(rename_all="kebab-case")]
+/// ApiToken id / secret pair
+pub struct ApiTokenSecret {
+    pub tokenid: Authid,
+    pub secret: String,
+}
+
+// Get exclusive lock
+fn lock_config() -> Result<BackupLockGuard, Error> {
+    open_backup_lockfile(LOCK_FILE, None, true)
+}
+
+fn read_file() -> Result<HashMap<Authid, String>, Error> {
+    let json = proxmox::tools::fs::file_get_json(CONF_FILE, Some(Value::Null))?;
+
+    if json == Value::Null {
+        Ok(HashMap::new())
+    } else {
+        // swallow serde error which might contain sensitive data
+        from_value(json).map_err(|_err| format_err!("unable to parse '{}'", CONF_FILE))
+    }
+}
+
+fn write_file(data: HashMap<Authid, String>) -> Result<(), Error> {
+    let backup_user = crate::backup_user()?;
+    let options = CreateOptions::new()
+        .perm(nix::sys::stat::Mode::from_bits_truncate(0o0640))
+        .owner(backup_user.uid)
+        .group(backup_user.gid);
+
+    let json = serde_json::to_vec(&data)?;
+    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() {
+        bail!("not an API token ID");
+    }
+
+    let data = read_file()?;
+    match data.get(tokenid) {
+        Some(hashed_secret) => {
+            pbs_tools::crypt::verify_crypt_pw(secret, &hashed_secret)
+        },
+        None => bail!("invalid API token"),
+    }
+}
+
+/// Adds a new entry for the given tokenid / API token secret. The secret is stored as salted hash.
+pub fn set_secret(tokenid: &Authid, secret: &str) -> Result<(), Error> {
+    if !tokenid.is_token() {
+        bail!("not an API token ID");
+    }
+
+    let _guard = lock_config()?;
+
+    let mut data = read_file()?;
+    let hashed_secret = pbs_tools::crypt::encrypt_pw(secret)?;
+    data.insert(tokenid.clone(), hashed_secret);
+    write_file(data)?;
+
+    Ok(())
+}
+
+/// Deletes the entry for the given tokenid.
+pub fn delete_secret(tokenid: &Authid) -> Result<(), Error> {
+    if !tokenid.is_token() {
+        bail!("not an API token ID");
+    }
+
+    let _guard = lock_config()?;
+
+    let mut data = read_file()?;
+    data.remove(tokenid);
+    write_file(data)?;
+
+    Ok(())
+}
diff --git a/pbs-tools/src/crypt.rs b/pbs-tools/src/crypt.rs
new file mode 100644 (file)
index 0000000..c47bfe2
--- /dev/null
@@ -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<String, Error> {
+    #[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<String, Error> {
+
+    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(())
+}
index bb82f7d4454ed14a8131a70fd5f7ae6773e5919e..000591c3e934f1094641456e0aca86db645ca7ee 100644 (file)
@@ -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;
index bb934093bcf4d0d82f60825885d3a07d5489a5f9..6d5aa0ee815fa52ae7b2914cb70987b1fa5f74b1 100644 (file)
@@ -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;
index aee183eec951c081064a9685b27de6034af03b34..33ec02864a1addd89b1bbf5dd09031f93b4b93b5 100644 (file)
@@ -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<String, Error> {
-    #[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<String, Error> {
-
-    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();
 
index c2cbe6f38ef7d7bf7d32ed40076f87e12e3b0b99..7b3d3add5114caed687180a1da9a97170f3aec5a 100644 (file)
@@ -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/config/token_shadow.rs b/src/config/token_shadow.rs
deleted file mode 100644 (file)
index 6a328c0..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-use std::collections::HashMap;
-
-use anyhow::{bail, format_err, Error};
-use serde::{Serialize, Deserialize};
-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;
-
-const LOCK_FILE: &str = pbs_buildcfg::configdir!("/token.shadow.lock");
-const CONF_FILE: &str = pbs_buildcfg::configdir!("/token.shadow");
-
-#[derive(Serialize, Deserialize)]
-#[serde(rename_all="kebab-case")]
-/// ApiToken id / secret pair
-pub struct ApiTokenSecret {
-    pub tokenid: Authid,
-    pub secret: String,
-}
-
-fn read_file() -> Result<HashMap<Authid, String>, Error> {
-    let json = proxmox::tools::fs::file_get_json(CONF_FILE, Some(Value::Null))?;
-
-    if json == Value::Null {
-        Ok(HashMap::new())
-    } else {
-        // swallow serde error which might contain sensitive data
-        from_value(json).map_err(|_err| format_err!("unable to parse '{}'", CONF_FILE))
-    }
-}
-
-fn write_file(data: HashMap<Authid, String>) -> Result<(), Error> {
-    let backup_user = pbs_config::backup_user()?;
-    let options = CreateOptions::new()
-        .perm(nix::sys::stat::Mode::from_bits_truncate(0o0640))
-        .owner(backup_user.uid)
-        .group(backup_user.gid);
-
-    let json = serde_json::to_vec(&data)?;
-    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() {
-        bail!("not an API token ID");
-    }
-
-    let data = read_file()?;
-    match data.get(tokenid) {
-        Some(hashed_secret) => {
-            auth::verify_crypt_pw(secret, &hashed_secret)
-        },
-        None => bail!("invalid API token"),
-    }
-}
-
-/// Adds a new entry for the given tokenid / API token secret. The secret is stored as salted hash.
-pub fn set_secret(tokenid: &Authid, secret: &str) -> Result<(), Error> {
-    if !tokenid.is_token() {
-        bail!("not an API token ID");
-    }
-
-    let _guard = open_backup_lockfile(LOCK_FILE, None, true)?;
-
-    let mut data = read_file()?;
-    let hashed_secret = auth::encrypt_pw(secret)?;
-    data.insert(tokenid.clone(), hashed_secret);
-    write_file(data)?;
-
-    Ok(())
-}
-
-/// Deletes the entry for the given tokenid.
-pub fn delete_secret(tokenid: &Authid) -> Result<(), Error> {
-    if !tokenid.is_token() {
-        bail!("not an API token ID");
-    }
-
-    let _guard = open_backup_lockfile(LOCK_FILE, None, true)?;
-
-    let mut data = read_file()?;
-    data.remove(tokenid);
-    write_file(data)?;
-
-    Ok(())
-}
index 9fb5a204733f3a1e2958042bbb3a98293a51545d..b5b0e0bc0267690d12af5e60755356e0e275aae2 100644 (file)
@@ -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)
             }