]> git.proxmox.com Git - cargo.git/blob - vendor/openssl-0.9.23/src/ssl/connector.rs
New upstream version 0.24.0
[cargo.git] / vendor / openssl-0.9.23 / src / ssl / connector.rs
1 use std::io::{Read, Write};
2 use std::ops::{Deref, DerefMut};
3
4 use dh::Dh;
5 use error::ErrorStack;
6 use ssl::{self, HandshakeError, Ssl, SslRef, SslContext, SslContextBuilder, SslMethod, SslStream,
7 SSL_VERIFY_PEER};
8 use pkey::PKeyRef;
9 use version;
10 use x509::X509Ref;
11
12 #[cfg(ossl101)]
13 lazy_static! {
14 static ref HOSTNAME_IDX: ::ex_data::Index<Ssl, String> = Ssl::new_ex_index().unwrap();
15 }
16
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-----
27 ";
28
29 fn ctx(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
30 let mut ctx = SslContextBuilder::new(method)?;
31
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);
43
44 let mut mode = ssl::SSL_MODE_AUTO_RETRY | ssl::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
45 | ssl::SSL_MODE_ENABLE_PARTIAL_WRITE;
46
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;
52 }
53
54 ctx.set_mode(mode);
55
56 Ok(ctx)
57 }
58
59 /// A builder for `SslConnector`s.
60 pub struct SslConnectorBuilder(SslContextBuilder);
61
62 impl SslConnectorBuilder {
63 /// Creates a new builder for TLS connections.
64 ///
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
70 ctx.set_cipher_list(
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",
76 )?;
77 setup_verify(&mut ctx);
78
79 Ok(SslConnectorBuilder(ctx))
80 }
81
82 #[deprecated(since = "0.9.23",
83 note = "SslConnectorBuilder now implements Deref<Target=SslContextBuilder>")]
84 pub fn builder(&self) -> &SslContextBuilder {
85 self
86 }
87
88 #[deprecated(since = "0.9.23",
89 note = "SslConnectorBuilder now implements DerefMut<Target=SslContextBuilder>")]
90 pub fn builder_mut(&mut self) -> &mut SslContextBuilder {
91 self
92 }
93
94 /// Consumes the builder, returning an `SslConnector`.
95 pub fn build(self) -> SslConnector {
96 SslConnector(self.0.build())
97 }
98 }
99
100 impl Deref for SslConnectorBuilder {
101 type Target = SslContextBuilder;
102
103 fn deref(&self) -> &SslContextBuilder {
104 &self.0
105 }
106 }
107
108 impl DerefMut for SslConnectorBuilder {
109 fn deref_mut(&mut self) -> &mut SslContextBuilder {
110 &mut self.0
111 }
112 }
113
114 /// A type which wraps client-side streams in a TLS session.
115 ///
116 /// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL
117 /// structures, configuring cipher suites, session options, hostname verification, and more.
118 ///
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.
121 #[derive(Clone)]
122 pub struct SslConnector(SslContext);
123
124 impl SslConnector {
125 /// Initiates a client-side TLS session on a stream.
126 ///
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>>
129 where
130 S: Read + Write,
131 {
132 self.configure()?.connect(domain, stream)
133 }
134
135 /// Initiates a client-side TLS session on a stream without performing hostname verification.
136 ///
137 /// # Warning
138 ///
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<
143 S,
144 >(
145 &self,
146 stream: S,
147 ) -> Result<SslStream<S>, HandshakeError<S>>
148 where
149 S: Read + Write,
150 {
151 self.configure()?
152 .danger_connect_without_providing_domain_for_certificate_verification_and_server_name_indication(stream)
153 }
154
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)
158 }
159 }
160
161 /// A type which allows for configuration of a client-side TLS session before connection.
162 pub struct ConnectConfiguration(Ssl);
163
164 impl ConnectConfiguration {
165 #[deprecated(since = "0.9.23",
166 note = "ConnectConfiguration now implements Deref<Target=SslRef>")]
167 pub fn ssl(&self) -> &Ssl {
168 &self.0
169 }
170
171 #[deprecated(since = "0.9.23",
172 note = "ConnectConfiguration now implements DerefMut<Target=SslRef>")]
173 pub fn ssl_mut(&mut self) -> &mut Ssl {
174 &mut self.0
175 }
176
177 /// Initiates a client-side TLS session on a stream.
178 ///
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>>
181 where
182 S: Read + Write,
183 {
184 self.0.set_hostname(domain)?;
185 setup_verify_hostname(&mut self.0, domain)?;
186
187 self.0.connect(stream)
188 }
189
190 /// Initiates a client-side TLS session on a stream without performing hostname verification.
191 ///
192 /// The verification configuration of the connector's `SslContext` is not overridden.
193 ///
194 /// # Warning
195 ///
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<
200 S,
201 >(
202 self,
203 stream: S,
204 ) -> Result<SslStream<S>, HandshakeError<S>>
205 where
206 S: Read + Write,
207 {
208 self.0.connect(stream)
209 }
210 }
211
212 impl Deref for ConnectConfiguration {
213 type Target = SslRef;
214
215 fn deref(&self) -> &SslRef {
216 &self.0
217 }
218 }
219
220 impl DerefMut for ConnectConfiguration {
221 fn deref_mut(&mut self) -> &mut SslRef {
222 &mut self.0
223 }
224 }
225
226 /// A builder for `SslAcceptor`s.
227 pub struct SslAcceptorBuilder(SslContextBuilder);
228
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.
232 ///
233 /// This corresponds to the intermediate configuration of Mozilla's server side TLS
234 /// recommendations. See its [documentation][docs] for more details on specifics.
235 ///
236 /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
237 pub fn mozilla_intermediate<I>(
238 method: SslMethod,
239 private_key: &PKeyRef,
240 certificate: &X509Ref,
241 chain: I,
242 ) -> Result<SslAcceptorBuilder, ErrorStack>
243 where
244 I: IntoIterator,
245 I::Item: AsRef<X509Ref>,
246 {
247 let builder = SslAcceptorBuilder::mozilla_intermediate_raw(method)?;
248 builder.finish_setup(private_key, certificate, chain)
249 }
250
251 /// Creates a new builder configured to connect to modern clients.
252 ///
253 /// This corresponds to the modern configuration of Mozilla's server side TLS recommendations.
254 /// See its [documentation][docs] for more details on specifics.
255 ///
256 /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS
257 pub fn mozilla_modern<I>(
258 method: SslMethod,
259 private_key: &PKeyRef,
260 certificate: &X509Ref,
261 chain: I,
262 ) -> Result<SslAcceptorBuilder, ErrorStack>
263 where
264 I: IntoIterator,
265 I::Item: AsRef<X509Ref>,
266 {
267 let builder = SslAcceptorBuilder::mozilla_modern_raw(method)?;
268 builder.finish_setup(private_key, certificate, chain)
269 }
270
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)?;
277 ctx.set_cipher_list(
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",
288 )?;
289 Ok(SslAcceptorBuilder(ctx))
290 }
291
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)?;
296 ctx.set_cipher_list(
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",
301 )?;
302 Ok(SslAcceptorBuilder(ctx))
303 }
304
305 fn finish_setup<I>(
306 mut self,
307 private_key: &PKeyRef,
308 certificate: &X509Ref,
309 chain: I,
310 ) -> Result<SslAcceptorBuilder, ErrorStack>
311 where
312 I: IntoIterator,
313 I::Item: AsRef<X509Ref>,
314 {
315 self.0.set_private_key(private_key)?;
316 self.0.set_certificate(certificate)?;
317 self.0.check_private_key()?;
318 for cert in chain {
319 self.0.add_extra_chain_cert(cert.as_ref().to_owned())?;
320 }
321 Ok(self)
322 }
323
324 #[deprecated(since = "0.9.23",
325 note = "SslAcceptorBuilder now implements Deref<Target=SslContextBuilder>")]
326 pub fn builder(&self) -> &SslContextBuilder {
327 self
328 }
329
330 #[deprecated(since = "0.9.23",
331 note = "SslAcceptorBuilder now implements DerefMut<Target=SslContextBuilder>")]
332 pub fn builder_mut(&mut self) -> &mut SslContextBuilder {
333 self
334 }
335
336 /// Consumes the builder, returning a `SslAcceptor`.
337 pub fn build(self) -> SslAcceptor {
338 SslAcceptor(self.0.build())
339 }
340 }
341
342 impl Deref for SslAcceptorBuilder {
343 type Target = SslContextBuilder;
344
345 fn deref(&self) -> &SslContextBuilder {
346 &self.0
347 }
348 }
349
350 impl DerefMut for SslAcceptorBuilder {
351 fn deref_mut(&mut self) -> &mut SslContextBuilder {
352 &mut self.0
353 }
354 }
355
356 #[cfg(ossl101)]
357 fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> {
358 use ec::EcKey;
359 use nid;
360
361 let curve = EcKey::from_curve_name(nid::X9_62_PRIME256V1)?;
362 ctx.set_tmp_ecdh(&curve)
363 }
364
365 #[cfg(ossl102)]
366 fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> {
367 ctx._set_ecdh_auto(true)
368 }
369
370 #[cfg(ossl110)]
371 fn setup_curves(_: &mut SslContextBuilder) -> Result<(), ErrorStack> {
372 Ok(())
373 }
374
375 /// A type which wraps server-side streams in a TLS session.
376 ///
377 /// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL
378 /// structures, configuring cipher suites, session options, and more.
379 #[derive(Clone)]
380 pub struct SslAcceptor(SslContext);
381
382 impl SslAcceptor {
383 /// Initiates a server-side TLS session on a stream.
384 pub fn accept<S>(&self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
385 where
386 S: Read + Write,
387 {
388 let ssl = Ssl::new(&self.0)?;
389 ssl.accept(stream)
390 }
391 }
392
393 #[cfg(any(ossl102, ossl110))]
394 fn setup_verify(ctx: &mut SslContextBuilder) {
395 ctx.set_verify(SSL_VERIFY_PEER);
396 }
397
398 #[cfg(ossl101)]
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),
403 _ => None,
404 };
405 match hostname {
406 Some(hostname) => verify::verify_callback(hostname, p, x509),
407 None => p,
408 }
409 });
410 }
411
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),
419 }
420 }
421
422 #[cfg(ossl101)]
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);
426 Ok(())
427 }
428
429 #[cfg(ossl101)]
430 mod verify {
431 use std::net::IpAddr;
432 use std::str;
433
434 use nid;
435 use x509::{GeneralName, X509NameRef, X509Ref, X509StoreContextRef};
436 use stack::Stack;
437
438 pub fn verify_callback(
439 domain: &str,
440 preverify_ok: bool,
441 x509_ctx: &X509StoreContextRef,
442 ) -> bool {
443 if !preverify_ok || x509_ctx.error_depth() != 0 {
444 return preverify_ok;
445 }
446
447 match x509_ctx.current_cert() {
448 Some(x509) => verify_hostname(domain, &x509),
449 None => true,
450 }
451 }
452
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()),
457 }
458 }
459
460 fn verify_subject_alt_names(domain: &str, names: Stack<GeneralName>) -> bool {
461 let ip = domain.parse();
462
463 for name in &names {
464 match ip {
465 Ok(ip) => {
466 if let Some(actual) = name.ipaddress() {
467 if matches_ip(&ip, actual) {
468 return true;
469 }
470 }
471 }
472 Err(_) => {
473 if let Some(pattern) = name.dnsname() {
474 if matches_dns(pattern, domain, false) {
475 return true;
476 }
477 }
478 }
479 }
480 }
481
482 false
483 }
484
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,
490 };
491
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();
496
497 if matches_dns(&pattern, domain, is_ip) {
498 return true;
499 }
500 }
501
502 false
503 }
504
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];
509 }
510 if hostname.ends_with('.') {
511 hostname = &hostname[..hostname.len() - 1];
512 }
513
514 matches_wildcard(pattern, hostname, is_ip).unwrap_or_else(|| pattern == hostname)
515 }
516
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--") {
520 return None;
521 }
522
523 let wildcard_location = match pattern.find('*') {
524 Some(l) => l,
525 None => return None,
526 };
527
528 let mut dot_idxs = pattern.match_indices('.').map(|(l, _)| l);
529 let wildcard_end = match dot_idxs.next() {
530 Some(l) => l,
531 None => return None,
532 };
533
534 // Never match wildcards if the pattern has less than 2 '.'s (no *.com)
535 //
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.
539 //
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() {
544 return None;
545 }
546
547 // Wildcards can only be in the first component
548 if wildcard_location > wildcard_end {
549 return None;
550 }
551
552 let hostname_label_end = match hostname.find('.') {
553 Some(l) => l,
554 None => return None,
555 };
556
557 // check that the non-wildcard parts are identical
558 if pattern[wildcard_end..] != hostname[hostname_label_end..] {
559 return Some(false);
560 }
561
562 let wildcard_prefix = &pattern[..wildcard_location];
563 let wildcard_suffix = &pattern[wildcard_location + 1..wildcard_end];
564
565 let hostname_label = &hostname[..hostname_label_end];
566
567 // check the prefix of the first label
568 if !hostname_label.starts_with(wildcard_prefix) {
569 return Some(false);
570 }
571
572 // and the suffix
573 if !hostname_label[wildcard_prefix.len()..].ends_with(wildcard_suffix) {
574 return Some(false);
575 }
576
577 Some(true)
578 }
579
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) => {
584 let segments = [
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,
593 ];
594 segments == addr.segments()
595 }
596 _ => false,
597 }
598 }
599 }