Skip to content

Commit

Permalink
Enable session resumption
Browse files Browse the repository at this point in the history
This implements `SSL_CTX_sess_set_cache_size`, `SSL_CTX_sess_get_cache_size` &
`SSL_CTX_set_session_id_context`.

TODO: need to implement `SSL_CTX_set_session_cache_mode`
  • Loading branch information
ctz committed Apr 17, 2024
1 parent a03b4b9 commit 439e34e
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 33 deletions.
4 changes: 2 additions & 2 deletions rustls-libssl/MATRIX.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
| `SSL_CTX_set_recv_max_early_data` | | | |
| `SSL_CTX_set_security_callback` | | | |
| `SSL_CTX_set_security_level` | | | |
| `SSL_CTX_set_session_id_context` | | :white_check_mark: | :exclamation: [^stub] |
| `SSL_CTX_set_session_id_context` | | :white_check_mark: | :white_check_mark: |
| `SSL_CTX_set_session_ticket_cb` | | | |
| `SSL_CTX_set_srp_cb_arg` [^deprecatedin_3_0] [^srp] | | | |
| `SSL_CTX_set_srp_client_pwd_callback` [^deprecatedin_3_0] [^srp] | | | |
Expand Down Expand Up @@ -461,7 +461,7 @@
| `SSL_set_security_callback` | | | |
| `SSL_set_security_level` | | | |
| `SSL_set_session` | :white_check_mark: | :white_check_mark: | :exclamation: [^stub] |
| `SSL_set_session_id_context` | | | :exclamation: [^stub] |
| `SSL_set_session_id_context` | | | |
| `SSL_set_session_secret_cb` | | | |
| `SSL_set_session_ticket_ext` | | | |
| `SSL_set_session_ticket_ext_cb` | | | |
Expand Down
1 change: 0 additions & 1 deletion rustls-libssl/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ const ENTRYPOINTS: &[&str] = &[
"SSL_set_post_handshake_auth",
"SSL_set_quiet_shutdown",
"SSL_set_session",
"SSL_set_session_id_context",
"SSL_set_shutdown",
"SSL_set_SSL_CTX",
"SSL_set_verify",
Expand Down
79 changes: 79 additions & 0 deletions rustls-libssl/src/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use std::sync::Arc;

use rustls::client::ClientSessionMemoryCache;
use rustls::client::ClientSessionStore;
use rustls::server::ServerSessionMemoryCache;
use rustls::server::{ProducesTickets, StoresServerSessions};

/// A container for session caches that can live inside
/// an `SSL_CTX` but outlive a rustls `ServerConfig`/`ClientConfig`
pub struct SessionCaches {
size: usize,
context: Vec<u8>,
client: Option<Arc<dyn ClientSessionStore + Send + Sync>>,
server: Option<Arc<dyn StoresServerSessions + Send + Sync>>,
ticketer: Option<Arc<dyn ProducesTickets>>,
}

impl SessionCaches {
pub fn with_size(size: usize) -> Self {
// a user who has one `SSL_CTX` for both clients and servers will end
// up with twice as many sessions as this, since rustls caches
// client and server sessions separately.
//
// the common case is to have those separate (it is, for example,
// impossible to configure certs/keys separately for client and
// servers in a given `SSL_CTX`) so this should be ok.
Self {
size,
context: vec![],
client: None,
server: None,
ticketer: None,
}
}

pub fn get_client(&mut self) -> Arc<dyn ClientSessionStore + Send + Sync> {
Arc::clone(
self.client
.get_or_insert_with(|| Arc::new(ClientSessionMemoryCache::new(self.size))),
)
}

pub fn get_server(&mut self) -> Arc<dyn StoresServerSessions + Send + Sync> {
Arc::clone(
self.server
.get_or_insert_with(|| ServerSessionMemoryCache::new(self.size)),
)
}

pub fn get_ticketer(&mut self) -> Arc<dyn ProducesTickets> {
Arc::clone(
self.ticketer
.get_or_insert_with(|| crate::provider::Ticketer::new().unwrap()),
)
}

pub fn set_context(&mut self, context: &[u8]) {
// This is a different behaviour to that described by `SSL_set_session_id_context()`:
// the context isn't bound to an individual session but instead is an epoch for
// the entire set of stored sessions & ticketer keying.
if context != self.context {
context.clone_into(&mut self.context);
self.client = None;
self.server = None;
self.ticketer = None;
}
}

pub fn size(&self) -> usize {
self.size
}
}

impl Default for SessionCaches {
fn default() -> Self {
// this is SSL_SESSION_CACHE_MAX_SIZE_DEFAULT
Self::with_size(1024 * 20)
}
}
52 changes: 34 additions & 18 deletions rustls-libssl/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,14 @@ entry! {
inner.set_servername_callback_context(parg);
C_INT_SUCCESS as c_long
}
Ok(SslCtrl::SetSessCacheSize) => {
if larg < 0 {
return 0;
}
inner.set_session_cache_size(larg as usize);
C_INT_SUCCESS as c_long
}
Ok(SslCtrl::GetSessCacheSize) => inner.get_session_cache_size() as c_long,
Err(()) => {
log::warn!("unimplemented _SSL_CTX_ctrl(..., {cmd}, {larg}, ...)");
0
Expand Down Expand Up @@ -763,6 +771,26 @@ entry! {
}
}

entry! {
pub fn _SSL_CTX_set_session_id_context(
ctx: *mut SSL_CTX,
sid_ctx: *const c_uchar,
sid_ctx_len: c_uint,
) -> c_int {
let ctx = try_clone_arc!(ctx);
let sid_ctx = try_slice!(sid_ctx, sid_ctx_len);

match ctx
.lock()
.map_err(|_| Error::cannot_lock())
.map(|mut ctx| ctx.set_session_id_context(sid_ctx))
{
Err(e) => e.raise().into(),
Ok(()) => C_INT_SUCCESS,
}
}
}

impl Castable for SSL_CTX {
type Ownership = OwnershipArc;
type RustType = Mutex<SSL_CTX>;
Expand Down Expand Up @@ -895,7 +923,10 @@ entry! {
C_INT_SUCCESS as i64
}
// not a defined operation in the OpenSSL API
Ok(SslCtrl::SetTlsExtServerNameCallback) | Ok(SslCtrl::SetTlsExtServerNameArg) => 0,
Ok(SslCtrl::SetTlsExtServerNameCallback)
| Ok(SslCtrl::SetTlsExtServerNameArg)
| Ok(SslCtrl::SetSessCacheSize)
| Ok(SslCtrl::GetSessCacheSize) => 0,
Err(()) => {
log::warn!("unimplemented _SSL_ctrl(..., {cmd}, {larg}, ...)");
0
Expand Down Expand Up @@ -1815,6 +1846,8 @@ num_enum! {
enum SslCtrl {
Mode = 33,
SetMsgCallbackArg = 16,
SetSessCacheSize = 42,
GetSessCacheSize = 43,
SetTlsExtServerNameCallback = 53,
SetTlsExtServerNameArg = 54,
SetTlsExtHostname = 55,
Expand Down Expand Up @@ -1897,23 +1930,6 @@ entry_stub! {
pub type SSL_CTX_sess_remove_cb =
Option<unsafe extern "C" fn(ctx: *mut SSL_CTX, sess: *mut SSL_SESSION)>;

entry_stub! {
pub fn _SSL_CTX_set_session_id_context(
_ctx: *mut SSL_CTX,
_sid_ctx: *const c_uchar,
_sid_ctx_len: c_uint,
) -> c_int;
}

entry_stub! {

pub fn _SSL_set_session_id_context(
_ssl: *mut SSL,
_sid_ctx: *const c_uchar,
_sid_ctx_len: c_uint,
) -> c_int;
}

entry_stub! {
pub fn _SSL_CTX_set_keylog_callback(_ctx: *mut SSL_CTX, _cb: SSL_CTX_keylog_cb_func);
}
Expand Down
49 changes: 37 additions & 12 deletions rustls-libssl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use openssl_sys::{
EVP_PKEY, SSL_ERROR_NONE, SSL_ERROR_SSL, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, X509,
X509_STORE, X509_V_ERR_UNSPECIFIED,
};
use rustls::client::Resumption;
use rustls::crypto::aws_lc_rs as provider;
use rustls::pki_types::{CertificateDer, ServerName};
use rustls::server::{Accepted, Acceptor};
Expand All @@ -20,6 +21,7 @@ use rustls::{
};

mod bio;
mod cache;
mod callbacks;
#[macro_use]
mod constants;
Expand Down Expand Up @@ -215,6 +217,7 @@ pub struct SslContext {
method: &'static SslMethod,
ex_data: ex_data::ExData,
versions: EnabledVersions,
caches: cache::SessionCaches,
raw_options: u64,
verify_mode: VerifyMode,
verify_depth: c_int,
Expand All @@ -236,6 +239,7 @@ impl SslContext {
method,
ex_data: ex_data::ExData::default(),
versions: EnabledVersions::default(),
caches: cache::SessionCaches::default(),
raw_options: 0,
verify_mode: VerifyMode::default(),
verify_depth: -1,
Expand Down Expand Up @@ -264,6 +268,11 @@ impl SslContext {
self.ex_data.get(idx)
}

fn option_is_set(&self, opt: SslOption) -> bool {
let mask = opt as u64;
self.raw_options & mask == mask
}

fn get_options(&self) -> u64 {
self.raw_options
}
Expand Down Expand Up @@ -308,6 +317,19 @@ impl SslContext {
.unwrap_or_default()
}

fn get_session_cache_size(&self) -> usize {
self.caches.size()
}

fn set_session_cache_size(&mut self, size: usize) {
// divergence: OpenSSL can adjust the cache size without emptying it.
self.caches = cache::SessionCaches::with_size(size);
}

fn set_session_id_context(&mut self, context: &[u8]) {
self.caches.set_context(context);
}

fn set_max_early_data(&mut self, max: u32) {
self.max_early_data = max;
}
Expand Down Expand Up @@ -748,11 +770,7 @@ impl Ssl {
None => ServerName::try_from("0.0.0.0").unwrap(),
};

let method = self
.ctx
.lock()
.map(|ctx| ctx.method)
.map_err(|_| error::Error::cannot_lock())?;
let mut ctx = self.ctx.lock().map_err(|_| error::Error::cannot_lock())?;

let provider = Arc::new(provider::default_provider());
let verifier = Arc::new(verifier::ServerVerifier::new(
Expand All @@ -762,7 +780,7 @@ impl Ssl {
&self.verify_server_name,
));

let versions = self.versions.reduce_versions(method.client_versions)?;
let versions = self.versions.reduce_versions(ctx.method.client_versions)?;

let wants_resolver = ClientConfig::builder_with_provider(provider)
.with_protocol_versions(&versions)
Expand All @@ -777,6 +795,7 @@ impl Ssl {
};

config.alpn_protocols.clone_from(&self.alpn);
config.resumption = Resumption::store(ctx.caches.get_client());

let client_conn = ClientConnection::new(Arc::new(config), sni_server_name.clone())
.map_err(error::Error::from_rustls)?;
Expand Down Expand Up @@ -843,11 +862,7 @@ impl Ssl {
}

fn init_server_conn(&mut self) -> Result<(), error::Error> {
let method = self
.ctx
.lock()
.map(|ctx| ctx.method)
.map_err(|_| error::Error::cannot_lock())?;
let mut ctx = self.ctx.lock().map_err(|_| error::Error::cannot_lock())?;

let provider = Arc::new(provider::default_provider());
let verifier = Arc::new(
Expand All @@ -864,7 +879,7 @@ impl Ssl {
.server_resolver()
.ok_or_else(|| error::Error::bad_data("missing server keys"))?;

let versions = self.versions.reduce_versions(method.server_versions)?;
let versions = self.versions.reduce_versions(ctx.method.server_versions)?;

let mut config = ServerConfig::builder_with_provider(provider)
.with_protocol_versions(&versions)
Expand All @@ -874,6 +889,10 @@ impl Ssl {

config.alpn_protocols = mem::take(&mut self.alpn);
config.max_early_data_size = self.max_early_data;
config.session_storage = ctx.caches.get_server();
if !ctx.option_is_set(SslOption::NoTicket) {
config.ticketer = ctx.caches.get_ticketer();
}

let accepted = match mem::replace(&mut self.conn, ConnState::Nothing) {
ConnState::Accepted(accepted) => accepted,
Expand Down Expand Up @@ -1380,6 +1399,12 @@ impl EnabledVersions {
}
}

/// Subset of SSL_OP values that we interpret.
#[repr(u64)]
enum SslOption {
NoTicket = 1 << 14,
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down

0 comments on commit 439e34e

Please sign in to comment.