-
-
Notifications
You must be signed in to change notification settings - Fork 749
/
mod.rs
4345 lines (3939 loc) · 153 KB
/
mod.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//! SSL/TLS support.
//!
//! `SslConnector` and `SslAcceptor` should be used in most cases - they handle
//! configuration of the OpenSSL primitives for you.
//!
//! # Examples
//!
//! To connect as a client to a remote server:
//!
//! ```no_run
//! use openssl::ssl::{SslMethod, SslConnector};
//! use std::io::{Read, Write};
//! use std::net::TcpStream;
//!
//! let connector = SslConnector::builder(SslMethod::tls()).unwrap().build();
//!
//! let stream = TcpStream::connect("google.com:443").unwrap();
//! let mut stream = connector.connect("google.com", stream).unwrap();
//!
//! stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
//! let mut res = vec![];
//! stream.read_to_end(&mut res).unwrap();
//! println!("{}", String::from_utf8_lossy(&res));
//! ```
//!
//! To accept connections as a server from remote clients:
//!
//! ```no_run
//! use openssl::ssl::{SslMethod, SslAcceptor, SslStream, SslFiletype};
//! use std::net::{TcpListener, TcpStream};
//! use std::sync::Arc;
//! use std::thread;
//!
//!
//! let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
//! acceptor.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
//! acceptor.set_certificate_chain_file("certs.pem").unwrap();
//! acceptor.check_private_key().unwrap();
//! let acceptor = Arc::new(acceptor.build());
//!
//! let listener = TcpListener::bind("0.0.0.0:8443").unwrap();
//!
//! fn handle_client(stream: SslStream<TcpStream>) {
//! // ...
//! }
//!
//! for stream in listener.incoming() {
//! match stream {
//! Ok(stream) => {
//! let acceptor = acceptor.clone();
//! thread::spawn(move || {
//! let stream = acceptor.accept(stream).unwrap();
//! handle_client(stream);
//! });
//! }
//! Err(e) => { /* connection failed */ }
//! }
//! }
//! ```
#[cfg(ossl300)]
use crate::cvt_long;
use crate::dh::{Dh, DhRef};
#[cfg(all(ossl101, not(ossl110)))]
use crate::ec::EcKey;
use crate::ec::EcKeyRef;
use crate::error::ErrorStack;
use crate::ex_data::Index;
#[cfg(ossl111)]
use crate::hash::MessageDigest;
#[cfg(any(ossl110, libressl270))]
use crate::nid::Nid;
use crate::pkey::{HasPrivate, PKeyRef, Params, Private};
#[cfg(ossl300)]
use crate::pkey::{PKey, Public};
use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef};
use crate::ssl::bio::BioMethod;
use crate::ssl::callbacks::*;
use crate::ssl::error::InnerError;
use crate::stack::{Stack, StackRef, Stackable};
use crate::util;
use crate::util::{ForeignTypeExt, ForeignTypeRefExt};
use crate::x509::store::{X509Store, X509StoreBuilderRef, X509StoreRef};
#[cfg(any(ossl102, boringssl, libressl261))]
use crate::x509::verify::X509VerifyParamRef;
use crate::x509::{X509Name, X509Ref, X509StoreContextRef, X509VerifyResult, X509};
use crate::{cvt, cvt_n, cvt_p, init};
use bitflags::bitflags;
use cfg_if::cfg_if;
use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_void};
use once_cell::sync::{Lazy, OnceCell};
use openssl_macros::corresponds;
use std::any::TypeId;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::fmt;
use std::io;
use std::io::prelude::*;
use std::marker::PhantomData;
use std::mem::{self, ManuallyDrop, MaybeUninit};
use std::ops::{Deref, DerefMut};
use std::panic::resume_unwind;
use std::path::Path;
use std::ptr;
use std::str;
use std::sync::{Arc, Mutex};
pub use crate::ssl::connector::{
ConnectConfiguration, SslAcceptor, SslAcceptorBuilder, SslConnector, SslConnectorBuilder,
};
pub use crate::ssl::error::{Error, ErrorCode, HandshakeError};
mod bio;
mod callbacks;
mod connector;
mod error;
#[cfg(test)]
mod test;
/// Returns the OpenSSL name of a cipher corresponding to an RFC-standard cipher name.
///
/// If the cipher has no corresponding OpenSSL name, the string `(NONE)` is returned.
///
/// Requires OpenSSL 1.1.1 or newer.
#[corresponds(OPENSSL_cipher_name)]
#[cfg(ossl111)]
pub fn cipher_name(std_name: &str) -> &'static str {
unsafe {
ffi::init();
let s = CString::new(std_name).unwrap();
let ptr = ffi::OPENSSL_cipher_name(s.as_ptr());
CStr::from_ptr(ptr).to_str().unwrap()
}
}
cfg_if! {
if #[cfg(ossl300)] {
type SslOptionsRepr = u64;
} else if #[cfg(boringssl)] {
type SslOptionsRepr = u32;
} else {
type SslOptionsRepr = libc::c_ulong;
}
}
bitflags! {
/// Options controlling the behavior of an `SslContext`.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct SslOptions: SslOptionsRepr {
/// Disables a countermeasure against an SSLv3/TLSv1.0 vulnerability affecting CBC ciphers.
const DONT_INSERT_EMPTY_FRAGMENTS = ffi::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS as SslOptionsRepr;
/// A "reasonable default" set of options which enables compatibility flags.
#[cfg(not(boringssl))]
const ALL = ffi::SSL_OP_ALL as SslOptionsRepr;
/// Do not query the MTU.
///
/// Only affects DTLS connections.
const NO_QUERY_MTU = ffi::SSL_OP_NO_QUERY_MTU as SslOptionsRepr;
/// Enables Cookie Exchange as described in [RFC 4347 Section 4.2.1].
///
/// Only affects DTLS connections.
///
/// [RFC 4347 Section 4.2.1]: https://tools.ietf.org/html/rfc4347#section-4.2.1
#[cfg(not(boringssl))]
const COOKIE_EXCHANGE = ffi::SSL_OP_COOKIE_EXCHANGE as SslOptionsRepr;
/// Disables the use of session tickets for session resumption.
const NO_TICKET = ffi::SSL_OP_NO_TICKET as SslOptionsRepr;
/// Always start a new session when performing a renegotiation on the server side.
#[cfg(not(boringssl))]
const NO_SESSION_RESUMPTION_ON_RENEGOTIATION =
ffi::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION as SslOptionsRepr;
/// Disables the use of TLS compression.
#[cfg(not(boringssl))]
const NO_COMPRESSION = ffi::SSL_OP_NO_COMPRESSION as SslOptionsRepr;
/// Allow legacy insecure renegotiation with servers or clients that do not support secure
/// renegotiation.
const ALLOW_UNSAFE_LEGACY_RENEGOTIATION =
ffi::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION as SslOptionsRepr;
/// Creates a new key for each session when using ECDHE.
///
/// This is always enabled in OpenSSL 1.1.0.
const SINGLE_ECDH_USE = ffi::SSL_OP_SINGLE_ECDH_USE as SslOptionsRepr;
/// Creates a new key for each session when using DHE.
///
/// This is always enabled in OpenSSL 1.1.0.
const SINGLE_DH_USE = ffi::SSL_OP_SINGLE_DH_USE as SslOptionsRepr;
/// Use the server's preferences rather than the client's when selecting a cipher.
///
/// This has no effect on the client side.
const CIPHER_SERVER_PREFERENCE = ffi::SSL_OP_CIPHER_SERVER_PREFERENCE as SslOptionsRepr;
/// Disables version rollback attach detection.
const TLS_ROLLBACK_BUG = ffi::SSL_OP_TLS_ROLLBACK_BUG as SslOptionsRepr;
/// Disables the use of SSLv2.
const NO_SSLV2 = ffi::SSL_OP_NO_SSLv2 as SslOptionsRepr;
/// Disables the use of SSLv3.
const NO_SSLV3 = ffi::SSL_OP_NO_SSLv3 as SslOptionsRepr;
/// Disables the use of TLSv1.0.
const NO_TLSV1 = ffi::SSL_OP_NO_TLSv1 as SslOptionsRepr;
/// Disables the use of TLSv1.1.
const NO_TLSV1_1 = ffi::SSL_OP_NO_TLSv1_1 as SslOptionsRepr;
/// Disables the use of TLSv1.2.
const NO_TLSV1_2 = ffi::SSL_OP_NO_TLSv1_2 as SslOptionsRepr;
/// Disables the use of TLSv1.3.
///
/// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
#[cfg(any(boringssl, ossl111, libressl340))]
const NO_TLSV1_3 = ffi::SSL_OP_NO_TLSv1_3 as SslOptionsRepr;
/// Disables the use of DTLSv1.0
///
/// Requires OpenSSL 1.0.2 or LibreSSL 3.3.2 or newer.
#[cfg(any(boringssl, ossl102, ossl110, libressl332))]
const NO_DTLSV1 = ffi::SSL_OP_NO_DTLSv1 as SslOptionsRepr;
/// Disables the use of DTLSv1.2.
///
/// Requires OpenSSL 1.0.2 or LibreSSL 3.3.2 or newer.
#[cfg(any(boringssl, ossl102, ossl110, libressl332))]
const NO_DTLSV1_2 = ffi::SSL_OP_NO_DTLSv1_2 as SslOptionsRepr;
/// Disables the use of all (D)TLS protocol versions.
///
/// This can be used as a mask when whitelisting protocol versions.
///
/// Requires OpenSSL 1.0.2 or newer.
///
/// # Examples
///
/// Only support TLSv1.2:
///
/// ```rust
/// use openssl::ssl::SslOptions;
///
/// let options = SslOptions::NO_SSL_MASK & !SslOptions::NO_TLSV1_2;
/// ```
#[cfg(any(ossl102, ossl110))]
const NO_SSL_MASK = ffi::SSL_OP_NO_SSL_MASK as SslOptionsRepr;
/// Disallow all renegotiation in TLSv1.2 and earlier.
///
/// Requires OpenSSL 1.1.0h or newer.
#[cfg(any(boringssl, ossl110h))]
const NO_RENEGOTIATION = ffi::SSL_OP_NO_RENEGOTIATION as SslOptionsRepr;
/// Enable TLSv1.3 Compatibility mode.
///
/// Requires OpenSSL 1.1.1 or newer. This is on by default in 1.1.1, but a future version
/// may have this disabled by default.
#[cfg(ossl111)]
const ENABLE_MIDDLEBOX_COMPAT = ffi::SSL_OP_ENABLE_MIDDLEBOX_COMPAT as SslOptionsRepr;
/// Prioritize ChaCha ciphers when preferred by clients.
///
/// Temporarily reprioritize ChaCha20-Poly1305 ciphers to the top of the server cipher list
/// if a ChaCha20-Poly1305 cipher is at the top of the client cipher list. This helps those
/// clients (e.g. mobile) use ChaCha20-Poly1305 if that cipher is anywhere in the server
/// cipher list; but still allows other clients to use AES and other ciphers.
///
/// Requires enable [`SslOptions::CIPHER_SERVER_PREFERENCE`].
/// Requires OpenSSL 1.1.1 or newer.
///
/// [`SslOptions::CIPHER_SERVER_PREFERENCE`]: struct.SslOptions.html#associatedconstant.CIPHER_SERVER_PREFERENCE
#[cfg(ossl111)]
const PRIORITIZE_CHACHA = ffi::SSL_OP_PRIORITIZE_CHACHA as SslOptionsRepr;
}
}
bitflags! {
/// Options controlling the behavior of an `SslContext`.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct SslMode: SslBitType {
/// Enables "short writes".
///
/// Normally, a write in OpenSSL will always write out all of the requested data, even if it
/// requires more than one TLS record or write to the underlying stream. This option will
/// cause a write to return after writing a single TLS record instead.
const ENABLE_PARTIAL_WRITE = ffi::SSL_MODE_ENABLE_PARTIAL_WRITE;
/// Disables a check that the data buffer has not moved between calls when operating in a
/// non-blocking context.
const ACCEPT_MOVING_WRITE_BUFFER = ffi::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER;
/// Enables automatic retries after TLS session events such as renegotiations or heartbeats.
///
/// By default, OpenSSL will return a `WantRead` error after a renegotiation or heartbeat.
/// This option will cause OpenSSL to automatically continue processing the requested
/// operation instead.
///
/// Note that `SslStream::read` and `SslStream::write` will automatically retry regardless
/// of the state of this option. It only affects `SslStream::ssl_read` and
/// `SslStream::ssl_write`.
const AUTO_RETRY = ffi::SSL_MODE_AUTO_RETRY;
/// Disables automatic chain building when verifying a peer's certificate.
///
/// TLS peers are responsible for sending the entire certificate chain from the leaf to a
/// trusted root, but some will incorrectly not do so. OpenSSL will try to build the chain
/// out of certificates it knows of, and this option will disable that behavior.
const NO_AUTO_CHAIN = ffi::SSL_MODE_NO_AUTO_CHAIN;
/// Release memory buffers when the session does not need them.
///
/// This saves ~34 KiB of memory for idle streams.
const RELEASE_BUFFERS = ffi::SSL_MODE_RELEASE_BUFFERS;
/// Sends the fake `TLS_FALLBACK_SCSV` cipher suite in the ClientHello message of a
/// handshake.
///
/// This should only be enabled if a client has failed to connect to a server which
/// attempted to downgrade the protocol version of the session.
///
/// Do not use this unless you know what you're doing!
#[cfg(not(libressl))]
const SEND_FALLBACK_SCSV = ffi::SSL_MODE_SEND_FALLBACK_SCSV;
}
}
/// A type specifying the kind of protocol an `SslContext` will speak.
#[derive(Copy, Clone)]
pub struct SslMethod(*const ffi::SSL_METHOD);
impl SslMethod {
/// Support all versions of the TLS protocol.
#[corresponds(TLS_method)]
pub fn tls() -> SslMethod {
unsafe { SslMethod(TLS_method()) }
}
/// Support all versions of the DTLS protocol.
#[corresponds(DTLS_method)]
pub fn dtls() -> SslMethod {
unsafe { SslMethod(DTLS_method()) }
}
/// Support all versions of the TLS protocol, explicitly as a client.
#[corresponds(TLS_client_method)]
pub fn tls_client() -> SslMethod {
unsafe { SslMethod(TLS_client_method()) }
}
/// Support all versions of the TLS protocol, explicitly as a server.
#[corresponds(TLS_server_method)]
pub fn tls_server() -> SslMethod {
unsafe { SslMethod(TLS_server_method()) }
}
/// Constructs an `SslMethod` from a pointer to the underlying OpenSSL value.
///
/// # Safety
///
/// The caller must ensure the pointer is valid.
pub unsafe fn from_ptr(ptr: *const ffi::SSL_METHOD) -> SslMethod {
SslMethod(ptr)
}
/// Returns a pointer to the underlying OpenSSL value.
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn as_ptr(&self) -> *const ffi::SSL_METHOD {
self.0
}
}
unsafe impl Sync for SslMethod {}
unsafe impl Send for SslMethod {}
bitflags! {
/// Options controlling the behavior of certificate verification.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct SslVerifyMode: i32 {
/// Verifies that the peer's certificate is trusted.
///
/// On the server side, this will cause OpenSSL to request a certificate from the client.
const PEER = ffi::SSL_VERIFY_PEER;
/// Disables verification of the peer's certificate.
///
/// On the server side, this will cause OpenSSL to not request a certificate from the
/// client. On the client side, the certificate will be checked for validity, but the
/// negotiation will continue regardless of the result of that check.
const NONE = ffi::SSL_VERIFY_NONE;
/// On the server side, abort the handshake if the client did not send a certificate.
///
/// This should be paired with `SSL_VERIFY_PEER`. It has no effect on the client side.
const FAIL_IF_NO_PEER_CERT = ffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
}
}
#[cfg(boringssl)]
type SslBitType = c_int;
#[cfg(not(boringssl))]
type SslBitType = c_long;
#[cfg(boringssl)]
type SslTimeTy = u64;
#[cfg(not(boringssl))]
type SslTimeTy = c_long;
bitflags! {
/// Options controlling the behavior of session caching.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct SslSessionCacheMode: SslBitType {
/// No session caching for the client or server takes place.
const OFF = ffi::SSL_SESS_CACHE_OFF;
/// Enable session caching on the client side.
///
/// OpenSSL has no way of identifying the proper session to reuse automatically, so the
/// application is responsible for setting it explicitly via [`SslRef::set_session`].
///
/// [`SslRef::set_session`]: struct.SslRef.html#method.set_session
const CLIENT = ffi::SSL_SESS_CACHE_CLIENT;
/// Enable session caching on the server side.
///
/// This is the default mode.
const SERVER = ffi::SSL_SESS_CACHE_SERVER;
/// Enable session caching on both the client and server side.
const BOTH = ffi::SSL_SESS_CACHE_BOTH;
/// Disable automatic removal of expired sessions from the session cache.
const NO_AUTO_CLEAR = ffi::SSL_SESS_CACHE_NO_AUTO_CLEAR;
/// Disable use of the internal session cache for session lookups.
const NO_INTERNAL_LOOKUP = ffi::SSL_SESS_CACHE_NO_INTERNAL_LOOKUP;
/// Disable use of the internal session cache for session storage.
const NO_INTERNAL_STORE = ffi::SSL_SESS_CACHE_NO_INTERNAL_STORE;
/// Disable use of the internal session cache for storage and lookup.
const NO_INTERNAL = ffi::SSL_SESS_CACHE_NO_INTERNAL;
}
}
#[cfg(ossl111)]
bitflags! {
/// Which messages and under which conditions an extension should be added or expected.
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct ExtensionContext: c_uint {
/// This extension is only allowed in TLS
const TLS_ONLY = ffi::SSL_EXT_TLS_ONLY;
/// This extension is only allowed in DTLS
const DTLS_ONLY = ffi::SSL_EXT_DTLS_ONLY;
/// Some extensions may be allowed in DTLS but we don't implement them for it
const TLS_IMPLEMENTATION_ONLY = ffi::SSL_EXT_TLS_IMPLEMENTATION_ONLY;
/// Most extensions are not defined for SSLv3 but EXT_TYPE_renegotiate is
const SSL3_ALLOWED = ffi::SSL_EXT_SSL3_ALLOWED;
/// Extension is only defined for TLS1.2 and below
const TLS1_2_AND_BELOW_ONLY = ffi::SSL_EXT_TLS1_2_AND_BELOW_ONLY;
/// Extension is only defined for TLS1.3 and above
const TLS1_3_ONLY = ffi::SSL_EXT_TLS1_3_ONLY;
/// Ignore this extension during parsing if we are resuming
const IGNORE_ON_RESUMPTION = ffi::SSL_EXT_IGNORE_ON_RESUMPTION;
const CLIENT_HELLO = ffi::SSL_EXT_CLIENT_HELLO;
/// Really means TLS1.2 or below
const TLS1_2_SERVER_HELLO = ffi::SSL_EXT_TLS1_2_SERVER_HELLO;
const TLS1_3_SERVER_HELLO = ffi::SSL_EXT_TLS1_3_SERVER_HELLO;
const TLS1_3_ENCRYPTED_EXTENSIONS = ffi::SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS;
const TLS1_3_HELLO_RETRY_REQUEST = ffi::SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST;
const TLS1_3_CERTIFICATE = ffi::SSL_EXT_TLS1_3_CERTIFICATE;
const TLS1_3_NEW_SESSION_TICKET = ffi::SSL_EXT_TLS1_3_NEW_SESSION_TICKET;
const TLS1_3_CERTIFICATE_REQUEST = ffi::SSL_EXT_TLS1_3_CERTIFICATE_REQUEST;
}
}
/// An identifier of the format of a certificate or key file.
#[derive(Copy, Clone)]
pub struct SslFiletype(c_int);
impl SslFiletype {
/// The PEM format.
///
/// This corresponds to `SSL_FILETYPE_PEM`.
pub const PEM: SslFiletype = SslFiletype(ffi::SSL_FILETYPE_PEM);
/// The ASN1 format.
///
/// This corresponds to `SSL_FILETYPE_ASN1`.
pub const ASN1: SslFiletype = SslFiletype(ffi::SSL_FILETYPE_ASN1);
/// Constructs an `SslFiletype` from a raw OpenSSL value.
pub fn from_raw(raw: c_int) -> SslFiletype {
SslFiletype(raw)
}
/// Returns the raw OpenSSL value represented by this type.
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn as_raw(&self) -> c_int {
self.0
}
}
/// An identifier of a certificate status type.
#[derive(Copy, Clone)]
pub struct StatusType(c_int);
impl StatusType {
/// An OSCP status.
pub const OCSP: StatusType = StatusType(ffi::TLSEXT_STATUSTYPE_ocsp);
/// Constructs a `StatusType` from a raw OpenSSL value.
pub fn from_raw(raw: c_int) -> StatusType {
StatusType(raw)
}
/// Returns the raw OpenSSL value represented by this type.
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn as_raw(&self) -> c_int {
self.0
}
}
/// An identifier of a session name type.
#[derive(Copy, Clone)]
pub struct NameType(c_int);
impl NameType {
/// A host name.
pub const HOST_NAME: NameType = NameType(ffi::TLSEXT_NAMETYPE_host_name);
/// Constructs a `StatusType` from a raw OpenSSL value.
pub fn from_raw(raw: c_int) -> StatusType {
StatusType(raw)
}
/// Returns the raw OpenSSL value represented by this type.
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn as_raw(&self) -> c_int {
self.0
}
}
static INDEXES: Lazy<Mutex<HashMap<TypeId, c_int>>> = Lazy::new(|| Mutex::new(HashMap::new()));
static SSL_INDEXES: Lazy<Mutex<HashMap<TypeId, c_int>>> = Lazy::new(|| Mutex::new(HashMap::new()));
static SESSION_CTX_INDEX: OnceCell<Index<Ssl, SslContext>> = OnceCell::new();
fn try_get_session_ctx_index() -> Result<&'static Index<Ssl, SslContext>, ErrorStack> {
SESSION_CTX_INDEX.get_or_try_init(Ssl::new_ex_index)
}
unsafe extern "C" fn free_data_box<T>(
_parent: *mut c_void,
ptr: *mut c_void,
_ad: *mut ffi::CRYPTO_EX_DATA,
_idx: c_int,
_argl: c_long,
_argp: *mut c_void,
) {
if !ptr.is_null() {
let _ = Box::<T>::from_raw(ptr as *mut T);
}
}
/// An error returned from the SNI callback.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SniError(c_int);
impl SniError {
/// Abort the handshake with a fatal alert.
pub const ALERT_FATAL: SniError = SniError(ffi::SSL_TLSEXT_ERR_ALERT_FATAL);
/// Send a warning alert to the client and continue the handshake.
pub const ALERT_WARNING: SniError = SniError(ffi::SSL_TLSEXT_ERR_ALERT_WARNING);
pub const NOACK: SniError = SniError(ffi::SSL_TLSEXT_ERR_NOACK);
}
/// An SSL/TLS alert.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SslAlert(c_int);
impl SslAlert {
/// Alert 112 - `unrecognized_name`.
pub const UNRECOGNIZED_NAME: SslAlert = SslAlert(ffi::SSL_AD_UNRECOGNIZED_NAME);
pub const ILLEGAL_PARAMETER: SslAlert = SslAlert(ffi::SSL_AD_ILLEGAL_PARAMETER);
pub const DECODE_ERROR: SslAlert = SslAlert(ffi::SSL_AD_DECODE_ERROR);
}
/// An error returned from an ALPN selection callback.
///
/// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer.
#[cfg(any(ossl102, libressl261))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct AlpnError(c_int);
#[cfg(any(ossl102, libressl261))]
impl AlpnError {
/// Terminate the handshake with a fatal alert.
///
/// Requires OpenSSL 1.1.0 or newer.
#[cfg(ossl110)]
pub const ALERT_FATAL: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_ALERT_FATAL);
/// Do not select a protocol, but continue the handshake.
pub const NOACK: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_NOACK);
}
/// The result of a client hello callback.
///
/// Requires OpenSSL 1.1.1 or newer.
#[cfg(ossl111)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ClientHelloResponse(c_int);
#[cfg(ossl111)]
impl ClientHelloResponse {
/// Continue the handshake.
pub const SUCCESS: ClientHelloResponse = ClientHelloResponse(ffi::SSL_CLIENT_HELLO_SUCCESS);
/// Return from the handshake with an `ErrorCode::WANT_CLIENT_HELLO_CB` error.
pub const RETRY: ClientHelloResponse = ClientHelloResponse(ffi::SSL_CLIENT_HELLO_RETRY);
}
/// An SSL/TLS protocol version.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SslVersion(c_int);
impl SslVersion {
/// SSLv3
pub const SSL3: SslVersion = SslVersion(ffi::SSL3_VERSION);
/// TLSv1.0
pub const TLS1: SslVersion = SslVersion(ffi::TLS1_VERSION);
/// TLSv1.1
pub const TLS1_1: SslVersion = SslVersion(ffi::TLS1_1_VERSION);
/// TLSv1.2
pub const TLS1_2: SslVersion = SslVersion(ffi::TLS1_2_VERSION);
/// TLSv1.3
///
/// Requires BoringSSL or OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer.
#[cfg(any(ossl111, libressl340, boringssl))]
pub const TLS1_3: SslVersion = SslVersion(ffi::TLS1_3_VERSION);
/// DTLSv1.0
///
/// DTLS 1.0 corresponds to TLS 1.1.
pub const DTLS1: SslVersion = SslVersion(ffi::DTLS1_VERSION);
/// DTLSv1.2
///
/// DTLS 1.2 corresponds to TLS 1.2 to harmonize versions. There was never a DTLS 1.1.
#[cfg(any(ossl102, libressl332, boringssl))]
pub const DTLS1_2: SslVersion = SslVersion(ffi::DTLS1_2_VERSION);
}
cfg_if! {
if #[cfg(boringssl)] {
type SslCacheTy = i64;
type SslCacheSize = libc::c_ulong;
type MtuTy = u32;
type SizeTy = usize;
} else {
type SslCacheTy = i64;
type SslCacheSize = c_long;
type MtuTy = c_long;
type SizeTy = u32;
}
}
/// A standard implementation of protocol selection for Application Layer Protocol Negotiation
/// (ALPN).
///
/// `server` should contain the server's list of supported protocols and `client` the client's. They
/// must both be in the ALPN wire format. See the documentation for
/// [`SslContextBuilder::set_alpn_protos`] for details.
///
/// It will select the first protocol supported by the server which is also supported by the client.
///
/// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos
#[corresponds(SSL_select_next_proto)]
pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8]> {
unsafe {
let mut out = ptr::null_mut();
let mut outlen = 0;
let r = ffi::SSL_select_next_proto(
&mut out,
&mut outlen,
server.as_ptr(),
server.len() as c_uint,
client.as_ptr(),
client.len() as c_uint,
);
if r == ffi::OPENSSL_NPN_NEGOTIATED {
Some(util::from_raw_parts(out as *const u8, outlen as usize))
} else {
None
}
}
}
/// A builder for `SslContext`s.
pub struct SslContextBuilder(SslContext);
impl SslContextBuilder {
/// Creates a new `SslContextBuilder`.
#[corresponds(SSL_CTX_new)]
pub fn new(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
unsafe {
init();
let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?;
Ok(SslContextBuilder::from_ptr(ctx))
}
}
/// Creates an `SslContextBuilder` from a pointer to a raw OpenSSL value.
///
/// # Safety
///
/// The caller must ensure that the pointer is valid and uniquely owned by the builder.
pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder {
SslContextBuilder(SslContext::from_ptr(ctx))
}
/// Returns a pointer to the raw OpenSSL value.
pub fn as_ptr(&self) -> *mut ffi::SSL_CTX {
self.0.as_ptr()
}
/// Configures the certificate verification method for new connections.
#[corresponds(SSL_CTX_set_verify)]
pub fn set_verify(&mut self, mode: SslVerifyMode) {
unsafe {
ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits() as c_int, None);
}
}
/// Configures the certificate verification method for new connections and
/// registers a verification callback.
///
/// The callback is passed a boolean indicating if OpenSSL's internal verification succeeded as
/// well as a reference to the `X509StoreContext` which can be used to examine the certificate
/// chain. It should return a boolean indicating if verification succeeded.
#[corresponds(SSL_CTX_set_verify)]
pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F)
where
F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
{
unsafe {
self.set_ex_data(SslContext::cached_ex_index::<F>(), verify);
ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits() as c_int, Some(raw_verify::<F>));
}
}
/// Configures the server name indication (SNI) callback for new connections.
///
/// SNI is used to allow a single server to handle requests for multiple domains, each of which
/// has its own certificate chain and configuration.
///
/// Obtain the server name with the `servername` method and then set the corresponding context
/// with `set_ssl_context`
#[corresponds(SSL_CTX_set_tlsext_servername_callback)]
// FIXME tlsext prefix?
pub fn set_servername_callback<F>(&mut self, callback: F)
where
F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send,
{
unsafe {
// The SNI callback is somewhat unique in that the callback associated with the original
// context associated with an SSL can be used even if the SSL's context has been swapped
// out. When that happens, we wouldn't be able to look up the callback's state in the
// context's ex data. Instead, pass the pointer directly as the servername arg. It's
// still stored in ex data to manage the lifetime.
let arg = self.set_ex_data_inner(SslContext::cached_ex_index::<F>(), callback);
ffi::SSL_CTX_set_tlsext_servername_arg(self.as_ptr(), arg);
#[cfg(boringssl)]
ffi::SSL_CTX_set_tlsext_servername_callback(self.as_ptr(), Some(raw_sni::<F>));
#[cfg(not(boringssl))]
ffi::SSL_CTX_set_tlsext_servername_callback__fixed_rust(
self.as_ptr(),
Some(raw_sni::<F>),
);
}
}
/// Sets the certificate verification depth.
///
/// If the peer's certificate chain is longer than this value, verification will fail.
#[corresponds(SSL_CTX_set_verify_depth)]
pub fn set_verify_depth(&mut self, depth: u32) {
unsafe {
ffi::SSL_CTX_set_verify_depth(self.as_ptr(), depth as c_int);
}
}
/// Sets a custom certificate store for verifying peer certificates.
///
/// Requires OpenSSL 1.0.2 or newer.
#[corresponds(SSL_CTX_set0_verify_cert_store)]
#[cfg(ossl102)]
pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> {
unsafe {
let ptr = cert_store.as_ptr();
cvt(ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) as c_int)?;
mem::forget(cert_store);
Ok(())
}
}
/// Replaces the context's certificate store.
#[corresponds(SSL_CTX_set_cert_store)]
pub fn set_cert_store(&mut self, cert_store: X509Store) {
unsafe {
ffi::SSL_CTX_set_cert_store(self.as_ptr(), cert_store.as_ptr());
mem::forget(cert_store);
}
}
/// Controls read ahead behavior.
///
/// If enabled, OpenSSL will read as much data as is available from the underlying stream,
/// instead of a single record at a time.
///
/// It has no effect when used with DTLS.
#[corresponds(SSL_CTX_set_read_ahead)]
pub fn set_read_ahead(&mut self, read_ahead: bool) {
unsafe {
ffi::SSL_CTX_set_read_ahead(self.as_ptr(), read_ahead as SslBitType);
}
}
/// Sets the mode used by the context, returning the previous mode.
#[corresponds(SSL_CTX_set_mode)]
pub fn set_mode(&mut self, mode: SslMode) -> SslMode {
unsafe {
let bits = ffi::SSL_CTX_set_mode(self.as_ptr(), mode.bits() as MtuTy) as SslBitType;
SslMode::from_bits_retain(bits)
}
}
/// Sets the parameters to be used during ephemeral Diffie-Hellman key exchange.
#[corresponds(SSL_CTX_set_tmp_dh)]
pub fn set_tmp_dh(&mut self, dh: &DhRef<Params>) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) }
}
/// Sets the callback which will generate parameters to be used during ephemeral Diffie-Hellman
/// key exchange.
///
/// The callback is provided with a reference to the `Ssl` for the session, as well as a boolean
/// indicating if the selected cipher is export-grade, and the key length. The export and key
/// length options are archaic and should be ignored in almost all cases.
#[corresponds(SSL_CTX_set_tmp_dh_callback)]
pub fn set_tmp_dh_callback<F>(&mut self, callback: F)
where
F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send,
{
unsafe {
self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
#[cfg(not(boringssl))]
ffi::SSL_CTX_set_tmp_dh_callback__fixed_rust(self.as_ptr(), Some(raw_tmp_dh::<F>));
#[cfg(boringssl)]
ffi::SSL_CTX_set_tmp_dh_callback(self.as_ptr(), Some(raw_tmp_dh::<F>));
}
}
/// Sets the parameters to be used during ephemeral elliptic curve Diffie-Hellman key exchange.
#[corresponds(SSL_CTX_set_tmp_ecdh)]
pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef<Params>) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) }
}
/// Sets the callback which will generate parameters to be used during ephemeral elliptic curve
/// Diffie-Hellman key exchange.
///
/// The callback is provided with a reference to the `Ssl` for the session, as well as a boolean
/// indicating if the selected cipher is export-grade, and the key length. The export and key
/// length options are archaic and should be ignored in almost all cases.
///
/// Requires OpenSSL 1.0.1 or 1.0.2.
#[corresponds(SSL_CTX_set_tmp_ecdh_callback)]
#[cfg(all(ossl101, not(ossl110)))]
#[deprecated(note = "this function leaks memory and does not exist on newer OpenSSL versions")]
pub fn set_tmp_ecdh_callback<F>(&mut self, callback: F)
where
F: Fn(&mut SslRef, bool, u32) -> Result<EcKey<Params>, ErrorStack> + 'static + Sync + Send,
{
unsafe {
self.set_ex_data(SslContext::cached_ex_index::<F>(), callback);
ffi::SSL_CTX_set_tmp_ecdh_callback__fixed_rust(self.as_ptr(), Some(raw_tmp_ecdh::<F>));
}
}
/// Use the default locations of trusted certificates for verification.
///
/// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables
/// if present, or defaults specified at OpenSSL build time otherwise.
#[corresponds(SSL_CTX_set_default_verify_paths)]
pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_set_default_verify_paths(self.as_ptr())).map(|_| ()) }
}
/// Loads trusted root certificates from a file.
///
/// The file should contain a sequence of PEM-formatted CA certificates.
#[corresponds(SSL_CTX_load_verify_locations)]
pub fn set_ca_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), ErrorStack> {
let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
unsafe {
cvt(ffi::SSL_CTX_load_verify_locations(
self.as_ptr(),
file.as_ptr() as *const _,
ptr::null(),
))
.map(|_| ())
}
}
/// Sets the list of CA names sent to the client.
///
/// The CA certificates must still be added to the trust root - they are not automatically set
/// as trusted by this method.
#[corresponds(SSL_CTX_set_client_CA_list)]
pub fn set_client_ca_list(&mut self, list: Stack<X509Name>) {
unsafe {
ffi::SSL_CTX_set_client_CA_list(self.as_ptr(), list.as_ptr());
mem::forget(list);
}
}
/// Add the provided CA certificate to the list sent by the server to the client when
/// requesting client-side TLS authentication.
#[corresponds(SSL_CTX_add_client_CA)]
pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> {
unsafe { cvt(ffi::SSL_CTX_add_client_CA(self.as_ptr(), cacert.as_ptr())).map(|_| ()) }
}
/// Set the context identifier for sessions.
///
/// This value identifies the server's session cache to clients, telling them when they're
/// able to reuse sessions. It should be set to a unique value per server, unless multiple
/// servers share a session cache.
///
/// This value should be set when using client certificates, or each request will fail its
/// handshake and need to be restarted.
#[corresponds(SSL_CTX_set_session_id_context)]
pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), ErrorStack> {
unsafe {
assert!(sid_ctx.len() <= c_uint::MAX as usize);
cvt(ffi::SSL_CTX_set_session_id_context(
self.as_ptr(),
sid_ctx.as_ptr(),
sid_ctx.len() as SizeTy,
))
.map(|_| ())
}
}
/// Loads a leaf certificate from a file.
///
/// Only a single certificate will be loaded - use `add_extra_chain_cert` to add the remainder
/// of the certificate chain, or `set_certificate_chain_file` to load the entire chain from a
/// single file.
#[corresponds(SSL_CTX_use_certificate_file)]
pub fn set_certificate_file<P: AsRef<Path>>(
&mut self,
file: P,
file_type: SslFiletype,
) -> Result<(), ErrorStack> {
let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap();
unsafe {
cvt(ffi::SSL_CTX_use_certificate_file(
self.as_ptr(),
file.as_ptr() as *const _,
file_type.as_raw(),
))
.map(|_| ())
}
}
/// Loads a certificate chain from a file.