diff --git a/rustls-libssl/MATRIX.md b/rustls-libssl/MATRIX.md index 8a0ad77..950adf5 100644 --- a/rustls-libssl/MATRIX.md +++ b/rustls-libssl/MATRIX.md @@ -379,7 +379,7 @@ | `SSL_get_state` | | | :white_check_mark: | | `SSL_get_verify_callback` | | | | | `SSL_get_verify_depth` | | | :white_check_mark: | -| `SSL_get_verify_mode` | | | | +| `SSL_get_verify_mode` | | | :white_check_mark: | | `SSL_get_verify_result` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_get_version` | :white_check_mark: | :white_check_mark: | :white_check_mark: | | `SSL_get_wbio` | | :white_check_mark: | :white_check_mark: | diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 1776576..3f44ac0 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -61,6 +61,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_clear_options", "SSL_CONF_cmd", "SSL_CONF_cmd_value_type", + "SSL_CONF_CTX_clear_flags", "SSL_CONF_CTX_finish", "SSL_CONF_CTX_free", "SSL_CONF_CTX_new", @@ -155,6 +156,7 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_get_SSL_CTX", "SSL_get_state", "SSL_get_verify_depth", + "SSL_get_verify_mode", "SSL_get_verify_result", "SSL_get_version", "SSL_get_wbio", diff --git a/rustls-libssl/src/conf.rs b/rustls-libssl/src/conf.rs index 98bbf60..6b4893b 100644 --- a/rustls-libssl/src/conf.rs +++ b/rustls-libssl/src/conf.rs @@ -6,7 +6,7 @@ use rustls::ProtocolVersion; use crate::error::Error; use crate::not_thread_safe::NotThreadSafe; -use crate::{Ssl, SslContext}; +use crate::{Ssl, SslContext, VerifyMode}; #[derive(Default)] pub(super) struct SslConfigCtx { @@ -123,6 +123,57 @@ impl SslConfigCtx { }) } + fn verify_mode(&mut self, raw_mode: Option<&str>) -> Result { + // If there's a SSL_CTX or SSL set then we want to mutate the existing verify_mode. + let mut verify_mode = match &self.state { + State::Validating => VerifyMode::default(), + State::ApplyingToSsl(ssl) => ssl.get().verify_mode, + State::ApplyingToCtx(ctx) => ctx.get().verify_mode, + }; + + let raw_mode = match raw_mode { + Some(raw_mode) => raw_mode, + None => return Ok(ActionResult::ValueRequired), + }; + + if !self.flags.is_server() && !self.flags.is_client() { + return Err(Error::bad_data("neither a client or a server")); + } + + // "The value argument is a comma separated list of flags to set." + let mode_parts = raw_mode.split(',').collect::>(); + for part in mode_parts { + match (part, self.flags.is_server()) { + // "Peer enables peer verification: for clients only." + ("Peer", false) => verify_mode.0 |= VerifyMode::PEER, + // "Request requests but does not require a certificate from the client. Servers only." + ("Request", true) => verify_mode.0 |= VerifyMode::PEER, + // "Require requests and requires a certificate from the client: an error occurs if the + // client does not present a certificate. Servers only." + ("Require", true) => { + verify_mode.0 |= VerifyMode::PEER | VerifyMode::FAIL_IF_NO_PEER_CERT + } + // We do not implement Once, RequestPostHandshake and RequiresPostHandshake, + // but don't error if provided. + ("Once" | "RequestPostHandshake" | "RequirePostHandshake", _) => {} + _ => return Err(Error::bad_data("unrecognized verify mode")), + } + } + + Ok(match &self.state { + // OpenSSL returns "2" for this case... + State::Validating => ActionResult::Applied, + State::ApplyingToCtx(ctx) => { + ctx.get_mut().set_verify(verify_mode); + ActionResult::Applied + } + State::ApplyingToSsl(ssl) => { + ssl.get_mut().set_verify(verify_mode); + ActionResult::Applied + } + }) + } + fn parse_protocol_version(proto: Option<&str>) -> Option { Some(match proto { Some("None") => 0, @@ -170,21 +221,17 @@ impl SslConfigCtx { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(i32)] -#[allow(dead_code)] // TODO(XXX): Remove once other value types are used. pub(super) enum ValueType { /// The option is unrecognized. 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 directory name. - Dir = 0x3, - /// The option value is not used. - None = 0x4, - /// The option is an X509 store - // NOTE(XXX): This one is missing from the OpenSSL man pages. - Store = 0x5, + // The option value is a filename. + //File = 0x2, + // The option value is a directory name. + //Dir = 0x3, + // The option value is not used. + //None = 0x4, } impl From for c_int { @@ -259,12 +306,14 @@ type CommandAction = fn(&mut SslConfigCtx, value: Option<&str>) -> Result for c_int { #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] pub(super) struct Flags(c_uint); -#[allow(dead_code)] // TODO(XXX): Remove once all flags are used. impl Flags { const ANY: c_uint = 0x0; @@ -290,6 +338,7 @@ impl Flags { const SERVER: c_uint = 0x8; const SHOW_ERRORS: c_uint = 0x10; const CERTIFICATE: c_uint = 0x20; + #[allow(dead_code)] // TODO(XXX): Remove once REQUIRE_PRIVATE is used. const REQUIRE_PRIVATE: c_uint = 0x40; fn is_cmdline(&self) -> bool { @@ -312,6 +361,7 @@ impl Flags { self.0 & Self::CERTIFICATE == Self::CERTIFICATE } + #[allow(dead_code)] // TODO(XXX): Remove once REQUIRE_PRIVATE is used. fn is_require_private(&self) -> bool { self.0 & Self::REQUIRE_PRIVATE == Self::REQUIRE_PRIVATE } @@ -343,4 +393,11 @@ const SUPPORTED_COMMANDS: &[Command] = &[ value_type: ValueType::String, action: SslConfigCtx::max_protocol, }, + Command { + name_file: Some("VerifyMode"), + name_cmdline: None, + flags: Flags(Flags::ANY), + value_type: ValueType::String, + action: SslConfigCtx::verify_mode, + }, ]; diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 4a372e6..fe67600 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -1362,6 +1362,12 @@ entry! { } } +entry! { + pub fn _SSL_get_verify_mode(ssl: *const SSL) -> c_int { + try_clone_arc!(ssl).get().get_verify_mode().into() + } +} + entry! { pub fn _SSL_set_verify_depth(ssl: *mut SSL, depth: c_int) { try_clone_arc!(ssl).get_mut().set_verify_depth(depth) diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index ccc4046..3a8ede3 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -890,6 +890,10 @@ impl Ssl { self.verify_mode = mode; } + fn get_verify_mode(&self) -> VerifyMode { + self.verify_mode + } + fn set_verify_depth(&mut self, depth: c_int) { self.verify_depth = depth; } diff --git a/rustls-libssl/tests/config.c b/rustls-libssl/tests/config.c index a8108e4..b5c4863 100644 --- a/rustls-libssl/tests/config.c +++ b/rustls-libssl/tests/config.c @@ -20,6 +20,8 @@ static const char *supported_cmds[] = { "-max_protocol", CUSTOM_PREFIX "max_protocol", "MaxProtocol", CUSTOM_PREFIX "MaxProtocol", + + "VerifyMode", CUSTOM_PREFIX "VerifyMode", }; #define NUM_SUPPORTED_CMDS (sizeof(supported_cmds) / sizeof(supported_cmds[0])) @@ -148,6 +150,86 @@ void test_min_max_versions(void) { SSL_free(ssl); } +static const char *verify_modes[] = { + NULL, "", "Ludicrous", "Ludicrous,Absurd", + "Peer", "Request", "Require", "Request,Require", +}; + +#define NUM_VERIFY_MODES (sizeof(verify_modes) / sizeof(verify_modes[0])) + +void set_verify_modes(SSL_CONF_CTX *cctx, SSL_CTX *ctx, SSL *ssl) { + for (unsigned long i = 0; i < NUM_VERIFY_MODES; i++) { + const char *mode = verify_modes[i]; + int res = SSL_CONF_cmd(cctx, "VerifyMode", mode); + printf("\t\tcmd VerifyMode '%s' returns %d\n", mode == NULL ? "NULL" : mode, + res); + if (ctx != NULL) { + printf("\t\tSSL_CTX_get_verify_mode %d\n", SSL_CTX_get_verify_mode(ctx)); + } + if (ssl != NULL) { + printf("\t\tSSL_get_verify_mode %d\n", SSL_get_verify_mode(ssl)); + } + } +} + +void test_verify_mode(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 client or server):\n"); + set_verify_modes(cctx, NULL, NULL); + + printf("\tPre-ctx (client):\n"); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); + set_verify_modes(cctx, NULL, NULL); + SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_CLIENT); + + printf("\tPre-ctx (server):\n"); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); + set_verify_modes(cctx, NULL, NULL); + SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_SERVER); + + SSL_CTX *ctx = SSL_CTX_new(TLS_method()); + assert(ctx != NULL); + SSL_CONF_CTX_set_ssl_ctx(cctx, ctx); + + printf("\tWith ctx (not client or server):\n"); + set_verify_modes(cctx, ctx, NULL); + + printf("\tWith ctx (client):\n"); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); + set_verify_modes(cctx, ctx, NULL); + SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_CLIENT); + + printf("\tWith ctx (server):\n"); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); + set_verify_modes(cctx, ctx, NULL); + SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_SERVER); + + SSL *ssl = SSL_new(ctx); + assert(ssl != NULL); + SSL_CONF_CTX_set_ssl(cctx, ssl); + + printf("\tWith ssl (not client or server):\n"); + set_verify_modes(cctx, NULL, ssl); + + printf("\tWith ssl (client):\n"); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT); + set_verify_modes(cctx, NULL, ssl); + SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_CLIENT); + + printf("\tWith ssl (server):\n"); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER); + set_verify_modes(cctx, NULL, ssl); + SSL_CONF_CTX_clear_flags(cctx, SSL_CONF_FLAG_SERVER); + + assert(SSL_CONF_CTX_finish(cctx)); + SSL_CONF_CTX_free(cctx); + SSL_CTX_free(ctx); + SSL_free(ssl); +} + int main(void) { printf("Supported commands:\n"); printf("no base flags, default prefix:\n"); @@ -170,4 +252,7 @@ int main(void) { printf("Min/Max version:\n"); test_min_max_versions(); + + printf("VerifyMode:\n"); + test_verify_mode(); }