From fd52bbe85c1b67a5416ded43a0845be3d1c57b59 Mon Sep 17 00:00:00 2001
From: Steven Fackler <sfackler@gmail.com>
Date: Sat, 15 Jul 2017 16:34:07 -0700
Subject: [PATCH] Add an API to install extra data

---
 .gitignore              |  1 +
 openssl/src/ex_data.rs  | 26 +++++++++++++
 openssl/src/lib.rs      |  1 +
 openssl/src/ssl/mod.rs  | 86 +++++++++++++++++++++++++++++++++++++----
 openssl/src/x509/mod.rs | 20 ++++++++--
 5 files changed, 123 insertions(+), 11 deletions(-)
 create mode 100644 openssl/src/ex_data.rs

diff --git a/.gitignore b/.gitignore
index 57f4300cc..a0db182ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@ target/
 Cargo.lock
 .idea/
 *.iml
+.vscode/
diff --git a/openssl/src/ex_data.rs b/openssl/src/ex_data.rs
new file mode 100644
index 000000000..450dd113f
--- /dev/null
+++ b/openssl/src/ex_data.rs
@@ -0,0 +1,26 @@
+use libc::c_int;
+use std::marker::PhantomData;
+
+/// A slot in a type's "extra data" structure.
+///
+/// It is parameterized over the type containing the extra data as well as the
+/// type of the data in the slot.
+pub struct Index<T, U>(c_int, PhantomData<(T, U)>);
+
+impl<T, U> Copy for Index<T, U> {}
+
+impl<T, U> Clone for Index<T, U> {
+    fn clone(&self) -> Index<T, U> {
+        *self
+    }
+}
+
+impl<T, U> Index<T, U> {
+    pub unsafe fn from_raw(idx: c_int) -> Index<T, U> {
+        Index(idx, PhantomData)
+    }
+
+    pub fn as_raw(&self) -> c_int {
+        self.0
+    }
+}
diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs
index 0c7a9bdb3..84947281e 100644
--- a/openssl/src/lib.rs
+++ b/openssl/src/lib.rs
@@ -36,6 +36,7 @@ pub mod dsa;
 pub mod ec;
 pub mod ec_key;
 pub mod error;
+pub mod ex_data;
 pub mod hash;
 pub mod memcmp;
 pub mod nid;
diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs
index 2c0a4b636..4f888f9d7 100644
--- a/openssl/src/ssl/mod.rs
+++ b/openssl/src/ssl/mod.rs
@@ -93,7 +93,7 @@ use std::slice;
 use std::str;
 use std::sync::Mutex;
 
-use {init, cvt, cvt_p};
+use {init, cvt, cvt_p, cvt_n};
 use dh::{Dh, DhRef};
 use ec::EcKeyRef;
 #[cfg(any(all(feature = "v101", ossl101), all(feature = "v102", ossl102)))]
@@ -106,8 +106,15 @@ use x509::store::X509Store;
 use verify::X509VerifyParamRef;
 use pkey::PKeyRef;
 use error::ErrorStack;
+use ex_data::Index;
 use util::Opaque;
 use stack::{Stack, StackRef};
+use ssl::bio::BioMethod;
+use ssl::callbacks::*;
+
+pub use ssl::connector::{SslConnectorBuilder, SslConnector, SslAcceptorBuilder, SslAcceptor,
+                         ConnectConfiguration};
+pub use ssl::error::{Error, HandshakeError};
 
 mod error;
 mod callbacks;
@@ -116,13 +123,6 @@ mod bio;
 #[cfg(test)]
 mod tests;
 
-use ssl::bio::BioMethod;
-use ssl::callbacks::*;
-
-pub use ssl::connector::{SslConnectorBuilder, SslConnector, SslAcceptorBuilder, SslAcceptor,
-                         ConnectConfiguration};
-pub use ssl::error::{Error, HandshakeError};
-
 // FIXME drop SSL_ prefix
 // FIXME remvove flags not used in OpenSSL 1.1
 bitflags! {
@@ -741,6 +741,14 @@ impl SslContextBuilder {
         }
     }
 
