(path: &Path)
| 543 | } |
| 544 | |
| 545 | fn server_cert_sans(path: &Path) -> Result<BTreeSet<CertSan>> { |
| 546 | use x509_parser::pem::parse_x509_pem; |
| 547 | use x509_parser::prelude::{FromDer, GeneralName, X509Certificate}; |
| 548 | |
| 549 | let pem = std::fs::read(path) |
| 550 | .into_diagnostic() |
| 551 | .wrap_err_with(|| format!("failed to read {}", path.display()))?; |
| 552 | let (_, pem) = parse_x509_pem(&pem).map_err(|e| { |
| 553 | miette::miette!( |
| 554 | "failed to parse server certificate PEM {}: {e:?}", |
| 555 | path.display() |
| 556 | ) |
| 557 | })?; |
| 558 | let (_, cert) = X509Certificate::from_der(&pem.contents).map_err(|e| { |
| 559 | miette::miette!( |
| 560 | "failed to parse server certificate {}: {e:?}", |
| 561 | path.display() |
| 562 | ) |
| 563 | })?; |
| 564 | |
| 565 | let Some(ext) = cert.subject_alternative_name().map_err(|e| { |
| 566 | miette::miette!( |
| 567 | "failed to read server certificate SANs {}: {e:?}", |
| 568 | path.display() |
| 569 | ) |
| 570 | })? |
| 571 | else { |
| 572 | return Ok(BTreeSet::new()); |
| 573 | }; |
| 574 | |
| 575 | let mut sans = BTreeSet::new(); |
| 576 | for name in &ext.value.general_names { |
| 577 | match name { |
| 578 | GeneralName::DNSName(name) => { |
| 579 | sans.insert(CertSan::Dns((*name).to_string())); |
| 580 | } |
| 581 | GeneralName::IPAddress(raw) => match raw.len() { |
| 582 | 4 => { |
| 583 | sans.insert(CertSan::Ip(IpAddr::V4(Ipv4Addr::new( |
| 584 | raw[0], raw[1], raw[2], raw[3], |
| 585 | )))); |
| 586 | } |
| 587 | 16 => { |
| 588 | let octets: [u8; 16] = (*raw).try_into().expect("checked IPv6 SAN length"); |
| 589 | sans.insert(CertSan::Ip(IpAddr::V6(Ipv6Addr::from(octets)))); |
| 590 | } |
| 591 | _ => {} |
| 592 | }, |
| 593 | _ => {} |
| 594 | } |
| 595 | } |
| 596 | Ok(sans) |
| 597 | } |
| 598 | |
| 599 | fn format_cert_sans(sans: &[CertSan]) -> String { |
| 600 | sans.iter() |
no test coverage detected