Skip to content

Commit

Permalink
Support ALPN with weak linkage
Browse files Browse the repository at this point in the history
  • Loading branch information
kzys committed Jul 26, 2018
1 parent 6634a20 commit 06fc4a5
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 13 deletions.
1 change: 1 addition & 0 deletions security-framework-sys/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub enum OpaqueSecPolicyRef {}
pub type SecPolicyRef = *mut OpaqueSecPolicyRef;

pub const errSecSuccess: OSStatus = 0;
pub const errSecUnimplemented: OSStatus = -4;
pub const errSecIO: OSStatus = -36;
pub const errSecParam: OSStatus = -50;
pub const errSecBadReq: OSStatus = -909;
Expand Down
4 changes: 3 additions & 1 deletion security-framework/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ tempdir = "0.3"
hex = "0.2"

[features]
alpn = []

OSX_10_9 = ["security-framework-sys/OSX_10_9"]
OSX_10_10 = ["OSX_10_9", "security-framework-sys/OSX_10_10"]
OSX_10_11 = ["OSX_10_10", "security-framework-sys/OSX_10_11"]
OSX_10_12 = ["OSX_10_11", "security-framework-sys/OSX_10_12"]
OSX_10_13 = ["OSX_10_12", "security-framework-sys/OSX_10_13"]
OSX_10_13 = ["OSX_10_12", "security-framework-sys/OSX_10_13", "alpn"]

nightly = []

Expand Down
50 changes: 50 additions & 0 deletions security-framework/src/dlsym.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// dlsym.rs is taken from mio
// https://github.com/carllerche/mio/blob/master/src/sys/unix/dlsym.rs

use std::marker;
use std::mem;
use std::sync::atomic::{AtomicUsize, Ordering};

use libc;

macro_rules! dlsym {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
#[allow(bad_style)]
static $name: ::dlsym::DlSym<unsafe extern fn($($t),*) -> $ret> =
::dlsym::DlSym {
name: concat!(stringify!($name), "\0"),
addr: ::std::sync::atomic::ATOMIC_USIZE_INIT,
_marker: ::std::marker::PhantomData,
};
)
}

pub struct DlSym<F> {
pub name: &'static str,
pub addr: AtomicUsize,
pub _marker: marker::PhantomData<F>,
}

impl<F> DlSym<F> {
pub fn get(&self) -> Option<&F> {
assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
unsafe {
if self.addr.load(Ordering::SeqCst) == 0 {
self.addr.store(fetch(self.name), Ordering::SeqCst);
}
if self.addr.load(Ordering::SeqCst) == 1 {
None
} else {
mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr)
}
}
}
}

unsafe fn fetch(name: &str) -> usize {
assert_eq!(name.as_bytes()[name.len() - 1], 0);
match libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize {
0 => 1,
n => n,
}
}
4 changes: 4 additions & 0 deletions security-framework/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ macro_rules! p {
};
}

#[cfg(all(not(feature = "OSX_10_13"), feature = "alpn"))]
#[macro_use]
mod dlsym;

pub mod base;
pub mod certificate;
pub mod cipher_suite;
Expand Down
79 changes: 67 additions & 12 deletions security-framework/src/secure_transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,19 @@
//! }
//!
//! ```
#[allow(unused_imports)]
use core_foundation::array::{CFArray, CFArrayRef};

use core_foundation::array::CFArray;
use core_foundation::base::{Boolean, TCFType};
#[cfg(feature = "OSX_10_13")]
use core_foundation::string::CFString;
use core_foundation_sys::base::{kCFAllocatorDefault, OSStatus};
use libc::{c_void, size_t};

#[allow(unused_imports)]
use security_framework_sys::base::{
errSecBadReq, errSecIO, errSecNotTrusted, errSecSuccess, errSecTrustSettingDeny,
errSecBadReq, errSecIO, errSecNotTrusted, errSecSuccess, errSecTrustSettingDeny, errSecUnimplemented
};

use security_framework_sys::secure_transport::*;
use std::any::Any;
use std::cmp;
Expand Down Expand Up @@ -701,11 +704,25 @@ impl SslContext {
}

