openssl/ssl/
callbacks.rs

1use foreign_types::ForeignType;
2use foreign_types::ForeignTypeRef;
3#[cfg(any(ossl111, not(osslconf = "OPENSSL_NO_PSK")))]
4use libc::c_char;
5#[cfg(ossl111)]
6use libc::size_t;
7use libc::{c_int, c_uchar, c_uint, c_void};
8#[cfg(any(ossl111, not(osslconf = "OPENSSL_NO_PSK")))]
9use std::ffi::CStr;
10use std::mem;
11use std::ptr;
12#[cfg(any(ossl111, boringssl, awslc))]
13use std::str;
14use std::sync::Arc;
15
16use crate::dh::Dh;
17use crate::error::ErrorStack;
18use crate::pkey::Params;
19use crate::ssl::AlpnError;
20use crate::ssl::{
21    try_get_session_ctx_index, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef,
22    SslSession, SslSessionRef,
23};
24#[cfg(ossl111)]
25use crate::ssl::{ClientHelloResponse, ExtensionContext};
26use crate::util;
27#[cfg(any(ossl111, boringssl, awslc))]
28use crate::util::ForeignTypeRefExt;
29#[cfg(ossl111)]
30use crate::x509::X509Ref;
31use crate::x509::{X509StoreContext, X509StoreContextRef};
32
33pub extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
34where
35    F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
36{
37    unsafe {
38        let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx);
39        let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing");
40        let session_ctx_index =
41            try_get_session_ctx_index().expect("BUG: session context index initialization failed");
42        let verify_idx = SslContext::cached_ex_index::<F>();
43
44        // The verify-callback function pointer is copied from the SSL_CTX into the SSL at SSL_new
45        // time and is *not* updated by SSL_set_SSL_CTX (e.g. an SNI swap). So this trampoline can
46        // fire even after the SSL's current SSL_CTX has been replaced. Look up the closure on the
47        // original SSL_CTX (stashed at SESSION_CTX_INDEX in Ssl::new) rather than on the current
48        // one, otherwise the lookup would miss and the .expect() would abort the process.
49        //
50        // raw pointer shenanigans to break the borrow of ctx
51        // the callback can't mess with its own ex_data slot so this is safe
52        let verify = ctx
53            .ex_data(ssl_idx)
54            .expect("BUG: store context missing ssl")
55            .ex_data(*session_ctx_index)
56            .expect("BUG: session context missing")
57            .ex_data(verify_idx)
58            .expect("BUG: verify callback missing") as *const F;
59
60        (*verify)(preverify_ok != 0, ctx) as c_int
61    }
62}
63
64#[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
65pub extern "C" fn raw_client_psk<F>(
66    ssl: *mut ffi::SSL,
67    hint: *const c_char,
68    identity: *mut c_char,
69    max_identity_len: c_uint,
70    psk: *mut c_uchar,
71    max_psk_len: c_uint,
72) -> c_uint
73where
74    F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result<usize, ErrorStack>
75        + 'static
76        + Sync
77        + Send,
78{
79    unsafe {
80        let ssl = SslRef::from_ptr_mut(ssl);
81        let session_ctx_index =
82            try_get_session_ctx_index().expect("BUG: session context index initialization failed");
83        let callback_idx = SslContext::cached_ex_index::<F>();
84
85        // See raw_verify for the rationale: psk_client_callback is copied from the SSL_CTX into
86        // the SSL at SSL_new time and is not updated by SSL_set_SSL_CTX, so we must look up the
87        // closure on the original SSL_CTX rather than the current (potentially swapped) one.
88        let callback = ssl
89            .ex_data(*session_ctx_index)
90            .expect("BUG: session context missing")
91            .ex_data(callback_idx)
92            .expect("BUG: psk callback missing") as *const F;
93        let hint = if !hint.is_null() {
94            Some(CStr::from_ptr(hint).to_bytes())
95        } else {
96            None
97        };
98        // Give the callback mutable slices into which it can write the identity and psk.
99        let identity_sl = util::from_raw_parts_mut(identity as *mut u8, max_identity_len as usize);
100        #[allow(clippy::unnecessary_cast)]
101        let psk_sl = util::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize);
102        let psk_cap = psk_sl.len();
103        match (*callback)(ssl, hint, identity_sl, psk_sl) {
104            Ok(psk_len) if psk_len <= psk_cap => psk_len as u32,
105            Ok(_) => 0,
106            Err(e) => {
107                e.put();
108                0
109            }
110        }
111    }
112}
113
114#[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
115pub extern "C" fn raw_server_psk<F>(
116    ssl: *mut ffi::SSL,
117    identity: *const c_char,
118    psk: *mut c_uchar,
119    max_psk_len: c_uint,
120) -> c_uint
121where
122    F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8]) -> Result<usize, ErrorStack>
123        + 'static
124        + Sync
125        + Send,
126{
127    unsafe {
128        let ssl = SslRef::from_ptr_mut(ssl);
129        let session_ctx_index =
130            try_get_session_ctx_index().expect("BUG: session context index initialization failed");
131        let callback_idx = SslContext::cached_ex_index::<F>();
132
133        // See raw_verify for the rationale: psk_server_callback is copied from the SSL_CTX into
134        // the SSL at SSL_new time and is not updated by SSL_set_SSL_CTX, so we must look up the
135        // closure on the original SSL_CTX rather than the current (potentially swapped) one.
136        let callback = ssl
137            .ex_data(*session_ctx_index)
138            .expect("BUG: session context missing")
139            .ex_data(callback_idx)
140            .expect("BUG: psk callback missing") as *const F;
141        let identity = if identity.is_null() {
142            None
143        } else {
144            Some(CStr::from_ptr(identity).to_bytes())
145        };
146        // Give the callback mutable slices into which it can write the psk.
147        #[allow(clippy::unnecessary_cast)]
148        let psk_sl = util::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize);
149        let psk_cap = psk_sl.len();
150        match (*callback)(ssl, identity, psk_sl) {
151            Ok(psk_len) if psk_len <= psk_cap => psk_len as u32,
152            Ok(_) => 0,
153            Err(e) => {
154                e.put();
155                0
156            }
157        }
158    }
159}
160
161pub extern "C" fn ssl_raw_verify<F>(
162    preverify_ok: c_int,
163    x509_ctx: *mut ffi::X509_STORE_CTX,
164) -> c_int
165where
166    F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
167{
168    unsafe {
169        let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx);
170        let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing");
171        let callback_idx = Ssl::cached_ex_index::<Arc<F>>();
172
173        let callback = ctx
174            .ex_data(ssl_idx)
175            .expect("BUG: store context missing ssl")
176            .ex_data(callback_idx)
177            .expect("BUG: ssl verify callback missing")
178            .clone();
179
180        callback(preverify_ok != 0, ctx) as c_int
181    }
182}
183
184pub extern "C" fn raw_sni<F>(ssl: *mut ffi::SSL, al: *mut c_int, arg: *mut c_void) -> c_int
185where
186    F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send,
187{
188    unsafe {
189        let ssl = SslRef::from_ptr_mut(ssl);
190        let callback = arg as *const F;
191        let mut alert = SslAlert(*al);
192
193        let r = (*callback)(ssl, &mut alert);
194        *al = alert.0;
195        match r {
196            Ok(()) => ffi::SSL_TLSEXT_ERR_OK,
197            Err(e) => e.0,
198        }
199    }
200}
201
202pub extern "C" fn raw_alpn_select<F>(
203    ssl: *mut ffi::SSL,
204    out: *mut *const c_uchar,
205    outlen: *mut c_uchar,
206    inbuf: *const c_uchar,
207    inlen: c_uint,
208    _arg: *mut c_void,
209) -> c_int
210where
211    F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send,
212{
213    unsafe {
214        let ssl = SslRef::from_ptr_mut(ssl);
215        let callback = ssl
216            .ssl_context()
217            .ex_data(SslContext::cached_ex_index::<F>())
218            .expect("BUG: alpn callback missing") as *const F;
219        #[allow(clippy::unnecessary_cast)]
220        let protos = util::from_raw_parts(inbuf as *const u8, inlen as usize);
221
222        match (*callback)(ssl, protos) {
223            Ok(proto) => {
224                *out = proto.as_ptr() as *const c_uchar;
225                *outlen = proto.len() as c_uchar;
226                ffi::SSL_TLSEXT_ERR_OK
227            }
228            Err(e) => e.0,
229        }
230    }
231}
232
233pub unsafe extern "C" fn raw_tmp_dh<F>(
234    ssl: *mut ffi::SSL,
235    is_export: c_int,
236    keylength: c_int,
237) -> *mut ffi::DH
238where
239    F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send,
240{
241    let ssl = SslRef::from_ptr_mut(ssl);
242    let callback = ssl
243        .ssl_context()
244        .ex_data(SslContext::cached_ex_index::<F>())
245        .expect("BUG: tmp dh callback missing") as *const F;
246
247    match (*callback)(ssl, is_export != 0, keylength as u32) {
248        Ok(dh) => {
249            let ptr = dh.as_ptr();
250            mem::forget(dh);
251            ptr
252        }
253        Err(e) => {
254            e.put();
255            ptr::null_mut()
256        }
257    }
258}
259
260pub unsafe extern "C" fn raw_tmp_dh_ssl<F>(
261    ssl: *mut ffi::SSL,
262    is_export: c_int,
263    keylength: c_int,
264) -> *mut ffi::DH
265where
266    F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send,
267{
268    let ssl = SslRef::from_ptr_mut(ssl);
269    let callback = ssl
270        .ex_data(Ssl::cached_ex_index::<Arc<F>>())
271        .expect("BUG: ssl tmp dh callback missing")
272        .clone();
273
274    match callback(ssl, is_export != 0, keylength as u32) {
275        Ok(dh) => {
276            let ptr = dh.as_ptr();
277            mem::forget(dh);
278            ptr
279        }
280        Err(e) => {
281            e.put();
282            ptr::null_mut()
283        }
284    }
285}
286
287pub unsafe extern "C" fn raw_tlsext_status<F>(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int
288where
289    F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + 'static + Sync + Send,
290{
291    let ssl = SslRef::from_ptr_mut(ssl);
292    let callback = ssl
293        .ssl_context()
294        .ex_data(SslContext::cached_ex_index::<F>())
295        .expect("BUG: ocsp callback missing") as *const F;
296    let ret = (*callback)(ssl);
297
298    if ssl.is_server() {
299        match ret {
300            Ok(true) => ffi::SSL_TLSEXT_ERR_OK,
301            Ok(false) => ffi::SSL_TLSEXT_ERR_NOACK,
302            Err(e) => {
303                e.put();
304                ffi::SSL_TLSEXT_ERR_ALERT_FATAL
305            }
306        }
307    } else {
308        match ret {
309            Ok(true) => 1,
310            Ok(false) => 0,
311            Err(e) => {
312                e.put();
313                -1
314            }
315        }
316    }
317}
318
319pub unsafe extern "C" fn raw_new_session<F>(
320    ssl: *mut ffi::SSL,
321    session: *mut ffi::SSL_SESSION,
322) -> c_int
323where
324    F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send,
325{
326    let session_ctx_index =
327        try_get_session_ctx_index().expect("BUG: session context index initialization failed");
328    let ssl = SslRef::from_ptr_mut(ssl);
329    let callback = ssl
330        .ex_data(*session_ctx_index)
331        .expect("BUG: session context missing")
332        .ex_data(SslContext::cached_ex_index::<F>())
333        .expect("BUG: new session callback missing") as *const F;
334    let session = SslSession::from_ptr(session);
335
336    (*callback)(ssl, session);
337
338    // the return code doesn't indicate error vs success, but whether or not we consumed the session
339    1
340}
341
342pub unsafe extern "C" fn raw_remove_session<F>(
343    ctx: *mut ffi::SSL_CTX,
344    session: *mut ffi::SSL_SESSION,
345) where
346    F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send,
347{
348    let ctx = SslContextRef::from_ptr(ctx);
349    let callback = ctx
350        .ex_data(SslContext::cached_ex_index::<F>())
351        .expect("BUG: remove session callback missing");
352    let session = SslSessionRef::from_ptr(session);
353
354    callback(ctx, session)
355}
356
357pub unsafe extern "C" fn raw_get_session<F>(
358    ssl: *mut ffi::SSL,
359    data: *const c_uchar,
360    len: c_int,
361    copy: *mut c_int,
362) -> *mut ffi::SSL_SESSION
363where
364    F: Fn(&mut SslRef, &[u8]) -> Option<SslSession> + 'static + Sync + Send,
365{
366    let session_ctx_index =
367        try_get_session_ctx_index().expect("BUG: session context index initialization failed");
368    let ssl = SslRef::from_ptr_mut(ssl);
369    let callback = ssl
370        .ex_data(*session_ctx_index)
371        .expect("BUG: session context missing")
372        .ex_data(SslContext::cached_ex_index::<F>())
373        .expect("BUG: get session callback missing") as *const F;
374    #[allow(clippy::unnecessary_cast)]
375    let data = util::from_raw_parts(data as *const u8, len as usize);
376
377    match (*callback)(ssl, data) {
378        Some(session) => {
379            let p = session.as_ptr();
380            mem::forget(session);
381            *copy = 0;
382            p
383        }
384        None => ptr::null_mut(),
385    }
386}
387
388#[cfg(any(ossl111, boringssl, awslc))]
389pub unsafe extern "C" fn raw_keylog<F>(ssl: *const ffi::SSL, line: *const c_char)
390where
391    F: Fn(&SslRef, &str) + 'static + Sync + Send,
392{
393    let ssl = SslRef::from_const_ptr(ssl);
394    let callback = ssl
395        .ssl_context()
396        .ex_data(SslContext::cached_ex_index::<F>())
397        .expect("BUG: get session callback missing");
398    let line = CStr::from_ptr(line).to_bytes();
399    let line = str::from_utf8_unchecked(line);
400
401    callback(ssl, line);
402}
403
404#[cfg(ossl111)]
405pub unsafe extern "C" fn raw_stateless_cookie_generate<F>(
406    ssl: *mut ffi::SSL,
407    cookie: *mut c_uchar,
408    cookie_len: *mut size_t,
409) -> c_int
410where
411    F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send,
412{
413    let ssl = SslRef::from_ptr_mut(ssl);
414    let callback = ssl
415        .ssl_context()
416        .ex_data(SslContext::cached_ex_index::<F>())
417        .expect("BUG: stateless cookie generate callback missing") as *const F;
418    #[allow(clippy::unnecessary_cast)]
419    let slice = util::from_raw_parts_mut(cookie as *mut u8, ffi::SSL_COOKIE_LENGTH as usize);
420    let cap = slice.len();
421    match (*callback)(ssl, slice) {
422        Ok(len) if len <= cap => {
423            *cookie_len = len as size_t;
424            1
425        }
426        Ok(_) => 0,
427        Err(e) => {
428            e.put();
429            0
430        }
431    }
432}
433
434#[cfg(ossl111)]
435pub unsafe extern "C" fn raw_stateless_cookie_verify<F>(
436    ssl: *mut ffi::SSL,
437    cookie: *const c_uchar,
438    cookie_len: size_t,
439) -> c_int
440where
441    F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send,
442{
443    let ssl = SslRef::from_ptr_mut(ssl);
444    let callback = ssl
445        .ssl_context()
446        .ex_data(SslContext::cached_ex_index::<F>())
447        .expect("BUG: stateless cookie verify callback missing") as *const F;
448    #[allow(clippy::unnecessary_cast)]
449    let slice = util::from_raw_parts(cookie as *const c_uchar as *const u8, cookie_len);
450    (*callback)(ssl, slice) as c_int
451}
452
453#[cfg(not(any(boringssl, awslc)))]
454pub extern "C" fn raw_cookie_generate<F>(
455    ssl: *mut ffi::SSL,
456    cookie: *mut c_uchar,
457    cookie_len: *mut c_uint,
458) -> c_int
459where
460    F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send,
461{
462    unsafe {
463        let ssl = SslRef::from_ptr_mut(ssl);
464        let callback = ssl
465            .ssl_context()
466            .ex_data(SslContext::cached_ex_index::<F>())
467            .expect("BUG: cookie generate callback missing") as *const F;
468        // We subtract 1 from DTLS1_COOKIE_LENGTH as the ostensible value, 256, is erroneous but retained for
469        // compatibility. See comments in dtls1.h.
470        #[allow(clippy::unnecessary_cast)]
471        let slice =
472            util::from_raw_parts_mut(cookie as *mut u8, ffi::DTLS1_COOKIE_LENGTH as usize - 1);
473        let cap = slice.len();
474        match (*callback)(ssl, slice) {
475            Ok(len) if len <= cap => {
476                *cookie_len = len as c_uint;
477                1
478            }
479            Ok(_) => 0,
480            Err(e) => {
481                e.put();
482                0
483            }
484        }
485    }
486}
487
488#[cfg(not(any(boringssl, awslc)))]
489pub extern "C" fn raw_cookie_verify<F>(
490    ssl: *mut ffi::SSL,
491    cookie: *const c_uchar,
492    cookie_len: c_uint,
493) -> c_int
494where
495    F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send,
496{
497    unsafe {
498        let ssl = SslRef::from_ptr_mut(ssl);
499        let callback = ssl
500            .ssl_context()
501            .ex_data(SslContext::cached_ex_index::<F>())
502            .expect("BUG: cookie verify callback missing") as *const F;
503        #[allow(clippy::unnecessary_cast)]
504        let slice =
505            util::from_raw_parts(cookie as *const c_uchar as *const u8, cookie_len as usize);
506        (*callback)(ssl, slice) as c_int
507    }
508}
509
510#[cfg(ossl111)]
511pub struct CustomExtAddState<T>(Option<T>);
512
513#[cfg(ossl111)]
514pub extern "C" fn raw_custom_ext_add<F, T>(
515    ssl: *mut ffi::SSL,
516    _: c_uint,
517    context: c_uint,
518    out: *mut *const c_uchar,
519    outlen: *mut size_t,
520    x: *mut ffi::X509,
521    chainidx: size_t,
522    al: *mut c_int,
523    _: *mut c_void,
524) -> c_int
525where
526    F: Fn(&mut SslRef, ExtensionContext, Option<(usize, &X509Ref)>) -> Result<Option<T>, SslAlert>
527        + 'static
528        + Sync
529        + Send,
530    T: AsRef<[u8]> + 'static + Sync + Send,
531{
532    unsafe {
533        let ssl = SslRef::from_ptr_mut(ssl);
534        let callback = ssl
535            .ssl_context()
536            .ex_data(SslContext::cached_ex_index::<F>())
537            .expect("BUG: custom ext add callback missing") as *const F;
538        let ectx = ExtensionContext::from_bits_truncate(context);
539        let cert = if ectx.contains(ExtensionContext::TLS1_3_CERTIFICATE) {
540            Some((chainidx, X509Ref::from_ptr(x)))
541        } else {
542            None
543        };
544        match (*callback)(ssl, ectx, cert) {
545            Ok(None) => 0,
546            Ok(Some(buf)) => {
547                let idx = Ssl::cached_ex_index::<CustomExtAddState<T>>();
548                let mut buf = Some(buf);
549                let new = match ssl.ex_data_mut(idx) {
550                    Some(state) => {
551                        state.0 = buf.take();
552                        false
553                    }
554                    None => true,
555                };
556                if new {
557                    ssl.set_ex_data(idx, CustomExtAddState(buf));
558                }
559
560                // Capture the out pointer AFTER buf has been moved into ex_data.
561                // The move invalidates any previous pointer into buf.
562                let stored = ssl.ex_data(idx).unwrap();
563                let data = stored.0.as_ref().unwrap().as_ref();
564                *outlen = data.len();
565                *out = data.as_ptr();
566
567                1
568            }
569            Err(alert) => {
570                *al = alert.0;
571                -1
572            }
573        }
574    }
575}
576
577#[cfg(ossl111)]
578pub extern "C" fn raw_custom_ext_free<T>(
579    ssl: *mut ffi::SSL,
580    _: c_uint,
581    _: c_uint,
582    _: *const c_uchar,
583    _: *mut c_void,
584) where
585    T: 'static + Sync + Send,
586{
587    unsafe {
588        let ssl = SslRef::from_ptr_mut(ssl);
589        let idx = Ssl::cached_ex_index::<CustomExtAddState<T>>();
590        if let Some(state) = ssl.ex_data_mut(idx) {
591            state.0 = None;
592        }
593    }
594}
595
596#[cfg(ossl111)]
597pub extern "C" fn raw_custom_ext_parse<F>(
598    ssl: *mut ffi::SSL,
599    _: c_uint,
600    context: c_uint,
601    input: *const c_uchar,
602    inlen: size_t,
603    x: *mut ffi::X509,
604    chainidx: size_t,
605    al: *mut c_int,
606    _: *mut c_void,
607) -> c_int
608where
609    F: Fn(&mut SslRef, ExtensionContext, &[u8], Option<(usize, &X509Ref)>) -> Result<(), SslAlert>
610        + 'static
611        + Sync
612        + Send,
613{
614    unsafe {
615        let ssl = SslRef::from_ptr_mut(ssl);
616        let callback = ssl
617            .ssl_context()
618            .ex_data(SslContext::cached_ex_index::<F>())
619            .expect("BUG: custom ext parse callback missing") as *const F;
620        let ectx = ExtensionContext::from_bits_truncate(context);
621        #[allow(clippy::unnecessary_cast)]
622        let slice = util::from_raw_parts(input as *const u8, inlen);
623        let cert = if ectx.contains(ExtensionContext::TLS1_3_CERTIFICATE) {
624            Some((chainidx, X509Ref::from_ptr(x)))
625        } else {
626            None
627        };
628        match (*callback)(ssl, ectx, slice, cert) {
629            Ok(()) => 1,
630            Err(alert) => {
631                *al = alert.0;
632                0
633            }
634        }
635    }
636}
637
638#[cfg(ossl111)]
639pub unsafe extern "C" fn raw_client_hello<F>(
640    ssl: *mut ffi::SSL,
641    al: *mut c_int,
642    arg: *mut c_void,
643) -> c_int
644where
645    F: Fn(&mut SslRef, &mut SslAlert) -> Result<ClientHelloResponse, ErrorStack>
646        + 'static
647        + Sync
648        + Send,
649{
650    let ssl = SslRef::from_ptr_mut(ssl);
651    let callback = arg as *const F;
652    let mut alert = SslAlert(*al);
653
654    let r = (*callback)(ssl, &mut alert);
655    *al = alert.0;
656    match r {
657        Ok(c) => c.0,
658        Err(e) => {
659            e.put();
660            ffi::SSL_CLIENT_HELLO_ERROR
661        }
662    }
663}