Skip to content

Commit

Permalink
conf: implement Certificate/PrivateKey commands
Browse files Browse the repository at this point in the history
  • Loading branch information
cpu committed Jun 25, 2024
1 parent 1bcfb4f commit 57024f4
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 30 deletions.
64 changes: 62 additions & 2 deletions rustls-libssl/src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::sync::Arc;

use rustls::ProtocolVersion;

use crate::entry::{use_cert_chain_file, use_private_key_file, FILETYPE_PEM};
use crate::error::Error;
use crate::not_thread_safe::NotThreadSafe;
use crate::{Ssl, SslContext, VerifyMode};
Expand Down Expand Up @@ -174,6 +175,51 @@ impl SslConfigCtx {
})
}

fn certificate(&mut self, path: Option<&str>) -> Result<ActionResult, Error> {
let path = match path {
Some(path) => path,
None => return Ok(ActionResult::ValueRequired),
};
let cert_chain = use_cert_chain_file(path)?;

Ok(match &self.state {
State::Validating => ActionResult::Applied,
State::ApplyingToCtx(ctx) => {
// the "Certificate" command after `SSL_CONF_CTX_set_ssl_ctx` is documented as using
// `SSL_CTX_use_certificate_chain_file`.
ctx.get_mut().stage_certificate_chain(cert_chain);
ActionResult::Applied
}
State::ApplyingToSsl(_) => {
// the "Certificate" command after `SSL_CONF_CTX_set_ssl` is documented as using
// `SSL_use_certificate_file` - this is NYI for rustls-libssl.
return Err(Error::not_supported(
"Certificate with SSL structure not supported",
));
}
})
}

fn private_key(&mut self, path: Option<&str>) -> Result<ActionResult, Error> {
let path = match path {
Some(path) => path,
None => return Ok(ActionResult::ValueRequired),
};
let key = use_private_key_file(path, FILETYPE_PEM)?;

match &self.state {
State::Validating => Ok(ActionResult::Applied),
State::ApplyingToCtx(ctx) => {
ctx.get_mut().commit_private_key(key)?;
Ok(ActionResult::Applied)
}
State::ApplyingToSsl(ssl) => {
ssl.get_mut().commit_private_key(key)?;
Ok(ActionResult::Applied)
}
}
}

fn parse_protocol_version(proto: Option<&str>) -> Option<u16> {
Some(match proto {
Some("None") => 0,
Expand Down Expand Up @@ -226,8 +272,8 @@ pub(super) enum ValueType {
Unknown = 0x0,
/// The option value is a string without any specific structure.
String = 0x1,
// The option value is a filename.
//File = 0x2,
/// The option value is a filename.
File = 0x2,
// The option value is a directory name.
//Dir = 0x3,
// The option value is not used.
Expand Down Expand Up @@ -400,4 +446,18 @@ const SUPPORTED_COMMANDS: &[Command] = &[
value_type: ValueType::String,
action: SslConfigCtx::verify_mode,
},
Command {
name_file: Some("Certificate"),
name_cmdline: Some("cert"),
flags: Flags(Flags::CERTIFICATE),
value_type: ValueType::File,
action: SslConfigCtx::certificate,
},
Command {
name_file: Some("PrivateKey"),
name_cmdline: Some("key"),
flags: Flags(Flags::CERTIFICATE),
value_type: ValueType::File,
action: SslConfigCtx::private_key,
},
];
56 changes: 31 additions & 25 deletions rustls-libssl/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,36 +416,42 @@ entry! {
file_name: *const c_char,
) -> c_int {
let ctx = try_clone_arc!(ctx);
let file_name = try_str!(file_name);

let mut file_reader = match fs::File::open(file_name) {
Ok(content) => io::BufReader::new(content),
Err(err) => return Error::from_io(err).raise().into(),
let chain = match use_cert_chain_file(try_str!(file_name)) {
Ok(chain) => chain,
Err(err) => return err.raise().into(),
};

let mut chain = Vec::new();
ctx.get_mut().stage_certificate_chain(chain);
C_INT_SUCCESS
}
}

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

match OwnedX509::parse_der(cert.as_ref()) {
Some(_) => chain.push(cert),
None => {
log::trace!("Failed to parse DER certificate");
return Error::bad_data("certificate").raise().into();
}
let mut chain = Vec::new();
for cert in rustls_pemfile::certs(&mut file_reader) {
let cert = match cert {
Ok(cert) => cert,
Err(err) => {
log::trace!("Failed to parse {file_name:?}: {err:?}");
return Err(Error::from_io(err));
}
}
};

ctx.get_mut().stage_certificate_chain(chain);
C_INT_SUCCESS
match OwnedX509::parse_der(cert.as_ref()) {
Some(_) => chain.push(cert),
None => {
log::trace!("Failed to parse DER certificate");
return Err(Error::bad_data("certificate"));
}
}
}

Ok(chain)
}

entry! {
Expand All @@ -464,7 +470,7 @@ entry! {
}
}

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

const FILETYPE_PEM: c_int = 1;
pub(crate) const FILETYPE_PEM: c_int = 1;
const FILETYPE_DER: c_int = 2;

const SSL_MAX_SID_CTX_LENGTH: usize = 32;
Expand Down
58 changes: 57 additions & 1 deletion rustls-libssl/tests/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ static const char *supported_cmds[] = {
"MaxProtocol", CUSTOM_PREFIX "MaxProtocol",

"VerifyMode", CUSTOM_PREFIX "VerifyMode",
};

"-cert", CUSTOM_PREFIX "cert",
"Certificate", CUSTOM_PREFIX "Certificate",

"-key", CUSTOM_PREFIX "key",
"PrivateKey", CUSTOM_PREFIX "PrivateKey"};

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

Expand Down Expand Up @@ -230,6 +235,54 @@ void test_verify_mode(void) {
SSL_free(ssl);
}

void set_cert_and_key(SSL_CONF_CTX *cctx) {
// Note: we don't test invalid values here - our implementation diverges
// slightly due to early processing of the cert/key pair.
printf("\t\tcmd Certificate NULL returns %d\n",
SSL_CONF_cmd(cctx, "Certificate", NULL));
printf("\t\tcmd Certificate 'test-ca/rsa/server.cert' returns %d\n",
SSL_CONF_cmd(cctx, "Certificate", "test-ca/rsa/server.cert"));

printf("\t\tcmd PrivateKey NULL returns %d\n",
SSL_CONF_cmd(cctx, "PrivateKey", NULL));
printf("\t\tcmd PrivateKey 'test-ca/rsa/server.key' returns %d\n",
SSL_CONF_cmd(cctx, "PrivateKey", "test-ca/rsa/server.key"));
}

void test_certificate_and_private_key(void) {
SSL_CONF_CTX *cctx = SSL_CONF_CTX_new();
assert(cctx != NULL);

SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE);
printf("\tPre-ctx (not certificate flag):\n");
set_cert_and_key(cctx);

printf("\tPre-ctx (certificate flag):\n");
SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
set_cert_and_key(cctx);
SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);

