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}
;
9 use anyhow
::{bail, format_err, Error}
;
12 use crate::api2
::types
::{Userid, UsernameRef, RealmRef}
;
14 pub trait ProxmoxAuthenticator
{
15 fn authenticate_user(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
>;
16 fn store_password(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
>;
21 impl ProxmoxAuthenticator
for PAM
{
23 fn authenticate_user(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
24 let mut auth
= pam
::Authenticator
::with_password("proxmox-backup-auth").unwrap();
25 auth
.get_handler().set_credentials(username
.as_str(), password
);
30 fn store_password(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
31 let mut child
= Command
::new("passwd")
32 .arg(username
.as_str())
33 .stdin(Stdio
::piped())
34 .stderr(Stdio
::piped())
36 .map_err(|err
| format_err
!(
37 "unable to set password for '{}' - execute passwd failed: {}",
42 // Note: passwd reads password twice from stdin (for verify)
43 writeln
!(child
.stdin
.as_mut().unwrap(), "{}\n{}", password
, password
)?
;
47 .map_err(|err
| format_err
!(
48 "unable to set password for '{}' - wait failed: {}",
53 if !output
.status
.success() {
55 "unable to set password for '{}' - {}",
57 String
::from_utf8_lossy(&output
.stderr
),
67 pub fn crypt(password
: &[u8], salt
: &str) -> Result
<String
, Error
> {
71 #[link_name = "crypt"]
72 fn __crypt(key
: *const libc
::c_char
, salt
: *const libc
::c_char
) -> * mut libc
::c_char
;
75 let salt
= CString
::new(salt
)?
;
76 let password
= CString
::new(password
)?
;
81 password
.as_c_str().as_ptr(),
82 salt
.as_c_str().as_ptr()
86 Ok(String
::from(res
.to_str()?
))
90 pub fn encrypt_pw(password
: &str) -> Result
<String
, Error
> {
92 let salt
= proxmox
::sys
::linux
::random_data(8)?
;
93 let salt
= format
!("$5${}$", base64
::encode_config(&salt
, base64
::CRYPT
));
95 crypt(password
.as_bytes(), &salt
)
98 pub fn verify_crypt_pw(password
: &str, enc_password
: &str) -> Result
<(), Error
> {
99 let verify
= crypt(password
.as_bytes(), enc_password
)?
;
100 if verify
!= enc_password
{
101 bail
!("invalid credentials");
106 const SHADOW_CONFIG_FILENAME
: &str = configdir
!("/shadow.json");
108 impl ProxmoxAuthenticator
for PBS
{
110 fn authenticate_user(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
111 let data
= proxmox
::tools
::fs
::file_get_json(SHADOW_CONFIG_FILENAME
, Some(json
!({}
)))?
;
112 match data
[username
.as_str()].as_str() {
113 None
=> bail
!("no password set"),
114 Some(enc_password
) => verify_crypt_pw(password
, enc_password
)?
,
119 fn store_password(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
120 let enc_password
= encrypt_pw(password
)?
;
121 let mut data
= proxmox
::tools
::fs
::file_get_json(SHADOW_CONFIG_FILENAME
, Some(json
!({}
)))?
;
122 data
[username
.as_str()] = enc_password
.into();
124 let mode
= nix
::sys
::stat
::Mode
::from_bits_truncate(0o0600);
125 let options
= proxmox
::tools
::fs
::CreateOptions
::new()
127 .owner(nix
::unistd
::ROOT
)
128 .group(nix
::unistd
::Gid
::from_raw(0));
130 let data
= serde_json
::to_vec_pretty(&data
)?
;
131 proxmox
::tools
::fs
::replace_file(SHADOW_CONFIG_FILENAME
, &data
, options
)?
;
137 /// Lookup the autenticator for the specified realm
138 pub fn lookup_authenticator(realm
: &RealmRef
) -> Result
<Box
<dyn ProxmoxAuthenticator
>, Error
> {
139 match realm
.as_str() {
140 "pam" => Ok(Box
::new(PAM())),
141 "pbs" => Ok(Box
::new(PBS())),
142 _
=> bail
!("unknown realm '{}'", realm
.as_str()),
146 /// Authenticate users
147 pub fn authenticate_user(userid
: &Userid
, password
: &str) -> Result
<(), Error
> {
149 lookup_authenticator(userid
.realm())?
150 .authenticate_user(userid
.name(), password
)