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