move token_shadow to pbs_config workspace
Also moved out crypt.rs (libcrypt bindings) to pbs_tools workspace.
This commit is contained in:
parent
6f4228809e
commit
1cb08a0a05
|
@ -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};
|
||||||
|
|
|
@ -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);
|
|
@ -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(())
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
75
src/auth.rs
75
src/auth.rs
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue