From 5689ad9260c8d90a738bf9d830186a1b31c05a7f Mon Sep 17 00:00:00 2001 From: Marko Lalic Date: Wed, 18 Mar 2015 15:50:29 +0100 Subject: [PATCH] openssl: Implement client-side NPN protocol selection After the `set_npn_protocols` method of the `SslContext` struct is called, any future TLS connections established with this context will perform NPN negotiation. The chosen protocol is the one with the highest priority in the server's protocol list that is also in the client's protocol list. (This is the default behavior provided by OpenSSL's `SSL_select_next_proto` function.) If there is no overlap between the two lists, no error is raised. --- openssl/src/ssl/mod.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 058231ca..e06d613b 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -1,4 +1,4 @@ -use libc::{c_int, c_void, c_long}; +use libc::{c_int, c_void, c_long, c_uint, c_uchar}; use std::ffi::{CStr, CString}; use std::fmt; use std::io; @@ -6,6 +6,7 @@ use std::io::prelude::*; use std::ffi::AsOsStr; use std::mem; use std::net; +use std::slice; use std::num::FromPrimitive; use std::num::Int; use std::path::Path; @@ -220,6 +221,34 @@ extern fn raw_verify_with_data(preverify_ok: c_int, } } +/// The function is given as the callback to `SSL_CTX_set_next_proto_select_cb`. +/// +/// It chooses the protocol that the client wishes to use, out of the given list of protocols +/// supported by the server. It achieves this by delegating to the `SSL_select_next_proto` +/// function. The list of protocols supported by the client is found in the extra data of the +/// OpenSSL context. +#[cfg(feature = "npn")] +extern fn raw_next_proto_select_cb(ssl: *mut ffi::SSL, + out: *mut *mut c_uchar, outlen: *mut c_uchar, + inbuf: *const c_uchar, inlen: c_uint, + _arg: *mut c_void) -> c_int { + unsafe { + // First, get the list of protocols (that the client should support) saved in the context + // extra data. + let ssl_ctx = ffi::SSL_get_SSL_CTX(ssl); + let protocols = ffi::SSL_CTX_get_ex_data(ssl_ctx, get_npn_protos_idx()); + let protocols: &Vec = mem::transmute(protocols); + // Prepare the client list parameters to be passed to the OpenSSL function... + let client = protocols.as_ptr(); + let client_len = protocols.len() as c_uint; + // Finally, let OpenSSL find a protocol to be used, by matching the given server and + // client lists. + ffi::SSL_select_next_proto(out, outlen, inbuf, inlen, client, client_len); + } + + ffi::SSL_TLSEXT_ERR_OK +} + /// The signature of functions that can be used to manually verify certificates pub type VerifyCallback = fn(preverify_ok: bool, x509_ctx: &X509StoreContext) -> bool; @@ -393,6 +422,10 @@ impl SslContext { // so that we can refer to it within the callback. ffi::SSL_CTX_set_ex_data(*self.ctx, get_npn_protos_idx(), mem::transmute(protocols)); + // Now register the callback that performs the default protocol + // matching based on the client-supported list of protocols that + // has been saved. + ffi::SSL_CTX_set_next_proto_select_cb(*self.ctx, raw_next_proto_select_cb, ptr::null_mut()); } } }