Source code

Revision control

Copy as Markdown

Other Tools

// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::ptr::{addr_of, NonNull};
use neqo_common::qerror;
use crate::{
err::secstatus_to_res,
null_safe_slice,
p11::{CERTCertListNode, CERT_GetCertificateDer, CertList, Item, SECItem, SECItemArray},
ssl::{
PRFileDesc, SSL_PeerCertificateChain, SSL_PeerSignedCertTimestamps,
SSL_PeerStapledOCSPResponses,
},
};
pub struct CertificateInfo {
certs: CertList,
cursor: *const CERTCertListNode,
/// `stapled_ocsp_responses` and `signed_cert_timestamp` are properties
/// associated with each of the certificates. Right now, NSS only
/// reports the value for the end-entity certificate (the first).
stapled_ocsp_responses: Option<Vec<Vec<u8>>>,
signed_cert_timestamp: Option<Vec<u8>>,
}
fn peer_certificate_chain(fd: *mut PRFileDesc) -> Option<(CertList, *const CERTCertListNode)> {
let chain = unsafe { SSL_PeerCertificateChain(fd) };
CertList::from_ptr(chain.cast()).ok().map(|certs| {
let cursor = CertificateInfo::head(&certs);
(certs, cursor)
})
}
// As explained in rfc6961, an OCSPResponseList can have at most
// 2^24 items. Casting its length is therefore safe even on 32 bits targets.
fn stapled_ocsp_responses(fd: *mut PRFileDesc) -> Option<Vec<Vec<u8>>> {
let ocsp_nss = unsafe { SSL_PeerStapledOCSPResponses(fd) };
match NonNull::new(ocsp_nss as *mut SECItemArray) {
Some(ocsp_ptr) => {
let mut ocsp_helper: Vec<Vec<u8>> = Vec::new();
let Ok(len) = isize::try_from(unsafe { ocsp_ptr.as_ref().len }) else {
qerror!([format!("{fd:p}")], "Received illegal OSCP length");
return None;
};
for idx in 0..len {
let itemp: *const SECItem = unsafe { ocsp_ptr.as_ref().items.offset(idx).cast() };
let item = unsafe { null_safe_slice((*itemp).data, (*itemp).len) };
ocsp_helper.push(item.to_owned());
}
Some(ocsp_helper)
}
None => None,
}
}
fn signed_cert_timestamp(fd: *mut PRFileDesc) -> Option<Vec<u8>> {
let sct_nss = unsafe { SSL_PeerSignedCertTimestamps(fd) };
match NonNull::new(sct_nss as *mut SECItem) {
Some(sct_ptr) => {
if unsafe { sct_ptr.as_ref().len == 0 || sct_ptr.as_ref().data.is_null() } {
Some(Vec::new())
} else {
let sct_slice =
unsafe { null_safe_slice(sct_ptr.as_ref().data, sct_ptr.as_ref().len) };
Some(sct_slice.to_owned())
}
}
None => None,
}
}
impl CertificateInfo {
pub(crate) fn new(fd: *mut PRFileDesc) -> Option<Self> {
peer_certificate_chain(fd).map(|(certs, cursor)| Self {
certs,
cursor,
stapled_ocsp_responses: stapled_ocsp_responses(fd),
signed_cert_timestamp: signed_cert_timestamp(fd),
})
}
fn head(certs: &CertList) -> *const CERTCertListNode {
// Three stars: one for the reference, one for the wrapper, one to deference the pointer.
unsafe { addr_of!((***certs).list).cast() }
}
}
impl<'a> Iterator for &'a mut CertificateInfo {
type Item = &'a [u8];
fn next(&mut self) -> Option<&'a [u8]> {
self.cursor = unsafe { *self.cursor }.links.next.cast();
if self.cursor == CertificateInfo::head(&self.certs) {
return None;
}
let mut item = Item::make_empty();
let cert = unsafe { *self.cursor }.cert;
secstatus_to_res(unsafe { CERT_GetCertificateDer(cert, &mut item) })
.expect("getting DER from certificate should work");
Some(unsafe { null_safe_slice(item.data, item.len) })
}
}
impl CertificateInfo {
pub fn stapled_ocsp_responses(&mut self) -> &Option<Vec<Vec<u8>>> {
&self.stapled_ocsp_responses
}
pub fn signed_cert_timestamp(&mut self) -> &Option<Vec<u8>> {
&self.signed_cert_timestamp
}
}