Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SSL_CONF_cmd Certificate and PrivateKey commands #32

Merged
merged 1 commit into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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