]> git.proxmox.com Git - proxmox-backup.git/blob - src/auth_helpers.rs
Set MMAP_THRESHOLD to a fixed value (128K)
[proxmox-backup.git] / src / auth_helpers.rs
1 use std::path::PathBuf;
2
3 use anyhow::{bail, format_err, Error};
4 use lazy_static::lazy_static;
5 use openssl::pkey::{PKey, Private, Public};
6 use openssl::rsa::Rsa;
7 use openssl::sha;
8
9 use proxmox_sys::fs::{file_get_contents, replace_file, CreateOptions};
10 use proxmox_lang::try_block;
11
12 use pbs_buildcfg::configdir;
13 use pbs_api_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_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_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 = pbs_config::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 true,
108 )?;
109
110 Ok(())
111 }
112
113 pub fn generate_auth_key() -> Result<(), Error> {
114
115 let priv_path = PathBuf::from(configdir!("/authkey.key"));
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
128 replace_file(
129 &priv_path,
130 &priv_pem,
131 CreateOptions::new().perm(Mode::from_bits_truncate(0o0600)),
132 true,
133 )?;
134
135 let public_pem = rsa.public_key_to_pem()?;
136
137 let backup_user = pbs_config::backup_user()?;
138
139 replace_file(
140 &public_path,
141 &public_pem,
142 CreateOptions::new()
143 .perm(Mode::from_bits_truncate(0o0640))
144 .owner(nix::unistd::ROOT)
145 .group(backup_user.gid),
146 true,
147 )?;
148
149 Ok(())
150 }
151
152 pub fn csrf_secret() -> &'static [u8] {
153
154 lazy_static! {
155 static ref SECRET: Vec<u8> =
156 file_get_contents(configdir!("/csrf.key")).unwrap();
157 }
158
159 &SECRET
160 }
161
162 fn load_public_auth_key() -> Result<PKey<Public>, Error> {
163
164 let pem = file_get_contents(configdir!("/authkey.pub"))?;
165 let rsa = Rsa::public_key_from_pem(&pem)?;
166 let key = PKey::from_rsa(rsa)?;
167
168 Ok(key)
169 }
170
171 pub fn public_auth_key() -> &'static PKey<Public> {
172
173 lazy_static! {
174 static ref KEY: PKey<Public> = load_public_auth_key().unwrap();
175 }
176
177 &KEY
178 }
179
180 fn load_private_auth_key() -> Result<PKey<Private>, Error> {
181 let pem = file_get_contents(configdir!("/authkey.key"))?;
182 let rsa = Rsa::private_key_from_pem(&pem)?;
183 let key = PKey::from_rsa(rsa)?;
184
185 Ok(key)
186 }
187
188 pub fn private_auth_key() -> &'static PKey<Private> {
189 lazy_static! {
190 static ref KEY: PKey<Private> = load_private_auth_key().unwrap();
191 }
192
193 &KEY
194 }