]> git.proxmox.com Git - proxmox-backup.git/blame - src/auth.rs
replace print with log macro
[proxmox-backup.git] / src / auth.rs
CommitLineData
7d817b03
DM
1//! Proxmox Backup Server Authentication
2//!
3//! This library contains helper to authenticate users.
4
7d817b03 5use std::io::Write;
9531d2c5 6use std::process::{Command, Stdio};
7d817b03 7
f7d4e4b5 8use anyhow::{bail, format_err, Error};
7d817b03
DM
9use serde_json::json;
10
9531d2c5 11use pbs_api_types::{RealmRef, Userid, UsernameRef};
af06decd 12use pbs_buildcfg::configdir;
e7cb4dc5 13
7d817b03 14pub trait ProxmoxAuthenticator {
e7cb4dc5
WB
15 fn authenticate_user(&self, username: &UsernameRef, password: &str) -> Result<(), Error>;
16 fn store_password(&self, username: &UsernameRef, password: &str) -> Result<(), Error>;
a4e871f5 17 fn remove_password(&self, username: &UsernameRef) -> Result<(), Error>;
7d817b03
DM
18}
19
1cb08a0a 20struct PAM();
7d817b03
DM
21
22impl ProxmoxAuthenticator for PAM {
e7cb4dc5 23 fn authenticate_user(&self, username: &UsernameRef, password: &str) -> Result<(), Error> {
7d817b03 24 let mut auth = pam::Authenticator::with_password("proxmox-backup-auth").unwrap();
9531d2c5
TL
25 auth.get_handler()
26 .set_credentials(username.as_str(), password);
7d817b03 27 auth.authenticate()?;
54aec2fa 28 Ok(())
7d817b03
DM
29 }
30
e7cb4dc5 31 fn store_password(&self, username: &UsernameRef, password: &str) -> Result<(), Error> {
7d817b03 32 let mut child = Command::new("passwd")
e7cb4dc5 33 .arg(username.as_str())
7d817b03
DM
34 .stdin(Stdio::piped())
35 .stderr(Stdio::piped())
36 .spawn()
9531d2c5
TL
37 .map_err(|err| {
38 format_err!(
39 "unable to set password for '{}' - execute passwd failed: {}",
40 username.as_str(),
41 err,
42 )
43 })?;
7d817b03
DM
44
45 // Note: passwd reads password twice from stdin (for verify)
46 writeln!(child.stdin.as_mut().unwrap(), "{}\n{}", password, password)?;
47
9531d2c5
TL
48 let output = child.wait_with_output().map_err(|err| {
49 format_err!(
e7cb4dc5
WB
50 "unable to set password for '{}' - wait failed: {}",
51 username.as_str(),
52 err,
9531d2c5
TL
53 )
54 })?;
7d817b03
DM
55
56 if !output.status.success() {
e7cb4dc5
WB
57 bail!(
58 "unable to set password for '{}' - {}",
59 username.as_str(),
60 String::from_utf8_lossy(&output.stderr),
61 );
7d817b03
DM
62 }
63
64 Ok(())
65 }
a4e871f5
DC
66
67 // do not remove password for pam users
68 fn remove_password(&self, _username: &UsernameRef) -> Result<(), Error> {
69 Ok(())
70 }
7d817b03
DM
71}
72
1cb08a0a 73struct PBS();
7d817b03 74
e7cb4dc5 75const SHADOW_CONFIG_FILENAME: &str = configdir!("/shadow.json");
7d817b03
DM
76
77impl ProxmoxAuthenticator for PBS {
e7cb4dc5 78 fn authenticate_user(&self, username: &UsernameRef, password: &str) -> Result<(), Error> {
25877d05 79 let data = proxmox_sys::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?;
e7cb4dc5 80 match data[username.as_str()].as_str() {
7d817b03 81 None => bail!("no password set"),
d5790a9f 82 Some(enc_password) => proxmox_sys::crypt::verify_crypt_pw(password, enc_password)?,
7d817b03
DM
83 }
84 Ok(())
85 }
86
e7cb4dc5 87 fn store_password(&self, username: &UsernameRef, password: &str) -> Result<(), Error> {
d5790a9f 88 let enc_password = proxmox_sys::crypt::encrypt_pw(password)?;
25877d05 89 let mut data = proxmox_sys::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?;
e7cb4dc5 90 data[username.as_str()] = enc_password.into();
7d817b03
DM
91
92 let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600);
9531d2c5 93 let options = proxmox_sys::fs::CreateOptions::new()
7d817b03
DM
94 .perm(mode)
95 .owner(nix::unistd::ROOT)
96 .group(nix::unistd::Gid::from_raw(0));
97
98 let data = serde_json::to_vec_pretty(&data)?;
25877d05 99 proxmox_sys::fs::replace_file(SHADOW_CONFIG_FILENAME, &data, options, true)?;
7d817b03
DM
100
101 Ok(())
102 }
a4e871f5
DC
103
104 fn remove_password(&self, username: &UsernameRef) -> Result<(), Error> {
25877d05 105 let mut data = proxmox_sys::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?;
a4e871f5
DC
106 if let Some(map) = data.as_object_mut() {
107 map.remove(username.as_str());
108 }
109
110 let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600);
9531d2c5 111 let options = proxmox_sys::fs::CreateOptions::new()
a4e871f5
DC
112 .perm(mode)
113 .owner(nix::unistd::ROOT)
114 .group(nix::unistd::Gid::from_raw(0));
115
116 let data = serde_json::to_vec_pretty(&data)?;
25877d05 117 proxmox_sys::fs::replace_file(SHADOW_CONFIG_FILENAME, &data, options, true)?;
a4e871f5
DC
118
119 Ok(())
120 }
7d817b03
DM
121}
122
4b40148c 123/// Lookup the autenticator for the specified realm
e7cb4dc5
WB
124pub fn lookup_authenticator(realm: &RealmRef) -> Result<Box<dyn ProxmoxAuthenticator>, Error> {
125 match realm.as_str() {
7d817b03
DM
126 "pam" => Ok(Box::new(PAM())),
127 "pbs" => Ok(Box::new(PBS())),
e7cb4dc5 128 _ => bail!("unknown realm '{}'", realm.as_str()),
7d817b03
DM
129 }
130}
131
4b40148c 132/// Authenticate users
e7cb4dc5 133pub fn authenticate_user(userid: &Userid, password: &str) -> Result<(), Error> {
9531d2c5 134 lookup_authenticator(userid.realm())?.authenticate_user(userid.name(), password)
7d817b03 135}