1 use std
::io
::{Read, Write}
;
2 use std
::ops
::{Deref, DerefMut}
;
6 use ssl
::{self, HandshakeError
, Ssl
, SslRef
, SslContext
, SslContextBuilder
, SslMethod
, SslStream
,
14 static ref HOSTNAME_IDX
: ::ex_data
::Index
<Ssl
, String
> = Ssl
::new_ex_index().unwrap();
17 // ffdhe2048 from https://wiki.mozilla.org/Security/Server_Side_TLS#ffdhe2048
18 const DHPARAM_PEM
: &'
static str = "
19 -----BEGIN DH PARAMETERS-----
20 MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
21 +8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
22 87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
23 YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
24 7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
25 ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
26 -----END DH PARAMETERS-----
29 fn ctx(method
: SslMethod
) -> Result
<SslContextBuilder
, ErrorStack
> {
30 let mut ctx
= SslContextBuilder
::new(method
)?
;
32 let mut opts
= ssl
::SSL_OP_ALL
;
33 opts
&= !ssl
::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
;
34 opts
&= !ssl
::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
;
35 opts
|= ssl
::SSL_OP_NO_TICKET
;
36 opts
|= ssl
::SSL_OP_NO_COMPRESSION
;
37 opts
|= ssl
::SSL_OP_NO_SSLV2
;
38 opts
|= ssl
::SSL_OP_NO_SSLV3
;
39 opts
|= ssl
::SSL_OP_SINGLE_DH_USE
;
40 opts
|= ssl
::SSL_OP_SINGLE_ECDH_USE
;
41 opts
|= ssl
::SSL_OP_CIPHER_SERVER_PREFERENCE
;
42 ctx
.set_options(opts
);
44 let mut mode
= ssl
::SSL_MODE_AUTO_RETRY
| ssl
::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
45 | ssl
::SSL_MODE_ENABLE_PARTIAL_WRITE
;
47 // This is quite a useful optimization for saving memory, but historically
48 // caused CVEs in OpenSSL pre-1.0.1h, according to
49 // https://bugs.python.org/issue25672
50 if version
::number() >= 0x1000108f {
51 mode
|= ssl
::SSL_MODE_RELEASE_BUFFERS
;
59 /// A builder for `SslConnector`s.
60 pub struct SslConnectorBuilder(SslContextBuilder
);
62 impl SslConnectorBuilder
{
63 /// Creates a new builder for TLS connections.
65 /// The default configuration is subject to change, and is currently derived from Python.
66 pub fn new(method
: SslMethod
) -> Result
<SslConnectorBuilder
, ErrorStack
> {
67 let mut ctx
= ctx(method
)?
;
68 ctx
.set_default_verify_paths()?
;
69 // From https://github.com/python/cpython/blob/a170fa162dc03f0a014373349e548954fff2e567/Lib/ssl.py#L193
71 "TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:\
72 TLS13-AES-128-GCM-SHA256:\
73 ECDH+AESGCM:ECDH+CHACHA20:DH+AESGCM:DH+CHACHA20:ECDH+AES256:DH+AES256:\
74 ECDH+AES128:DH+AES:ECDH+HIGH:DH+HIGH:RSA+AESGCM:RSA+AES:RSA+HIGH:\
75 !aNULL:!eNULL:!MD5:!3DES",
77 setup_verify(&mut ctx
);
79 Ok(SslConnectorBuilder(ctx
))
82 #[deprecated(since = "0.9.23",
83 note
= "SslConnectorBuilder now implements Deref<Target=SslContextBuilder>")]
84 pub fn builder(&self) -> &SslContextBuilder
{
88 #[deprecated(since = "0.9.23",
89 note
= "SslConnectorBuilder now implements DerefMut<Target=SslContextBuilder>")]
90 pub fn builder_mut(&mut self) -> &mut SslContextBuilder
{
94 /// Consumes the builder, returning an `SslConnector`.
95 pub fn build(self) -> SslConnector
{
96 SslConnector(self.0.build())
100 impl Deref
for SslConnectorBuilder
{
101 type Target
= SslContextBuilder
;
103 fn deref(&self) -> &SslContextBuilder
{
108 impl DerefMut
for SslConnectorBuilder
{
109 fn deref_mut(&mut self) -> &mut SslContextBuilder
{
114 /// A type which wraps client-side streams in a TLS session.
116 /// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL
117 /// structures, configuring cipher suites, session options, hostname verification, and more.
119 /// OpenSSL's built in hostname verification is used when linking against OpenSSL 1.0.2 or 1.1.0,
120 /// and a custom implementation is used when linking against OpenSSL 1.0.1.
122 pub struct SslConnector(SslContext
);
125 /// Initiates a client-side TLS session on a stream.
127 /// The domain is used for SNI and hostname verification.
128 pub fn connect
<S
>(&self, domain
: &str, stream
: S
) -> Result
<SslStream
<S
>, HandshakeError
<S
>>
132 self.configure()?
.connect(domain
, stream
)
135 /// Initiates a client-side TLS session on a stream without performing hostname verification.
139 /// You should think very carefully before you use this method. If hostname verification is not
140 /// used, *any* valid certificate for *any* site will be trusted for use from any other. This
141 /// introduces a significant vulnerability to man-in-the-middle attacks.
142 pub fn danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication
<
147 ) -> Result
<SslStream
<S
>, HandshakeError
<S
>>
152 .danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(stream
)
155 /// Returns a structure allowing for configuration of a single TLS session before connection.
156 pub fn configure(&self) -> Result
<ConnectConfiguration
, ErrorStack
> {
157 Ssl
::new(&self.0).map(ConnectConfiguration
)
161 /// A type which allows for configuration of a client-side TLS session before connection.
162 pub struct ConnectConfiguration(Ssl
);
164 impl ConnectConfiguration
{
165 #[deprecated(since = "0.9.23",
166 note
= "ConnectConfiguration now implements Deref<Target=SslRef>")]
167 pub fn ssl(&self) -> &Ssl
{
171 #[deprecated(since = "0.9.23",
172 note
= "ConnectConfiguration now implements DerefMut<Target=SslRef>")]
173 pub fn ssl_mut(&mut self) -> &mut Ssl
{
177 /// Initiates a client-side TLS session on a stream.
179 /// The domain is used for SNI and hostname verification.
180 pub fn connect
<S
>(mut self, domain
: &str, stream
: S
) -> Result
<SslStream
<S
>, HandshakeError
<S
>>
184 self.0.set_hostname(domain
)?
;
185 setup_verify_hostname(&mut self.0, domain
)?
;
187 self.0.connect(stream
)
190 /// Initiates a client-side TLS session on a stream without performing hostname verification.
192 /// The verification configuration of the connector's `SslContext` is not overridden.
196 /// You should think very carefully before you use this method. If hostname verification is not
197 /// used, *any* valid certificate for *any* site will be trusted for use from any other. This
198 /// introduces a significant vulnerability to man-in-the-middle attacks.
199 pub fn danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication
<
204 ) -> Result
<SslStream
<S
>, HandshakeError
<S
>>
208 self.0.connect(stream
)
212 impl Deref
for ConnectConfiguration
{
213 type Target
= SslRef
;
215 fn deref(&self) -> &SslRef
{
220 impl DerefMut
for ConnectConfiguration
{
221 fn deref_mut(&mut self) -> &mut SslRef
{
226 /// A builder for `SslAcceptor`s.
227 pub struct SslAcceptorBuilder(SslContextBuilder
);
229 impl SslAcceptorBuilder
{
230 /// Creates a new builder configured to connect to non-legacy clients. This should generally be
231 /// considered a reasonable default choice.
233 /// This corresponds to the intermediate configuration of Mozilla's server side TLS
234 /// recommendations. See its [documentation][docs] for more details on specifics.
236 /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
237 pub fn mozilla_intermediate
<I
>(
239 private_key
: &PKeyRef
,
240 certificate
: &X509Ref
,
242 ) -> Result
<SslAcceptorBuilder
, ErrorStack
>
245 I
::Item
: AsRef
<X509Ref
>,
247 let builder
= SslAcceptorBuilder
::mozilla_intermediate_raw(method
)?
;
248 builder
.finish_setup(private_key
, certificate
, chain
)
251 /// Creates a new builder configured to connect to modern clients.
253 /// This corresponds to the modern configuration of Mozilla's server side TLS recommendations.
254 /// See its [documentation][docs] for more details on specifics.
256 /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
257 pub fn mozilla_modern
<I
>(
259 private_key
: &PKeyRef
,
260 certificate
: &X509Ref
,
262 ) -> Result
<SslAcceptorBuilder
, ErrorStack
>
265 I
::Item
: AsRef
<X509Ref
>,
267 let builder
= SslAcceptorBuilder
::mozilla_modern_raw(method
)?
;
268 builder
.finish_setup(private_key
, certificate
, chain
)
271 /// Like `mozilla_intermediate`, but does not load the certificate chain and private key.
272 pub fn mozilla_intermediate_raw(method
: SslMethod
) -> Result
<SslAcceptorBuilder
, ErrorStack
> {
273 let mut ctx
= ctx(method
)?
;
274 let dh
= Dh
::from_pem(DHPARAM_PEM
.as_bytes())?
;
275 ctx
.set_tmp_dh(&dh
)?
;
276 setup_curves(&mut ctx
)?
;
278 "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
279 ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
280 ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
281 DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:\
282 ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:\
283 ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:\
284 ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:\
285 DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:\
286 EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:\
287 AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS",
289 Ok(SslAcceptorBuilder(ctx
))
292 /// Like `mozilla_modern`, but does not load the certificate chain and private key.
293 pub fn mozilla_modern_raw(method
: SslMethod
) -> Result
<SslAcceptorBuilder
, ErrorStack
> {
294 let mut ctx
= ctx(method
)?
;
295 setup_curves(&mut ctx
)?
;
297 "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
298 ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
299 ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:\
300 ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256",
302 Ok(SslAcceptorBuilder(ctx
))
307 private_key
: &PKeyRef
,
308 certificate
: &X509Ref
,
310 ) -> Result
<SslAcceptorBuilder
, ErrorStack
>
313 I
::Item
: AsRef
<X509Ref
>,
315 self.0.set_private_key(private_key
)?
;
316 self.0.set_certificate(certificate
)?
;
317 self.0.check_private_key()?
;
319 self.0.add_extra_chain_cert(cert
.as_ref().to_owned())?
;
324 #[deprecated(since = "0.9.23",
325 note
= "SslAcceptorBuilder now implements Deref<Target=SslContextBuilder>")]
326 pub fn builder(&self) -> &SslContextBuilder
{
330 #[deprecated(since = "0.9.23",
331 note
= "SslAcceptorBuilder now implements DerefMut<Target=SslContextBuilder>")]
332 pub fn builder_mut(&mut self) -> &mut SslContextBuilder
{
336 /// Consumes the builder, returning a `SslAcceptor`.
337 pub fn build(self) -> SslAcceptor
{
338 SslAcceptor(self.0.build())
342 impl Deref
for SslAcceptorBuilder
{
343 type Target
= SslContextBuilder
;
345 fn deref(&self) -> &SslContextBuilder
{
350 impl DerefMut
for SslAcceptorBuilder
{
351 fn deref_mut(&mut self) -> &mut SslContextBuilder
{
357 fn setup_curves(ctx
: &mut SslContextBuilder
) -> Result
<(), ErrorStack
> {
361 let curve
= EcKey
::from_curve_name(nid
::X9_62_PRIME256V1
)?
;
362 ctx
.set_tmp_ecdh(&curve
)
366 fn setup_curves(ctx
: &mut SslContextBuilder
) -> Result
<(), ErrorStack
> {
367 ctx
._set_ecdh_auto(true)
371 fn setup_curves(_
: &mut SslContextBuilder
) -> Result
<(), ErrorStack
> {
375 /// A type which wraps server-side streams in a TLS session.
377 /// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL
378 /// structures, configuring cipher suites, session options, and more.
380 pub struct SslAcceptor(SslContext
);
383 /// Initiates a server-side TLS session on a stream.
384 pub fn accept
<S
>(&self, stream
: S
) -> Result
<SslStream
<S
>, HandshakeError
<S
>>
388 let ssl
= Ssl
::new(&self.0)?
;
393 #[cfg(any(ossl102, ossl110))]
394 fn setup_verify(ctx
: &mut SslContextBuilder
) {
395 ctx
.set_verify(SSL_VERIFY_PEER
);
399 fn setup_verify(ctx
: &mut SslContextBuilder
) {
400 ctx
.set_verify_callback(SSL_VERIFY_PEER
, |p
, x509
| {
401 let hostname
= match x509
.ssl() {
402 Ok(Some(ssl
)) => ssl
.ex_data(*HOSTNAME_IDX
),
406 Some(hostname
) => verify
::verify_callback(hostname
, p
, x509
),
412 #[cfg(any(ossl102, ossl110))]
413 fn setup_verify_hostname(ssl
: &mut Ssl
, domain
: &str) -> Result
<(), ErrorStack
> {
414 let param
= ssl
._param_mut();
415 param
.set_hostflags(::verify
::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
);
416 match domain
.parse() {
417 Ok(ip
) => param
.set_ip(ip
),
418 Err(_
) => param
.set_host(domain
),
423 fn setup_verify_hostname(ssl
: &mut Ssl
, domain
: &str) -> Result
<(), ErrorStack
> {
424 let domain
= domain
.to_string();
425 ssl
.set_ex_data(*HOSTNAME_IDX
, domain
);
431 use std
::net
::IpAddr
;
435 use x509
::{GeneralName, X509NameRef, X509Ref, X509StoreContextRef}
;
438 pub fn verify_callback(
441 x509_ctx
: &X509StoreContextRef
,
443 if !preverify_ok
|| x509_ctx
.error_depth() != 0 {
447 match x509_ctx
.current_cert() {
448 Some(x509
) => verify_hostname(domain
, &x509
),
453 fn verify_hostname(domain
: &str, cert
: &X509Ref
) -> bool
{
454 match cert
.subject_alt_names() {
455 Some(names
) => verify_subject_alt_names(domain
, names
),
456 None
=> verify_subject_name(domain
, &cert
.subject_name()),
460 fn verify_subject_alt_names(domain
: &str, names
: Stack
<GeneralName
>) -> bool
{
461 let ip
= domain
.parse();
466 if let Some(actual
) = name
.ipaddress() {
467 if matches_ip(&ip
, actual
) {
473 if let Some(pattern
) = name
.dnsname() {
474 if matches_dns(pattern
, domain
, false) {
485 fn verify_subject_name(domain
: &str, subject_name
: &X509NameRef
) -> bool
{
486 if let Some(pattern
) = subject_name
.entries_by_nid(nid
::COMMONNAME
).next() {
487 let pattern
= match str::from_utf8(pattern
.data().as_slice()) {
488 Ok(pattern
) => pattern
,
489 Err(_
) => return false,
492 // Unlike with SANs, IP addresses in the subject name don't have a
493 // different encoding. We need to pass this down to matches_dns to
494 // disallow wildcard matches with bogus patterns like *.0.0.1
495 let is_ip
= domain
.parse
::<IpAddr
>().is_ok();
497 if matches_dns(&pattern
, domain
, is_ip
) {
505 fn matches_dns(mut pattern
: &str, mut hostname
: &str, is_ip
: bool
) -> bool
{
506 // first strip trailing . off of pattern and hostname to normalize
507 if pattern
.ends_with('
.'
) {
508 pattern
= &pattern
[..pattern
.len() - 1];
510 if hostname
.ends_with('
.'
) {
511 hostname
= &hostname
[..hostname
.len() - 1];
514 matches_wildcard(pattern
, hostname
, is_ip
).unwrap_or_else(|| pattern
== hostname
)
517 fn matches_wildcard(pattern
: &str, hostname
: &str, is_ip
: bool
) -> Option
<bool
> {
518 // IP addresses and internationalized domains can't involved in wildcards
519 if is_ip
|| pattern
.starts_with("xn--") {
523 let wildcard_location
= match pattern
.find('
*'
) {
528 let mut dot_idxs
= pattern
.match_indices('
.'
).map(|(l
, _
)| l
);
529 let wildcard_end
= match dot_idxs
.next() {
534 // Never match wildcards if the pattern has less than 2 '.'s (no *.com)
536 // This is a bit dubious, as it doesn't disallow other TLDs like *.co.uk.
537 // Chrome has a black- and white-list for this, but Firefox (via NSS) does
538 // the same thing we do here.
540 // The Public Suffix (https://www.publicsuffix.org/) list could
541 // potentially be used here, but it's both huge and updated frequently
542 // enough that management would be a PITA.
543 if dot_idxs
.next().is_none() {
547 // Wildcards can only be in the first component
548 if wildcard_location
> wildcard_end
{
552 let hostname_label_end
= match hostname
.find('
.'
) {
557 // check that the non-wildcard parts are identical
558 if pattern
[wildcard_end
..] != hostname
[hostname_label_end
..] {
562 let wildcard_prefix
= &pattern
[..wildcard_location
];
563 let wildcard_suffix
= &pattern
[wildcard_location
+ 1..wildcard_end
];
565 let hostname_label
= &hostname
[..hostname_label_end
];
567 // check the prefix of the first label
568 if !hostname_label
.starts_with(wildcard_prefix
) {
573 if !hostname_label
[wildcard_prefix
.len()..].ends_with(wildcard_suffix
) {
580 fn matches_ip(expected
: &IpAddr
, actual
: &[u8]) -> bool
{
581 match (expected
, actual
.len()) {
582 (&IpAddr
::V4(ref addr
), 4) => actual
== addr
.octets(),
583 (&IpAddr
::V6(ref addr
), 16) => {
585 ((actual
[0] as u16) << 8) | actual
[1] as u16,
586 ((actual
[2] as u16) << 8) | actual
[3] as u16,
587 ((actual
[4] as u16) << 8) | actual
[5] as u16,
588 ((actual
[6] as u16) << 8) | actual
[7] as u16,
589 ((actual
[8] as u16) << 8) | actual
[9] as u16,
590 ((actual
[10] as u16) << 8) | actual
[11] as u16,
591 ((actual
[12] as u16) << 8) | actual
[13] as u16,
592 ((actual
[14] as u16) << 8) | actual
[15] as u16,
594 segments
== addr
.segments()