Also moved out crypt.rs (libcrypt bindings) to pbs_tools workspace.
pub mod sync;
pub mod tape_encryption_keys;
pub mod tape_job;
+pub mod token_shadow;
pub mod verify;
use anyhow::{format_err, Error};
--- /dev/null
+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(())
+}
--- /dev/null
+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(())
+}
pub mod cert;
pub mod cli;
pub mod compression;
+pub mod crypt;
pub mod crypt_config;
pub mod format;
pub mod fd;
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;
use std::process::{Command, Stdio};
use std::io::Write;
-use std::ffi::CStr;
use anyhow::{bail, format_err, Error};
use serde_json::json;
fn remove_password(&self, username: &UsernameRef) -> Result<(), Error>;
}
-pub struct PAM();
+struct PAM();
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");
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();
pub mod datastore;
pub mod node;
pub mod tfa;
-pub mod token_shadow;
pub mod user;
/// Check configuration directory permissions
+++ /dev/null
-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(())
-}
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::*;
.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)
}