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