]> git.proxmox.com Git - proxmox-backup.git/blame - src/auth_helpers.rs
bump version to 0.1.1
[proxmox-backup.git] / src / auth_helpers.rs
CommitLineData
6c30068e 1use failure::*;
d01e2420 2use lazy_static::lazy_static;
6c30068e
DM
3
4use openssl::rsa::{Rsa};
d01e2420 5use openssl::pkey::{PKey, Public, Private};
1e76cbc6 6use openssl::sha;
d01e2420 7
6c30068e
DM
8use std::path::PathBuf;
9
e18a6c9e
DM
10use proxmox::tools::{
11 try_block,
feaa1ad3 12 fs::{file_get_contents, replace_file, CreateOptions},
e18a6c9e
DM
13};
14
5ddf8cb1
DM
15fn compute_csrf_secret_digest(
16 timestamp: i64,
1e76cbc6
DM
17 secret: &[u8],
18 username: &str,
19) -> String {
20
1e76cbc6 21 let mut hasher = sha::Sha256::new();
5ddf8cb1 22 let data = format!("{:08X}:{}:", timestamp, username);
cf671670 23 hasher.update(data.as_bytes());
1e76cbc6
DM
24 hasher.update(secret);
25
5ddf8cb1
DM
26 base64::encode_config(&hasher.finish(), base64::STANDARD_NO_PAD)
27}
28
29pub 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);
1e76cbc6 38
cf671670 39 format!("{:08X}:{}", epoch, digest)
1e76cbc6
DM
40}
41
5ddf8cb1
DM
42pub 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
6c30068e
DM
88pub fn generate_csrf_key() -> Result<(), Error> {
89
9f4962d3 90 let path = PathBuf::from(configdir!("/csrf.key"));
6c30068e
DM
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
f74a03da 100 let backup_user = crate::backup::backup_user()?;
17ed456c 101
feaa1ad3
WB
102 replace_file(
103 &path,
104 &pem,
105 CreateOptions::new()
106 .perm(Mode::from_bits_truncate(0o0640))
107 .owner(nix::unistd::ROOT)
f74a03da 108 .group(backup_user.gid),
feaa1ad3 109 )?;
6c30068e
DM
110
111 Ok(())
112}
113
114pub fn generate_auth_key() -> Result<(), Error> {
115
9f4962d3 116 let priv_path = PathBuf::from(configdir!("/authkey.key"));
6c30068e
DM
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
feaa1ad3
WB
129 replace_file(
130 &priv_path, &priv_pem, CreateOptions::new().perm(Mode::from_bits_truncate(0o0600)))?;
6c30068e
DM
131
132 let public_pem = rsa.public_key_to_pem()?;
133
f74a03da 134 let backup_user = crate::backup::backup_user()?;
ba3eb88d 135
feaa1ad3
WB
136 replace_file(
137 &public_path,
138 &public_pem,
139 CreateOptions::new()
140 .perm(Mode::from_bits_truncate(0o0640))
141 .owner(nix::unistd::ROOT)
f74a03da 142 .group(backup_user.gid),
feaa1ad3 143 )?;
6c30068e
DM
144
145 Ok(())
146}
d01e2420
DM
147
148pub fn csrf_secret() -> &'static [u8] {
149
150 lazy_static! {
151 static ref SECRET: Vec<u8> =
e18a6c9e 152 file_get_contents(configdir!("/csrf.key")).unwrap();
d01e2420
DM
153 }
154
155 &SECRET
156}
157
158fn load_private_auth_key() -> Result<PKey<Private>, Error> {
159
e18a6c9e 160 let pem = file_get_contents(configdir!("/authkey.key"))?;
d01e2420
DM
161 let rsa = Rsa::private_key_from_pem(&pem)?;
162 let key = PKey::from_rsa(rsa)?;
163
164 Ok(key)
165}
166
167pub 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
176fn load_public_auth_key() -> Result<PKey<Public>, Error> {
177
e18a6c9e 178 let pem = file_get_contents(configdir!("/authkey.pub"))?;
d01e2420
DM
179 let rsa = Rsa::public_key_from_pem(&pem)?;
180 let key = PKey::from_rsa(rsa)?;
181
182 Ok(key)
183}
184
185pub 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}