1 //! Proxmox Backup Server Authentication
3 //! This library contains helper to authenticate users.
5 use std
::process
::{Command, Stdio}
;
7 use std
::ffi
::{CString, CStr}
;
10 use anyhow
::{bail, format_err, Error}
;
13 use crate::api2
::types
::{Userid, UsernameRef, RealmRef}
;
15 pub trait ProxmoxAuthenticator
{
16 fn authenticate_user(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
>;
17 fn store_password(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
>;
22 impl ProxmoxAuthenticator
for PAM
{
24 fn authenticate_user(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
25 let mut auth
= pam
::Authenticator
::with_password("proxmox-backup-auth").unwrap();
26 auth
.get_handler().set_credentials(username
.as_str(), password
);
32 fn store_password(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
33 let mut child
= Command
::new("passwd")
34 .arg(username
.as_str())
35 .stdin(Stdio
::piped())
36 .stderr(Stdio
::piped())
38 .map_err(|err
| format_err
!(
39 "unable to set password for '{}' - execute passwd failed: {}",
44 // Note: passwd reads password twice from stdin (for verify)
45 writeln
!(child
.stdin
.as_mut().unwrap(), "{}\n{}", password
, password
)?
;
49 .map_err(|err
| format_err
!(
50 "unable to set password for '{}' - wait failed: {}",
55 if !output
.status
.success() {
57 "unable to set password for '{}' - {}",
59 String
::from_utf8_lossy(&output
.stderr
),
69 pub fn crypt(password
: &[u8], salt
: &str) -> Result
<String
, Error
> {
73 #[link_name = "crypt"]
74 fn __crypt(key
: *const libc
::c_char
, salt
: *const libc
::c_char
) -> * mut libc
::c_char
;
77 let salt
= CString
::new(salt
)?
;
78 let password
= CString
::new(password
)?
;
83 password
.as_c_str().as_ptr(),
84 salt
.as_c_str().as_ptr()
88 Ok(String
::from(res
.to_str()?
))
92 pub fn encrypt_pw(password
: &str) -> Result
<String
, Error
> {
94 let salt
= proxmox
::sys
::linux
::random_data(8)?
;
95 let salt
= format
!("$5${}$", base64
::encode_config(&salt
, base64
::CRYPT
));
97 crypt(password
.as_bytes(), &salt
)
100 pub fn verify_crypt_pw(password
: &str, enc_password
: &str) -> Result
<(), Error
> {
101 let verify
= crypt(password
.as_bytes(), enc_password
)?
;
102 if &verify
!= enc_password
{
103 bail
!("invalid credentials");
108 const SHADOW_CONFIG_FILENAME
: &str = configdir
!("/shadow.json");
110 impl ProxmoxAuthenticator
for PBS
{
112 fn authenticate_user(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
113 let data
= proxmox
::tools
::fs
::file_get_json(SHADOW_CONFIG_FILENAME
, Some(json
!({}
)))?
;
114 match data
[username
.as_str()].as_str() {
115 None
=> bail
!("no password set"),
116 Some(enc_password
) => verify_crypt_pw(password
, enc_password
)?
,
121 fn store_password(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
122 let enc_password
= encrypt_pw(password
)?
;
123 let mut data
= proxmox
::tools
::fs
::file_get_json(SHADOW_CONFIG_FILENAME
, Some(json
!({}
)))?
;
124 data
[username
.as_str()] = enc_password
.into();
126 let mode
= nix
::sys
::stat
::Mode
::from_bits_truncate(0o0600);
127 let options
= proxmox
::tools
::fs
::CreateOptions
::new()
129 .owner(nix
::unistd
::ROOT
)
130 .group(nix
::unistd
::Gid
::from_raw(0));
132 let data
= serde_json
::to_vec_pretty(&data
)?
;
133 proxmox
::tools
::fs
::replace_file(SHADOW_CONFIG_FILENAME
, &data
, options
)?
;
139 /// Lookup the autenticator for the specified realm
140 pub fn lookup_authenticator(realm
: &RealmRef
) -> Result
<Box
<dyn ProxmoxAuthenticator
>, Error
> {
141 match realm
.as_str() {
142 "pam" => Ok(Box
::new(PAM())),
143 "pbs" => Ok(Box
::new(PBS())),
144 _
=> bail
!("unknown realm '{}'", realm
.as_str()),
148 /// Authenticate users
149 pub fn authenticate_user(userid
: &Userid
, password
: &str) -> Result
<(), Error
> {
151 lookup_authenticator(userid
.realm())?
152 .authenticate_user(userid
.name(), password
)