Skip to content

Commit

Permalink
updated to use cert select callback instead of the sni callback.
Browse files Browse the repository at this point in the history
  • Loading branch information
axos88 committed Oct 10, 2022
1 parent 634104c commit 7dba47e
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 121 deletions.
7 changes: 5 additions & 2 deletions src/http.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#[cfg(all(
esp_idf_esp_tls_server_cert_select_hook,
esp_idf_comp_esp_http_server_enabled
))]
pub mod cert_select;
#[cfg(esp_idf_comp_esp_http_client_enabled)]
pub mod client;
#[cfg(esp_idf_comp_esp_http_server_enabled)]
pub mod server;
#[cfg(all(esp_idf_esp_tls_server_sni_hook, esp_idf_comp_esp_http_server_enabled))]
pub mod sni;
131 changes: 131 additions & 0 deletions src/http/cert_select.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use esp_idf_sys::*;
use log::*;
use std::ffi::CStr;
use std::ptr;

// Workaround for unstable feature 'trait_alias'
pub trait CertSelectCallback<'a>: FnMut(&'a str) -> CallbackResult<'a> {}

// Workaround for unstable feature 'trait_alias'
impl<'a, T> CertSelectCallback<'a> for T where T: FnMut(&'a str) -> CallbackResult<'a> {}

pub struct HandshakeServerCertificate<'a> {
pub pk: &'a mut mbedtls_pk_context,
pub cert: &'a mut mbedtls_x509_crt,
}

pub struct HandshakeCertifiacteAuthority<'a> {
pub ca: &'a mut mbedtls_x509_crt,
pub crl: &'a mut mbedtls_x509_crl,
}

pub struct HandshakeVerifyMode(c_types::c_int);

pub struct CallbackResult<'a> {
server_certificate: Option<HandshakeServerCertificate<'a>>,
certificate_authority: Option<HandshakeCertifiacteAuthority<'a>>,
verify_mode: Option<HandshakeVerifyMode>,
}

impl<'a> CallbackResult<'a> {
pub fn new() -> CallbackResult<'a> {
CallbackResult {
server_certificate: None,
certificate_authority: None,
verify_mode: None,
}
}

pub fn set_hs_server_certficate(
mut self,
pk: &'a mut mbedtls_pk_context,
cert: &'a mut mbedtls_x509_crt,
) -> CallbackResult<'a> {
self.server_certificate = Some(HandshakeServerCertificate { pk, cert });
self
}

pub fn set_hs_certificate_authority(
mut self,
ca: &'a mut mbedtls_x509_crt,
crl: &'a mut mbedtls_x509_crl,
) -> CallbackResult<'a> {
self.certificate_authority = Some(HandshakeCertifiacteAuthority { ca, crl });
self
}

pub fn set_hs_verify_mode(mut self, verify_mode: u32) -> CallbackResult<'a> {
self.verify_mode = Some(HandshakeVerifyMode(verify_mode as _));
self
}
}

unsafe extern "C" fn f_rng(_arg: *mut c_types::c_void, ptr: *mut u8, bytes: u32) -> i32 {
esp_fill_random(ptr as _, bytes);
bytes as _
}

pub(crate) unsafe extern "C" fn cert_select_trampoline<'a>(
ssl: *mut mbedtls_ssl_context,
) -> esp_err_t {
// Need to use ->private_user_data as the getter function is static inline, and
// bindgen can't generate bindings for static inline yet.
// https://github.com/rust-lang/rust-bindgen/issues/1090

let ssl_conf = (*ssl).private_conf;

if ssl_conf == ptr::null_mut() {
return ESP_ERR_INVALID_ARG;
}

let cb_ptr = (*ssl_conf).private_user_data.p;

if cb_ptr == ptr::null_mut() {
return ESP_ERR_INVALID_ARG;
}

let cb = &mut *(cb_ptr as *mut Box<dyn CertSelectCallback<'a>>);
let mut namelen: u32 = 0;

let name = mbedtls_ssl_get_hs_sni(ssl, &mut namelen);
let name = CStr::from_ptr(name as _).to_str().unwrap();

let CallbackResult {
server_certificate,
certificate_authority,
verify_mode,
} = cb(name);

if let Some(HandshakeServerCertificate { pk, cert }) = server_certificate {
if let Err(err) = esp!(mbedtls_pk_check_pair(
&mut cert.pk,
pk,
Some(f_rng),
ptr::null_mut()
)) {
error!(
"Certificate and private key supplied by the SNI callback do not match: {:?}",
err
);
return err.code();
};

if let Err(err) = esp!(mbedtls_ssl_set_hs_own_cert(ssl, cert, pk)) {
error!(
"Could not set handshake certificate and private key: {:?}",
err
);
return err.code();
};
};

if let Some(HandshakeCertifiacteAuthority { ca, crl }) = certificate_authority {
mbedtls_ssl_set_hs_ca_chain(ssl, ca, crl)
};

if let Some(HandshakeVerifyMode(authmode)) = verify_mode {
mbedtls_ssl_set_hs_authmode(ssl, authmode)
};

return ESP_OK;
}
116 changes: 82 additions & 34 deletions src/http/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,19 @@ use crate::private::common::Newtype;
use crate::private::cstr::{CStr, CString};
use crate::private::mutex::{Mutex, RawMutex};

#[cfg(all(esp_idf_esp_tls_server_sni_hook, esp_idf_comp_esp_http_server_enabled))]
use super::sni::*;

#[cfg_attr(not(esp_idf_esp_tls_server_sni_hook), derive(Debug))]
#[cfg(all(
esp_idf_esp_tls_server_cert_select_hook,
esp_idf_comp_esp_http_server_enabled
))]
use super::cert_select::*;

