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
;
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
>;
18 fn remove_password(&self, username
: &UsernameRef
) -> Result
<(), Error
>;
23 impl ProxmoxAuthenticator
for PAM
{
25 fn authenticate_user(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
26 let mut auth
= pam
::Authenticator
::with_password("proxmox-backup-auth").unwrap();
27 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
),
66 // do not remove password for pam users
67 fn remove_password(&self, _username
: &UsernameRef
) -> Result
<(), Error
> {
74 const SHADOW_CONFIG_FILENAME
: &str = configdir
!("/shadow.json");
76 impl ProxmoxAuthenticator
for PBS
{
78 fn authenticate_user(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
79 let data
= proxmox
::tools
::fs
::file_get_json(SHADOW_CONFIG_FILENAME
, Some(json
!({}
)))?
;
80 match data
[username
.as_str()].as_str() {
81 None
=> bail
!("no password set"),
82 Some(enc_password
) => pbs_tools
::crypt
::verify_crypt_pw(password
, enc_password
)?
,
87 fn store_password(&self, username
: &UsernameRef
, password
: &str) -> Result
<(), Error
> {
88 let enc_password
= pbs_tools
::crypt
::encrypt_pw(password
)?
;
89 let mut data
= proxmox
::tools
::fs
::file_get_json(SHADOW_CONFIG_FILENAME
, Some(json
!({}
)))?
;
90 data
[username
.as_str()] = enc_password
.into();
92 let mode
= nix
::sys
::stat
::Mode
::from_bits_truncate(0o0600);
93 let options
= proxmox
::tools
::fs
::CreateOptions
::new()
95 .owner(nix
::unistd
::ROOT
)
96 .group(nix
::unistd
::Gid
::from_raw(0));
98 let data
= serde_json
::to_vec_pretty(&data
)?
;
99 proxmox
::tools
::fs
::replace_file(SHADOW_CONFIG_FILENAME
, &data
, options
)?
;
104 fn remove_password(&self, username
: &UsernameRef
) -> Result
<(), Error
> {
105 let mut data
= proxmox
::tools
::fs
::file_get_json(SHADOW_CONFIG_FILENAME
, Some(json
!({}
)))?
;
106 if let Some(map
) = data
.as_object_mut() {
107 map
.remove(username
.as_str());
110 let mode
= nix
::sys
::stat
::Mode
::from_bits_truncate(0o0600);
111 let options
= proxmox
::tools
::fs
::CreateOptions
::new()
113 .owner(nix
::unistd
::ROOT
)
114 .group(nix
::unistd
::Gid
::from_raw(0));
116 let data
= serde_json
::to_vec_pretty(&data
)?
;
117 proxmox
::tools
::fs
::replace_file(SHADOW_CONFIG_FILENAME
, &data
, options
)?
;
123 /// Lookup the autenticator for the specified realm
124 pub fn lookup_authenticator(realm
: &RealmRef
) -> Result
<Box
<dyn ProxmoxAuthenticator
>, Error
> {
125 match realm
.as_str() {
126 "pam" => Ok(Box
::new(PAM())),
127 "pbs" => Ok(Box
::new(PBS())),
128 _
=> bail
!("unknown realm '{}'", realm
.as_str()),
132 /// Authenticate users
133 pub fn authenticate_user(userid
: &Userid
, password
: &str) -> Result
<(), Error
> {
135 lookup_authenticator(userid
.realm())?
136 .authenticate_user(userid
.name(), password
)