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