Skip to content

Commit

Permalink
add sni support
Browse files Browse the repository at this point in the history
  • Loading branch information
axos88 committed Oct 2, 2022
1 parent 34dfee2 commit 0c39a31
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 19 deletions.
2 changes: 2 additions & 0 deletions src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
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;
108 changes: 89 additions & 19 deletions src/http/server.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use core::cell::UnsafeCell;
use core::fmt::{Debug, Display};
use core::fmt::{Debug, Display, Formatter};
use core::sync::atomic::{AtomicBool, Ordering};
use core::time::*;
use core::{mem, ptr};
Expand Down Expand Up @@ -30,8 +30,11 @@ use crate::private::common::Newtype;
use crate::private::cstr::{CStr, CString};
use crate::private::mutex::{Mutex, RawMutex};

#[derive(Copy, Clone, Debug)]
pub struct Configuration {
#[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))]
pub struct Configuration<'a> {
pub http_port: u16,
pub https_port: u16,
pub max_sessions: usize,
Expand All @@ -45,9 +48,13 @@ pub struct Configuration {
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 ()>
}

impl Default for Configuration {
impl<'a> Default for Configuration<'a> {
fn default() -> Self {
Configuration {
http_port: 80,
Expand All @@ -66,12 +73,43 @@ impl Default for Configuration {
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()
}
}
}

impl From<&Configuration> for Newtype<httpd_config_t> {
fn from(conf: &Configuration) -> Self {
//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)]
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" };

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 = {} }}",
self.http_port,
self.https_port,
self.max_sessions,
self.session_timeout,
self.stack_size,
self.max_open_sockets,
self.max_uri_handlers,
self.max_resp_handlers,
self.lru_purge_enable,
self.server_certificate,
self.private_key,
sni_s
))
}
}

impl<'a> From<&Configuration<'a>> for Newtype<httpd_config_t> {
fn from(conf: &Configuration<'a>) -> Self {
Self(httpd_config_t {
task_priority: 5,
stack_size: conf.stack_size as _,
Expand Down Expand Up @@ -138,18 +176,33 @@ impl From<Newtype<c_types::c_uint>> for Method {
}
}


#[cfg(esp_idf_esp_https_server_enable)]
impl From<&Configuration> for Newtype<httpd_ssl_config_t> {
fn from(conf: &Configuration) -> Self {
impl<'a> From<&Configuration<'a>> for Newtype<httpd_ssl_config_t> {
fn from(conf: &Configuration<'a>) -> Self {
let http_config: Newtype<httpd_config_t> = conf.into();
// start in insecure mode if no certificates are set
let transport_mode = match (conf.server_certificate, conf.private_key) {
(Some(_), Some(_)) => httpd_ssl_transport_mode_t_HTTPD_SSL_TRANSPORT_SECURE,

#[cfg(not(esp_idf_esp_tls_server_sni_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()) {
(_, _, 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.");
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 @@ -161,9 +214,15 @@ impl From<&Configuration> for Newtype<httpd_ssl_config_t> {
cacert_len: 0,
prvtkey_pem: ptr::null(),
prvtkey_len: 0,
client_verify_cert_pem: ptr::null(),
client_verify_cert_len: 0,
servercert: ptr::null(),
servercert_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()),
})
}
}
Expand Down Expand Up @@ -244,16 +303,27 @@ impl EspHttpServer {
let cert = CString::new(cert).unwrap().into_bytes_with_nul();
let private_key = CString::new(private_key).unwrap().into_bytes_with_nul();

config.0.cacert_pem = cert.as_ptr();
config.0.cacert_len = cert.len() as u32;
#[cfg(not(esp_idf_version_major = "5"))]
{
config.0.cacert_pem = cert.as_ptr();
config.0.cacert_len = cert.len() as u32;

config.0.prvtkey_pem = private_key.as_ptr() as _;
config.0.prvtkey_len = private_key.len() as u32;
config.0.prvtkey_pem = private_key.as_ptr() as _;
config.0.prvtkey_len = private_key.len() as u32;
};

#[cfg(esp_idf_version_major = "5")]
{
config.0.servercert = cert.as_ptr();
config.0.servercert_len = cert.len() as u32;

config.0.prvtkey_pem = private_key.as_ptr() as _;
config.0.prvtkey_len = private_key.len() as u32;
};

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);
Expand Down
85 changes: 85 additions & 0 deletions src/http/sni.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use std::ffi::CStr;
use std::ptr;
use esp_idf_sys::*;
use log::*;

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

// Workaround for unstable feature 'trait_alias'
impl<'a, T> SNICB<'a> for T
where T: FnMut(&'a str) -> SNIResult<'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 SNIResult<'a> {
server_certificate: Option<HandshakeServerCertificate<'a>>,
certificate_authority: Option<HandshakeCertifiacteAuthority<'a>>,
verify_mode: Option<HandshakeVerifyMode>
}

impl<'a> SNIResult<'a> {
pub fn new() -> SNIResult<'a> { SNIResult { 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) -> SNIResult<'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) -> SNIResult<'a> {
self.certificate_authority = Some(HandshakeCertifiacteAuthority { ca, crl });
self
}

pub fn set_hs_verify_mode(mut self, verify_mode: u32) -> SNIResult<'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 sni_trampoline<'a>(p_info: *mut c_types::c_void, ssl: *mut mbedtls_ssl_context, name: *const c_types::c_uchar, _len: c_types::c_uint) -> esp_err_t
{
let cb = &mut *(p_info as *mut Box<dyn SNICB<'a>>);

let name = CStr::from_ptr(name as _).to_str().unwrap();

let SNIResult { 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;
}

0 comments on commit 0c39a31

Please sign in to comment.