]> git.proxmox.com Git - proxmox-backup.git/blob - src/auth_helpers.rs
src/bin/pxar.rs: allow to pass paths and match patterns as args to pxar extract
[proxmox-backup.git] / src / auth_helpers.rs
1 use crate::tools;
2
3 use failure::*;
4 use lazy_static::lazy_static;
5
6 use openssl::rsa::{Rsa};
7 use openssl::pkey::{PKey, Public, Private};
8 use openssl::sha;
9
10 use std::path::PathBuf;
11
12 fn compute_csrf_secret_digest(
13 timestamp: i64,
14 secret: &[u8],
15 username: &str,
16 ) -> String {
17
18 let mut hasher = sha::Sha256::new();
19 let data = format!("{:08X}:{}:", timestamp, username);
20 hasher.update(data.as_bytes());
21 hasher.update(secret);
22
23 base64::encode_config(&hasher.finish(), base64::STANDARD_NO_PAD)
24 }
25
26 pub fn assemble_csrf_prevention_token(
27 secret: &[u8],
28 username: &str,
29 ) -> String {
30
31 let epoch = std::time::SystemTime::now().duration_since(
32 std::time::SystemTime::UNIX_EPOCH).unwrap().as_secs() as i64;
33
34 let digest = compute_csrf_secret_digest(epoch, secret, username);
35
36 format!("{:08X}:{}", epoch, digest)
37 }
38
39 pub fn verify_csrf_prevention_token(
40 secret: &[u8],
41 username: &str,
42 token: &str,
43 min_age: i64,
44 max_age: i64,
45 ) -> Result<i64, Error> {
46
47 use std::collections::VecDeque;
48
49 let mut parts: VecDeque<&str> = token.split(':').collect();
50
51 try_block!({
52
53 if parts.len() != 2 {
54 bail!("format error - wrong number of parts.");
55 }
56
57 let timestamp = parts.pop_front().unwrap();
58 let sig = parts.pop_front().unwrap();
59
60 let ttime = i64::from_str_radix(timestamp, 16).
61 map_err(|err| format_err!("timestamp format error - {}", err))?;
62
63 let digest = compute_csrf_secret_digest(ttime, secret, username);
64
65 if digest != sig {
66 bail!("invalid signature.");
67 }
68
69 let now = std::time::SystemTime::now().duration_since(
70 std::time::SystemTime::UNIX_EPOCH)?.as_secs() as i64;
71
72 let age = now - ttime;
73 if age < min_age {
74 bail!("timestamp newer than expected.");
75 }
76
77 if age > max_age {
78 bail!("timestamp too old.");
79 }
80
81 Ok(age)
82 }).map_err(|err| format_err!("invalid csrf token - {}", err))
83 }
84
85 pub fn generate_csrf_key() -> Result<(), Error> {
86
87 let path = PathBuf::from(configdir!("/csrf.key"));
88
89 if path.exists() { return Ok(()); }
90
91 let rsa = Rsa::generate(2048).unwrap();
92
93 let pem = rsa.private_key_to_pem()?;
94
95 use nix::sys::stat::Mode;
96
97 let (_, backup_gid) = tools::getpwnam_ugid("backup")?;
98 let uid = Some(nix::unistd::ROOT);
99 let gid = Some(nix::unistd::Gid::from_raw(backup_gid));
100
101 tools::file_set_contents_full(
102 &path, &pem, Some(Mode::from_bits_truncate(0o0640)), uid, gid)?;
103
104 Ok(())
105 }
106
107 pub fn generate_auth_key() -> Result<(), Error> {
108
109 let priv_path = PathBuf::from(configdir!("/authkey.key"));
110
111 let mut public_path = priv_path.clone();
112 public_path.set_extension("pub");
113
114 if priv_path.exists() && public_path.exists() { return Ok(()); }
115
116 let rsa = Rsa::generate(4096).unwrap();
117
118 let priv_pem = rsa.private_key_to_pem()?;
119
120 use nix::sys::stat::Mode;
121
122 tools::file_set_contents(
123 &priv_path, &priv_pem, Some(Mode::from_bits_truncate(0o0600)))?;
124
125
126 let public_pem = rsa.public_key_to_pem()?;
127
128 tools::file_set_contents(&public_path, &public_pem, None)?;
129
130 Ok(())
131 }
132
133 pub fn csrf_secret() -> &'static [u8] {
134
135 lazy_static! {
136 static ref SECRET: Vec<u8> =
137 tools::file_get_contents(configdir!("/csrf.key")).unwrap();
138 }
139
140 &SECRET
141 }
142
143 fn load_private_auth_key() -> Result<PKey<Private>, Error> {
144
145 let pem = tools::file_get_contents(configdir!("/authkey.key"))?;
146 let rsa = Rsa::private_key_from_pem(&pem)?;
147 let key = PKey::from_rsa(rsa)?;
148
149 Ok(key)
150 }
151
152 pub fn private_auth_key() -> &'static PKey<Private> {
153
154 lazy_static! {
155 static ref KEY: PKey<Private> = load_private_auth_key().unwrap();
156 }
157
158 &KEY
159 }
160
161 fn load_public_auth_key() -> Result<PKey<Public>, Error> {
162
163 let pem = tools::file_get_contents(configdir!("/authkey.pub"))?;
164 let rsa = Rsa::public_key_from_pem(&pem)?;
165 let key = PKey::from_rsa(rsa)?;
166
167 Ok(key)
168 }
169
170 pub fn public_auth_key() -> &'static PKey<Public> {
171
172 lazy_static! {
173 static ref KEY: PKey<Public> = load_public_auth_key().unwrap();
174 }
175
176 &KEY
177 }