/// Returns the set of protocols selected via ALPN if it succeeded.
#[cfg(feature = "OSX_10_13")]
#[cfg(feature = "alpn")]
pub fn alpn_protocols(&self) -> Result<Vec<String>> {
let mut array = ptr::null();
unsafe {
let mut array = ptr::null();
cvt(SSLCopyALPNProtocols(self.0, &mut array))?;
#[cfg(feature = "OSX_10_13")]
{
cvt(SSLCopyALPNProtocols(self.0, &mut array))?;
}

#[cfg(not(feature = "OSX_10_13"))]
{
dlsym! { fn SSLCopyALPNProtocols(SSLContextRef, *mut CFArrayRef) -> OSStatus }
if let Some(f) = SSLCopyALPNProtocols.get() {
cvt(f(self.0, &mut array))?;
} else {
return Err(Error::from_code(errSecUnimplemented))
}
}

if array.is_null() {
return Ok(vec![]);
}
Expand All @@ -718,7 +735,7 @@ impl SslContext {
/// Configures the set of protocols use for ALPN.
///
/// This is only used for client-side connections.
#[cfg(feature = "OSX_10_13")]
#[cfg(feature = "alpn")]
pub fn set_alpn_protocols(&mut self, protocols: &[&str]) -> Result<()> {
// When CFMutableArray is added to core-foundation and IntoIterator trait
// is implemented for CFMutableArray, the code below should directly collect
Expand All @@ -730,7 +747,19 @@ impl SslContext {
.collect::<Vec<_>>(),
);

unsafe { cvt(SSLSetALPNProtocols(self.0, protocols.as_concrete_TypeRef())) }
#[cfg(feature = "OSX_10_13")]
{
unsafe { cvt(SSLSetALPNProtocols(self.0, protocols.as_concrete_TypeRef())) }
}
#[cfg(not(feature = "OSX_10_13"))]
{
dlsym! { fn SSLSetALPNProtocols(SSLContextRef, CFArrayRef) -> OSStatus }
if let Some(f) = SSLSetALPNProtocols.get() {
unsafe { cvt(f(self.0, protocols.as_concrete_TypeRef())) }
} else {
Err(Error::from_code(errSecUnimplemented))
}
}
}

/// Sets whether a protocol is enabled or not.
Expand Down Expand Up @@ -1117,7 +1146,6 @@ pub struct ClientBuilder {
danger_accept_invalid_hostnames: bool,
whitelisted_ciphers: Vec<CipherSuite>,
blacklisted_ciphers: Vec<CipherSuite>,
#[cfg(feature = "OSX_10_13")]
alpn: Option<Vec<String>>,
}

Expand All @@ -1142,7 +1170,6 @@ impl ClientBuilder {
danger_accept_invalid_hostnames: false,
whitelisted_ciphers: Vec::new(),
blacklisted_ciphers: Vec::new(),
#[cfg(feature = "OSX_10_13")]
alpn: None,
}
}
Expand Down Expand Up @@ -1227,7 +1254,7 @@ impl ClientBuilder {
}

/// Configures the set of protocols used for ALPN.
#[cfg(feature = "OSX_10_13")]
#[cfg(feature = "alpn")]
pub fn alpn_protocols(&mut self, protocols: &[&str]) -> &mut Self {
self.alpn = Some(protocols.iter().map(|s| s.to_string()).collect());
self
Expand Down Expand Up @@ -1278,7 +1305,7 @@ impl ClientBuilder {
if let Some(ref identity) = self.identity {
ctx.set_certificate(identity, &self.chain)?;
}
#[cfg(feature = "OSX_10_13")]
#[cfg(feature = "alpn")]
{
if let Some(ref alpn) = self.alpn {
ctx.set_alpn_protocols(&alpn.iter().map(|s| &**s).collect::<Vec<_>>())?;
Expand Down Expand Up @@ -1398,6 +1425,34 @@ mod test {
println!("{}", String::from_utf8_lossy(&buf));
}

#[test]
#[cfg(feature = "alpn")]
fn client_alpn_accept() {
let mut ctx = p!(SslContext::new(
SslProtocolSide::CLIENT,
SslConnectionType::STREAM
));
p!(ctx.set_peer_domain_name("google.com"));
p!(ctx.set_alpn_protocols(&vec!["h2"]));
let stream = p!(TcpStream::connect("google.com:443"));
let stream = ctx.handshake(stream).unwrap();
assert_eq!(vec!["h2"], stream.context().alpn_protocols().unwrap());
}

#[test]
#[cfg(feature = "alpn")]
fn client_alpn_reject() {
let mut ctx = p!(SslContext::new(
SslProtocolSide::CLIENT,
SslConnectionType::STREAM
));
p!(ctx.set_peer_domain_name("google.com"));
p!(ctx.set_alpn_protocols(&vec!["h2c"]));
let stream = p!(TcpStream::connect("google.com:443"));
let stream = ctx.handshake(stream).unwrap();
assert!(stream.context().alpn_protocols().is_err());
}

#[test]
fn client_no_anchor_certs() {
let stream = p!(TcpStream::connect("google.com:443"));
Expand Down

0 comments on commit 06fc4a5

Please sign in to comment.