]>
Commit | Line | Data |
---|---|---|
f7d4e4b5 | 1 | use anyhow::{bail, format_err, Error}; |
d01e2420 | 2 | use lazy_static::lazy_static; |
6c30068e DM |
3 | |
4 | use openssl::rsa::{Rsa}; | |
d01e2420 | 5 | use openssl::pkey::{PKey, Public, Private}; |
1e76cbc6 | 6 | use openssl::sha; |
d01e2420 | 7 | |
6c30068e DM |
8 | use std::path::PathBuf; |
9 | ||
9ea4bce4 WB |
10 | use proxmox::tools::fs::{file_get_contents, replace_file, CreateOptions}; |
11 | use proxmox::try_block; | |
e18a6c9e | 12 | |
e7cb4dc5 | 13 | use crate::api2::types::Userid; |
e693818a DC |
14 | use crate::tools::epoch_now_u64; |
15 | ||
5ddf8cb1 DM |
16 | fn compute_csrf_secret_digest( |
17 | timestamp: i64, | |
1e76cbc6 | 18 | secret: &[u8], |
e7cb4dc5 | 19 | userid: &Userid, |
1e76cbc6 DM |
20 | ) -> String { |
21 | ||
1e76cbc6 | 22 | let mut hasher = sha::Sha256::new(); |
e7cb4dc5 | 23 | let data = format!("{:08X}:{}:", timestamp, userid); |
cf671670 | 24 | hasher.update(data.as_bytes()); |
1e76cbc6 DM |
25 | hasher.update(secret); |
26 | ||
5ddf8cb1 DM |
27 | base64::encode_config(&hasher.finish(), base64::STANDARD_NO_PAD) |
28 | } | |
29 | ||
30 | pub fn assemble_csrf_prevention_token( | |
31 | secret: &[u8], | |
e7cb4dc5 | 32 | userid: &Userid, |
5ddf8cb1 DM |
33 | ) -> String { |
34 | ||
e693818a | 35 | let epoch = epoch_now_u64().unwrap() as i64; |
5ddf8cb1 | 36 | |
e7cb4dc5 | 37 | let digest = compute_csrf_secret_digest(epoch, secret, userid); |
1e76cbc6 | 38 | |
cf671670 | 39 | format!("{:08X}:{}", epoch, digest) |
1e76cbc6 DM |
40 | } |
41 | ||
5ddf8cb1 DM |
42 | pub fn verify_csrf_prevention_token( |
43 | secret: &[u8], | |
e7cb4dc5 | 44 | userid: &Userid, |
5ddf8cb1 DM |
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 | ||
e7cb4dc5 | 66 | let digest = compute_csrf_secret_digest(ttime, secret, userid); |
5ddf8cb1 DM |
67 | |
68 | if digest != sig { | |
69 | bail!("invalid signature."); | |
70 | } | |
71 | ||
e693818a | 72 | let now = epoch_now_u64()? as i64; |
5ddf8cb1 DM |
73 | |
74 | let age = now - ttime; | |
75 | if age < min_age { | |
76 | bail!("timestamp newer than expected."); | |
77 | } | |
78 | ||
79 | if age > max_age { | |
80 | bail!("timestamp too old."); | |
81 | } | |
82 | ||
83 | Ok(age) | |
84 | }).map_err(|err| format_err!("invalid csrf token - {}", err)) | |
85 | } | |
86 | ||
6c30068e DM |
87 | pub fn generate_csrf_key() -> Result<(), Error> { |
88 | ||
9f4962d3 | 89 | let path = PathBuf::from(configdir!("/csrf.key")); |
6c30068e DM |
90 | |
91 | if path.exists() { return Ok(()); } | |
92 | ||
93 | let rsa = Rsa::generate(2048).unwrap(); | |
94 | ||
95 | let pem = rsa.private_key_to_pem()?; | |
96 | ||
97 | use nix::sys::stat::Mode; | |
98 | ||
f74a03da | 99 | let backup_user = crate::backup::backup_user()?; |
17ed456c | 100 | |
feaa1ad3 WB |
101 | replace_file( |
102 | &path, | |
103 | &pem, | |
104 | CreateOptions::new() | |
105 | .perm(Mode::from_bits_truncate(0o0640)) | |
106 | .owner(nix::unistd::ROOT) | |
f74a03da | 107 | .group(backup_user.gid), |
feaa1ad3 | 108 | )?; |
6c30068e DM |
109 | |
110 | Ok(()) | |
111 | } | |
112 | ||
113 | pub fn generate_auth_key() -> Result<(), Error> { | |
114 | ||
9f4962d3 | 115 | let priv_path = PathBuf::from(configdir!("/authkey.key")); |
6c30068e DM |
116 | |
117 | let mut public_path = priv_path.clone(); | |
118 | public_path.set_extension("pub"); | |
119 | ||
120 | if priv_path.exists() && public_path.exists() { return Ok(()); } | |
121 | ||
122 | let rsa = Rsa::generate(4096).unwrap(); | |
123 | ||
124 | let priv_pem = rsa.private_key_to_pem()?; | |
125 | ||
126 | use nix::sys::stat::Mode; | |
127 | ||
feaa1ad3 WB |
128 | replace_file( |
129 | &priv_path, &priv_pem, CreateOptions::new().perm(Mode::from_bits_truncate(0o0600)))?; | |
6c30068e DM |
130 | |
131 | let public_pem = rsa.public_key_to_pem()?; | |
132 | ||
f74a03da | 133 | let backup_user = crate::backup::backup_user()?; |
ba3eb88d | 134 | |
feaa1ad3 WB |
135 | replace_file( |
136 | &public_path, | |
137 | &public_pem, | |
138 | CreateOptions::new() | |
139 | .perm(Mode::from_bits_truncate(0o0640)) | |
140 | .owner(nix::unistd::ROOT) | |
f74a03da | 141 | .group(backup_user.gid), |
feaa1ad3 | 142 | )?; |
6c30068e DM |
143 | |
144 | Ok(()) | |
145 | } | |
d01e2420 DM |
146 | |
147 | pub fn csrf_secret() -> &'static [u8] { | |
148 | ||
149 | lazy_static! { | |
150 | static ref SECRET: Vec<u8> = | |
e18a6c9e | 151 | file_get_contents(configdir!("/csrf.key")).unwrap(); |
d01e2420 DM |
152 | } |
153 | ||
154 | &SECRET | |
155 | } | |
156 | ||
157 | fn load_private_auth_key() -> Result<PKey<Private>, Error> { | |
158 | ||
e18a6c9e | 159 | let pem = file_get_contents(configdir!("/authkey.key"))?; |
d01e2420 DM |
160 | let rsa = Rsa::private_key_from_pem(&pem)?; |
161 | let key = PKey::from_rsa(rsa)?; | |
162 | ||
163 | Ok(key) | |
164 | } | |
165 | ||
166 | pub fn private_auth_key() -> &'static PKey<Private> { | |
167 | ||
168 | lazy_static! { | |
169 | static ref KEY: PKey<Private> = load_private_auth_key().unwrap(); | |
170 | } | |
171 | ||
172 | &KEY | |
173 | } | |
174 | ||
175 | fn load_public_auth_key() -> Result<PKey<Public>, Error> { | |
176 | ||
e18a6c9e | 177 | let pem = file_get_contents(configdir!("/authkey.pub"))?; |
d01e2420 DM |
178 | let rsa = Rsa::public_key_from_pem(&pem)?; |
179 | let key = PKey::from_rsa(rsa)?; | |
180 | ||
181 | Ok(key) | |
182 | } | |
183 | ||
184 | pub fn public_auth_key() -> &'static PKey<Public> { | |
185 | ||
186 | lazy_static! { | |
187 | static ref KEY: PKey<Public> = load_public_auth_key().unwrap(); | |
188 | } | |
189 | ||
190 | &KEY | |
191 | } |