]> git.proxmox.com Git - proxmox-backup.git/blame - src/auth_helpers.rs
use proxmox-mini-journalreader to display syslog
[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,
12 fs::{file_get_contents, file_set_contents, file_set_contents_full},
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
e18a6c9e 100 let (_, backup_gid) = crate::tools::getpwnam_ugid("backup")?;
35950380
DM
101 let uid = Some(nix::unistd::ROOT);
102 let gid = Some(nix::unistd::Gid::from_raw(backup_gid));
17ed456c 103
e18a6c9e 104 file_set_contents_full(
35950380 105 &path, &pem, Some(Mode::from_bits_truncate(0o0640)), uid, gid)?;
6c30068e
DM
106
107 Ok(())
108}
109
110pub fn generate_auth_key() -> Result<(), Error> {
111
9f4962d3 112 let priv_path = PathBuf::from(configdir!("/authkey.key"));
6c30068e
DM
113
114 let mut public_path = priv_path.clone();
115 public_path.set_extension("pub");
116
117 if priv_path.exists() && public_path.exists() { return Ok(()); }
118
119 let rsa = Rsa::generate(4096).unwrap();
120
121 let priv_pem = rsa.private_key_to_pem()?;
122
123 use nix::sys::stat::Mode;
124
e18a6c9e 125 file_set_contents(
6c30068e
DM
126 &priv_path, &priv_pem, Some(Mode::from_bits_truncate(0o0600)))?;
127
128
129 let public_pem = rsa.public_key_to_pem()?;
130
ba3eb88d
TL
131 let (_, backup_gid) = crate::tools::getpwnam_ugid("backup")?;
132 let uid = Some(nix::unistd::ROOT);
133 let gid = Some(nix::unistd::Gid::from_raw(backup_gid));
134
135 file_set_contents_full(
136 &public_path, &public_pem, Some(Mode::from_bits_truncate(0o0640)), uid, gid)?;
6c30068e
DM
137
138 Ok(())
139}
d01e2420
DM
140
141pub fn csrf_secret() -> &'static [u8] {
142
143 lazy_static! {
144 static ref SECRET: Vec<u8> =
e18a6c9e 145 file_get_contents(configdir!("/csrf.key")).unwrap();
d01e2420
DM
146 }
147
148 &SECRET
149}
150
151fn load_private_auth_key() -> Result<PKey<Private>, Error> {
152
e18a6c9e 153 let pem = file_get_contents(configdir!("/authkey.key"))?;
d01e2420
DM
154 let rsa = Rsa::private_key_from_pem(&pem)?;
155 let key = PKey::from_rsa(rsa)?;
156
157 Ok(key)
158}
159
160pub fn private_auth_key() -> &'static PKey<Private> {
161
162 lazy_static! {
163 static ref KEY: PKey<Private> = load_private_auth_key().unwrap();
164 }
165
166 &KEY
167}
168
169fn load_public_auth_key() -> Result<PKey<Public>, Error> {
170
e18a6c9e 171 let pem = file_get_contents(configdir!("/authkey.pub"))?;
d01e2420
DM
172 let rsa = Rsa::public_key_from_pem(&pem)?;
173 let key = PKey::from_rsa(rsa)?;
174
175 Ok(key)
176}
177
178pub fn public_auth_key() -> &'static PKey<Public> {
179
180 lazy_static! {
181 static ref KEY: PKey<Public> = load_public_auth_key().unwrap();
182 }
183
184 &KEY
185}