1use tonic::Request;
2use tonic::transport::server::{TcpConnectInfo, TlsConnectInfo};
3use x509_parser::oid_registry::Oid;
4use x509_parser::parse_x509_certificate;
5use x509_parser::x509::X509Name;
6
7use crate::Error;
8
9#[derive(Clone, Debug, Eq, PartialEq)]
11pub struct Application {
12 pub app_id: String,
14 pub box_id: String,
16 pub app_domain: String,
18}
19
20pub fn peer_application<T>(request: &Request<T>) -> Result<Application, Error> {
27 let certificates = request
28 .extensions()
29 .get::<TlsConnectInfo<TcpConnectInfo>>()
30 .and_then(TlsConnectInfo::peer_certs);
31 #[cfg(unix)]
32 let certificates = certificates.or_else(|| {
33 request
34 .extensions()
35 .get::<TlsConnectInfo<tonic::transport::server::UdsConnectInfo>>()
36 .and_then(TlsConnectInfo::peer_certs)
37 });
38 let certificates = certificates.ok_or(Error::UnauthenticatedPeer)?;
39 application_from_certificates(certificates.as_ref())
40}
41
42fn application_from_certificates(
43 certificates: &[rustls::pki_types::CertificateDer<'static>],
44) -> Result<Application, Error> {
45 if certificates.len() != 1 {
46 return Err(Error::InvalidPeerCertificateCount {
47 count: certificates.len(),
48 });
49 }
50 let (_, certificate) = parse_x509_certificate(certificates[0].as_ref())
51 .map_err(|_| Error::InvalidPeerCertificate)?;
52 Ok(Application {
53 app_id: serial_number(certificate.subject()),
54 box_id: serial_number(certificate.issuer()),
55 app_domain: certificate
56 .subject()
57 .iter_common_name()
58 .next()
59 .and_then(|name| name.as_str().ok())
60 .unwrap_or_default()
61 .to_owned(),
62 })
63}
64
65fn serial_number(name: &X509Name<'_>) -> String {
66 let oid = Oid::from(&[2, 5, 4, 5]).expect("serialNumber OID is valid");
67 name.iter_by_oid(&oid)
68 .next()
69 .and_then(|value| value.as_str().ok())
70 .unwrap_or_default()
71 .to_owned()
72}
73
74#[cfg(test)]
75mod tests {
76 use rustls::pki_types::CertificateDer;
77
78 use crate::Error;
79
80 use super::application_from_certificates;
81
82 #[test]
83 fn rejects_zero_or_multiple_peer_certificates() {
84 let zero = application_from_certificates(&[]).expect_err("zero certificates");
85 assert!(matches!(
86 zero,
87 Error::InvalidPeerCertificateCount { count: 0 }
88 ));
89
90 let invalid = CertificateDer::from(vec![0_u8]);
91 let multiple = application_from_certificates(&[invalid.clone(), invalid])
92 .expect_err("multiple certificates");
93 assert!(matches!(
94 multiple,
95 Error::InvalidPeerCertificateCount { count: 2 }
96 ));
97 }
98}