1 //! Proxmox Backup Server Authentication
3 //! This library contains helper to authenticate users.
5 use std
::process
::{Command, Stdio}
;
8 use anyhow
::{bail, format_err, Error}
;
11 use pbs_buildcfg
::configdir
;
12 use pbs_api_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
>;
17 fn remove_password(&self, username
: &UsernameRef
) -> 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
);
31 fn store_password(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
32 let mut child
= Command
::new("passwd")
33 .arg(username
.as_str())
34 .stdin(Stdio
::piped())
35 .stderr(Stdio
::piped())
37 .map_err(|err
| format_err
!(
38 "unable to set password for '{}' - execute passwd failed: {}",
43 // Note: passwd reads password twice from stdin (for verify)
44 writeln
!(child
.stdin
.as_mut().unwrap(), "{}\n{}", password
, password
)?
;
48 .map_err(|err
| format_err
!(
49 "unable to set password for '{}' - wait failed: {}",
54 if !output
.status
.success() {
56 "unable to set password for '{}' - {}",
58 String
::from_utf8_lossy(&output
.stderr
),
65 // do not remove password for pam users
66 fn remove_password(&self, _username
: &UsernameRef
) -> Result
<(), Error
> {
73 const SHADOW_CONFIG_FILENAME
: &str = configdir
!("/shadow.json");
75 impl ProxmoxAuthenticator
for PBS
{
77 fn authenticate_user(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
78 let data
= proxmox
::tools
::fs
::file_get_json(SHADOW_CONFIG_FILENAME
, Some(json
!({}
)))?
;
79 match data
[username
.as_str()].as_str() {
80 None
=> bail
!("no password set"),
81 Some(enc_password
) => pbs_tools
::crypt
::verify_crypt_pw(password
, enc_password
)?
,
86 fn store_password(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
87 let enc_password
= pbs_tools
::crypt
::encrypt_pw(password
)?
;
88 let mut data
= proxmox
::tools
::fs
::file_get_json(SHADOW_CONFIG_FILENAME
, Some(json
!({}
)))?
;
89 data
[username
.as_str()] = enc_password
.into();
91 let mode
= nix
::sys
::stat
::Mode
::from_bits_truncate(0o0600);
92 let options
= proxmox
::tools
::fs
::CreateOptions
::new()
94 .owner(nix
::unistd
::ROOT
)
95 .group(nix
::unistd
::Gid
::from_raw(0));
97 let data
= serde_json
::to_vec_pretty(&data
)?
;
98 proxmox
::tools
::fs
::replace_file(SHADOW_CONFIG_FILENAME
, &data
, options
, true)?
;
103 fn remove_password(&self, username
: &UsernameRef
) -> Result
<(), Error
> {
104 let mut data
= proxmox
::tools
::fs
::file_get_json(SHADOW_CONFIG_FILENAME
, Some(json
!({}
)))?
;
105 if let Some(map
) = data
.as_object_mut() {
106 map
.remove(username
.as_str());
109 let mode
= nix
::sys
::stat
::Mode
::from_bits_truncate(0o0600);
110 let options
= proxmox
::tools
::fs
::CreateOptions
::new()
112 .owner(nix
::unistd
::ROOT
)
113 .group(nix
::unistd
::Gid
::from_raw(0));
115 let data
= serde_json
::to_vec_pretty(&data
)?
;
116 proxmox
::tools
::fs
::replace_file(SHADOW_CONFIG_FILENAME
, &data
, options
, true)?
;
122 /// Lookup the autenticator for the specified realm
123 pub fn lookup_authenticator(realm
: &RealmRef
) -> Result
<Box
<dyn ProxmoxAuthenticator
>, Error
> {
124 match realm
.as_str() {
125 "pam" => Ok(Box
::new(PAM())),
126 "pbs" => Ok(Box
::new(PBS())),
127 _
=> bail
!("unknown realm '{}'", realm
.as_str()),
131 /// Authenticate users
132 pub fn authenticate_user(userid
: &Userid
, password
: &str) -> Result
<(), Error
> {
134 lookup_authenticator(userid
.realm())?
135 .authenticate_user(userid
.name(), password
)