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