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
|
use std::io::{Read, Write};
use dh::Dh;
use error::ErrorStack;
use ssl::{self, SslMethod, SslContextBuilder, SslContext, Ssl, SSL_VERIFY_PEER, SslStream,
HandshakeError};
use pkey::PKey;
use x509::X509Ref;
// apps/dh2048.pem
const DHPARAM_PEM: &'static str = r#"
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb
IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft
awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT
mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh
fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq
5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg==
-----END DH PARAMETERS-----
These are the 2048-bit DH parameters from "More Modular Exponential
(MODP) Diffie-Hellman groups for Internet Key Exchange (IKE)":
https://tools.ietf.org/html/rfc3526
See https://tools.ietf.org/html/rfc2412 for how they were generated."#;
fn ctx(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> {
let mut ctx = try!(SslContextBuilder::new(method));
// options to enable and cipher list lifted from libcurl
let mut opts = ssl::SSL_OP_ALL;
opts |= ssl::SSL_OP_NO_TICKET;
opts |= ssl::SSL_OP_NO_COMPRESSION;
opts &= !ssl::SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
opts &= !ssl::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
opts |= ssl::SSL_OP_NO_SSLV2;
opts |= ssl::SSL_OP_NO_SSLV3;
ctx.set_options(opts);
Ok(ctx)
}
pub struct ClientConnectorBuilder(SslContextBuilder);
impl ClientConnectorBuilder {
pub fn tls() -> Result<ClientConnectorBuilder, ErrorStack> {
ClientConnectorBuilder::new(SslMethod::tls())
}
fn new(method: SslMethod) -> Result<ClientConnectorBuilder, ErrorStack> {
let mut ctx = try!(ctx(method));
try!(ctx.set_default_verify_paths());
try!(ctx.set_cipher_list("ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH"));
Ok(ClientConnectorBuilder(ctx))
}
pub fn context(&self) -> &SslContextBuilder {
&self.0
}
pub fn context_mut(&mut self) -> &mut SslContextBuilder {
&mut self.0
}
pub fn build(self) -> ClientConnector {
ClientConnector(self.0.build())
}
}
pub struct ClientConnector(SslContext);
impl ClientConnector {
pub fn connect<S>(&self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
where S: Read + Write
{
let mut ssl = try!(Ssl::new(&self.0));
try!(ssl.set_hostname(domain));
try!(setup_verify(&mut ssl, domain));
ssl.connect(stream)
}
}
pub struct ServerConnectorBuilder(SslContextBuilder);
impl ServerConnectorBuilder {
pub fn tls<I, T>(private_key: &PKey,
certificate: &X509Ref,
chain: I)
-> Result<ServerConnectorBuilder, ErrorStack>
where I: IntoIterator<Item = T>,
T: AsRef<X509Ref>
{
ServerConnectorBuilder::new(SslMethod::tls(), private_key, certificate, chain)
}
fn new<I, T>(method: SslMethod,
private_key: &PKey,
certificate: &X509Ref,
chain: I)
-> Result<ServerConnectorBuilder, ErrorStack>
where I: IntoIterator<Item = T>,
T: AsRef<X509Ref>
{
let mut ctx = try!(ctx(method));
ctx.set_options(ssl::SSL_OP_SINGLE_DH_USE | ssl::SSL_OP_CIPHER_SERVER_PREFERENCE);
let dh = try!(Dh::from_pem(DHPARAM_PEM.as_bytes()));
try!(ctx.set_tmp_dh(&dh));
try!(ctx.set_cipher_list(
"ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\
ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\
ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:\
ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:\
ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:\
ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:\
DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:\
EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:\
AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS"));
try!(ctx.set_private_key(private_key));
try!(ctx.set_certificate(certificate));
try!(ctx.check_private_key());
for cert in chain {
try!(ctx.add_extra_chain_cert(cert.as_ref().to_owned()));
}
Ok(ServerConnectorBuilder(ctx))
}
pub fn context(&self) -> &SslContextBuilder {
&self.0
}
pub fn context_mut(&mut self) -> &mut SslContextBuilder {
&mut self.0
}
pub fn build(self) -> ServerConnector {
ServerConnector(self.0.build())
}
}
pub struct ServerConnector(SslContext);
impl ServerConnector {
pub fn connect<S>(&self, stream: S) -> Result<SslStream<S>, HandshakeError<S>>
where S: Read + Write
{
let ssl = try!(Ssl::new(&self.0));
ssl.accept(stream)
}
}
#[cfg(any(ossl102, ossl110))]
fn setup_verify(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> {
ssl.set_verify(SSL_VERIFY_PEER);
let param = ssl._param_mut();
param.set_hostflags(::verify::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
param.set_host(domain)
}
#[cfg(not(any(ossl102, ossl110)))]
fn setup_verify(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> {
let domain = domain.to_owned();
ssl.set_verify_callback(SSL_VERIFY_PEER, move |p, x| verify::verify_callback(&domain, p, x));
Ok(())
}
#[cfg(not(any(ossl102, ossl110)))]
mod verify {
use std::net::IpAddr;
use nid;
use x509::{X509StoreContextRef, X509Ref, GeneralNames, X509NameRef};
pub fn verify_callback(domain: &str,
preverify_ok: bool,
x509_ctx: &X509StoreContextRef)
-> bool {
if !preverify_ok || x509_ctx.error_depth() != 0 {
return preverify_ok;
}
match x509_ctx.current_cert() {
Some(x509) => verify_hostname(domain, &x509),
None => true,
}
}
fn verify_hostname(domain: &str, cert: &X509Ref) -> bool {
match cert.subject_alt_names() {
Some(names) => verify_subject_alt_names(domain, &names),
None => verify_subject_name(domain, &cert.subject_name()),
}
}
fn verify_subject_alt_names(domain: &str, names: &GeneralNames) -> bool {
let ip = domain.parse();
for name in names {
match ip {
Ok(ip) => {
if let Some(actual) = name.ipaddress() {
if matches_ip(&ip, actual) {
return true;
}
}
}
Err(_) => {
if let Some(pattern) = name.dnsname() {
if matches_dns(pattern, domain, false) {
return true;
}
}
}
}
}
false
}
fn verify_subject_name(domain: &str, subject_name: &X509NameRef) -> bool {
if let Some(pattern) = subject_name.text_by_nid(nid::COMMONNAME) {
// Unlike with SANs, IP addresses in the subject name don't have a
// different encoding. We need to pass this down to matches_dns to
// disallow wildcard matches with bogus patterns like *.0.0.1
let is_ip = domain.parse::<IpAddr>().is_ok();
if matches_dns(&pattern, domain, is_ip) {
return true;
}
}
false
}
fn matches_dns(mut pattern: &str, mut hostname: &str, is_ip: bool) -> bool {
// first strip trailing . off of pattern and hostname to normalize
if pattern.ends_with('.') {
pattern = &pattern[..pattern.len() - 1];
}
if hostname.ends_with('.') {
hostname = &hostname[..hostname.len() - 1];
}
matches_wildcard(pattern, hostname, is_ip).unwrap_or_else(|| pattern == hostname)
}
fn matches_wildcard(pattern: &str, hostname: &str, is_ip: bool) -> Option<bool> {
// IP addresses and internationalized domains can't involved in wildcards
if is_ip || pattern.starts_with("xn--") {
return None;
}
let wildcard_location = match pattern.find('*') {
Some(l) => l,
None => return None,
};
let mut dot_idxs = pattern.match_indices('.').map(|(l, _)| l);
let wildcard_end = match dot_idxs.next() {
Some(l) => l,
None => return None,
};
// Never match wildcards if the pattern has less than 2 '.'s (no *.com)
//
// This is a bit dubious, as it doesn't disallow other TLDs like *.co.uk.
// Chrome has a black- and white-list for this, but Firefox (via NSS) does
// the same thing we do here.
//
// The Public Suffix (https://www.publicsuffix.org/) list could
// potentically be used here, but it's both huge and updated frequently
// enough that management would be a PITA.
if dot_idxs.next().is_none() {
return None;
}
// Wildcards can only be in the first component
if wildcard_location > wildcard_end {
return None;
}
let hostname_label_end = match hostname.find('.') {
Some(l) => l,
None => return None,
};
// check that the non-wildcard parts are identical
if pattern[wildcard_end..] != hostname[hostname_label_end..] {
return Some(false);
}
let wildcard_prefix = &pattern[..wildcard_location];
let wildcard_suffix = &pattern[wildcard_location + 1..wildcard_end];
let hostname_label = &hostname[..hostname_label_end];
// check the prefix of the first label
if !hostname_label.starts_with(wildcard_prefix) {
return Some(false);
}
// and the suffix
if !hostname_label[wildcard_prefix.len()..].ends_with(wildcard_suffix) {
return Some(false);
}
Some(true)
}
fn matches_ip(expected: &IpAddr, actual: &[u8]) -> bool {
match (expected, actual.len()) {
(&IpAddr::V4(ref addr), 4) => actual == addr.octets(),
(&IpAddr::V6(ref addr), 16) => {
let segments = [((actual[0] as u16) << 8) | actual[1] as u16,
((actual[2] as u16) << 8) | actual[3] as u16,
((actual[4] as u16) << 8) | actual[5] as u16,
((actual[6] as u16) << 8) | actual[7] as u16,
((actual[8] as u16) << 8) | actual[9] as u16,
((actual[10] as u16) << 8) | actual[11] as u16,
((actual[12] as u16) << 8) | actual[13] as u16,
((actual[14] as u16) << 8) | actual[15] as u16];
segments == addr.segments()
}
_ => false,
}
}
}
|