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 VerifyMode command. #31

Merged
merged 4 commits 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
2 changes: 1 addition & 1 deletion rustls-libssl/MATRIX.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
2 changes: 2 additions & 0 deletions rustls-libssl/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
83 changes: 70 additions & 13 deletions rustls-libssl/src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -123,6 +123,57 @@ impl SslConfigCtx {
})
}

fn verify_mode(&mut self, raw_mode: Option<&str>) -> Result<ActionResult, Error> {
// 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::<Vec<&str>>();
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<u16> {
Some(match proto {
Some("None") => 0,
Expand Down Expand Up @@ -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<ValueType> for c_int {
Expand Down Expand Up @@ -259,12 +306,14 @@ type CommandAction = fn(&mut SslConfigCtx, value: Option<&str>) -> Result<Action

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(i32)]
#[allow(dead_code)] // TODO(XXX): Remove with first usage of NotApplied.
enum ActionResult {
/// The action required a value but was provided NULL.
ValueRequired = -3,
/// The action value was recognized, but not applied.
///
/// For example, if no `SSL_CTX` has been set a [`CommandAction`] may return `NotApplied` after
/// validating the command value.
#[allow(dead_code)] // TODO(XXX): remove with first ref.
NotApplied = 1,
/// The action value was recognized and applied.
Applied = 2,
Expand All @@ -279,7 +328,6 @@ impl From<ActionResult> 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;

Expand All @@ -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 {
Expand All @@ -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
}
Expand Down Expand Up @@ -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,
},
];
6 changes: 6 additions & 0 deletions rustls-libssl/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions rustls-libssl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
85 changes: 85 additions & 0 deletions rustls-libssl/tests/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
Expand Down Expand Up @@ -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",
};
cpu marked this conversation as resolved.
Show resolved Hide resolved

#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");
Expand All @@ -170,4 +252,7 @@ int main(void) {

printf("Min/Max version:\n");
test_min_max_versions();

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