1use openssl::ec::{EcGroup, EcKey};
5use openssl::error::ErrorStack;
6use openssl::nid::Nid;
7use openssl::pkey::{PKeyRef, Private};
8use openssl::rsa::Rsa;
9use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslSessionCacheMode, SslVerifyMode};
10use openssl::x509::{
11 extension::{
12 AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage,
13 SubjectAlternativeName, SubjectKeyIdentifier,
14 },
15 X509NameBuilder, X509ReqBuilder, X509,
16};
17use openssl::{asn1, bn, hash, pkey};
18use sketching::*;
19
20use crate::config::TlsConfiguration;
21
22use std::fs;
23use std::io::{Read, Write};
24use std::path::Path;
25
26const CA_VALID_DAYS: u32 = 30;
27const CERT_VALID_DAYS: u32 = 5;
28
29const RSA_MIN_KEY_SIZE_BITS: u64 = 2048;
45const EC_MIN_KEY_SIZE_BITS: u64 = 224;
46
47fn get_signing_func() -> hash::MessageDigest {
49 hash::MessageDigest::sha256()
50}
51
52pub fn check_privkey_minimums(privkey: &PKeyRef<Private>) -> Result<(), String> {
54 if let Ok(key) = privkey.rsa() {
55 if key.size() < (RSA_MIN_KEY_SIZE_BITS / 8) as u32 {
56 Err(format!(
57 "TLS RSA key is less than {} bits!",
58 RSA_MIN_KEY_SIZE_BITS
59 ))
60 } else {
61 debug!(
62 "The RSA private key size is: {} bits, that's OK!",
63 key.size() * 8
64 );
65 Ok(())
66 }
67 } else if let Ok(key) = privkey.ec_key() {
68 #[allow(clippy::panic)]
70 let key_bits: u64 = key.private_key().num_bits().try_into().unwrap_or_else(|_| {
71 panic!(
72 "Failed to convert EC bitlength {} to u64",
73 key.private_key().num_bits()
74 )
75 });
76
77 if key_bits < EC_MIN_KEY_SIZE_BITS {
78 Err(format!(
79 "TLS EC key is less than {} bits! Got: {}",
80 EC_MIN_KEY_SIZE_BITS, key_bits
81 ))
82 } else {
83 #[cfg(any(test, debug_assertions))]
84 println!("The EC private key size is: {} bits, that's OK!", key_bits);
85 debug!("The EC private key size is: {} bits, that's OK!", key_bits);
86 Ok(())
87 }
88 } else {
89 error!("TLS key is not RSA or EC, cannot check minimums!");
90 Ok(())
91 }
92}
93
94pub fn setup_tls(
97 tls_config: &Option<TlsConfiguration>,
98) -> Result<Option<SslAcceptor>, std::io::Error> {
99 let Some(tls_param) = tls_config.as_ref() else {
100 return Ok(None);
101 };
102
103 let mut tls_builder = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls())?;
104
105 tls_builder
106 .set_certificate_chain_file(tls_param.chain.clone())
107 .map_err(|err| {
108 std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
109 })?;
110
111 tls_builder
112 .set_private_key_file(tls_param.key.clone(), SslFiletype::PEM)
113 .map_err(|err| {
114 std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
115 })?;
116
117 tls_builder.check_private_key().map_err(|err| {
118 std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
119 })?;
120
121 if let Some(client_ca) = tls_param.client_ca.as_ref() {
123 info!("Loading client certificates from {}", client_ca.display());
124
125 let verify = SslVerifyMode::PEER;
126 tls_builder.set_verify(verify);
129
130 tls_builder.set_session_cache_mode(SslSessionCacheMode::OFF);
142
143 let read_dir = fs::read_dir(client_ca).map_err(|err| {
144 std::io::Error::other(format!(
145 "Failed to create TLS listener while loading client ca from {}: {:?}",
146 client_ca.display(),
147 err
148 ))
149 })?;
150
151 for cert_dir_ent in read_dir.filter_map(|item| item.ok()).filter(|item| {
152 item.file_name()
153 .to_str()
154 .map(|fname| fname.ends_with(".0"))
157 .unwrap_or_default()
158 }) {
159 let mut cert_pem = String::new();
160 fs::File::open(cert_dir_ent.path())
161 .and_then(|mut file| file.read_to_string(&mut cert_pem))
162 .map_err(|err| {
163 std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
164 })?;
165
166 let cert = X509::from_pem(cert_pem.as_bytes()).map_err(|err| {
167 std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
168 })?;
169
170 let cert_store = tls_builder.cert_store_mut();
171 cert_store.add_cert(cert.clone()).map_err(|err| {
172 std::io::Error::other(format!(
173 "Failed to load cert store while creating TLS listener: {:?}",
174 err
175 ))
176 })?;
177 tls_builder.add_client_ca(&cert).map_err(|err| {
180 std::io::Error::other(format!("Failed to create TLS listener: {:?}", err))
181 })?;
182 }
183
184 if enabled!(tracing::Level::TRACE) {
189 tls_builder.set_verify_callback(verify, |status, x509store| {
190 if let Some(current_cert) = x509store.current_cert() {
191 let cert_text_bytes = current_cert.to_text().unwrap_or_default();
192 let cert_text = String::from_utf8_lossy(cert_text_bytes.as_slice());
193 tracing::warn!(client_cert = %cert_text);
194 };
195
196 if let Some(chain) = x509store.chain() {
197 for cert in chain.iter() {
198 let cert_text_bytes = cert.to_text().unwrap_or_default();
199 let cert_text = String::from_utf8_lossy(cert_text_bytes.as_slice());
200 tracing::warn!(chain_cert = %cert_text);
201 }
202 }
203
204 status
205 });
206 }
207
208 }
210
211 let tls_acceptor = tls_builder.build();
212
213 let privkey = tls_acceptor.context().private_key().ok_or_else(|| {
215 std::io::Error::other("Failed to access tls_acceptor private key".to_string())
216 })?;
217
218 check_privkey_minimums(privkey).map_err(|err| {
219 std::io::Error::other(format!("Private key minimums were not met: {:?}", err))
220 })?;
221
222 Ok(Some(tls_acceptor))
223}
224
225fn get_ec_group() -> Result<EcGroup, ErrorStack> {
226 EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)
227}
228
229#[derive(Debug)]
230pub(crate) struct CaHandle {
231 key: pkey::PKey<pkey::Private>,
232 cert: X509,
233}
234
235pub(crate) fn write_ca(
236 key_ar: impl AsRef<Path>,
237 cert_ar: impl AsRef<Path>,
238 handle: &CaHandle,
239) -> Result<(), ()> {
240 let key_path: &Path = key_ar.as_ref();
241 let cert_path: &Path = cert_ar.as_ref();
242
243 let key_pem = handle.key.private_key_to_pem_pkcs8().map_err(|e| {
244 error!(err = ?e, "Failed to convert key to PEM");
245 })?;
246
247 let cert_pem = handle.cert.to_pem().map_err(|e| {
248 error!(err = ?e, "Failed to convert cert to PEM");
249 })?;
250
251 fs::File::create(key_path)
252 .and_then(|mut file| file.write_all(&key_pem))
253 .map_err(|e| {
254 error!(err = ?e, "Failed to create {:?}", key_path);
255 })?;
256
257 fs::File::create(cert_path)
258 .and_then(|mut file| file.write_all(&cert_pem))
259 .map_err(|e| {
260 error!(err = ?e, "Failed to create {:?}", cert_path);
261 })
262}
263
264#[derive(Debug)]
265pub enum KeyType {
266 #[allow(dead_code)]
267 Rsa,
268 Ec,
269}
270impl Default for KeyType {
271 fn default() -> Self {
272 Self::Ec
273 }
274}
275
276#[derive(Debug)]
277pub struct CAConfig {
278 pub key_type: KeyType,
279 pub key_bits: u64,
280 pub skip_enforce_minimums: bool,
281}
282
283impl Default for CAConfig {
284 fn default() -> Self {
285 #[allow(clippy::expect_used)]
286 Self::new(KeyType::Ec, 256, false)
287 .expect("Somehow the defaults failed to pass validation while building a CA Config?")
288 }
289}
290
291impl CAConfig {
292 fn new(key_type: KeyType, key_bits: u64, skip_enforce_minimums: bool) -> Result<Self, String> {
293 let res = Self {
294 key_type,
295 key_bits,
296 skip_enforce_minimums,
297 };
298 if !skip_enforce_minimums {
299 res.enforce_minimums()?;
300 };
301 Ok(res)
302 }
303
304 fn enforce_minimums(&self) -> Result<(), String> {
306 match self.key_type {
307 KeyType::Rsa => {
308 trace!(
309 "Generating CA Config for RSA Key with {} bits",
310 self.key_bits
311 );
312 if self.key_bits < RSA_MIN_KEY_SIZE_BITS {
313 return Err(format!(
314 "RSA key size must be at least {} bits",
315 RSA_MIN_KEY_SIZE_BITS
316 ));
317 }
318 }
319 KeyType::Ec => {
320 trace!("Generating CA Config for EcKey with {} bits", self.key_bits);
321 if self.key_bits < EC_MIN_KEY_SIZE_BITS {
322 return Err(format!(
323 "EC key size must be at least {} bits",
324 EC_MIN_KEY_SIZE_BITS
325 ));
326 }
327 }
328 };
329 Ok(())
330 }
331}
332
333pub(crate) fn gen_private_key(
334 key_type: &KeyType,
335 key_bits: Option<u64>,
336) -> Result<pkey::PKey<pkey::Private>, ErrorStack> {
337 match key_type {
338 KeyType::Rsa => {
339 let key_bits = key_bits.unwrap_or(RSA_MIN_KEY_SIZE_BITS);
340 let rsa = Rsa::generate(key_bits as u32)?;
341 pkey::PKey::from_rsa(rsa)
342 }
343 KeyType::Ec => {
344 let ecgroup = get_ec_group()?;
346 let eckey = EcKey::generate(&ecgroup)?;
347 pkey::PKey::from_ec_key(eckey)
348 }
349 }
350}
351
352pub(crate) fn build_ca(ca_config: Option<CAConfig>) -> Result<CaHandle, ErrorStack> {
354 let ca_config = ca_config.unwrap_or_default();
355
356 let ca_key = gen_private_key(&ca_config.key_type, Some(ca_config.key_bits))?;
357
358 if !ca_config.skip_enforce_minimums {
359 check_privkey_minimums(&ca_key).map_err(|err| {
360 admin_error!("failed to build_ca due to privkey minimums {}", err);
361 #[cfg(any(test, debug_assertions))]
362 println!("failed to build_ca due to privkey minimums: {}", err);
363 ErrorStack::get() })?;
365 }
366 let mut x509_name = X509NameBuilder::new()?;
367
368 x509_name.append_entry_by_text("C", "AU")?;
369 x509_name.append_entry_by_text("ST", "QLD")?;
370 x509_name.append_entry_by_text("O", "Kanidm")?;
371 x509_name.append_entry_by_text("CN", "Kanidm Generated CA")?;
372 x509_name.append_entry_by_text("OU", "Development and Evaluation - NOT FOR PRODUCTION")?;
373 let x509_name = x509_name.build();
374
375 let mut cert_builder = X509::builder()?;
376 cert_builder.set_version(2)?;
378
379 let serial_number = bn::BigNum::from_u32(1).and_then(|serial| serial.to_asn1_integer())?;
380
381 cert_builder.set_serial_number(&serial_number)?;
382 cert_builder.set_subject_name(&x509_name)?;
383 cert_builder.set_issuer_name(&x509_name)?;
384
385 let not_before = asn1::Asn1Time::days_from_now(0)?;
386 cert_builder.set_not_before(¬_before)?;
387 let not_after = asn1::Asn1Time::days_from_now(CA_VALID_DAYS)?;
388 cert_builder.set_not_after(¬_after)?;
389
390 cert_builder.append_extension(BasicConstraints::new().critical().ca().pathlen(0).build()?)?;
391 cert_builder.append_extension(
392 KeyUsage::new()
393 .critical()
394 .key_cert_sign()
395 .crl_sign()
396 .build()?,
397 )?;
398
399 let subject_key_identifier =
400 SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?;
401 cert_builder.append_extension(subject_key_identifier)?;
402
403 cert_builder.set_pubkey(&ca_key)?;
404
405 cert_builder.sign(&ca_key, get_signing_func())?;
406 let ca_cert = cert_builder.build();
407
408 Ok(CaHandle {
409 key: ca_key,
410 cert: ca_cert,
411 })
412}
413
414pub(crate) fn load_ca(
415 ca_key_ar: impl AsRef<Path>,
416 ca_cert_ar: impl AsRef<Path>,
417) -> Result<CaHandle, ()> {
418 let ca_key_path: &Path = ca_key_ar.as_ref();
419 let ca_cert_path: &Path = ca_cert_ar.as_ref();
420
421 let mut ca_key_pem = vec![];
422 fs::File::open(ca_key_path)
423 .and_then(|mut file| file.read_to_end(&mut ca_key_pem))
424 .map_err(|e| {
425 error!(err = ?e, "Failed to read {:?}", ca_key_path);
426 })?;
427
428 let mut ca_cert_pem = vec![];
429 fs::File::open(ca_cert_path)
430 .and_then(|mut file| file.read_to_end(&mut ca_cert_pem))
431 .map_err(|e| {
432 error!(err = ?e, "Failed to read {:?}", ca_cert_path);
433 })?;
434
435 let ca_key = pkey::PKey::private_key_from_pem(&ca_key_pem).map_err(|e| {
436 error!(err = ?e, "Failed to convert PEM to key");
437 })?;
438
439 check_privkey_minimums(&ca_key).map_err(|err| {
440 #[cfg(any(test, debug_assertions))]
441 println!("{:?}", err);
442 admin_error!("{}", err);
443 })?;
444
445 let ca_cert = X509::from_pem(&ca_cert_pem).map_err(|e| {
446 error!(err = ?e, "Failed to convert PEM to cert");
447 })?;
448
449 Ok(CaHandle {
450 key: ca_key,
451 cert: ca_cert,
452 })
453}
454
455pub(crate) struct CertHandle {
456 key: pkey::PKey<pkey::Private>,
457 cert: X509,
458 chain: Vec<X509>,
459}
460
461pub(crate) fn write_cert(
462 key_ar: impl AsRef<Path>,
463 chain_ar: impl AsRef<Path>,
464 cert_ar: impl AsRef<Path>,
465 handle: &CertHandle,
466) -> Result<(), ()> {
467 let key_path: &Path = key_ar.as_ref();
468 let chain_path: &Path = chain_ar.as_ref();
469 let cert_path: &Path = cert_ar.as_ref();
470
471 let key_pem = handle.key.private_key_to_pem_pkcs8().map_err(|e| {
472 error!(err = ?e, "Failed to convert key to PEM");
473 })?;
474
475 let cert_pem = handle.cert.to_pem().map_err(|e| {
476 error!(err = ?e, "Failed to convert cert to PEM");
477 })?;
478
479 let mut chain_pem = cert_pem.clone();
480
481 for ca_cert in &handle.chain {
483 match ca_cert.to_pem() {
484 Ok(c) => {
485 chain_pem.extend_from_slice(&c);
486 }
487 Err(e) => {
488 error!(err = ?e, "Failed to convert cert to PEM");
489 return Err(());
490 }
491 }
492 }
493
494 fs::File::create(key_path)
495 .and_then(|mut file| file.write_all(&key_pem))
496 .map_err(|e| {
497 error!(err = ?e, "Failed to create {:?}", key_path);
498 })?;
499
500 fs::File::create(chain_path)
501 .and_then(|mut file| file.write_all(&chain_pem))
502 .map_err(|e| {
503 error!(err = ?e, "Failed to create {:?}", chain_path);
504 })?;
505
506 fs::File::create(cert_path)
507 .and_then(|mut file| file.write_all(&cert_pem))
508 .map_err(|e| {
509 error!(err = ?e, "Failed to create {:?}", cert_path);
510 })
511}
512
513pub(crate) fn build_cert(
514 domain_name: &str,
515 ca_handle: &CaHandle,
516 key_type: Option<KeyType>,
517 key_bits: Option<u64>,
518) -> Result<CertHandle, ErrorStack> {
519 let key_type = key_type.unwrap_or_default();
520 let int_key = gen_private_key(&key_type, key_bits)?;
521
522 let mut req_builder = X509ReqBuilder::new()?;
523 req_builder.set_pubkey(&int_key)?;
524
525 let mut x509_name = X509NameBuilder::new()?;
526 x509_name.append_entry_by_text("C", "AU")?;
527 x509_name.append_entry_by_text("ST", "QLD")?;
528 x509_name.append_entry_by_text("O", "Kanidm")?;
529 x509_name.append_entry_by_text("CN", domain_name)?;
530 x509_name.append_entry_by_text("OU", "Development and Evaluation - NOT FOR PRODUCTION")?;
532 let x509_name = x509_name.build();
533
534 req_builder.set_subject_name(&x509_name)?;
535 req_builder.sign(&int_key, get_signing_func())?;
536 let req = req_builder.build();
537 let mut cert_builder = X509::builder()?;
540 cert_builder.set_version(2)?;
542 let serial_number = bn::BigNum::from_u32(2).and_then(|serial| serial.to_asn1_integer())?;
543
544 cert_builder.set_pubkey(&int_key)?;
545
546 cert_builder.set_serial_number(&serial_number)?;
547 cert_builder.set_subject_name(req.subject_name())?;
548 cert_builder.set_issuer_name(ca_handle.cert.subject_name())?;
549
550 let not_before = asn1::Asn1Time::days_from_now(0)?;
551 cert_builder.set_not_before(¬_before)?;
552 let not_after = asn1::Asn1Time::days_from_now(CERT_VALID_DAYS)?;
553 cert_builder.set_not_after(¬_after)?;
554
555 cert_builder.append_extension(BasicConstraints::new().build()?)?;
556
557 cert_builder.append_extension(
558 KeyUsage::new()
559 .critical()
560 .digital_signature()
561 .key_encipherment()
562 .build()?,
563 )?;
564
565 cert_builder.append_extension(
566 ExtendedKeyUsage::new()
567 .server_auth()
569 .build()?,
570 )?;
571
572 let subject_key_identifier = SubjectKeyIdentifier::new()
573 .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
574 cert_builder.append_extension(subject_key_identifier)?;
575
576 let auth_key_identifier = AuthorityKeyIdentifier::new()
577 .keyid(false)
578 .issuer(false)
579 .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
580 cert_builder.append_extension(auth_key_identifier)?;
581
582 let subject_alt_name = SubjectAlternativeName::new()
583 .dns(domain_name)
584 .build(&cert_builder.x509v3_context(Some(&ca_handle.cert), None))?;
585 cert_builder.append_extension(subject_alt_name)?;
586
587 cert_builder.sign(&ca_handle.key, get_signing_func())?;
588 let int_cert = cert_builder.build();
589
590 Ok(CertHandle {
591 key: int_key,
592 cert: int_cert,
593 chain: vec![ca_handle.cert.clone()],
594 })
595}
596
597#[test]
598fn test_enforced_minimums() {
600 let good_ca_configs = vec![
601 (KeyType::Rsa, 4096, false),
603 (KeyType::Rsa, 2048, false),
605 (KeyType::Ec, 256, false),
607 ];
608 good_ca_configs.into_iter().for_each(|config| {
609 dbg!(&config);
610 assert!(CAConfig::new(config.0, config.1, config.2).is_ok());
611 });
612 let bad_ca_configs = vec![
613 (KeyType::Rsa, 1024, false),
615 (KeyType::Ec, 128, false),
617 ];
618 bad_ca_configs.into_iter().for_each(|config| {
619 dbg!(&config);
620 assert!(CAConfig::new(config.0, config.1, config.2).is_err());
621 });
622}
623
624#[test]
625fn test_ca_loader() {
626 let ca_key_tempfile = tempfile::NamedTempFile::new().unwrap();
627 let ca_cert_tempfile = tempfile::NamedTempFile::new().unwrap();
628 let ca_config = CAConfig::default();
631 if let Ok(ca) = build_ca(Some(ca_config)) {
632 write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
633 assert!(load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path()).is_ok());
634 };
635
636 let good_ca_configs = vec![
637 (KeyType::Rsa, 4096, false),
639 (KeyType::Rsa, 2048, false),
641 (KeyType::Ec, 256, false),
643 ];
644 good_ca_configs.into_iter().for_each(|config| {
645 println!("testing good config {:?}", config);
646 let ca_config = CAConfig::new(config.0, config.1, config.2).unwrap();
647 let ca = build_ca(Some(ca_config)).unwrap();
648 write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
649 let ca_result = load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path());
650 println!("result: {:?}", ca_result);
651 assert!(ca_result.is_ok());
652 });
653 let bad_ca_configs = vec![
654 (KeyType::Rsa, 1024, true),
656 ];
657 bad_ca_configs.into_iter().for_each(|config| {
658 println!(
659 "\ntesting bad config keytype: {:?} key size: {}, skip_enforce_minimums: {}",
660 config.0, config.1, config.2
661 );
662 let ca_config = CAConfig::new(config.0, config.1, config.2).unwrap();
663 let ca = build_ca(Some(ca_config)).unwrap();
664 write_ca(ca_key_tempfile.path(), ca_cert_tempfile.path(), &ca).unwrap();
665 let ca_result = load_ca(ca_key_tempfile.path(), ca_cert_tempfile.path());
666 println!("result: {:?}", ca_result);
667 assert!(ca_result.is_err());
668 });
669}