aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Fackler <[email protected]>2015-12-15 19:22:39 -0800
committerSteven Fackler <[email protected]>2015-12-15 19:22:39 -0800
commit514c5ec4150eb6dd35535e780888e42f236f8cfd (patch)
treee90e96a63c6545131d5b82719c90854a62da79ce
parentDocument unused variant (diff)
parentIncrement SSL_CTX's reference count in Ssl::get_ssl_context() (diff)
downloadrust-openssl-514c5ec4150eb6dd35535e780888e42f236f8cfd.tar.xz
rust-openssl-514c5ec4150eb6dd35535e780888e42f236f8cfd.zip
Merge pull request #309 from Geal/master
Add support for Server Name indication (SNI) on the server's side
-rw-r--r--openssl-sys-extras/src/lib.rs8
-rw-r--r--openssl-sys-extras/src/openssl_shim.c13
-rw-r--r--openssl-sys/src/lib.rs6
-rw-r--r--openssl/src/ssl/mod.rs112
4 files changed, 137 insertions, 2 deletions
diff --git a/openssl-sys-extras/src/lib.rs b/openssl-sys-extras/src/lib.rs
index 3c114726..f8751d18 100644
--- a/openssl-sys-extras/src/lib.rs
+++ b/openssl-sys-extras/src/lib.rs
@@ -4,7 +4,7 @@
extern crate openssl_sys;
extern crate libc;
-use libc::{c_int, c_uint, c_long, c_char};
+use libc::{c_int, c_uint, c_long, c_char, c_void};
use openssl_sys::{HMAC_CTX, EVP_MD, ENGINE, SSL_CTX, BIO, X509, stack_st_X509_EXTENSION, SSL, DH};
macro_rules! import_options {
@@ -73,4 +73,10 @@ extern {
pub fn SSL_CTX_set_tmp_dh(s: *mut SSL, dh: *const DH) -> c_long;
#[link_name = "X509_get_extensions_shim"]
pub fn X509_get_extensions(x: *mut X509) -> *mut stack_st_X509_EXTENSION;
+ #[link_name = "SSL_CTX_set_tlsext_servername_callback_shim"]
+ pub fn SSL_CTX_set_tlsext_servername_callback(ssl: *mut SSL_CTX, callback: Option<extern fn()>);
+ #[link_name = "SSL_CTX_set_tlsext_servername_arg_shim"]
+ pub fn SSL_CTX_set_tlsext_servername_arg(ssl: *mut SSL_CTX, arg: *const c_void);
+ #[link_name = "SSL_CTX_increment_refcount_shim"]
+ pub fn SSL_CTX_increment_refcount(ssl: *mut SSL_CTX) -> c_long;
}
diff --git a/openssl-sys-extras/src/openssl_shim.c b/openssl-sys-extras/src/openssl_shim.c
index cc42fbf4..3acd3d50 100644
--- a/openssl-sys-extras/src/openssl_shim.c
+++ b/openssl-sys-extras/src/openssl_shim.c
@@ -131,6 +131,19 @@ long SSL_CTX_set_tmp_dh_shim(SSL_CTX *ctx, DH *dh) {
return SSL_CTX_set_tmp_dh(ctx, dh);
}
+long SSL_CTX_set_tlsext_servername_callback_shim(SSL_CTX *ctx, int (*callback)(SSL_CTX *, int *, void*)) {
+ return SSL_CTX_set_tlsext_servername_callback(ctx, callback);
+}
+
+long SSL_CTX_set_tlsext_servername_arg_shim(SSL_CTX *ctx, void* arg) {
+ return SSL_CTX_set_tlsext_servername_arg(ctx, arg);
+}
+
+long SSL_CTX_increment_refcount_shim(SSL_CTX *ctx) {
+ int i = CRYPTO_add(&ctx->references,1,CRYPTO_LOCK_SSL_CTX);
+ return i;
+}
+
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
int SSL_CTX_set_ecdh_auto_shim(SSL_CTX *ctx, int onoff) {
return SSL_CTX_set_ecdh_auto(ctx, onoff);
diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs
index 07401abb..4221e808 100644
--- a/openssl-sys/src/lib.rs
+++ b/openssl-sys/src/lib.rs
@@ -194,6 +194,8 @@ pub const PKCS5_SALT_LEN: c_int = 8;
pub const SSL_CTRL_OPTIONS: c_int = 32;
pub const SSL_CTRL_CLEAR_OPTIONS: c_int = 77;
+pub const SSL_CTRL_SET_TLSEXT_SERVERNAME_CB: c_int = 53;
+pub const SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG: c_int = 54;
pub const SSL_CTRL_SET_TLSEXT_HOSTNAME: c_int = 55;
pub const SSL_CTRL_EXTRA_CHAIN_CERT: c_int = 14;
@@ -591,12 +593,15 @@ extern "C" {
pub fn SSL_write(ssl: *mut SSL, buf: *const c_void, num: c_int) -> c_int;
pub fn SSL_get_ex_data_X509_STORE_CTX_idx() -> c_int;
pub fn SSL_get_SSL_CTX(ssl: *mut SSL) -> *mut SSL_CTX;
+ pub fn SSL_set_SSL_CTX(ssl: *mut SSL, ctx: *mut SSL_CTX) -> *mut SSL_CTX;
pub fn SSL_get_current_compression(ssl: *mut SSL) -> *const COMP_METHOD;
pub fn SSL_get_peer_certificate(ssl: *mut SSL) -> *mut X509;
pub fn SSL_get_ssl_method(ssl: *mut SSL) -> *const SSL_METHOD;
pub fn SSL_state_string(ssl: *mut SSL) -> *const c_char;
pub fn SSL_state_string_long(ssl: *mut SSL) -> *const c_char;
+ pub fn SSL_get_servername(ssl: *const SSL, name_type: c_long) -> *const c_char;
+
pub fn SSL_COMP_get_name(comp: *const COMP_METHOD) -> *const c_char;
pub fn SSL_CTX_new(method: *const SSL_METHOD) -> *mut SSL_CTX;
@@ -625,7 +630,6 @@ extern "C" {
pub fn SSL_CTX_set_cipher_list(ssl: *mut SSL_CTX, s: *const c_char) -> c_int;
- pub fn SSL_CTX_ctrl(ssl: *mut SSL_CTX, cmd: c_int, larg: c_long, parg: *mut c_void) -> c_long;
#[cfg(feature = "npn")]
pub fn SSL_CTX_set_next_protos_advertised_cb(ssl: *mut SSL_CTX,
cb: extern "C" fn(ssl: *mut SSL,
diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs
index c1535900..dffcfdb8 100644
--- a/openssl/src/ssl/mod.rs
+++ b/openssl/src/ssl/mod.rs
@@ -44,6 +44,7 @@ extern "C" {
}
static mut VERIFY_IDX: c_int = -1;
+static mut SNI_IDX: c_int = -1;
/// Manually initialize SSL.
/// It is optional to call this function and safe to do so more than once.
@@ -58,6 +59,11 @@ pub fn init() {
None, None);
assert!(verify_idx >= 0);
VERIFY_IDX = verify_idx;
+
+ let sni_idx = ffi::SSL_CTX_get_ex_new_index(0, ptr::null(), None,
+ None, None);
+ assert!(sni_idx >= 0);
+ SNI_IDX = sni_idx;
});
}
}
@@ -309,6 +315,53 @@ extern fn raw_verify_with_data<T>(preverify_ok: c_int,
}
}
+extern fn raw_sni(ssl: *mut ffi::SSL, ad: &mut c_int, arg: *mut c_void)
+ -> c_int {
+ unsafe {
+ let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl);
+ let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, SNI_IDX);
+ let callback: Option<ServerNameCallback> = mem::transmute(callback);
+ let mut s = Ssl { ssl: ssl };
+
+ let res = match callback {
+ None => ffi::SSL_TLSEXT_ERR_ALERT_FATAL,
+ Some(callback) => callback(&mut s, ad)
+ };
+
+ // Allows dropping the Ssl instance without calling SSL_FREE on the SSL object
+ mem::forget(s);
+ res
+ }
+}
+
+extern fn raw_sni_with_data<T>(ssl: *mut ffi::SSL, ad: &mut c_int, arg: *mut c_void) -> c_int
+ where T: Any + 'static {
+ unsafe {
+ let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl);
+
+ let callback = ffi::SSL_CTX_get_ex_data(ssl_ctx, SNI_IDX);
+ let callback: Option<ServerNameCallbackData<T>> = mem::transmute(callback);
+ let mut s = Ssl { ssl: ssl };
+
+ let data: &T = mem::transmute(arg);
+
+ let res = match callback {
+ None => ffi::SSL_TLSEXT_ERR_ALERT_FATAL,
+ Some(callback) => callback(&mut s, ad, &*data)
+ };
+
+ // Allows dropping the Ssl instance without calling SSL_FREE on the SSL object
+ mem::forget(s);
+
+ // Since data might be required on the next verification
+ // it is time to forget about it and avoid dropping
+ // data will be freed once OpenSSL considers it is time
+ // to free all context data
+ res
+ }
+}
+
+
#[cfg(any(feature = "npn", feature = "alpn"))]
unsafe fn select_proto_using(ssl: *mut ffi::SSL,
out: *mut *mut c_uchar, outlen: *mut c_uchar,
@@ -416,6 +469,11 @@ pub type VerifyCallbackData<T> = fn(preverify_ok: bool,
x509_ctx: &X509StoreContext,
data: &T) -> bool;
+/// The signature of functions that can be used to choose the context depending on the server name
+pub type ServerNameCallback = fn(ssl: &mut Ssl, ad: &mut i32) -> i32;
+
+pub type ServerNameCallbackData<T> = fn(ssl: &mut Ssl, ad: &mut i32, data: &T) -> i32;
+
// FIXME: macro may be instead of inlining?
#[inline]
fn wrap_ssl_result(res: c_int) -> Result<(),SslError> {
@@ -497,6 +555,35 @@ impl SslContext {
}
}
+ /// Configures the server name indication (SNI) callback for new connections
+ ///
+ /// obtain the server name with `get_servername` then set the corresponding context
+ /// with `set_ssl_context`
+ pub fn set_servername_callback(&mut self, callback: Option<ServerNameCallback>) {
+ unsafe {
+ ffi::SSL_CTX_set_ex_data(self.ctx, SNI_IDX,
+ mem::transmute(callback));
+ let f: extern fn() = mem::transmute(raw_sni);
+ ffi_extras::SSL_CTX_set_tlsext_servername_callback(self.ctx, Some(f));
+ }
+ }
+
+ /// Configures the server name indication (SNI) callback for new connections
+ /// carrying supplied data
+ pub fn set_servername_callback_with_data<T>(&mut self, callback: ServerNameCallbackData<T>,
+ data: T)
+ where T: Any + 'static {
+ let data = Box::new(data);
+ unsafe {
+ ffi::SSL_CTX_set_ex_data(self.ctx, SNI_IDX,
+ mem::transmute(Some(callback)));
+
+ ffi_extras::SSL_CTX_set_tlsext_servername_arg(self.ctx, mem::transmute(data));
+ let f: extern fn() = mem::transmute(raw_sni_with_data::<T>);
+ ffi_extras::SSL_CTX_set_tlsext_servername_callback(self.ctx, Some(f));
+ }
+ }
+
/// Sets verification depth
pub fn set_verify_depth(&mut self, depth: u32) {
unsafe {
@@ -685,6 +772,7 @@ impl SslContext {
ffi::SSL_CTX_set_alpn_select_cb(self.ctx, raw_alpn_select_cb, ptr::null_mut());
}
}
+
}
pub struct Ssl {
@@ -874,6 +962,30 @@ impl Ssl {
SslMethod::from_raw(method)
}
}
+
+ /// Returns the server's name for the current connection
+ pub fn get_servername(&self) -> Option<String> {
+ let name = unsafe { ffi::SSL_get_servername(self.ssl, ffi::TLSEXT_NAMETYPE_host_name) };
+ if name == ptr::null() {
+ return None;
+ }
+
+ unsafe {
+ String::from_utf8(CStr::from_ptr(name).to_bytes().to_vec()).ok()
+ }
+ }
+
+ /// change the context corresponding to the current connection
+ pub fn set_ssl_context(&self, ctx: &SslContext) -> SslContext {
+ SslContext { ctx: unsafe { ffi::SSL_set_SSL_CTX(self.ssl, ctx.ctx) } }
+ }
+
+ /// obtain the context corresponding to the current connection
+ pub fn get_ssl_context(&self) -> SslContext {
+ let ssl_ctx = unsafe { ffi::SSL_get_SSL_CTX(self.ssl) };
+ let count = unsafe { ffi_extras::SSL_CTX_increment_refcount(ssl_ctx) };
+ SslContext { ctx: ssl_ctx }
+ }
}
macro_rules! make_LibSslError {