diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index afbe673..fdf5a6b 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -69,8 +69,17 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_CTX_set_verify", "SSL_CTX_up_ref", "SSL_free", + "SSL_get0_alpn_selected", + "SSL_get0_peer_certificate", + "SSL_get0_verified_chain", + "SSL_get1_peer_certificate", + "SSL_get_current_cipher", + "SSL_get_error", "SSL_get_options", + "SSL_get_peer_cert_chain", "SSL_get_shutdown", + "SSL_get_verify_result", + "SSL_get_version", "SSL_has_pending", "SSL_is_server", "SSL_new", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index e5519a2..b7a103a 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -8,7 +8,9 @@ use std::os::raw::{c_char, c_int, c_long, c_uchar, c_uint, c_void}; use std::sync::Mutex; use std::{fs, io, path::PathBuf}; -use openssl_sys::{OPENSSL_malloc, X509_STORE, X509_STORE_CTX}; +use openssl_sys::{ + stack_st_X509, OPENSSL_malloc, X509, X509_STORE, X509_STORE_CTX, X509_V_ERR_UNSPECIFIED, +}; use crate::bio::{Bio, BIO}; use crate::error::{ffi_panic_boundary, Error, MysteriouslyOppositeReturnValue}; @@ -632,6 +634,113 @@ entry! { } } +entry! { + pub fn _SSL_get_error(ssl: *const SSL, _ret_code: c_int) -> c_int { + let ssl = try_clone_arc!(ssl); + ssl.lock() + .map_err(|_| Error::cannot_lock()) + .map(|mut ssl| ssl.get_error() as c_int) + .map_err(|err| err.raise()) + .unwrap_or_default() + } +} + +entry! { + pub fn _SSL_get0_alpn_selected(ssl: *const SSL, data: *mut *const c_uchar, len: *mut c_uint) { + if data.is_null() || len.is_null() { + return; + } + + let ssl = try_clone_arc!(ssl); + + match ssl.lock().ok().and_then(|mut ssl| { + ssl.get_agreed_alpn().map(|proto| { + unsafe { + // nb. alpn protocols are limited to 255 octets + ptr::write(len, proto.len() as u32); + ptr::write(data, proto.as_ptr()); + }; + }) + }) { + Some(()) => {} + None => unsafe { + ptr::write(len, 0); + ptr::write(data, ptr::null()); + }, + } + } +} + +entry! { + pub fn _SSL_get_peer_cert_chain(ssl: *const SSL) -> *mut stack_st_X509 { + let ssl = try_clone_arc!(ssl); + ssl.lock() + .ok() + .and_then(|mut ssl| ssl.get_peer_cert_chain().map(|x509| x509.pointer())) + .unwrap_or_else(ptr::null_mut) + } +} + +entry! { + pub fn _SSL_get0_verified_chain(ssl: *const SSL) -> *mut stack_st_X509 { + _SSL_get_peer_cert_chain(ssl) + } +} + +entry! { + pub fn _SSL_get0_peer_certificate(ssl: *const SSL) -> *mut X509 { + let ssl = try_clone_arc!(ssl); + ssl.lock() + .ok() + .and_then(|mut ssl| ssl.get_peer_cert().map(|x509| x509.borrow_ref())) + .unwrap_or_else(ptr::null_mut) + } +} + +entry! { + pub fn _SSL_get1_peer_certificate(ssl: *const SSL) -> *mut X509 { + let ssl = try_clone_arc!(ssl); + ssl.lock() + .ok() + .and_then(|mut ssl| ssl.get_peer_cert().map(|x509| x509.up_ref())) + .unwrap_or_else(ptr::null_mut) + } +} + +entry! { + pub fn _SSL_get_current_cipher(ssl: *const SSL) -> *const SSL_CIPHER { + let ssl = try_clone_arc!(ssl); + ssl.lock() + .ok() + .and_then(|ssl| ssl.get_negotiated_cipher_suite_id()) + .and_then(crate::SslCipher::find_by_id) + .map(|cipher| cipher as *const SSL_CIPHER) + .unwrap_or_else(ptr::null) + } +} + +entry! { + pub fn _SSL_get_version(ssl: *const SSL) -> *const c_char { + let ssl = try_clone_arc!(ssl); + ssl.lock() + .ok() + .and_then(|ssl| ssl.get_negotiated_cipher_suite_id()) + .and_then(crate::SslCipher::find_by_id) + .map(|cipher| cipher.version.as_ptr()) + .unwrap_or_else(ptr::null) + } +} + +entry! { + pub fn _SSL_get_verify_result(ssl: *const SSL) -> c_long { + let ssl = try_clone_arc!(ssl); + ssl.lock() + .ok() + .map(|ssl| ssl.get_last_verification_result()) + .unwrap_or(X509_V_ERR_UNSPECIFIED as i64) + } +} + impl Castable for SSL { type Ownership = OwnershipArc; type RustType = Mutex; diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index 3c1e3bf..94ead95 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -1,8 +1,11 @@ -use core::ffi::CStr; +use core::ffi::{c_int, CStr}; use std::io::{ErrorKind, Read, Write}; use std::sync::{Arc, Mutex}; -use openssl_sys::X509_STORE; +use openssl_sys::{ + SSL_ERROR_NONE, SSL_ERROR_SSL, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, X509_STORE, + X509_V_ERR_UNSPECIFIED, +}; use rustls::crypto::ring as provider; use rustls::pki_types::{CertificateDer, ServerName}; use rustls::{CipherSuite, ClientConfig, ClientConnection, Connection, RootCertStore}; @@ -288,6 +291,8 @@ struct Ssl { bio: Option, conn: Option, verifier: Option>, + peer_cert: Option, + peer_cert_chain: Option, shutdown_flags: ShutdownFlags, } @@ -305,6 +310,8 @@ impl Ssl { bio: None, conn: None, verifier: None, + peer_cert: None, + peer_cert_chain: None, shutdown_flags: ShutdownFlags::default(), } } @@ -536,6 +543,96 @@ impl Ssl { }) .unwrap_or_default() } + + fn get_agreed_alpn(&mut self) -> Option<&[u8]> { + self.conn.as_ref().and_then(|conn| conn.alpn_protocol()) + } + + fn init_peer_cert(&mut self) { + let conn = match &self.conn { + Some(conn) => conn, + None => return, + }; + + let certs = match conn.peer_certificates() { + Some(certs) => certs, + None => return, + }; + + let mut stack = x509::OwnedX509Stack::empty(); + for (i, cert) in certs.iter().enumerate() { + let converted = match x509::OwnedX509::parse_der(cert.as_ref()) { + Some(converted) => converted, + None => return, + }; + + if i == 0 { + if !self.is_server() { + // See docs for `SSL_get_peer_cert_chain`: + // "If called on the client side, the stack also contains + // the peer's certificate; if called on the server side, the peer's + // certificate must be obtained separately" + stack.push(&converted); + } + self.peer_cert = Some(converted); + } else { + stack.push(&converted); + } + } + + self.peer_cert_chain = Some(stack); + } + + fn get_peer_cert(&mut self) -> Option<&x509::OwnedX509> { + if self.peer_cert.is_none() { + self.init_peer_cert(); + } + self.peer_cert.as_ref() + } + + fn get_peer_cert_chain(&mut self) -> Option<&x509::OwnedX509Stack> { + if self.peer_cert_chain.is_none() { + self.init_peer_cert(); + } + self.peer_cert_chain.as_ref() + } + + fn get_negotiated_cipher_suite_id(&self) -> Option { + self.conn + .as_ref() + .and_then(|conn| conn.negotiated_cipher_suite()) + .map(|suite| suite.suite()) + } + + fn get_last_verification_result(&self) -> i64 { + if let Some(verifier) = &self.verifier { + verifier.last_result() + } else { + X509_V_ERR_UNSPECIFIED as i64 + } + } + + fn get_error(&mut self) -> c_int { + match &mut self.conn { + Some(ref mut conn) => { + if let Err(e) = conn.process_new_packets() { + error::Error::from_rustls(e).raise(); + return SSL_ERROR_SSL; + } + + if let Some(bio) = self.bio.as_ref() { + if bio.read_would_block() { + return SSL_ERROR_WANT_READ; + } else if bio.write_would_block() { + return SSL_ERROR_WANT_WRITE; + } + } + + SSL_ERROR_NONE + } + None => SSL_ERROR_SSL, + } + } } #[derive(Default)]