SSL_CTX *ctx = SSL_CTX_new(TLS_method());
assert(ctx != NULL);
SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);

printf("\tWith ctx (not certificate flag):\n");
set_cert_and_key(cctx);

printf("\tWith ctx (certificate flag):\n");
SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);
set_cert_and_key(cctx);
SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);

// Note: we do not test with `SSL_CONF_set_ssl()` here - we lack
// support for the `Certificate` command updating an `SSL`
// struct at this time.

assert(SSL_CONF_CTX_finish(cctx));
SSL_CONF_CTX_free(cctx);
SSL_CTX_free(ctx);
}

int main(void) {
printf("Supported commands:\n");
printf("no base flags, default prefix:\n");
Expand All @@ -255,4 +308,7 @@ int main(void) {

printf("VerifyMode:\n");
test_verify_mode();

printf("Certificate/PrivateKey:\n");
test_certificate_and_private_key();
}
8 changes: 6 additions & 2 deletions rustls-libssl/tests/nginx_1_24.conf
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ http {
server {
# Custom configuration w/ ssl_conf_command:
# * TLS 1.3 or greater only
# * Certificate override of ssl_certificate
# * PrivateKey override of ssl_certificate_key
listen 8447 ssl;
ssl_certificate ../../../test-ca/rsa/server.cert;
ssl_certificate_key ../../../test-ca/rsa/server.key;
ssl_certificate ../../../test-ca/ed25519/server.cert;
ssl_certificate_key ../../../test-ca/ed25519/server.key;
server_name localhost;

ssl_conf_command Certificate ../../../test-ca/rsa/server.cert;
ssl_conf_command PrivateKey ../../../test-ca/rsa/server.key;
ssl_conf_command MinProtocol TLSv1.3;

location = / {
Expand Down
2 changes: 2 additions & 0 deletions rustls-libssl/tests/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,8 @@ fn nginx_1_24() {
35
);
// TLS 1.3 to the TLS 1.3 only port should succeed.
// The RSA CA cert should allow verification to succeed, showing the overrides of
// the ED25519 ssl_certificate/ssl_certificate_key directives worked.
assert_eq!(
Command::new("curl")
.env("LD_LIBRARY_PATH", "")
Expand Down

0 comments on commit 57024f4

Please sign in to comment.