]> git.proxmox.com Git - proxmox-backup.git/blame - src/auth.rs
tape/drive: improve tape device locking behaviour
[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;
7use std::ffi::{CString, CStr};
8
f7d4e4b5 9use anyhow::{bail, format_err, Error};
7d817b03
DM
10use serde_json::json;
11
e7cb4dc5
WB
12use crate::api2::types::{Userid, UsernameRef, RealmRef};
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
20pub struct PAM();
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
71pub struct PBS();
72
73pub fn crypt(password: &[u8], salt: &str) -> Result<String, Error> {
74
75 #[link(name="crypt")]
76 extern "C" {
77 #[link_name = "crypt"]
78 fn __crypt(key: *const libc::c_char, salt: *const libc::c_char) -> * mut libc::c_char;
79 }
80
81 let salt = CString::new(salt)?;
82 let password = CString::new(password)?;
83
84 let res = unsafe {
85 CStr::from_ptr(
86 __crypt(
87 password.as_c_str().as_ptr(),
88 salt.as_c_str().as_ptr()
89 )
90 )
91 };
92 Ok(String::from(res.to_str()?))
93}
94
95
96pub fn encrypt_pw(password: &str) -> Result<String, Error> {
97
98 let salt = proxmox::sys::linux::random_data(8)?;
99 let salt = format!("$5${}$", base64::encode_config(&salt, base64::CRYPT));
100
101 crypt(password.as_bytes(), &salt)
102}
103
104pub fn verify_crypt_pw(password: &str, enc_password: &str) -> Result<(), Error> {
105 let verify = crypt(password.as_bytes(), enc_password)?;
54aec2fa 106 if verify != enc_password {
7d817b03
DM
107 bail!("invalid credentials");
108 }
109 Ok(())
110}
111
e7cb4dc5 112const SHADOW_CONFIG_FILENAME: &str = configdir!("/shadow.json");
7d817b03
DM
113
114impl ProxmoxAuthenticator for PBS {
115
e7cb4dc5 116 fn authenticate_user(&self, username: &UsernameRef, password: &str) -> Result<(), Error> {
7d817b03 117 let data = proxmox::tools::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?;
e7cb4dc5 118 match data[username.as_str()].as_str() {
7d817b03
DM
119 None => bail!("no password set"),
120 Some(enc_password) => verify_crypt_pw(password, enc_password)?,
121 }
122 Ok(())
123 }
124
e7cb4dc5 125 fn store_password(&self, username: &UsernameRef, password: &str) -> Result<(), Error> {
7d817b03
DM
126 let enc_password = encrypt_pw(password)?;
127 let mut data = proxmox::tools::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?;
e7cb4dc5 128 data[username.as_str()] = enc_password.into();
7d817b03
DM
129
130 let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600);
131 let options = proxmox::tools::fs::CreateOptions::new()
132 .perm(mode)
133 .owner(nix::unistd::ROOT)
134 .group(nix::unistd::Gid::from_raw(0));
135
136 let data = serde_json::to_vec_pretty(&data)?;
137 proxmox::tools::fs::replace_file(SHADOW_CONFIG_FILENAME, &data, options)?;
138
139 Ok(())
140 }
a4e871f5
DC
141
142 fn remove_password(&self, username: &UsernameRef) -> Result<(), Error> {
143 let mut data = proxmox::tools::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?;
144 if let Some(map) = data.as_object_mut() {
145 map.remove(username.as_str());
146 }
147
148 let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600);
149 let options = proxmox::tools::fs::CreateOptions::new()
150 .perm(mode)
151 .owner(nix::unistd::ROOT)
152 .group(nix::unistd::Gid::from_raw(0));
153
154 let data = serde_json::to_vec_pretty(&data)?;
155 proxmox::tools::fs::replace_file(SHADOW_CONFIG_FILENAME, &data, options)?;
156
157 Ok(())
158 }
7d817b03
DM
159}
160
4b40148c 161/// Lookup the autenticator for the specified realm
e7cb4dc5
WB
162pub fn lookup_authenticator(realm: &RealmRef) -> Result<Box<dyn ProxmoxAuthenticator>, Error> {
163 match realm.as_str() {
7d817b03
DM
164 "pam" => Ok(Box::new(PAM())),
165 "pbs" => Ok(Box::new(PBS())),
e7cb4dc5 166 _ => bail!("unknown realm '{}'", realm.as_str()),
7d817b03
DM
167 }
168}
169
4b40148c 170/// Authenticate users
e7cb4dc5 171pub fn authenticate_user(userid: &Userid, password: &str) -> Result<(), Error> {
7d817b03 172
e7cb4dc5
WB
173 lookup_authenticator(userid.realm())?
174 .authenticate_user(userid.name(), password)
7d817b03 175}