Skip to content

Commit ade5191

Browse files
committed
conf: implement Certificate/PrivateKey commands
1 parent 7cf7898 commit ade5191

File tree

5 files changed

+158
-30
lines changed

5 files changed

+158
-30
lines changed

rustls-libssl/src/conf.rs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::sync::Arc;
44

55
use rustls::ProtocolVersion;
66

7+
use crate::entry::{use_cert_chain_file, use_private_key_file, FILETYPE_PEM};
78
use crate::error::Error;
89
use crate::not_thread_safe::NotThreadSafe;
910
use crate::{Ssl, SslContext, VerifyMode};
@@ -174,6 +175,51 @@ impl SslConfigCtx {
174175
})
175176
}
176177

178+
fn certificate(&mut self, path: Option<&str>) -> Result<ActionResult, Error> {
179+
let path = match path {
180+
Some(path) => path,
181+
None => return Ok(ActionResult::ValueRequired),
182+
};
183+
let cert_chain = use_cert_chain_file(path)?;
184+
185+
Ok(match &self.state {
186+
State::Validating => ActionResult::Applied,
187+
State::ApplyingToCtx(ctx) => {
188+
// the "Certificate" command after `SSL_CONF_CTX_set_ssl_ctx` is documented as using
189+
// `SSL_CTX_use_certificate_chain_file`.
190+
ctx.get_mut().stage_certificate_chain(cert_chain);
191+
ActionResult::Applied
192+
}
193+
State::ApplyingToSsl(_) => {
194+
// the "Certificate" command after `SSL_CONF_CTX_set_ssl` is documented as using
195+
// `SSL_use_certificate_file` - this is NYI for rustls-libssl.
196+
return Err(Error::not_supported(
197+
"Certificate with SSL structure not supported",
198+
));
199+
}
200+
})
201+
}
202+
203+
fn private_key(&mut self, path: Option<&str>) -> Result<ActionResult, Error> {
204+
let path = match path {
205+
Some(path) => path,
206+
None => return Ok(ActionResult::ValueRequired),
207+
};
208+
let key = use_private_key_file(path, FILETYPE_PEM)?;
209+
210+
match &self.state {
211+
State::Validating => Ok(ActionResult::Applied),
212+
State::ApplyingToCtx(ctx) => {
213+
ctx.get_mut().commit_private_key(key)?;
214+
Ok(ActionResult::Applied)
215+
}
216+
State::ApplyingToSsl(ssl) => {
217+
ssl.get_mut().commit_private_key(key)?;
218+
Ok(ActionResult::Applied)
219+
}
220+
}
221+
}
222+
177223
fn parse_protocol_version(proto: Option<&str>) -> Option<u16> {
178224
Some(match proto {
179225
Some("None") => 0,
@@ -226,8 +272,8 @@ pub(super) enum ValueType {
226272
Unknown = 0x0,
227273
/// The option value is a string without any specific structure.
228274
String = 0x1,
229-
// The option value is a filename.
230-
//File = 0x2,
275+
/// The option value is a filename.
276+
File = 0x2,
231277
// The option value is a directory name.
232278
//Dir = 0x3,
233279
// The option value is not used.
@@ -400,4 +446,18 @@ const SUPPORTED_COMMANDS: &[Command] = &[
400446
value_type: ValueType::String,
401447
action: SslConfigCtx::verify_mode,
402448
},
449+
Command {
450+
name_file: Some("Certificate"),
451+
name_cmdline: Some("cert"),
452+
flags: Flags(Flags::CERTIFICATE),
453+
value_type: ValueType::File,
454+
action: SslConfigCtx::certificate,
455+
},
456+
Command {
457+
name_file: Some("PrivateKey"),
458+
name_cmdline: Some("key"),
459+
flags: Flags(Flags::CERTIFICATE),
460+
value_type: ValueType::File,
461+
action: SslConfigCtx::private_key,
462+
},
403463
];

rustls-libssl/src/entry.rs

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -416,36 +416,42 @@ entry! {
416416
file_name: *const c_char,
417417
) -> c_int {
418418
let ctx = try_clone_arc!(ctx);
419-
let file_name = try_str!(file_name);
420-
421-
let mut file_reader = match fs::File::open(file_name) {
422-
Ok(content) => io::BufReader::new(content),
423-
Err(err) => return Error::from_io(err).raise().into(),
419+
let chain = match use_cert_chain_file(try_str!(file_name)) {
420+
Ok(chain) => chain,
421+
Err(err) => return err.raise().into(),
424422
};
425423

426-
let mut chain = Vec::new();
424+
ctx.get_mut().stage_certificate_chain(chain);
425+
C_INT_SUCCESS
426+
}
427+
}
427428

428-
for cert in rustls_pemfile::certs(&mut file_reader) {
429-
let cert = match cert {
430-
Ok(cert) => cert,
431-
Err(err) => {
432-
log::trace!("Failed to parse {file_name:?}: {err:?}");
433-
return Error::from_io(err).raise().into();
434-
}
435-
};
429+
pub(crate) fn use_cert_chain_file(file_name: &str) -> Result<Vec<CertificateDer<'static>>, Error> {
430+
let mut file_reader = match fs::File::open(file_name) {
431+
Ok(content) => io::BufReader::new(content),
432+
Err(err) => return Err(Error::from_io(err)),
433+
};
436434

437-
match OwnedX509::parse_der(cert.as_ref()) {
438-
Some(_) => chain.push(cert),
439-
None => {
440-
log::trace!("Failed to parse DER certificate");
441-
return Error::bad_data("certificate").raise().into();
442-
}
435+
let mut chain = Vec::new();
436+
for cert in rustls_pemfile::certs(&mut file_reader) {
437+
let cert = match cert {
438+
Ok(cert) => cert,
439+
Err(err) => {
440+
log::trace!("Failed to parse {file_name:?}: {err:?}");
441+
return Err(Error::from_io(err));
443442
}
444-
}
443+
};
445444

446-
ctx.get_mut().stage_certificate_chain(chain);
447-
C_INT_SUCCESS
445+
match OwnedX509::parse_der(cert.as_ref()) {
446+
Some(_) => chain.push(cert),
447+
None => {
448+
log::trace!("Failed to parse DER certificate");
449+
return Err(Error::bad_data("certificate"));
450+
}
451+
}
448452
}
453+
454+
Ok(chain)
449455
}
450456

451457
entry! {
@@ -464,7 +470,7 @@ entry! {
464470
}
465471
}
466472

467-
fn use_private_key_file(file_name: &str, file_type: c_int) -> Result<EvpPkey, Error> {
473+
pub(crate) fn use_private_key_file(file_name: &str, file_type: c_int) -> Result<EvpPkey, Error> {
468474
let der_data = match file_type {
469475
FILETYPE_PEM => {
470476
let mut file_reader = match fs::File::open(file_name) {
@@ -1806,7 +1812,7 @@ impl Castable for SSL_CONF_CTX {
18061812
/// Compare [`crate::ffi::MysteriouslyOppositeReturnValue`].
18071813
const C_INT_SUCCESS: c_int = 1;
18081814

1809-
const FILETYPE_PEM: c_int = 1;
1815+
pub(crate) const FILETYPE_PEM: c_int = 1;
18101816
const FILETYPE_DER: c_int = 2;
18111817

18121818
const SSL_MAX_SID_CTX_LENGTH: usize = 32;

rustls-libssl/tests/config.c

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ static const char *supported_cmds[] = {
2222
"MaxProtocol", CUSTOM_PREFIX "MaxProtocol",
2323

2424
"VerifyMode", CUSTOM_PREFIX "VerifyMode",
25-
};
25+
26+
"-cert", CUSTOM_PREFIX "cert",
27+
"Certificate", CUSTOM_PREFIX "Certificate",
28+
29+
"-key", CUSTOM_PREFIX "key",
30+
"PrivateKey", CUSTOM_PREFIX "PrivateKey"};
2631

2732
#define NUM_SUPPORTED_CMDS (sizeof(supported_cmds) / sizeof(supported_cmds[0]))
2833

@@ -230,6 +235,54 @@ void test_verify_mode(void) {
230235
SSL_free(ssl);
231236
}
232237

238+
void set_cert_and_key(SSL_CONF_CTX *cctx) {
239+
// Note: we don't test invalid values here - our implementation diverges
240+
// slightly due to early processing of the cert/key pair.
241+
printf("\t\tcmd Certificate NULL returns %d\n",
242+
SSL_CONF_cmd(cctx, "Certificate", NULL));
243+
printf("\t\tcmd Certificate 'test-ca/rsa/server.cert' returns %d\n",
244+
SSL_CONF_cmd(cctx, "Certificate", "test-ca/rsa/server.cert"));
245+
246+
printf("\t\tcmd PrivateKey NULL returns %d\n",
247+
SSL_CONF_cmd(cctx, "PrivateKey", NULL));
248+
printf("\t\tcmd PrivateKey 'test-ca/rsa/server.key' returns %d\n",
249+
SSL_CONF_cmd(cctx, "PrivateKey", "test-ca/rsa/server.key"));
250+
}
251+
252+
void test_certificate_and_private_key(void) {
253+
SSL_CONF_CTX *cctx = SSL_CONF_CTX_new();
254+
assert(cctx != NULL);
255+
256+
SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE);
257+
printf("\tPre-ctx (not certificate flag):\n");
258+
set_cert_and_key(cctx);
259+
260+
printf("\tPre-ctx (certificate flag):\n");
261+
SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
262+
set_cert_and_key(cctx);
263+
SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
264+
265+
SSL_CTX *ctx = SSL_CTX_new(TLS_method());
266+
assert(ctx != NULL);
267+
SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);
268+
269+
printf("\tWith ctx (not certificate flag):\n");
270+
set_cert_and_key(cctx);
271+
272+
printf("\tWith ctx (certificate flag):\n");
273+
SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
274+
set_cert_and_key(cctx);
275+
SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
276+
277+
// Note: we do not test with `SSL_CONF_set_ssl()` here - we lack
278+
// support for the `Certificate` command updating an `SSL`
279+
// struct at this time.
280+
281+
assert(SSL_CONF_CTX_finish(cctx));
282+
SSL_CONF_CTX_free(cctx);
283+
SSL_CTX_free(ctx);
284+
}
285+
233286
int main(void) {
234287
printf("Supported commands:\n");
235288
printf("no base flags, default prefix:\n");
@@ -255,4 +308,7 @@ int main(void) {
255308

256309
printf("VerifyMode:\n");
257310
test_verify_mode();
311+
312+
printf("Certificate/PrivateKey:\n");
313+
test_certificate_and_private_key();
258314
}

rustls-libssl/tests/nginx_1_24.conf

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ http {
1313
server {
1414
# Custom configuration w/ ssl_conf_command:
1515
# * TLS 1.3 or greater only
16+
# * Certificate override of ssl_certificate
17+
# * PrivateKey override of ssl_certificate_key
1618
listen 8447 ssl;
17-
ssl_certificate ../../../test-ca/rsa/server.cert;
18-
ssl_certificate_key ../../../test-ca/rsa/server.key;
19+
ssl_certificate ../../../test-ca/ed25519/server.cert;
20+
ssl_certificate_key ../../../test-ca/ed25519/server.key;
1921
server_name localhost;
2022

23+
ssl_conf_command Certificate ../../../test-ca/rsa/server.cert;
24+
ssl_conf_command PrivateKey ../../../test-ca/rsa/server.key;
2125
ssl_conf_command MinProtocol TLSv1.3;
2226

2327
location = / {

rustls-libssl/tests/runner.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,8 @@ fn nginx_1_24() {
613613
35
614614
);
615615
// TLS 1.3 to the TLS 1.3 only port should succeed.
616+
// The RSA CA cert should allow verification to succeed, showing the overrides of
617+
// the ED25519 ssl_certificate/ssl_certificate_key directives worked.
616618
assert_eq!(
617619
Command::new("curl")
618620
.env("LD_LIBRARY_PATH", "")

0 commit comments

Comments
 (0)