#[cfg_attr(
not(all(
esp_idf_esp_https_server_enable,
esp_idf_esp_tls_server_cert_select_hook
)),
derive(Debug)
)]
pub struct Configuration<'a> {
pub http_port: u16,
pub https_port: u16,
Expand All @@ -48,10 +57,16 @@ pub struct Configuration<'a> {
pub server_certificate: Option<&'static str>,
#[cfg(esp_idf_esp_https_server_enable)]
pub private_key: Option<&'static str>,
#[cfg(all(esp_idf_esp_https_server_enable, esp_idf_esp_tls_server_sni_hook))]
pub sni: Option<Box<dyn SNICB<'a>>>,
#[cfg(not(all(esp_idf_esp_https_server_enable, esp_idf_esp_tls_server_sni_hook)))]
pub _phantom_data: std::marker::PhantomData<&'a ()>
#[cfg(all(
esp_idf_esp_https_server_enable,
esp_idf_esp_tls_server_cert_select_hook
))]
pub cert_select: Option<Box<dyn CertSelectCallback<'a>>>,
#[cfg(not(all(
esp_idf_esp_https_server_enable,
esp_idf_esp_tls_server_cert_select_hook
)))]
pub _phantom_data: std::marker::PhantomData<&'a ()>,
}

impl<'a> Default for Configuration<'a> {
Expand All @@ -73,25 +88,40 @@ impl<'a> Default for Configuration<'a> {
server_certificate: None,
#[cfg(esp_idf_esp_https_server_enable)]
private_key: None,
#[cfg(all(esp_idf_esp_https_server_enable, esp_idf_esp_tls_server_sni_hook))]
sni: None,
#[cfg(not(all(esp_idf_esp_https_server_enable, esp_idf_esp_tls_server_sni_hook)))]
_phantom_data: Default::default()
#[cfg(all(
esp_idf_esp_https_server_enable,
esp_idf_esp_tls_server_cert_select_hook
))]
cert_select: None,
#[cfg(not(all(
esp_idf_esp_https_server_enable,
esp_idf_esp_tls_server_cert_select_hook
)))]
_phantom_data: Default::default(),
}
}
}

//TODO: Can we simply ignore the sni field in the Derive macro somehow? Derivative crate is supposed to be for that, but can't get it to work
#[cfg(esp_idf_esp_tls_server_sni_hook)]
//TODO: Can we simply ignore the cert select field in the Derive macro somehow? Derivative crate is supposed to be for that, but can't get it to work
#[cfg(all(
esp_idf_esp_https_server_enable,
esp_idf_esp_tls_server_cert_select_hook
))]
impl<'a> Debug for Configuration<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {

let sni_s = if self.sni.is_some() {
"Some(..)"
} else { "None" };
let cert_select_s = if self.cert_select.is_some() {
let ptr = self
.cert_select
.as_ref()
.map(|cb| cb as *const _ as *mut c_types::c_void)
.unwrap_or(ptr::null_mut());
format!("Some({:?})", ptr)
} else {
"None".into()
};

