]> git.proxmox.com Git - proxmox-backup.git/blame - src/auth_helpers.rs
drop pbs_tools::auth
[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
9ea4bce4
WB
9use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions};
10use proxmox::try_block;
e18a6c9e 11
af06decd 12use pbs_buildcfg::configdir;
4805edc4 13use pbs_api_types::Userid;
e693818a 14
5ddf8cb1
DM
15fn compute_csrf_secret_digest(
16 timestamp: i64,
1e76cbc6 17 secret: &[u8],
e7cb4dc5 18 userid: &Userid,
1e76cbc6
DM
19) -> String {
20
1e76cbc6 21 let mut hasher = sha::Sha256::new();
e7cb4dc5 22 let data = format!("{:08X}:{}:", timestamp, userid);
cf671670 23 hasher.update(data.as_bytes());
1e76cbc6
DM
24 hasher.update(secret);
25
5ddf8cb1
DM
26 base64::encode_config(&hasher.finish(), base64::STANDARD_NO_PAD)
27}
28
29pub fn assemble_csrf_prevention_token(
30 secret: &[u8],
e7cb4dc5 31 userid: &Userid,
5ddf8cb1
DM
32) -> String {
33
6a7be83e 34 let epoch = proxmox::tools::time::epoch_i64();
5ddf8cb1 35
e7cb4dc5 36 let digest = compute_csrf_secret_digest(epoch, secret, userid);
1e76cbc6 37
cf671670 38 format!("{:08X}:{}", epoch, digest)
1e76cbc6
DM
39}
40
5ddf8cb1
DM
41pub fn verify_csrf_prevention_token(
42 secret: &[u8],
e7cb4dc5 43 userid: &Userid,
5ddf8cb1
DM
44 token: &str,
45 min_age: i64,
46 max_age: i64,
47) -> Result<i64, Error> {
48
49 use std::collections::VecDeque;
50
51 let mut parts: VecDeque<&str> = token.split(':').collect();
52
53 try_block!({
54
55 if parts.len() != 2 {
56 bail!("format error - wrong number of parts.");
57 }
58
59 let timestamp = parts.pop_front().unwrap();
60 let sig = parts.pop_front().unwrap();
61
62 let ttime = i64::from_str_radix(timestamp, 16).
63 map_err(|err| format_err!("timestamp format error - {}", err))?;
64
e7cb4dc5 65 let digest = compute_csrf_secret_digest(ttime, secret, userid);
5ddf8cb1
DM
66
67 if digest != sig {
68 bail!("invalid signature.");
69 }
70
6a7be83e 71 let now = proxmox::tools::time::epoch_i64();
5ddf8cb1
DM
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
21211748 98 let backup_user = pbs_config::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
21211748 132 let backup_user = pbs_config::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
d01e2420
DM
156fn load_public_auth_key() -> Result<PKey<Public>, Error> {
157
e18a6c9e 158 let pem = file_get_contents(configdir!("/authkey.pub"))?;
d01e2420
DM
159 let rsa = Rsa::public_key_from_pem(&pem)?;
160 let key = PKey::from_rsa(rsa)?;
161
162 Ok(key)
163}
164
165pub fn public_auth_key() -> &'static PKey<Public> {
166
167 lazy_static! {
168 static ref KEY: PKey<Public> = load_public_auth_key().unwrap();
169 }
170
171 &KEY
172}
01a08021
WB
173
174fn load_private_auth_key() -> Result<PKey<Private>, Error> {
175 let pem = file_get_contents(configdir!("/authkey.key"))?;
176 let rsa = Rsa::private_key_from_pem(&pem)?;
177 let key = PKey::from_rsa(rsa)?;
178
179 Ok(key)
180}
181
182pub fn private_auth_key() -> &'static PKey<Private> {
183 lazy_static! {
184 static ref KEY: PKey<Private> = load_private_auth_key().unwrap();
185 }
186
187 &KEY
188}