Skip to content

Commit c1548c1

Browse files
committed
Handle all orders of cert/key configuration API use
`haproxy` configures its cert/key using this order: - `SSL_CTX_use_PrivateKey` - `SSL_CTX_use_certificate` - `SSL_CTX_set1_chain` Which is contrary to the OpenSSL documentation, which says: > To change a [certificate/private-key] pair, the new certificate needs to be set first with > SSL_use_certificate() or SSL_CTX_use_certificate() before setting the private key with > SSL_CTX_use_PrivateKey() or SSL_use_PrivateKey(). Our previous implementation followed this ordering. This commit also prepares the ground for certificate switching by type.
1 parent eb4304a commit c1548c1

1 file changed

Lines changed: 65 additions & 36 deletions

File tree

src/sign.rs

Lines changed: 65 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,7 @@ use crate::x509::OwnedX509Stack;
1919
/// and `SSL_CTX_use_PrivateKey_file`, and matching man pages.
2020
#[derive(Clone, Default, Debug)]
2121
pub struct CertifiedKeySet {
22-
/// Last `SSL_CTX_use_certificate_chain_file` result, pending a matching
23-
/// `SSL_CTX_use_PrivateKey_file`.
24-
pending_cert_chain: Option<Vec<CertificateDer<'static>>>,
25-
26-
/// Last `SSL_CTX_use_certificate` result, prepended to chain during commit.
27-
/// May be absent.
28-
pending_cert_end_entity: Option<CertificateDer<'static>>,
29-
30-
/// The key and certificate we're currently using.
31-
///
32-
/// TODO: support multiple key types, and demultiplex them by type.
33-
current_key: Option<OpenSslCertifiedKey>,
22+
item: KeySetItem,
3423
}
3524

3625
impl CertifiedKeySet {
@@ -58,63 +47,103 @@ impl CertifiedKeySet {
5847
&mut self,
5948
chain: Vec<CertificateDer<'static>>,
6049
) -> Result<(), error::Error> {
61-
self.pending_cert_chain = Some(chain);
50+
self.item.adopt_chain_tail(Some(chain));
6251
Ok(())
6352
}
6453

6554
pub fn stage_certificate_end_entity(
6655
&mut self,
6756
end: CertificateDer<'static>,
6857
) -> Result<(), error::Error> {
69-
self.pending_cert_end_entity = Some(end);
70-
Ok(())
58+
self.item.cert_end_entity = Some(end);
59+
self.item.promote()
7160
}
7261

7362
pub fn commit_private_key(&mut self, key: EvpPkey) -> Result<(), error::Error> {
74-
let chain = match (
75-
self.pending_cert_end_entity.take(),
76-
self.pending_cert_chain.take(),
77-
) {
78-
(Some(end_entity), Some(mut chain)) => {
79-
chain.insert(0, end_entity);
80-
chain
81-
}
82-
(None, Some(chain)) => chain,
83-
(Some(end_entity), None) => vec![end_entity],
84-
(None, None) => {
85-
return Err(error::Error::bad_data("no certificate found for key"));
86-
}
87-
};
88-
89-
self.current_key = Some(OpenSslCertifiedKey::new(chain, key)?);
90-
Ok(())
63+
self.item.key = Some(key);
64+
self.item.promote()
9165
}
9266

9367
pub fn client_resolver(&self) -> Option<Arc<dyn ResolvesClientCert>> {
94-
self.current_key.as_ref().map(|ck| ck.client_resolver())
68+
self.item
69+
.constructed
70+
.as_ref()
71+
.map(|ck| ck.client_resolver())
9572
}
9673

9774
pub fn server_resolver(&self) -> Option<Arc<dyn ResolvesServerCert>> {
98-
self.current_key.as_ref().map(|ck| ck.server_resolver())
75+
self.item
76+
.constructed
77+
.as_ref()
78+
.map(|ck| ck.server_resolver())
9979
}
10080

10181
/// For `SSL_get_certificate`
10282
pub fn borrow_current_cert(&self) -> *mut X509 {
103-
self.current_key
83+
self.item
84+
.constructed
10485
.as_ref()
10586
.map(|ck| ck.borrow_cert())
10687
.unwrap_or(ptr::null_mut())
10788
}
10889

10990
/// For `SSL_get_privatekey`
11091
pub fn borrow_current_key(&self) -> *mut EVP_PKEY {
111-
self.current_key
92+
self.item
93+
.constructed
11294
.as_ref()
11395
.map(|ck| ck.borrow_key())
11496
.unwrap_or(ptr::null_mut())
11597
}
11698
}
11799

100+
#[derive(Clone, Debug, Default)]
101+
pub struct KeySetItem {
102+
/// Most recent certificate chain tail.
103+
cert_chain_tail: Option<Vec<CertificateDer<'static>>>,
104+
105+
/// Most recent end-entity certificate.
106+
cert_end_entity: Option<CertificateDer<'static>>,
107+
108+
/// Most recent value from `SSL_CTX_use_PrivateKey_file`
109+
key: Option<EvpPkey>,
110+
111+
/// The key and certificate we're currently using.
112+
///
113+
/// This is constructed eagerly to validate the cert/key are consistent.
114+
constructed: Option<OpenSslCertifiedKey>,
115+
}
116+
117+
impl KeySetItem {
118+
fn adopt_chain_tail(&mut self, cert_chain_tail: Option<Vec<CertificateDer<'static>>>) {
119+
if let Some(tail) = cert_chain_tail {
120+
self.cert_chain_tail = Some(tail);
121+
}
122+
}
123+
124+
/// If `self` has enough parts (a key and at least an end-entity cert) then fill in
125+
/// `constructed`.
126+
fn promote(&mut self) -> Result<(), error::Error> {
127+
let Some(key) = &self.key else {
128+
return Ok(());
129+
};
130+
131+
// Reconstitute full chain from parts.
132+
let chain = match (&self.cert_end_entity, &self.cert_chain_tail) {
133+
(Some(end_entity), Some(tail)) => {
134+
let mut chain = tail.clone();
135+
chain.insert(0, end_entity.clone());
136+
chain
137+
}
138+
(Some(end_entity), None) => vec![end_entity.clone()],
139+
_ => return Ok(()),
140+
};
141+
142+
self.constructed = Some(OpenSslCertifiedKey::new(chain, key.clone())?);
143+
Ok(())
144+
}
145+
}
146+
118147
#[derive(Clone, Debug)]
119148
pub(super) struct OpenSslCertifiedKey {
120149
key: EvpPkey,

0 commit comments

Comments
 (0)