+    /// Sets the extra data at the specified index.
+    pub fn set_ex_data<T>(&mut self, index: Index<SslContext, T>, data: T) {
+        unsafe {
+            let data = Box::new(data);
+            ffi::SSL_CTX_set_ex_data(self.as_ptr(), index.as_raw(), Box::into_raw(data) as *mut c_void);
+        }
+    }
+
     pub fn build(self) -> SslContext {
         let ctx = SslContext(self.0);
         mem::forget(self);
@@ -779,6 +787,20 @@ impl SslContext {
     pub fn builder(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
         SslContextBuilder::new(method)
     }
+
+    /// Returns a new extra data index.
+    ///
+    /// Each invocation of this function is guaranteed to return a distinct
+    /// index.
+    pub fn new_ex_index<T>() -> Result<Index<SslContext, T>, ErrorStack>
+    where
+        T: 'static + Sync + Send
+    {
+        unsafe {
+            let idx = try!(cvt_n(compat::get_new_idx(free_data_box::<T>)));
+            Ok(Index::from_raw(idx))
+        }
+    }
 }
 
 impl SslContextRef {
@@ -825,6 +847,18 @@ impl SslContextRef {
             StackRef::from_ptr(chain)
         }
     }
+
+    /// Returns a reference to the extra data at the specified index.
+    pub fn ex_data<T>(&self, index: Index<SslContext, T>) -> Option<&T> {
+        unsafe {
+            let data = ffi::SSL_CTX_get_ex_data(self.as_ptr(), index.as_raw());
+            if data.is_null() {
+                None
+            } else {
+                Some(&*(data as *const T))
+            }
+        }
+    }
 }
 
 pub struct CipherBits {
@@ -981,6 +1015,22 @@ foreign_type! {
     pub struct SslRef;
 }
 
+impl Ssl {
+    /// Returns a new extra data index.
+    ///
+    /// Each invocation of this function is guaranteed to return a distinct
+    /// index.
+    pub fn new_ex_index<T>() -> Result<Index<Ssl, T>, ErrorStack>
+    where
+        T: 'static + Sync + Send
+    {
+        unsafe {
+           let idx = try!(cvt_n(compat::get_new_ssl_idx(free_data_box::<T>)));
+            Ok(Index::from_raw(idx))
+        }
+    }
+}
+
 impl fmt::Debug for SslRef {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         let mut builder = fmt.debug_struct("Ssl");
@@ -1353,6 +1403,26 @@ impl SslRef {
     pub fn is_server(&self) -> bool {
         unsafe { compat::SSL_is_server(self.as_ptr()) != 0 }
     }
+
+    /// Sets the extra data at the specified index.
+    pub fn set_ex_data<T>(&mut self, index: Index<Ssl, T>, data: T) {
+        unsafe {
+            let data = Box::new(data);
+            ffi::SSL_set_ex_data(self.as_ptr(), index.as_raw(), Box::into_raw(data) as *mut c_void);
+        }
+    }
+
+    /// Returns a reference to the extra data at the specified index.
+    pub fn ex_data<T>(&self, index: Index<Ssl, T>) -> Option<&T> {
+        unsafe {
+            let data = ffi::SSL_get_ex_data(self.as_ptr(), index.as_raw());
+            if data.is_null() {
+                None
+            } else {
+                Some(&*(data as *const T))
+            }
+        }
+    }
 }
 
 unsafe impl Sync for Ssl {}
diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs
index bab1d711a..2bbadd954 100644
--- a/openssl/src/x509/mod.rs
+++ b/openssl/src/x509/mod.rs
@@ -14,7 +14,7 @@ use std::ptr;
 use std::slice;
 use std::str;
 
-use {cvt, cvt_p};
+use {cvt, cvt_p, cvt_n};
 use asn1::{Asn1StringRef, Asn1Time, Asn1TimeRef, Asn1BitStringRef, Asn1IntegerRef, Asn1ObjectRef};
 use bio::MemBioSlice;
 use bn::{BigNum, MSB_MAYBE_ZERO};
@@ -25,6 +25,7 @@ use nid::{self, Nid};
 use pkey::{PKey, PKeyRef};
 use stack::{Stack, StackRef, Stackable};
 use string::OpensslString;
+use ssl::SslRef;
 
 #[cfg(ossl10x)]
 use ffi::{X509_set_notBefore, X509_set_notAfter, ASN1_STRING_data, X509_STORE_CTX_get_chain};
@@ -95,6 +96,19 @@ impl X509StoreContextRef {
             Some(StackRef::from_ptr(chain))
         }
     }
+
+    /// Returns a reference to the `Ssl` associated with this context.
+    pub fn ssl(&self) -> Result<Option<&SslRef>, ErrorStack> {
+        unsafe {
+            let idx = try!(cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()));
+            let ssl = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), idx);
+            if ssl.is_null() {
+                Ok(None)
+            } else {
+                Ok(Some(SslRef::from_ptr(ssl as *mut ffi::SSL)))
+            }
+        }
+    }
 }
 
 #[deprecated(since = "0.9.7", note = "use X509Builder and X509ReqBuilder instead")]
@@ -1142,9 +1156,9 @@ mod compat {
     {
         (*(*x).req_info).subject
     }
-  
+
     pub unsafe fn X509_get0_signature(psig: *mut *const ffi::ASN1_BIT_STRING,
-                                      palg: *mut *const ffi::X509_ALGOR, 
+                                      palg: *mut *const ffi::X509_ALGOR,
                                       x: *const ffi::X509) {
         if !psig.is_null() {
             *psig = (*x).signature;
-- 
GitLab