1use std::{
8 collections::HashMap,
9 ffi::OsStr,
10 fs::{self, File},
11 io::Write as _,
12 net::IpAddr,
13 num::TryFromIntError,
14 ops::DerefMut,
15 time,
16};
17
18use bytes::{BufMut as _, BytesMut};
19use openssl::{
20 asn1::{Asn1Integer, Asn1Time},
21 bn::{BigNum, MsbOption},
22 ec::{Asn1Flag, EcGroup, EcKey},
23 hash::MessageDigest,
24 nid::Nid,
25 pkey::PKey,
26 ssl::{
27 AlpnError, SslAcceptor, SslAcceptorBuilder, SslConnectorBuilder, SslContextBuilder,
28 SslFiletype, SslMethod, SslVerifyMode,
29 },
30 x509::{
31 X509, X509NameBuilder,
32 extension::SubjectAlternativeName,
33 store::{X509Store, X509StoreBuilder},
34 },
35};
36
37use crate::config::{
38 ConfigError, os_country,
39 ssl::{SslConfig, SslConfigContext, SslStore, Store},
40};
41
42impl SslStore<X509, X509Store> for Store {
43 fn get_file_certificates(path: &std::path::Path) -> Result<Vec<X509>, ConfigError> {
44 if path.is_file() {
45 match &path.extension().and_then(OsStr::to_str) {
46 Some("pem" | "crt") => match fs::read(path) {
47 Ok(pem_file) => Ok(vec![X509::from_pem(&pem_file)?]),
48 Err(io) => Err(ConfigError::IoFile(
49 path.to_str().unwrap_or_default().into(),
50 io,
51 )),
52 },
53 Some("der" | "cer") => match fs::read(path) {
54 Ok(der_file) => Ok(vec![X509::from_der(&der_file)?]),
55 Err(io) => Err(ConfigError::IoFile(
56 path.to_str().unwrap_or_default().into(),
57 io,
58 )),
59 },
60 _ => Ok(Vec::new()),
61 }
62 } else if path.is_symlink() {
63 if let Ok(link) = path.read_link() {
64 <Self as SslStore<X509, X509Store>>::get_file_certificates(&link)
65 } else {
66 Ok(Vec::new())
67 }
68 } else if let Ok(path_dir) = path.read_dir() {
69 let mut cert_list = Vec::new();
70 for dir_entry in path_dir.flatten() {
71 cert_list.append(
72 &mut <Self as SslStore<X509, X509Store>>::get_file_certificates(
73 &dir_entry.path(),
74 )?,
75 );
76 }
77
78 Ok(cert_list)
79 } else {
80 Ok(Vec::new())
81 }
82 }
83
84 fn get_store(&self) -> Result<X509Store, ConfigError> {
93 let mut store = X509StoreBuilder::new()?;
94 match self {
95 Store::System => {
96 store.set_default_paths()?;
97 Ok(store.build())
98 }
99 Store::File { path } => match glob::glob(path) {
100 Ok(certs) => {
101 for cert_path in certs.flatten() {
102 for cert in
103 <Self as SslStore<X509, X509Store>>::get_file_certificates(&cert_path)?
104 {
105 store.add_cert(cert)?;
106 }
107 }
108
109 Ok(store.build())
110 }
111 Err(e) => Err(ConfigError::WrongPathPattern(path.clone(), e)),
112 },
113 Store::Cert { certs } => {
114 for cert in certs {
115 store.add_cert(X509::from_pem(cert.as_bytes())?)?;
116 }
117
118 Ok(store.build())
119 }
120 }
121 }
122
123 fn get_certs(&self) -> Result<HashMap<String, X509>, ConfigError> {
137 match self {
138 Store::System => {
139 #[cfg(ossl300)]
140 {
141 let store: X509Store = self.get_store()?;
142 let mut certs_map = HashMap::new();
143
144 for cert in store.all_certificates() {
145 if let Some(name) = cert
146 .subject_name()
147 .entries_by_nid(Nid::COMMONNAME)
148 .last()
149 .and_then(|cn| cn.data().as_utf8().map(|cn| cn.to_string()).ok())
150 {
151 certs_map.insert(name, cert);
152 }
153 }
154
155 Ok(certs_map)
156 }
157
158 #[cfg(not(ossl300))]
159 Ok(HashMap::new())
160 }
161 Store::File { path } => match glob::glob(path) {
162 Ok(certs) => {
163 let mut certs_map = HashMap::new();
164 for cert_path in certs.flatten() {
165 let certs =
166 <Self as SslStore<X509, X509Store>>::get_file_certificates(&cert_path)?;
167 for cert in certs {
168 if let Some(name) = cert
169 .subject_name()
170 .entries_by_nid(Nid::COMMONNAME)
171 .last()
172 .and_then(|cn| cn.data().as_utf8().map(|cn| cn.to_string()).ok())
173 {
174 certs_map.insert(name, cert);
175 } else if let Some(cert_name) = cert_path.to_str().and_then(|p| {
176 p.strip_suffix(".pem").or(p
177 .strip_suffix(".crt")
178 .or(p.strip_suffix(".der").or(p.strip_suffix(".cer"))))
179 }) {
180 certs_map.insert(cert_name.into(), cert);
181 }
182 }
183 }
184
185 Ok(certs_map)
186 }
187 Err(e) => Err(ConfigError::WrongPathPattern(path.clone(), e)),
188 },
189 Store::Cert { certs } => {
190 let mut certs_map = HashMap::new();
191 for cert_pem in certs {
192 let cert = X509::from_pem(cert_pem.as_bytes())?;
193 if let Some(name) = cert
194 .subject_name()
195 .entries_by_nid(Nid::COMMONNAME)
196 .last()
197 .and_then(|cn| cn.data().as_utf8().map(|cn| cn.to_string()).ok())
198 {
199 certs_map.insert(name, cert);
200 }
201 }
202
203 Ok(certs_map)
204 }
205 }
206 }
207}
208
209pub(crate) fn init_openssl_context<B>(
211 config: &SslConfig,
212 mut context_builder: B,
213 is_server: bool,
214 host: Option<&str>,
215) -> Result<B, ConfigError>
216where
217 B: DerefMut<Target = SslContextBuilder>,
218{
219 if let Some(pkcs12_path) = &config.pkcs12 {
220 match fs::read(pkcs12_path) {
221 Ok(pkcs12_file) => {
222 let pkcs12 = ::openssl::pkcs12::Pkcs12::from_der(pkcs12_file.as_ref())?
223 .parse2(config.passphrase.as_ref().unwrap_or(&String::from("")))?;
224
225 if let Some(pkey) = pkcs12.pkey {
226 context_builder.set_private_key(&pkey)?;
227 }
228
229 if let Some(cert) = pkcs12.cert {
230 context_builder.set_certificate(&cert)?;
231 }
232
233 if let Some(ca) = pkcs12.ca {
234 for cert in ca {
235 context_builder.add_extra_chain_cert(cert)?;
236 }
237 }
238 }
239 Err(io) => return Err(ConfigError::IoFile(pkcs12_path.to_string(), io)),
240 }
241 } else if let (Some(cert_path), Some(key_path)) = (&config.cert, &config.key) {
242 context_builder.set_certificate_file(cert_path, SslFiletype::PEM)?;
243
244 match fs::read(key_path) {
245 Ok(key_file) => {
246 let pkey = if key_path.ends_with(".der") || key_path.ends_with(".cer") {
247 PKey::private_key_from_der(key_file.as_slice())?
248 } else if let Some(passphrase) = &config.passphrase {
249 PKey::private_key_from_pem_passphrase(
250 key_file.as_slice(),
251 passphrase.as_bytes(),
252 )?
253 } else {
254 PKey::private_key_from_pem(key_file.as_slice())?
255 };
256
257 context_builder.set_private_key(&pkey)?;
258 }
259 Err(io) => return Err(ConfigError::IoFile(key_path.to_string(), io)),
260 }
261 } else if is_server {
262 let mut group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)?;
263 group.set_asn1_flag(Asn1Flag::NAMED_CURVE);
264 let pkey = PKey::from_ec_key(EcKey::generate(&group)?)?;
265 context_builder.set_private_key(&pkey)?;
266
267 let mut cert = X509::builder()?;
268 cert.set_version(2)?;
269 cert.set_pubkey(&pkey)?;
270
271 let mut serial_bn = BigNum::new()?;
272 serial_bn.pseudo_rand(64, MsbOption::MAYBE_ZERO, true)?;
273 let serial_number = Asn1Integer::from_bn(&serial_bn)?;
274 cert.set_serial_number(&serial_number)?;
275
276 let begin_valid_time = Asn1Time::from_unix(
277 (time::UNIX_EPOCH
278 .elapsed()
279 .map_err(|e| {
280 ConfigError::WrongValue("time::UNIX_EPOCH".to_string(), e.to_string())
281 })?
282 .as_secs()
283 - 360)
284 .try_into()
285 .map_err(|e: TryFromIntError| {
286 ConfigError::WrongValue("Asn1Time::from_UNIX_EPOCH".to_string(), e.to_string())
287 })?,
288 )?;
289 cert.set_not_before(&begin_valid_time)?;
290 let end_valid_time = Asn1Time::days_from_now(1461)?; cert.set_not_after(&end_valid_time)?;
292
293 let mut x509_name = X509NameBuilder::new()?;
294 if let Some(cn) = os_country() {
295 x509_name.append_entry_by_text("C", cn.as_str())?;
296 }
297 x509_name.append_entry_by_text("CN", "ProSA")?;
298 let x509_name = x509_name.build();
299 cert.set_subject_name(&x509_name)?;
300 cert.set_issuer_name(&x509_name)?;
301
302 if let Some(host) = host {
304 if let Ok(ip) = host.parse::<IpAddr>() {
305 if !ip.is_unspecified() && !ip.is_loopback() {
306 let mut subject_alternative_name = SubjectAlternativeName::new();
307 let x509_extension = subject_alternative_name
308 .ip(host)
309 .build(&cert.x509v3_context(None, None))?;
310 cert.append_extension2(&x509_extension)?;
311 }
312 } else {
313 let mut subject_alternative_name = SubjectAlternativeName::new();
314 let x509_extension = subject_alternative_name
315 .dns(host)
316 .build(&cert.x509v3_context(None, None))?;
317 cert.append_extension2(&x509_extension)?;
318 }
319 }
320
321 cert.sign(&pkey, MessageDigest::sha256())?;
322 let cert_x509 = cert.build();
323
324 if let Some(cert_path) = &config.cert {
326 let mut cert_file =
327 File::create(cert_path).map_err(|e| ConfigError::IoFile(cert_path.clone(), e))?;
328 if cert_path.ends_with(".der") || cert_path.ends_with(".cer") {
329 cert_file
330 .write_all(&cert_x509.to_der()?)
331 .map_err(|e| ConfigError::IoFile(cert_path.clone(), e))?;
332 } else {
333 cert_file
334 .write_all(&cert_x509.to_pem()?)
335 .map_err(|e| ConfigError::IoFile(cert_path.clone(), e))?;
336 }
337 }
338
339 context_builder.set_certificate(&cert_x509)?;
340 }
341
342 if let Some(store) = &config.store {
343 context_builder.set_cert_store(store.get_store()?);
344 if is_server {
345 context_builder.set_verify(SslVerifyMode::PEER);
346 }
347 } else if !is_server {
348 context_builder.set_cert_store(Store::default().get_store()?);
349 } else {
350 context_builder.set_verify(SslVerifyMode::NONE);
351 }
352
353 if !config.alpn.is_empty() {
354 if is_server {
355 let alpn_list = config.alpn.clone();
356 context_builder.set_alpn_select_callback(move |_ssl, alpn| {
357 let mut cli_alpn = HashMap::new();
358
359 let mut current_split = alpn;
360 while let Some(length) = current_split.first() {
361 if current_split.len() > *length as usize
362 && let (left, right) = current_split.split_at(*length as usize + 1)
363 && let Ok(alpn_key) = String::from_utf8(left[1..].to_vec())
364 {
365 cli_alpn.insert(alpn_key, &left[1..]);
366 current_split = right;
367 } else {
368 return Err(AlpnError::ALERT_FATAL);
369 }
370 }
371
372 for alpn_name in &alpn_list {
373 if let Some(alpn) = cli_alpn.get(alpn_name) {
374 return Ok(alpn);
375 }
376 }
377
378 Err(AlpnError::NOACK)
379 });
380 } else {
381 let mut alpn_bytes = BytesMut::new();
382 for alpn in &config.alpn {
383 alpn_bytes.put_u8(alpn.len() as u8);
384 alpn_bytes.put(alpn.as_bytes());
385 }
386
387 context_builder.set_alpn_protos(&alpn_bytes)?;
388 }
389 }
390
391 Ok(context_builder)
392}
393
394impl SslConfigContext<SslConnectorBuilder, SslAcceptorBuilder> for SslConfig {
395 fn init_tls_client_context(&self) -> Result<openssl::ssl::SslConnectorBuilder, ConfigError> {
426 let mut ssl_connector = openssl::ssl::SslConnector::builder(SslMethod::tls_client())?;
427 init_openssl_context(self, ssl_connector.deref_mut(), false, None)?;
428 Ok(ssl_connector)
429 }
430
431 fn init_tls_server_context(
467 &self,
468 host: Option<&str>,
469 ) -> Result<openssl::ssl::SslAcceptorBuilder, ConfigError> {
470 let mut ssl_acceptor = SslAcceptor::mozilla_modern(SslMethod::tls_server())?;
471 init_openssl_context(self, ssl_acceptor.deref_mut(), true, host)?;
472 Ok(ssl_acceptor)
473 }
474}
475
476#[cfg(test)]
477mod tests {
478 use super::*;
479
480 #[test]
481 fn test_store() {
482 let store_le_x1_x2 = Store::Cert {
483 certs: vec![
484 "-----BEGIN CERTIFICATE-----
485MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
486TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
487cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
488WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
489ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
490MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
491h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
4920TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
493A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
494T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
495B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
496B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
497KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
498OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
499jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
500qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
501rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
502HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
503hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
504ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
5053BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
506NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
507ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
508TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
509jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
510oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
5114RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
512mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
513emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
514-----END CERTIFICATE-----"
515 .to_string(),
516 "-----BEGIN CERTIFICATE-----
517MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
518CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
519R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
520MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
521ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
522EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
523+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
524ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
525AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
526zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
527tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
528/q4AaOeMSQ+2b1tbFfLn
529-----END CERTIFICATE-----"
530 .to_string(),
531 ],
532 };
533
534 let certs: HashMap<String, ::openssl::x509::X509> = store_le_x1_x2
535 .get_certs()
536 .expect("Certificates should be retrieved");
537 assert!(!certs.is_empty());
538
539 let ossl_store: ::openssl::x509::store::X509Store = store_le_x1_x2
540 .get_store()
541 .expect("Certificates store should be retrieved");
542 assert!(!ossl_store.all_certificates().is_empty())
543 }
544
545 #[cfg(ossl300)]
546 #[test]
547 fn test_system_store() {
548 let certs_map: HashMap<String, X509> = Store::System.get_certs().unwrap();
549 assert!(!certs_map.is_empty());
550 }
551
552 #[test]
553 fn test_tls_server_context() {
554 let ssl_config = SslConfig::default();
555 let ssl_acceptor_builder: ::openssl::ssl::SslAcceptorBuilder = ssl_config
556 .init_tls_server_context(None)
557 .expect("The TLS server context should be init");
558 let ssl_acceptor = ssl_acceptor_builder.build();
559
560 assert!(ssl_acceptor.context().private_key().is_some());
562 assert!(ssl_acceptor.context().certificate().is_some());
563 }
564}