]> git.proxmox.com Git - proxmox-backup.git/blame - src/auth_helpers.rs
add tools/disks.rs (work in progress...)
[proxmox-backup.git] / src / auth_helpers.rs
CommitLineData
f7d4e4b5 1use anyhow::{bail, format_err, Error};
d01e2420 2use lazy_static::lazy_static;
6c30068e
DM
3
4use openssl::rsa::{Rsa};
d01e2420 5use openssl::pkey::{PKey, Public, Private};
1e76cbc6 6use openssl::sha;
d01e2420 7
6c30068e
DM
8use std::path::PathBuf;
9
9ea4bce4
WB
10use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
11use proxmox::try_block;
e18a6c9e 12
5ddf8cb1
DM
13fn compute_csrf_secret_digest(
14 timestamp: i64,
1e76cbc6
DM
15 secret: &[u8],
16 username: &str,
17) -> String {
18
1e76cbc6 19 let mut hasher = sha::Sha256::new();
5ddf8cb1 20 let data = format!("{:08X}:{}:", timestamp, username);
cf671670 21 hasher.update(data.as_bytes());
1e76cbc6
DM
22 hasher.update(secret);
23
5ddf8cb1
DM
24 base64::encode_config(&hasher.finish(), base64::STANDARD_NO_PAD)
25}
26
27pub fn assemble_csrf_prevention_token(
28 secret: &[u8],
29 username: &str,
30) -> String {
31
32 let epoch = std::time::SystemTime::now().duration_since(
33 std::time::SystemTime::UNIX_EPOCH).unwrap().as_secs() as i64;
34
35 let digest = compute_csrf_secret_digest(epoch, secret, username);
1e76cbc6 36
cf671670 37 format!("{:08X}:{}", epoch, digest)
1e76cbc6
DM
38}
39
5ddf8cb1
DM
40pub fn verify_csrf_prevention_token(
41 secret: &[u8],
42 username: &str,
43 token: &str,
44 min_age: i64,
45 max_age: i64,
46) -> Result<i64, Error> {
47
48 use std::collections::VecDeque;
49
50 let mut parts: VecDeque<&str> = token.split(':').collect();
51
52 try_block!({
53
54 if parts.len() != 2 {
55 bail!("format error - wrong number of parts.");
56 }
57
58 let timestamp = parts.pop_front().unwrap();
59 let sig = parts.pop_front().unwrap();
60
61 let ttime = i64::from_str_radix(timestamp, 16).
62 map_err(|err| format_err!("timestamp format error - {}", err))?;
63
64 let digest = compute_csrf_secret_digest(ttime, secret, username);
65
66 if digest != sig {
67 bail!("invalid signature.");
68 }
69
70 let now = std::time::SystemTime::now().duration_since(
71 std::time::SystemTime::UNIX_EPOCH)?.as_secs() as i64;
72
73 let age = now - ttime;
74 if age < min_age {
75 bail!("timestamp newer than expected.");
76 }
77
78 if age > max_age {
79 bail!("timestamp too old.");
80 }
81
82 Ok(age)
83 }).map_err(|err| format_err!("invalid csrf token - {}", err))
84}
85
6c30068e
DM
86pub fn generate_csrf_key() -> Result<(), Error> {
87
9f4962d3 88 let path = PathBuf::from(configdir!("/csrf.key"));
6c30068e
DM
89
90 if path.exists() { return Ok(()); }
91
92 let rsa = Rsa::generate(2048).unwrap();
93
94 let pem = rsa.private_key_to_pem()?;
95
96 use nix::sys::stat::Mode;
97
f74a03da 98 let backup_user = crate::backup::backup_user()?;
17ed456c 99
feaa1ad3
WB
100 replace_file(
101 &path,
102 &pem,
103 CreateOptions::new()
104 .perm(Mode::from_bits_truncate(0o0640))
105 .owner(nix::unistd::ROOT)
f74a03da 106 .group(backup_user.gid),
feaa1ad3 107 )?;
6c30068e
DM
108
109 Ok(())
110}
111
112pub fn generate_auth_key() -> Result<(), Error> {
113
9f4962d3 114 let priv_path = PathBuf::from(configdir!("/authkey.key"));
6c30068e
DM
115
116 let mut public_path = priv_path.clone();
117 public_path.set_extension("pub");
118
119 if priv_path.exists() && public_path.exists() { return Ok(()); }
120
121 let rsa = Rsa::generate(4096).unwrap();
122
123 let priv_pem = rsa.private_key_to_pem()?;
124
125 use nix::sys::stat::Mode;
126
feaa1ad3
WB
127 replace_file(
128 &priv_path, &priv_pem, CreateOptions::new().perm(Mode::from_bits_truncate(0o0600)))?;
6c30068e
DM
129
130 let public_pem = rsa.public_key_to_pem()?;
131
f74a03da 132 let backup_user = crate::backup::backup_user()?;
ba3eb88d 133
feaa1ad3
WB
134 replace_file(
135 &public_path,
136 &public_pem,
137 CreateOptions::new()
138 .perm(Mode::from_bits_truncate(0o0640))
139 .owner(nix::unistd::ROOT)
f74a03da 140 .group(backup_user.gid),
feaa1ad3 141 )?;
6c30068e
DM
142
143 Ok(())
144}
d01e2420
DM
145
146pub fn csrf_secret() -> &'static [u8] {
147
148 lazy_static! {
149 static ref SECRET: Vec<u8> =
e18a6c9e 150 file_get_contents(configdir!("/csrf.key")).unwrap();
d01e2420
DM
151 }
152
153 &SECRET
154}
155
156fn load_private_auth_key() -> Result<PKey<Private>, Error> {
157
e18a6c9e 158 let pem = file_get_contents(configdir!("/authkey.key"))?;
d01e2420
DM
159 let rsa = Rsa::private_key_from_pem(&pem)?;
160 let key = PKey::from_rsa(rsa)?;
161
162 Ok(key)
163}
164
165pub fn private_auth_key() -> &'static PKey<Private> {
166
167 lazy_static! {
168 static ref KEY: PKey<Private> = load_private_auth_key().unwrap();
169 }
170
171 &KEY
172}
173
174fn load_public_auth_key() -> Result<PKey<Public>, Error> {
175
e18a6c9e 176 let pem = file_get_contents(configdir!("/authkey.pub"))?;
d01e2420
DM
177 let rsa = Rsa::public_key_from_pem(&pem)?;
178 let key = PKey::from_rsa(rsa)?;
179
180 Ok(key)
181}
182
183pub fn public_auth_key() -> &'static PKey<Public> {
184
185 lazy_static! {
186 static ref KEY: PKey<Public> = load_public_auth_key().unwrap();
187 }
188
189 &KEY
190}