Skip to content

Commit 42ca6f6

Browse files
committed
Implement SSL_CTX_set_info_callback and SSL_set_info_callback
This fires only for received alerts, and alerts sent at the start of the server's handshake.
1 parent 073fcf5 commit 42ca6f6

5 files changed

Lines changed: 94 additions & 17 deletions

File tree

MATRIX.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@
158158
| `SSL_CTX_set_default_verify_store` | | | | :exclamation: [^stub] |
159159
| `SSL_CTX_set_ex_data` | | :white_check_mark: | :white_check_mark: | :white_check_mark: |
160160
| `SSL_CTX_set_generate_session_id` | | | | |
161-
| `SSL_CTX_set_info_callback` | | :white_check_mark: | :white_check_mark: | :exclamation: [^stub] |
161+
| `SSL_CTX_set_info_callback` | | :white_check_mark: | :white_check_mark: | :white_check_mark: |
162162
| `SSL_CTX_set_keylog_callback` | :white_check_mark: | :white_check_mark: | :white_check_mark: | :exclamation: [^stub] |
163163
| `SSL_CTX_set_max_early_data` | | :white_check_mark: | | :white_check_mark: |
164164
| `SSL_CTX_set_msg_callback` | :white_check_mark: | | :white_check_mark: | :exclamation: [^stub] |
@@ -440,7 +440,7 @@
440440
| `SSL_set_fd` [^sock] | :white_check_mark: | :white_check_mark: | | :white_check_mark: |
441441
| `SSL_set_generate_session_id` | | | | |
442442
| `SSL_set_hostflags` | | | | |
443-
| `SSL_set_info_callback` | | | | |
443+
| `SSL_set_info_callback` | | | | :white_check_mark: |
444444
| `SSL_set_max_early_data` | | :white_check_mark: | :white_check_mark: | :white_check_mark: |
445445
| `SSL_set_msg_callback` | | :white_check_mark: | :white_check_mark: | :exclamation: [^stub] |
446446
| `SSL_set_not_resumable_session_callback` | | | | |

build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ const ENTRYPOINTS: &[&str] = &[
230230
"SSL_set_connect_state",
231231
"SSL_set_ex_data",
232232
"SSL_set_fd",
233+
"SSL_set_info_callback",
233234
"SSL_set_max_early_data",
234235
"SSL_set_msg_callback",
235236
"SSL_set_num_tickets",

src/callbacks.rs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use openssl_sys::{SSL_TLSEXT_ERR_NOACK, SSL_TLSEXT_ERR_OK};
77
use rustls::AlertDescription;
88

99
use crate::entry::{
10-
SSL_CTX_alpn_select_cb_func, SSL_CTX_cert_cb_func, SSL_CTX_new_session_cb,
11-
SSL_CTX_servername_callback_func, SSL_CTX_sess_get_cb, SSL_CTX_sess_remove_cb,
12-
_SSL_SESSION_free, SSL, SSL_CTX, SSL_SESSION,
10+
SSL_CTX_alpn_select_cb_func, SSL_CTX_cert_cb_func, SSL_CTX_info_callback_func,
11+
SSL_CTX_new_session_cb, SSL_CTX_servername_callback_func, SSL_CTX_sess_get_cb,
12+
SSL_CTX_sess_remove_cb, _SSL_SESSION_free, SSL, SSL_CTX, SSL_SESSION,
1313
};
1414
use crate::error::Error;
1515
use crate::ffi;
@@ -237,3 +237,47 @@ pub fn invoke_session_remove_callback(
237237

238238
_SSL_SESSION_free(sess_ptr);
239239
}
240+
241+
/// Configuration needed to call an [`SSL_CTX_info_callback_func`] later
242+
#[derive(Debug, Default, Clone)]
243+
pub struct InfoCallbackConfig {
244+
pub cb: SSL_CTX_info_callback_func,
245+
}
246+
247+
impl InfoCallbackConfig {
248+
pub fn invoke(&self, info: Info) {
249+
let Some(callback) = self.cb else {
250+
return;
251+
};
252+
253+
let ssl = SslCallbackContext::ssl_ptr();
254+
unsafe { callback(ssl, info.typ(), info.val()) };
255+
}
256+
}
257+
258+
pub enum Info {
259+
/// `SSL_CB_ALERT | SSL_CB_READ`
260+
AlertReceived(AlertDescription),
261+
262+
/// `SSL_CB_ALERT | SSL_CB_WRITE`
263+
AlertSent(AlertDescription),
264+
}
265+
266+
impl Info {
267+
fn typ(&self) -> c_int {
268+
match self {
269+
Self::AlertReceived(_) => SSL_CB_ALERT | SSL_CB_READ,
270+
Self::AlertSent(_) => SSL_CB_ALERT | SSL_CB_WRITE,
271+
}
272+
}
273+
274+
fn val(&self) -> c_int {
275+
match self {
276+
Self::AlertReceived(a) | Self::AlertSent(a) => u8::from(*a) as c_int,
277+
}
278+
}
279+
}
280+
281+
const SSL_CB_ALERT: c_int = 0x4000;
282+
const SSL_CB_READ: c_int = 0x0004;
283+
const SSL_CB_WRITE: c_int = 0x0008;

src/entry.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,15 @@ entry! {
703703
}
704704
}
705705