f.write_fmt(format_args!(
"Configuration {{ http_port = {}, https_port = {}, max_sessions = {}, session_timeout = {:?}, stack_size = {}, max_open_sockets = {}, max_uri_handlers = {}, max_resp_handlers = {}, lru_purge_enable = {}, server_certificate = {:?}, private_key = {:?}, sni = {} }}",
"Configuration {{ http_port = {}, https_port = {}, max_sessions = {}, session_timeout = {:?}, stack_size = {}, max_open_sockets = {}, max_uri_handlers = {}, max_resp_handlers = {}, lru_purge_enable = {}, server_certificate = {:?}, private_key = {:?}, cert_select = {} }}",
self.http_port,
self.https_port,
self.max_sessions,
Expand All @@ -103,7 +133,7 @@ impl<'a> Debug for Configuration<'a> {
self.lru_purge_enable,
self.server_certificate,
self.private_key,
sni_s
cert_select_s
))
}
}
Expand Down Expand Up @@ -187,26 +217,32 @@ impl<'a> From<&Configuration<'a>> for Newtype<httpd_ssl_config_t> {
let http_config: Newtype<httpd_config_t> = conf.into();
// start in insecure mode if no certificates are set

#[cfg(not(esp_idf_esp_tls_server_sni_hook))]
let transport_mode = match (conf.server_certificate.is_some(), conf.private_key.is_some()) {
#[cfg(not(esp_idf_esp_tls_server_cert_select_hook))]
let transport_mode = match (
conf.server_certificate.is_some(),
conf.private_key.is_some(),
) {
(true, true) => httpd_ssl_transport_mode_t_HTTPD_SSL_TRANSPORT_SECURE,
_ => {
warn!("Starting server in insecure mode because no certificates were set in the http config.");
httpd_ssl_transport_mode_t_HTTPD_SSL_TRANSPORT_INSECURE
}
};

#[cfg(esp_idf_esp_tls_server_sni_hook)]
let transport_mode = match (conf.server_certificate.is_some(), conf.private_key.is_some(), conf.sni.is_some()) {
#[cfg(esp_idf_esp_tls_server_cert_select_hook)]
let transport_mode = match (
conf.server_certificate.is_some(),
conf.private_key.is_some(),
conf.cert_select.is_some(),
) {
(_, _, true) => httpd_ssl_transport_mode_t_HTTPD_SSL_TRANSPORT_SECURE,
(true, true, _) => httpd_ssl_transport_mode_t_HTTPD_SSL_TRANSPORT_SECURE,
_ => {
warn!("Starting server in insecure mode because no certificates were set in the http config and no SNI callback is defined.");
warn!("Starting server in insecure mode because no certificates were set in the http config and no certificate selection callback is defined.");
httpd_ssl_transport_mode_t_HTTPD_SSL_TRANSPORT_INSECURE
}
};


// Default values taken from: https://github.com/espressif/esp-idf/blob/master/components/esp_https_server/include/esp_https_server.h#L114
Self(httpd_ssl_config_t {
httpd: http_config.0,
Expand All @@ -218,15 +254,27 @@ impl<'a> From<&Configuration<'a>> for Newtype<httpd_ssl_config_t> {
cacert_len: 0,
prvtkey_pem: ptr::null(),
prvtkey_len: 0,
#[cfg(esp_idf_version_major = "5")]
servercert: ptr::null(),
#[cfg(esp_idf_version_major = "5")]
servercert_len: 0,
#[cfg(esp_idf_version_major = "4")]
client_verify_cert_pem: ptr::null(),
#[cfg(esp_idf_version_major = "4")]
client_verify_cert_len: 0,
user_cb: None,
#[cfg(esp_idf_version_major = "5")]
use_secure_element: false,
#[cfg(esp_idf_esp_tls_server_sni_hook)]
sni_callback: Some(sni_trampoline),
#[cfg(esp_idf_esp_tls_server_sni_hook)]
sni_callback_p_info: conf.sni.as_ref().map(|cb| cb as *const _ as *mut c_types::c_void).unwrap_or(ptr::null_mut()),
#[cfg(esp_idf_esp_tls_server_cert_select_hook)]
cert_select_cb: Some(cert_select_trampoline),
#[cfg(esp_idf_esp_tls_server_cert_select_hook)]
ssl_userdata: conf
.cert_select
.as_ref()
.map(|cb| cb as *const _ as *mut c_types::c_void)
.unwrap_or(ptr::null_mut()),
#[cfg(not(esp_idf_esp_tls_server_cert_select_hook))]
ssl_userdata: ptr::null_mut(),
})
}
}
Expand Down Expand Up @@ -325,13 +373,13 @@ impl EspHttpServer {
config.0.prvtkey_len = private_key.len() as u32;
};

//See above why this is duplicated
esp!(unsafe { httpd_ssl_start(handle_ref, &mut config.0) })?;
} else {
esp!(unsafe { httpd_ssl_start(handle_ref, &mut config.0) })?;
}

esp!(unsafe { httpd_ssl_start(handle_ref, &mut config.0) })?;
}

info!("Started Httpd server with config {:?}", conf);

let server = EspHttpServer {
sd: handle,
registrations: Vec::new(),
Expand Down
Loading

0 comments on commit 7dba47e

Please sign in to comment.