]>
Commit | Line | Data |
---|---|---|
6c30068e DM |
1 | use crate::tools; |
2 | ||
3 | use failure::*; | |
d01e2420 | 4 | use lazy_static::lazy_static; |
6c30068e DM |
5 | |
6 | use openssl::rsa::{Rsa}; | |
d01e2420 | 7 | use openssl::pkey::{PKey, Public, Private}; |
1e76cbc6 | 8 | use openssl::sha; |
d01e2420 | 9 | |
6c30068e DM |
10 | use std::path::PathBuf; |
11 | ||
5ddf8cb1 DM |
12 | fn compute_csrf_secret_digest( |
13 | timestamp: i64, | |
1e76cbc6 DM |
14 | secret: &[u8], |
15 | username: &str, | |
16 | ) -> String { | |
17 | ||
1e76cbc6 | 18 | let mut hasher = sha::Sha256::new(); |
5ddf8cb1 | 19 | let data = format!("{:08X}:{}:", timestamp, username); |
cf671670 | 20 | hasher.update(data.as_bytes()); |
1e76cbc6 DM |
21 | hasher.update(secret); |
22 | ||
5ddf8cb1 DM |
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); | |
1e76cbc6 | 35 | |
cf671670 | 36 | format!("{:08X}:{}", epoch, digest) |
1e76cbc6 DM |
37 | } |
38 | ||
5ddf8cb1 DM |
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 | ||
6c30068e DM |
85 | pub fn generate_csrf_key() -> Result<(), Error> { |
86 | ||
9f4962d3 | 87 | let path = PathBuf::from(configdir!("/csrf.key")); |
6c30068e DM |
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 | ||
17ed456c | 97 | let (_, backup_gid) = tools::getpwnam_ugid("backup")?; |
35950380 DM |
98 | let uid = Some(nix::unistd::ROOT); |
99 | let gid = Some(nix::unistd::Gid::from_raw(backup_gid)); | |
17ed456c | 100 | |
35950380 DM |
101 | tools::file_set_contents_full( |
102 | &path, &pem, Some(Mode::from_bits_truncate(0o0640)), uid, gid)?; | |
6c30068e DM |
103 | |
104 | Ok(()) | |
105 | } | |
106 | ||
107 | pub fn generate_auth_key() -> Result<(), Error> { | |
108 | ||
9f4962d3 | 109 | let priv_path = PathBuf::from(configdir!("/authkey.key")); |
6c30068e DM |
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 | } | |
d01e2420 DM |
132 | |
133 | pub fn csrf_secret() -> &'static [u8] { | |
134 | ||
135 | lazy_static! { | |
136 | static ref SECRET: Vec<u8> = | |
9f4962d3 | 137 | tools::file_get_contents(configdir!("/csrf.key")).unwrap(); |
d01e2420 DM |
138 | } |
139 | ||
140 | &SECRET | |
141 | } | |
142 | ||
143 | fn load_private_auth_key() -> Result<PKey<Private>, Error> { | |
144 | ||
9f4962d3 | 145 | let pem = tools::file_get_contents(configdir!("/authkey.key"))?; |
d01e2420 DM |
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 | ||
9f4962d3 | 163 | let pem = tools::file_get_contents(configdir!("/authkey.pub"))?; |
d01e2420 DM |
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 | } |