]> git.proxmox.com Git - proxmox-backup.git/blob - src/auth.rs
REST: don't print CSRF token
[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 use std::ffi::{CString, CStr};
8
9 use base64;
10 use anyhow::{bail, format_err, Error};
11 use serde_json::json;
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 }
19
20 pub 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 return Ok(());
29
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
67 pub struct PBS();
68
69 pub fn crypt(password: &[u8], salt: &str) -> Result<String, Error> {
70
71 #[link(name="crypt")]
72 extern "C" {
73 #[link_name = "crypt"]
74 fn __crypt(key: *const libc::c_char, salt: *const libc::c_char) -> * mut libc::c_char;
75 }
76
77 let salt = CString::new(salt)?;
78 let password = CString::new(password)?;
79
80 let res = unsafe {
81 CStr::from_ptr(
82 __crypt(
83 password.as_c_str().as_ptr(),
84 salt.as_c_str().as_ptr()
85 )
86 )
87 };
88 Ok(String::from(res.to_str()?))
89 }
90
91
92 pub fn encrypt_pw(password: &str) -> Result<String, Error> {
93
94 let salt = proxmox::sys::linux::random_data(8)?;
95 let salt = format!("$5${}$", base64::encode_config(&salt, base64::CRYPT));
96
97 crypt(password.as_bytes(), &salt)
98 }
99
100 pub fn verify_crypt_pw(password: &str, enc_password: &str) -> Result<(), Error> {
101 let verify = crypt(password.as_bytes(), enc_password)?;
102 if &verify != enc_password {
103 bail!("invalid credentials");
104 }
105 Ok(())
106 }
107
108 const SHADOW_CONFIG_FILENAME: &str = configdir!("/shadow.json");
109
110 impl ProxmoxAuthenticator for PBS {
111
112 fn authenticate_user(&self, username: &UsernameRef, password: &str) -> Result<(), Error> {
113 let data = proxmox::tools::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?;
114 match data[username.as_str()].as_str() {
115 None => bail!("no password set"),
116 Some(enc_password) => verify_crypt_pw(password, enc_password)?,
117 }
118 Ok(())
119 }
120
121 fn store_password(&self, username: &UsernameRef, password: &str) -> Result<(), Error> {
122 let enc_password = encrypt_pw(password)?;
123 let mut data = proxmox::tools::fs::file_get_json(SHADOW_CONFIG_FILENAME, Some(json!({})))?;
124 data[username.as_str()] = enc_password.into();
125
126 let mode = nix::sys::stat::Mode::from_bits_truncate(0o0600);
127 let options = proxmox::tools::fs::CreateOptions::new()
128 .perm(mode)
129 .owner(nix::unistd::ROOT)
130 .group(nix::unistd::Gid::from_raw(0));
131
132 let data = serde_json::to_vec_pretty(&data)?;
133 proxmox::tools::fs::replace_file(SHADOW_CONFIG_FILENAME, &data, options)?;
134
135 Ok(())
136 }
137 }
138
139 /// Lookup the autenticator for the specified realm
140 pub fn lookup_authenticator(realm: &RealmRef) -> Result<Box<dyn ProxmoxAuthenticator>, Error> {
141 match realm.as_str() {
142 "pam" => Ok(Box::new(PAM())),
143 "pbs" => Ok(Box::new(PBS())),
144 _ => bail!("unknown realm '{}'", realm.as_str()),
145 }
146 }
147
148 /// Authenticate users
149 pub fn authenticate_user(userid: &Userid, password: &str) -> Result<(), Error> {
150
151 lookup_authenticator(userid.realm())?
152 .authenticate_user(userid.name(), password)
153 }