]> git.proxmox.com Git - proxmox-backup.git/blob - src/auth.rs
use new fsync parameter to replace_file and atomic_open_or_create
[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 use pbs_api_types::{Userid, UsernameRef, RealmRef};
13
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>;
18 }
19
20 struct PAM();
21
22 impl ProxmoxAuthenticator for PAM {
23
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);
27 auth.authenticate()?;
28 Ok(())
29 }
30
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())
36 .spawn()
37 .map_err(|err| format_err!(
38 "unable to set password for '{}' - execute passwd failed: {}",
39 username.as_str(),
40 err,
41 ))?;
42
43 // Note: passwd reads password twice from stdin (for verify)
44 writeln!(child.stdin.as_mut().unwrap(), "{}\n{}", password, password)?;
45
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 ))?;
53
54 if !output.status.success() {
55 bail!(
56 "unable to set password for '{}' - {}",
57 username.as_str(),
58 String::from_utf8_lossy(&output.stderr),
59 );
60 }
61
62 Ok(())
63 }
64
65 // do not remove password for pam users
66 fn remove_password(&self, _username: &UsernameRef) -> Result<(), Error> {
67 Ok(())
68 }
69 }
70
71 struct PBS();
72
73 const SHADOW_CONFIG_FILENAME: &str = configdir!("/shadow.json");
74
75 impl ProxmoxAuthenticator for PBS {
76
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)?,
82 }
83 Ok(())
84 }
85
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();
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)?;
98 proxmox::tools::fs::replace_file(SHADOW_CONFIG_FILENAME, &data, options, true)?;
99
100 Ok(())
101 }
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)?;
116 proxmox::tools::fs::replace_file(SHADOW_CONFIG_FILENAME, &data, options, true)?;
117
118 Ok(())
119 }
120 }
121
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()),
128 }
129 }
130
131 /// Authenticate users
132 pub fn authenticate_user(userid: &Userid, password: &str) -> Result<(), Error> {
133
134 lookup_authenticator(userid.realm())?
135 .authenticate_user(userid.name(), password)
136 }