]> git.proxmox.com Git - proxmox-backup.git/blob - src/auth_helpers.rs
bump proxmox crate to 0.1.7
[proxmox-backup.git] / src / auth_helpers.rs
1 use failure::*;
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 fn compute_csrf_secret_digest(
14 timestamp: i64,
15 secret: &[u8],
16 username: &str,
17 ) -> String {
18
19 let mut hasher = sha::Sha256::new();
20 let data = format!("{:08X}:{}:", timestamp, username);
21 hasher.update(data.as_bytes());
22 hasher.update(secret);
23
24 base64::encode_config(&hasher.finish(), base64::STANDARD_NO_PAD)
25 }
26
27 pub fn assemble_csrf_prevention_token(
28 secret: &[u8],
29 username: &str,
30 ) -> String {
31
32 let epoch = std::time::SystemTime::now().duration_since(
33 std::time::SystemTime::UNIX_EPOCH).unwrap().as_secs() as i64;
34
35 let digest = compute_csrf_secret_digest(epoch, secret, username);
36
37 format!("{:08X}:{}", epoch, digest)
38 }
39
40 pub fn verify_csrf_prevention_token(
41 secret: &[u8],
42 username: &str,
43 token: &str,
44 min_age: i64,
45 max_age: i64,
46 ) -> Result<i64, Error> {
47
48 use std::collections::VecDeque;
49
50 let mut parts: VecDeque<&str> = token.split(':').collect();
51
52 try_block!({
53
54 if parts.len() != 2 {
55 bail!("format error - wrong number of parts.");
56 }
57
58 let timestamp = parts.pop_front().unwrap();
59 let sig = parts.pop_front().unwrap();
60
61 let ttime = i64::from_str_radix(timestamp, 16).
62 map_err(|err| format_err!("timestamp format error - {}", err))?;
63
64 let digest = compute_csrf_secret_digest(ttime, secret, username);
65
66 if digest != sig {
67 bail!("invalid signature.");
68 }
69
70 let now = std::time::SystemTime::now().duration_since(
71 std::time::SystemTime::UNIX_EPOCH)?.as_secs() as 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 }