]> git.proxmox.com Git - proxmox-backup.git/blob - src/auth_helpers.rs
depend on libjs-qrcodejs
[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 crate::api2::types::Userid;
14
15 fn compute_csrf_secret_digest(
16 timestamp: i64,
17 secret: &[u8],
18 userid: &Userid,
19 ) -> String {
20
21 let mut hasher = sha::Sha256::new();
22 let data = format!("{:08X}:{}:", timestamp, userid);
23 hasher.update(data.as_bytes());
24 hasher.update(secret);
25
26 base64::encode_config(&hasher.finish(), base64::STANDARD_NO_PAD)
27 }
28
29 pub fn assemble_csrf_prevention_token(
30 secret: &[u8],
31 userid: &Userid,
32 ) -> String {
33
34 let epoch = proxmox::tools::time::epoch_i64();
35
36 let digest = compute_csrf_secret_digest(epoch, secret, userid);
37
38 format!("{:08X}:{}", epoch, digest)
39 }
40
41 pub fn verify_csrf_prevention_token(
42 secret: &[u8],
43 userid: &Userid,
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
65 let digest = compute_csrf_secret_digest(ttime, secret, userid);
66
67 if digest != sig {
68 bail!("invalid signature.");
69 }
70
71 let now = proxmox::tools::time::epoch_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
86 pub fn generate_csrf_key() -> Result<(), Error> {
87
88 let path = PathBuf::from(configdir!("/csrf.key"));
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
98 let backup_user = crate::backup::backup_user()?;
99
100 replace_file(
101 &path,
102 &pem,
103 CreateOptions::new()
104 .perm(Mode::from_bits_truncate(0o0640))
105 .owner(nix::unistd::ROOT)
106 .group(backup_user.gid),
107 )?;
108
109 Ok(())
110 }
111
112 pub fn generate_auth_key() -> Result<(), Error> {
113
114 let priv_path = PathBuf::from(configdir!("/authkey.key"));
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
127 replace_file(
128 &priv_path, &priv_pem, CreateOptions::new().perm(Mode::from_bits_truncate(0o0600)))?;
129
130 let public_pem = rsa.public_key_to_pem()?;
131
132 let backup_user = crate::backup::backup_user()?;
133
134 replace_file(
135 &public_path,
136 &public_pem,
137 CreateOptions::new()
138 .perm(Mode::from_bits_truncate(0o0640))
139 .owner(nix::unistd::ROOT)
140 .group(backup_user.gid),
141 )?;
142
143 Ok(())
144 }
145
146 pub fn csrf_secret() -> &'static [u8] {
147
148 lazy_static! {
149 static ref SECRET: Vec<u8> =
150 file_get_contents(configdir!("/csrf.key")).unwrap();
151 }
152
153 &SECRET
154 }
155
156 fn load_private_auth_key() -> Result<PKey<Private>, Error> {
157
158 let pem = file_get_contents(configdir!("/authkey.key"))?;
159 let rsa = Rsa::private_key_from_pem(&pem)?;
160 let key = PKey::from_rsa(rsa)?;
161
162 Ok(key)
163 }
164
165 pub 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
174 fn load_public_auth_key() -> Result<PKey<Public>, Error> {
175
176 let pem = file_get_contents(configdir!("/authkey.pub"))?;
177 let rsa = Rsa::public_key_from_pem(&pem)?;
178 let key = PKey::from_rsa(rsa)?;
179
180 Ok(key)
181 }
182
183 pub 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 }