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