706+
entry! {
707+
pub fn _SSL_CTX_set_info_callback(ctx: *mut SSL_CTX, cb: SSL_CTX_info_callback_func) {
708+
try_clone_arc!(ctx).get_mut().set_info_callback(cb);
709+
}
710+
}
711+
712+
pub type SSL_CTX_info_callback_func =
713+
Option<unsafe extern "C" fn(ssl: *mut SSL, type_: c_int, val: c_int)>;
714+
706715
entry! {
707716
pub fn _SSL_CTX_get_max_early_data(ctx: *const SSL_CTX) -> u32 {
708717
try_clone_arc!(ctx).get().get_max_early_data()
@@ -1047,6 +1056,12 @@ entry! {
10471056
}
10481057
}
10491058

1059+
entry! {
1060+
pub fn _SSL_set_info_callback(ssl: *mut SSL, cb: SSL_CTX_info_callback_func) {
1061+
try_clone_arc!(ssl).get_mut().set_info_callback(cb);
1062+
}
1063+
}
1064+
10501065
entry! {
10511066
pub fn _SSL_set_alpn_protos(
10521067
ssl: *mut SSL,
@@ -2327,15 +2342,6 @@ pub type SSL_CTX_msg_cb_func = Option<
23272342
),
23282343
>;
23292344

2330-
// no state machine observation
2331-
2332-
entry_stub! {
2333-
pub fn _SSL_CTX_set_info_callback(
2334-
_ctx: *mut SSL_CTX,
2335-
_cb: Option<unsafe extern "C" fn(ssl: *const SSL, type_: c_int, val: c_int)>,
2336-
);
2337-
}
2338-
23392345
// no NPN (obsolete precursor to ALPN)
23402346

23412347
entry_stub! {

src/lib.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use rustls::crypto::{aws_lc_rs as provider, SupportedKxGroup};
1616
use rustls::pki_types::{CertificateDer, ServerName};
1717
use rustls::server::{Accepted, Acceptor, ProducesTickets};
1818
use rustls::{
19-
CipherSuite, ClientConfig, ClientConnection, Connection, HandshakeKind, ProtocolVersion,
20-
ServerConfig, SignatureScheme, SupportedProtocolVersion,
19+
AlertDescription, CipherSuite, ClientConfig, ClientConnection, Connection, HandshakeKind,
20+
ProtocolVersion, ServerConfig, SignatureScheme, SupportedProtocolVersion,
2121
};
2222

2323
use not_thread_safe::NotThreadSafe;
@@ -456,6 +456,7 @@ pub struct SslContext {
456456
alpn_callback: callbacks::AlpnCallbackConfig,
457457
cert_callback: callbacks::CertCallbackConfig,
458458
servername_callback: callbacks::ServerNameCallbackConfig,
459+
info_callback: callbacks::InfoCallbackConfig,
459460
auth_keys: sign::CertifiedKeySet,
460461
max_early_data: u32,
461462
}
@@ -486,6 +487,7 @@ impl SslContext {
486487
alpn_callback: callbacks::AlpnCallbackConfig::default(),
487488
cert_callback: callbacks::CertCallbackConfig::default(),
488489
servername_callback: callbacks::ServerNameCallbackConfig::default(),
490+
info_callback: callbacks::InfoCallbackConfig::default(),
489491
auth_keys: sign::CertifiedKeySet::default(),
490492
max_early_data: 0,
491493
}
@@ -600,6 +602,10 @@ impl SslContext {
600602
self.caches.flush_all();
601603
}
602604

605+
fn set_info_callback(&mut self, callback: entry::SSL_CTX_info_callback_func) {
606+
self.info_callback.cb = callback;
607+
}
608+
603609
fn set_max_early_data(&mut self, max: u32) {
604610
self.max_early_data = max;
605611
}
@@ -776,6 +782,7 @@ struct Ssl {
776782
alpn: Vec<Vec<u8>>,
777783
alpn_callback: callbacks::AlpnCallbackConfig,
778784
cert_callback: callbacks::CertCallbackConfig,
785+
info_callback: callbacks::InfoCallbackConfig,
779786
servername_callback: callbacks::ServerNameCallbackConfig,
780787
sni_server_name: Option<ServerName<'static>>,
781788
server_name: Option<CString>,
@@ -817,6 +824,7 @@ impl Ssl {
817824
alpn: inner.alpn.clone(),
818825
alpn_callback: inner.alpn_callback.clone(),
819826
cert_callback: inner.cert_callback.clone(),
827+
info_callback: inner.info_callback.clone(),
820828
servername_callback: inner.servername_callback.clone(),
821829
sni_server_name: None,
822830
server_name: None,
@@ -927,6 +935,10 @@ impl Ssl {
927935
.unwrap_or_default()
928936
}
929937

938+
fn set_info_callback(&mut self, cb: entry::SSL_CTX_info_callback_func) {
939+
self.info_callback.cb = cb;
940+
}
941+
930942
fn set_alpn_offer(&mut self, alpn: Vec<Vec<u8>>) {
931943
self.alpn = alpn;
932944
}
@@ -1301,6 +1313,10 @@ impl Ssl {
13011313
// obtain underlying TLS protocol error (if any), and let it stamp
13021314
// out the one wrapped in io::Error.
13031315
if let Some(tls_err) = conn.process_new_packets().err() {
1316+
if let rustls::Error::AlertReceived(alert) = &tls_err {
1317+
self.info_callback
1318+
.invoke(callbacks::Info::AlertReceived(*alert));
1319+
}
13041320
return Err(error::Error::from_rustls(tls_err));
13051321
}
13061322
return Err(error::Error::from_io(e));
@@ -1326,7 +1342,17 @@ impl Ssl {
13261342
self.invoke_accepted_callbacks()
13271343
}
13281344
Err((error, mut alert)) => {
1329-
alert.write_all(bio).map_err(error::Error::from_io)?;
1345+
let mut buffer = Vec::new();
1346+
alert.write_all(&mut buffer).unwrap();
1347+
1348+
// this only works for unencrypted alerts (header plus `Alert` structure)
1349+
if buffer.len() == (5 + 2) {
1350+
self.info_callback.invoke(callbacks::Info::AlertSent(
1351+
AlertDescription::from(buffer[6]),
1352+
));
1353+
}
1354+
1355+
bio.write_all(&buffer).map_err(error::Error::from_io)?;
13301356
Err(error::Error::from_rustls(error))
13311357
}
13321358
}

0 commit comments

Comments
 (0)