move token_shadow to pbs_config workspace

Also moved out crypt.rs (libcrypt bindings) to pbs_tools workspace.
This commit is contained in:
Dietmar Maurer 2021-09-08 14:00:14 +02:00
parent 6f4228809e
commit 1cb08a0a05
8 changed files with 91 additions and 82 deletions

View File

@ -7,6 +7,7 @@ pub mod remote;
pub mod sync; pub mod sync;
pub mod tape_encryption_keys; pub mod tape_encryption_keys;
pub mod tape_job; pub mod tape_job;
pub mod token_shadow;
pub mod verify; pub mod verify;
use anyhow::{format_err, Error}; use anyhow::{format_err, Error};

View File

@ -6,9 +6,9 @@ use serde_json::{from_value, Value};
use proxmox::tools::fs::CreateOptions; use proxmox::tools::fs::CreateOptions;
use crate::api2::types::Authid; use pbs_api_types::Authid;
use crate::auth; //use crate::auth;
use pbs_config::open_backup_lockfile; use crate::{open_backup_lockfile, BackupLockGuard};
const LOCK_FILE: &str = pbs_buildcfg::configdir!("/token.shadow.lock"); const LOCK_FILE: &str = pbs_buildcfg::configdir!("/token.shadow.lock");
const CONF_FILE: &str = pbs_buildcfg::configdir!("/token.shadow"); const CONF_FILE: &str = pbs_buildcfg::configdir!("/token.shadow");
@ -21,6 +21,11 @@ pub struct ApiTokenSecret {
pub secret: String, 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> { fn read_file() -> Result<HashMap<Authid, String>, Error> {
let json = proxmox::tools::fs::file_get_json(CONF_FILE, Some(Value::Null))?; let json = proxmox::tools::fs::file_get_json(CONF_FILE, Some(Value::Null))?;
@ -33,7 +38,7 @@ fn read_file() -> Result<HashMap<Authid, String>, Error> {
} }
fn write_file(data: HashMap<Authid, String>) -> Result<(), Error> { fn write_file(data: HashMap<Authid, String>) -> Result<(), Error> {
let backup_user = pbs_config::backup_user()?; let backup_user = crate::backup_user()?;
let options = CreateOptions::new() let options = CreateOptions::new()
.perm(nix::sys::stat::Mode::from_bits_truncate(0o0640)) .perm(nix::sys::stat::Mode::from_bits_truncate(0o0640))
.owner(backup_user.uid) .owner(backup_user.uid)
@ -43,6 +48,7 @@ fn write_file(data: HashMap<Authid, String>) -> Result<(), Error> {
proxmox::tools::fs::replace_file(CONF_FILE, &json, options) proxmox::tools::fs::replace_file(CONF_FILE, &json, options)
} }
/// Verifies that an entry for given tokenid / API token secret exists /// Verifies that an entry for given tokenid / API token secret exists
pub fn verify_secret(tokenid: &Authid, secret: &str) -> Result<(), Error> { pub fn verify_secret(tokenid: &Authid, secret: &str) -> Result<(), Error> {
if !tokenid.is_token() { if !tokenid.is_token() {
@ -52,7 +58,7 @@ pub fn verify_secret(tokenid: &Authid, secret: &str) -> Result<(), Error> {
let data = read_file()?; let data = read_file()?;
match data.get(tokenid) { match data.get(tokenid) {
Some(hashed_secret) => { Some(hashed_secret) => {
auth::verify_crypt_pw(secret, &hashed_secret) pbs_tools::crypt::verify_crypt_pw(secret, &hashed_secret)
}, },
None => bail!("invalid API token"), 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"); 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 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); data.insert(tokenid.clone(), hashed_secret);
write_file(data)?; write_file(data)?;
@ -80,7 +86,7 @@ pub fn delete_secret(tokenid: &Authid) -> Result<(), Error> {
bail!("not an API token ID"); 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 mut data = read_file()?;
data.remove(tokenid); data.remove(tokenid);

68
pbs-tools/src/crypt.rs Normal file
View File

@ -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(())
}

View File

@ -6,6 +6,7 @@ pub mod broadcast_future;
pub mod cert; pub mod cert;
pub mod cli; pub mod cli;
pub mod compression; pub mod compression;
pub mod crypt;
pub mod crypt_config; pub mod crypt_config;
pub mod format; pub mod format;
pub mod fd; pub mod fd;

View File

@ -13,9 +13,9 @@ use pbs_api_types::{
PASSWORD_FORMAT, PROXMOX_CONFIG_DIGEST_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA, Authid, PASSWORD_FORMAT, PROXMOX_CONFIG_DIGEST_SCHEMA, SINGLE_LINE_COMMENT_SCHEMA, Authid,
Tokenname, UserWithTokens, Userid, Tokenname, UserWithTokens, Userid,
}; };
use pbs_config::token_shadow;
use crate::config::user; use crate::config::user;
use crate::config::token_shadow;
use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY}; use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY};
use crate::config::cached_user_info::CachedUserInfo; use crate::config::cached_user_info::CachedUserInfo;
use pbs_config::open_backup_lockfile; use pbs_config::open_backup_lockfile;

View File

@ -4,7 +4,6 @@
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::io::Write; use std::io::Write;
use std::ffi::CStr;
use anyhow::{bail, format_err, Error}; use anyhow::{bail, format_err, Error};
use serde_json::json; use serde_json::json;
@ -19,7 +18,7 @@ pub trait ProxmoxAuthenticator {
fn remove_password(&self, username: &UsernameRef) -> Result<(), Error>; fn remove_password(&self, username: &UsernameRef) -> Result<(), Error>;
} }
pub struct PAM(); struct PAM();
impl ProxmoxAuthenticator for PAM { impl ProxmoxAuthenticator for PAM {
@ -70,73 +69,7 @@ impl ProxmoxAuthenticator for PAM {
} }
} }
pub struct PBS(); 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(())
}
const SHADOW_CONFIG_FILENAME: &str = configdir!("/shadow.json"); 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!({})))?; let data = proxmox::tools::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?;
match data[username.as_str()].as_str() { match data[username.as_str()].as_str() {
None => bail!("no password set"), 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(()) Ok(())
} }
fn store_password(&self, username: &UsernameRef, password: &str) -> Result<(), Error> { 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!({})))?; let mut data = proxmox::tools::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?;
data[username.as_str()] = enc_password.into(); data[username.as_str()] = enc_password.into();

View File

@ -20,7 +20,6 @@ pub mod cached_user_info;
pub mod datastore; pub mod datastore;
pub mod node; pub mod node;
pub mod tfa; pub mod tfa;
pub mod token_shadow;
pub mod user; pub mod user;
/// Check configuration directory permissions /// Check configuration directory permissions

View File

@ -4,6 +4,7 @@ use anyhow::{format_err, Error};
use std::sync::Arc; use std::sync::Arc;
use pbs_tools::ticket::{self, Ticket}; use pbs_tools::ticket::{self, Ticket};
use pbs_config::token_shadow;
use crate::api2::types::{Authid, Userid}; use crate::api2::types::{Authid, Userid};
use crate::auth_helpers::*; use crate::auth_helpers::*;
@ -131,7 +132,7 @@ impl ApiAuth for UserApiAuth {
.decode_utf8() .decode_utf8()
.map_err(|_| format_err!("failed to decode API token header"))?; .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) Ok(tokenid)
} }