]> git.proxmox.com Git - proxmox-backup.git/blob - src/auth.rs
move token_shadow to pbs_config workspace
[proxmox-backup.git] / src / auth.rs
1 //! Proxmox Backup Server Authentication
2 //!
3 //! This library contains helper to authenticate users.
4
5 use std::process::{Command, Stdio};
6 use std::io::Write;
7
8 use anyhow::{bail, format_err, Error};
9 use serde_json::json;
10
11 use pbs_buildcfg::configdir;
12
13 use crate::api2::types::{Userid, UsernameRef, RealmRef};
14
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>;
19 }
20
21 struct PAM();
22
23 impl ProxmoxAuthenticator for PAM {
24
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);
28 auth.authenticate()?;
29 Ok(())
30 }
31
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())
37 .spawn()
38 .map_err(|err| format_err!(
39 "unable to set password for '{}' - execute passwd failed: {}",
40 username.as_str(),
41 err,
42 ))?;
43
44 // Note: passwd reads password twice from stdin (for verify)
45 writeln!(child.stdin.as_mut().unwrap(), "{}\n{}", password, password)?;
46
47 let output = child
48 .wait_with_output()
49 .map_err(|err| format_err!(
50 "unable to set password for '{}' - wait failed: {}",
51 username.as_str(),
52 err,
53 ))?;
54
55 if !output.status.success() {
56 bail!(
57 "unable to set password for '{}' - {}",
58 username.as_str(),
59 String::from_utf8_lossy(&output.stderr),
60 );
61 }
62
63 Ok(())
64 }
65
66 // do not remove password for pam users
67 fn remove_password(&self, _username: &UsernameRef) -> Result<(), Error> {
68 Ok(())
69 }
70 }
71
72 struct PBS();
73
74 const SHADOW_CONFIG_FILENAME: &str = configdir!("/shadow.json");
75
76 impl ProxmoxAuthenticator for PBS {
77
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)?,
83 }
84 Ok(())
85 }
86
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();
91
92 let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600);
93 let options = proxmox::tools::fs::CreateOptions::new()
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)?;
99 proxmox::tools::fs::replace_file(SHADOW_CONFIG_FILENAME, &data, options)?;
100
101 Ok(())
102 }
103
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());
108 }
109
110 let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600);
111 let options = proxmox::tools::fs::CreateOptions::new()
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)?;
117 proxmox::tools::fs::replace_file(SHADOW_CONFIG_FILENAME, &data, options)?;
118
119 Ok(())
120 }
121 }
122
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()),
129 }
130 }
131
132 /// Authenticate users
133 pub fn authenticate_user(userid: &Userid, password: &str) -> Result<(), Error> {
134
135 lookup_authenticator(userid.realm())?
136 .authenticate_user(userid.name(), password)
137 }