|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 // Original author: ekr@rtfm.com |
|
8 |
|
9 #include <queue> |
|
10 #include <algorithm> |
|
11 |
|
12 #include "logging.h" |
|
13 #include "ssl.h" |
|
14 #include "sslerr.h" |
|
15 #include "sslproto.h" |
|
16 #include "keyhi.h" |
|
17 |
|
18 #include "nsCOMPtr.h" |
|
19 #include "nsComponentManagerUtils.h" |
|
20 #include "nsIEventTarget.h" |
|
21 #include "nsNetCID.h" |
|
22 #include "nsComponentManagerUtils.h" |
|
23 #include "nsServiceManagerUtils.h" |
|
24 |
|
25 #include "dtlsidentity.h" |
|
26 #include "transportflow.h" |
|
27 #include "transportlayerdtls.h" |
|
28 |
|
29 namespace mozilla { |
|
30 |
|
31 MOZ_MTLOG_MODULE("mtransport") |
|
32 |
|
33 static PRDescIdentity transport_layer_identity = PR_INVALID_IO_LAYER; |
|
34 |
|
35 // TODO: Implement a mode for this where |
|
36 // the channel is not ready until confirmed externally |
|
37 // (e.g., after cert check). |
|
38 |
|
39 #define UNIMPLEMENTED \ |
|
40 MOZ_MTLOG(ML_ERROR, \ |
|
41 "Call to unimplemented function "<< __FUNCTION__); \ |
|
42 MOZ_ASSERT(false); \ |
|
43 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0) |
|
44 |
|
45 |
|
46 // We need to adapt the NSPR/libssl model to the TransportFlow model. |
|
47 // The former wants pull semantics and TransportFlow wants push. |
|
48 // |
|
49 // - A TransportLayerDtls assumes it is sitting on top of another |
|
50 // TransportLayer, which means that events come in asynchronously. |
|
51 // - NSS (libssl) wants to sit on top of a PRFileDesc and poll. |
|
52 // - The TransportLayerNSPRAdapter is a PRFileDesc containing a |
|
53 // FIFO. |
|
54 // - When TransportLayerDtls.PacketReceived() is called, we insert |
|
55 // the packets in the FIFO and then do a PR_Recv() on the NSS |
|
56 // PRFileDesc, which eventually reads off the FIFO. |
|
57 // |
|
58 // All of this stuff is assumed to happen solely in a single thread |
|
59 // (generally the SocketTransportService thread) |
|
60 struct Packet { |
|
61 Packet() : data_(nullptr), len_(0), offset_(0) {} |
|
62 |
|
63 void Assign(const void *data, int32_t len) { |
|
64 data_ = new uint8_t[len]; |
|
65 memcpy(data_, data, len); |
|
66 len_ = len; |
|
67 } |
|
68 |
|
69 ScopedDeleteArray<uint8_t> data_; |
|
70 int32_t len_; |
|
71 int32_t offset_; |
|
72 }; |
|
73 |
|
74 void TransportLayerNSPRAdapter::PacketReceived(const void *data, int32_t len) { |
|
75 input_.push(new Packet()); |
|
76 input_.back()->Assign(data, len); |
|
77 } |
|
78 |
|
79 int32_t TransportLayerNSPRAdapter::Read(void *data, int32_t len) { |
|
80 if (input_.empty()) { |
|
81 PR_SetError(PR_WOULD_BLOCK_ERROR, 0); |
|
82 return TE_WOULDBLOCK; |
|
83 } |
|
84 |
|
85 Packet* front = input_.front(); |
|
86 int32_t to_read = std::min(len, front->len_ - front->offset_); |
|
87 memcpy(data, front->data_, to_read); |
|
88 front->offset_ += to_read; |
|
89 |
|
90 if (front->offset_ == front->len_) { |
|
91 input_.pop(); |
|
92 delete front; |
|
93 } |
|
94 |
|
95 return to_read; |
|
96 } |
|
97 |
|
98 int32_t TransportLayerNSPRAdapter::Write(const void *buf, int32_t length) { |
|
99 TransportResult r = output_->SendPacket( |
|
100 static_cast<const unsigned char *>(buf), length); |
|
101 if (r >= 0) { |
|
102 return r; |
|
103 } |
|
104 |
|
105 if (r == TE_WOULDBLOCK) { |
|
106 PR_SetError(PR_WOULD_BLOCK_ERROR, 0); |
|
107 } else { |
|
108 PR_SetError(PR_IO_ERROR, 0); |
|
109 } |
|
110 |
|
111 return -1; |
|
112 } |
|
113 |
|
114 |
|
115 // Implementation of NSPR methods |
|
116 static PRStatus TransportLayerClose(PRFileDesc *f) { |
|
117 f->secret = nullptr; |
|
118 return PR_SUCCESS; |
|
119 } |
|
120 |
|
121 static int32_t TransportLayerRead(PRFileDesc *f, void *buf, int32_t length) { |
|
122 TransportLayerNSPRAdapter *io = reinterpret_cast<TransportLayerNSPRAdapter *>(f->secret); |
|
123 return io->Read(buf, length); |
|
124 } |
|
125 |
|
126 static int32_t TransportLayerWrite(PRFileDesc *f, const void *buf, int32_t length) { |
|
127 TransportLayerNSPRAdapter *io = reinterpret_cast<TransportLayerNSPRAdapter *>(f->secret); |
|
128 return io->Write(buf, length); |
|
129 } |
|
130 |
|
131 static int32_t TransportLayerAvailable(PRFileDesc *f) { |
|
132 UNIMPLEMENTED; |
|
133 return -1; |
|
134 } |
|
135 |
|
136 int64_t TransportLayerAvailable64(PRFileDesc *f) { |
|
137 UNIMPLEMENTED; |
|
138 return -1; |
|
139 } |
|
140 |
|
141 static PRStatus TransportLayerSync(PRFileDesc *f) { |
|
142 UNIMPLEMENTED; |
|
143 return PR_FAILURE; |
|
144 } |
|
145 |
|
146 static int32_t TransportLayerSeek(PRFileDesc *f, int32_t offset, |
|
147 PRSeekWhence how) { |
|
148 UNIMPLEMENTED; |
|
149 return -1; |
|
150 } |
|
151 |
|
152 static int64_t TransportLayerSeek64(PRFileDesc *f, int64_t offset, |
|
153 PRSeekWhence how) { |
|
154 UNIMPLEMENTED; |
|
155 return -1; |
|
156 } |
|
157 |
|
158 static PRStatus TransportLayerFileInfo(PRFileDesc *f, PRFileInfo *info) { |
|
159 UNIMPLEMENTED; |
|
160 return PR_FAILURE; |
|
161 } |
|
162 |
|
163 static PRStatus TransportLayerFileInfo64(PRFileDesc *f, PRFileInfo64 *info) { |
|
164 UNIMPLEMENTED; |
|
165 return PR_FAILURE; |
|
166 } |
|
167 |
|
168 static int32_t TransportLayerWritev(PRFileDesc *f, const PRIOVec *iov, |
|
169 int32_t iov_size, PRIntervalTime to) { |
|
170 UNIMPLEMENTED; |
|
171 return -1; |
|
172 } |
|
173 |
|
174 static PRStatus TransportLayerConnect(PRFileDesc *f, const PRNetAddr *addr, |
|
175 PRIntervalTime to) { |
|
176 UNIMPLEMENTED; |
|
177 return PR_FAILURE; |
|
178 } |
|
179 |
|
180 static PRFileDesc *TransportLayerAccept(PRFileDesc *sd, PRNetAddr *addr, |
|
181 PRIntervalTime to) { |
|
182 UNIMPLEMENTED; |
|
183 return nullptr; |
|
184 } |
|
185 |
|
186 static PRStatus TransportLayerBind(PRFileDesc *f, const PRNetAddr *addr) { |
|
187 UNIMPLEMENTED; |
|
188 return PR_FAILURE; |
|
189 } |
|
190 |
|
191 static PRStatus TransportLayerListen(PRFileDesc *f, int32_t depth) { |
|
192 UNIMPLEMENTED; |
|
193 return PR_FAILURE; |
|
194 } |
|
195 |
|
196 static PRStatus TransportLayerShutdown(PRFileDesc *f, int32_t how) { |
|
197 UNIMPLEMENTED; |
|
198 return PR_FAILURE; |
|
199 } |
|
200 |
|
201 // This function does not support peek. |
|
202 static int32_t TransportLayerRecv(PRFileDesc *f, void *buf, int32_t amount, |
|
203 int32_t flags, PRIntervalTime to) { |
|
204 MOZ_ASSERT(flags == 0); |
|
205 if (flags != 0) { |
|
206 PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); |
|
207 return -1; |
|
208 } |
|
209 |
|
210 return TransportLayerRead(f, buf, amount); |
|
211 } |
|
212 |
|
213 // Note: this is always nonblocking and assumes a zero timeout. |
|
214 static int32_t TransportLayerSend(PRFileDesc *f, const void *buf, int32_t amount, |
|
215 int32_t flags, PRIntervalTime to) { |
|
216 int32_t written = TransportLayerWrite(f, buf, amount); |
|
217 return written; |
|
218 } |
|
219 |
|
220 static int32_t TransportLayerRecvfrom(PRFileDesc *f, void *buf, int32_t amount, |
|
221 int32_t flags, PRNetAddr *addr, PRIntervalTime to) { |
|
222 UNIMPLEMENTED; |
|
223 return -1; |
|
224 } |
|
225 |
|
226 static int32_t TransportLayerSendto(PRFileDesc *f, const void *buf, int32_t amount, |
|
227 int32_t flags, const PRNetAddr *addr, PRIntervalTime to) { |
|
228 UNIMPLEMENTED; |
|
229 return -1; |
|
230 } |
|
231 |
|
232 static int16_t TransportLayerPoll(PRFileDesc *f, int16_t in_flags, int16_t *out_flags) { |
|
233 UNIMPLEMENTED; |
|
234 return -1; |
|
235 } |
|
236 |
|
237 static int32_t TransportLayerAcceptRead(PRFileDesc *sd, PRFileDesc **nd, |
|
238 PRNetAddr **raddr, |
|
239 void *buf, int32_t amount, PRIntervalTime t) { |
|
240 UNIMPLEMENTED; |
|
241 return -1; |
|
242 } |
|
243 |
|
244 static int32_t TransportLayerTransmitFile(PRFileDesc *sd, PRFileDesc *f, |
|
245 const void *headers, int32_t hlen, |
|
246 PRTransmitFileFlags flags, PRIntervalTime t) { |
|
247 UNIMPLEMENTED; |
|
248 return -1; |
|
249 } |
|
250 |
|
251 static PRStatus TransportLayerGetpeername(PRFileDesc *f, PRNetAddr *addr) { |
|
252 // TODO: Modify to return unique names for each channel |
|
253 // somehow, as opposed to always the same static address. The current |
|
254 // implementation messes up the session cache, which is why it's off |
|
255 // elsewhere |
|
256 addr->inet.family = PR_AF_INET; |
|
257 addr->inet.port = 0; |
|
258 addr->inet.ip = 0; |
|
259 |
|
260 return PR_SUCCESS; |
|
261 } |
|
262 |
|
263 static PRStatus TransportLayerGetsockname(PRFileDesc *f, PRNetAddr *addr) { |
|
264 UNIMPLEMENTED; |
|
265 return PR_FAILURE; |
|
266 } |
|
267 |
|
268 static PRStatus TransportLayerGetsockoption(PRFileDesc *f, PRSocketOptionData *opt) { |
|
269 switch (opt->option) { |
|
270 case PR_SockOpt_Nonblocking: |
|
271 opt->value.non_blocking = PR_TRUE; |
|
272 return PR_SUCCESS; |
|
273 default: |
|
274 UNIMPLEMENTED; |
|
275 break; |
|
276 } |
|
277 |
|
278 return PR_FAILURE; |
|
279 } |
|
280 |
|
281 // Imitate setting socket options. These are mostly noops. |
|
282 static PRStatus TransportLayerSetsockoption(PRFileDesc *f, |
|
283 const PRSocketOptionData *opt) { |
|
284 switch (opt->option) { |
|
285 case PR_SockOpt_Nonblocking: |
|
286 return PR_SUCCESS; |
|
287 case PR_SockOpt_NoDelay: |
|
288 return PR_SUCCESS; |
|
289 default: |
|
290 UNIMPLEMENTED; |
|
291 break; |
|
292 } |
|
293 |
|
294 return PR_FAILURE; |
|
295 } |
|
296 |
|
297 static int32_t TransportLayerSendfile(PRFileDesc *out, PRSendFileData *in, |
|
298 PRTransmitFileFlags flags, PRIntervalTime to) { |
|
299 UNIMPLEMENTED; |
|
300 return -1; |
|
301 } |
|
302 |
|
303 static PRStatus TransportLayerConnectContinue(PRFileDesc *f, int16_t flags) { |
|
304 UNIMPLEMENTED; |
|
305 return PR_FAILURE; |
|
306 } |
|
307 |
|
308 static int32_t TransportLayerReserved(PRFileDesc *f) { |
|
309 UNIMPLEMENTED; |
|
310 return -1; |
|
311 } |
|
312 |
|
313 static const struct PRIOMethods TransportLayerMethods = { |
|
314 PR_DESC_LAYERED, |
|
315 TransportLayerClose, |
|
316 TransportLayerRead, |
|
317 TransportLayerWrite, |
|
318 TransportLayerAvailable, |
|
319 TransportLayerAvailable64, |
|
320 TransportLayerSync, |
|
321 TransportLayerSeek, |
|
322 TransportLayerSeek64, |
|
323 TransportLayerFileInfo, |
|
324 TransportLayerFileInfo64, |
|
325 TransportLayerWritev, |
|
326 TransportLayerConnect, |
|
327 TransportLayerAccept, |
|
328 TransportLayerBind, |
|
329 TransportLayerListen, |
|
330 TransportLayerShutdown, |
|
331 TransportLayerRecv, |
|
332 TransportLayerSend, |
|
333 TransportLayerRecvfrom, |
|
334 TransportLayerSendto, |
|
335 TransportLayerPoll, |
|
336 TransportLayerAcceptRead, |
|
337 TransportLayerTransmitFile, |
|
338 TransportLayerGetsockname, |
|
339 TransportLayerGetpeername, |
|
340 TransportLayerReserved, |
|
341 TransportLayerReserved, |
|
342 TransportLayerGetsockoption, |
|
343 TransportLayerSetsockoption, |
|
344 TransportLayerSendfile, |
|
345 TransportLayerConnectContinue, |
|
346 TransportLayerReserved, |
|
347 TransportLayerReserved, |
|
348 TransportLayerReserved, |
|
349 TransportLayerReserved |
|
350 }; |
|
351 |
|
352 TransportLayerDtls::~TransportLayerDtls() { |
|
353 if (timer_) { |
|
354 timer_->Cancel(); |
|
355 } |
|
356 } |
|
357 |
|
358 nsresult TransportLayerDtls::InitInternal() { |
|
359 // Get the transport service as an event target |
|
360 nsresult rv; |
|
361 target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); |
|
362 |
|
363 if (NS_FAILED(rv)) { |
|
364 MOZ_MTLOG(ML_ERROR, "Couldn't get socket transport service"); |
|
365 return rv; |
|
366 } |
|
367 |
|
368 timer_ = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); |
|
369 if (NS_FAILED(rv)) { |
|
370 MOZ_MTLOG(ML_ERROR, "Couldn't get timer"); |
|
371 return rv; |
|
372 } |
|
373 |
|
374 return NS_OK; |
|
375 } |
|
376 |
|
377 |
|
378 void TransportLayerDtls::WasInserted() { |
|
379 // Connect to the lower layers |
|
380 if (!Setup()) { |
|
381 TL_SET_STATE(TS_ERROR); |
|
382 } |
|
383 } |
|
384 |
|
385 |
|
386 nsresult TransportLayerDtls::SetVerificationAllowAll() { |
|
387 // Defensive programming |
|
388 if (verification_mode_ != VERIFY_UNSET) |
|
389 return NS_ERROR_ALREADY_INITIALIZED; |
|
390 |
|
391 verification_mode_ = VERIFY_ALLOW_ALL; |
|
392 |
|
393 return NS_OK; |
|
394 } |
|
395 |
|
396 nsresult |
|
397 TransportLayerDtls::SetVerificationDigest(const std::string digest_algorithm, |
|
398 const unsigned char *digest_value, |
|
399 size_t digest_len) { |
|
400 // Defensive programming |
|
401 if (verification_mode_ != VERIFY_UNSET && |
|
402 verification_mode_ != VERIFY_DIGEST) { |
|
403 return NS_ERROR_ALREADY_INITIALIZED; |
|
404 } |
|
405 |
|
406 // Note that we do not sanity check these values for length. |
|
407 // We merely ensure they will fit into the buffer. |
|
408 // TODO: is there a Data construct we could use? |
|
409 if (digest_len > kMaxDigestLength) |
|
410 return NS_ERROR_INVALID_ARG; |
|
411 |
|
412 digests_.push_back(new VerificationDigest( |
|
413 digest_algorithm, digest_value, digest_len)); |
|
414 |
|
415 verification_mode_ = VERIFY_DIGEST; |
|
416 |
|
417 return NS_OK; |
|
418 } |
|
419 |
|
420 // TODO: make sure this is called from STS. Otherwise |
|
421 // we have thread safety issues |
|
422 bool TransportLayerDtls::Setup() { |
|
423 CheckThread(); |
|
424 SECStatus rv; |
|
425 |
|
426 if (!downward_) { |
|
427 MOZ_MTLOG(ML_ERROR, "DTLS layer with nothing below. This is useless"); |
|
428 return false; |
|
429 } |
|
430 nspr_io_adapter_ = new TransportLayerNSPRAdapter(downward_); |
|
431 |
|
432 if (!identity_) { |
|
433 MOZ_MTLOG(ML_ERROR, "Can't start DTLS without an identity"); |
|
434 return false; |
|
435 } |
|
436 |
|
437 if (verification_mode_ == VERIFY_UNSET) { |
|
438 MOZ_MTLOG(ML_ERROR, |
|
439 "Can't start DTLS without specifying a verification mode"); |
|
440 return false; |
|
441 } |
|
442 |
|
443 if (transport_layer_identity == PR_INVALID_IO_LAYER) { |
|
444 transport_layer_identity = PR_GetUniqueIdentity("nssstreamadapter"); |
|
445 } |
|
446 |
|
447 ScopedPRFileDesc pr_fd(PR_CreateIOLayerStub(transport_layer_identity, |
|
448 &TransportLayerMethods)); |
|
449 MOZ_ASSERT(pr_fd != nullptr); |
|
450 if (!pr_fd) |
|
451 return false; |
|
452 pr_fd->secret = reinterpret_cast<PRFilePrivate *>(nspr_io_adapter_.get()); |
|
453 |
|
454 ScopedPRFileDesc ssl_fd; |
|
455 if (mode_ == DGRAM) { |
|
456 ssl_fd = DTLS_ImportFD(nullptr, pr_fd); |
|
457 } else { |
|
458 ssl_fd = SSL_ImportFD(nullptr, pr_fd); |
|
459 } |
|
460 |
|
461 MOZ_ASSERT(ssl_fd != nullptr); // This should never happen |
|
462 if (!ssl_fd) { |
|
463 return false; |
|
464 } |
|
465 |
|
466 pr_fd.forget(); // ownership transfered to ssl_fd; |
|
467 |
|
468 if (role_ == CLIENT) { |
|
469 MOZ_MTLOG(ML_DEBUG, "Setting up DTLS as client"); |
|
470 rv = SSL_GetClientAuthDataHook(ssl_fd, GetClientAuthDataHook, |
|
471 this); |
|
472 if (rv != SECSuccess) { |
|
473 MOZ_MTLOG(ML_ERROR, "Couldn't set identity"); |
|
474 return false; |
|
475 } |
|
476 } else { |
|
477 MOZ_MTLOG(ML_DEBUG, "Setting up DTLS as server"); |
|
478 // Server side |
|
479 rv = SSL_ConfigSecureServer(ssl_fd, identity_->cert(), |
|
480 identity_->privkey(), |
|
481 kt_rsa); |
|
482 if (rv != SECSuccess) { |
|
483 MOZ_MTLOG(ML_ERROR, "Couldn't set identity"); |
|
484 return false; |
|
485 } |
|
486 |
|
487 // Insist on a certificate from the client |
|
488 rv = SSL_OptionSet(ssl_fd, SSL_REQUEST_CERTIFICATE, PR_TRUE); |
|
489 if (rv != SECSuccess) { |
|
490 MOZ_MTLOG(ML_ERROR, "Couldn't request certificate"); |
|
491 return false; |
|
492 } |
|
493 |
|
494 rv = SSL_OptionSet(ssl_fd, SSL_REQUIRE_CERTIFICATE, PR_TRUE); |
|
495 if (rv != SECSuccess) { |
|
496 MOZ_MTLOG(ML_ERROR, "Couldn't require certificate"); |
|
497 return false; |
|
498 } |
|
499 } |
|
500 |
|
501 // Require TLS 1.1. Perhaps some day in the future we will allow |
|
502 // TLS 1.0 for stream modes. |
|
503 SSLVersionRange version_range = { |
|
504 SSL_LIBRARY_VERSION_TLS_1_1, |
|
505 SSL_LIBRARY_VERSION_TLS_1_1 |
|
506 }; |
|
507 |
|
508 rv = SSL_VersionRangeSet(ssl_fd, &version_range); |
|
509 if (rv != SECSuccess) { |
|
510 MOZ_MTLOG(ML_ERROR, "Can't disable SSLv3"); |
|
511 return false; |
|
512 } |
|
513 |
|
514 rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_SESSION_TICKETS, PR_FALSE); |
|
515 if (rv != SECSuccess) { |
|
516 MOZ_MTLOG(ML_ERROR, "Couldn't disable session tickets"); |
|
517 return false; |
|
518 } |
|
519 |
|
520 rv = SSL_OptionSet(ssl_fd, SSL_NO_CACHE, PR_TRUE); |
|
521 if (rv != SECSuccess) { |
|
522 MOZ_MTLOG(ML_ERROR, "Couldn't disable session caching"); |
|
523 return false; |
|
524 } |
|
525 |
|
526 rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_DEFLATE, PR_FALSE); |
|
527 if (rv != SECSuccess) { |
|
528 MOZ_MTLOG(ML_ERROR, "Couldn't disable deflate"); |
|
529 return false; |
|
530 } |
|
531 |
|
532 rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_NEVER); |
|
533 if (rv != SECSuccess) { |
|
534 MOZ_MTLOG(ML_ERROR, "Couldn't disable renegotiation"); |
|
535 return false; |
|
536 } |
|
537 |
|
538 rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_FALSE_START, PR_FALSE); |
|
539 if (rv != SECSuccess) { |
|
540 MOZ_MTLOG(ML_ERROR, "Couldn't disable false start"); |
|
541 return false; |
|
542 } |
|
543 |
|
544 rv = SSL_OptionSet(ssl_fd, SSL_NO_LOCKS, PR_TRUE); |
|
545 if (rv != SECSuccess) { |
|
546 MOZ_MTLOG(ML_ERROR, "Couldn't disable locks"); |
|
547 return false; |
|
548 } |
|
549 |
|
550 // Set the SRTP ciphers |
|
551 if (srtp_ciphers_.size()) { |
|
552 // Note: std::vector is guaranteed to contiguous |
|
553 rv = SSL_SetSRTPCiphers(ssl_fd, &srtp_ciphers_[0], |
|
554 srtp_ciphers_.size()); |
|
555 |
|
556 if (rv != SECSuccess) { |
|
557 MOZ_MTLOG(ML_ERROR, "Couldn't set SRTP cipher suite"); |
|
558 return false; |
|
559 } |
|
560 } |
|
561 |
|
562 // Certificate validation |
|
563 rv = SSL_AuthCertificateHook(ssl_fd, AuthCertificateHook, |
|
564 reinterpret_cast<void *>(this)); |
|
565 if (rv != SECSuccess) { |
|
566 MOZ_MTLOG(ML_ERROR, "Couldn't set certificate validation hook"); |
|
567 return false; |
|
568 } |
|
569 |
|
570 // Now start the handshake |
|
571 rv = SSL_ResetHandshake(ssl_fd, role_ == SERVER ? PR_TRUE : PR_FALSE); |
|
572 if (rv != SECSuccess) { |
|
573 MOZ_MTLOG(ML_ERROR, "Couldn't reset handshake"); |
|
574 return false; |
|
575 } |
|
576 ssl_fd_ = ssl_fd.forget(); |
|
577 |
|
578 // Finally, get ready to receive data |
|
579 downward_->SignalStateChange.connect(this, &TransportLayerDtls::StateChange); |
|
580 downward_->SignalPacketReceived.connect(this, &TransportLayerDtls::PacketReceived); |
|
581 |
|
582 if (downward_->state() == TS_OPEN) { |
|
583 Handshake(); |
|
584 } |
|
585 |
|
586 return true; |
|
587 } |
|
588 |
|
589 |
|
590 void TransportLayerDtls::StateChange(TransportLayer *layer, State state) { |
|
591 if (state <= state_) { |
|
592 MOZ_MTLOG(ML_ERROR, "Lower layer state is going backwards from ours"); |
|
593 TL_SET_STATE(TS_ERROR); |
|
594 return; |
|
595 } |
|
596 |
|
597 switch (state) { |
|
598 case TS_NONE: |
|
599 MOZ_ASSERT(false); // Can't happen |
|
600 break; |
|
601 |
|
602 case TS_INIT: |
|
603 MOZ_MTLOG(ML_ERROR, |
|
604 LAYER_INFO << "State change of lower layer to INIT forbidden"); |
|
605 TL_SET_STATE(TS_ERROR); |
|
606 break; |
|
607 |
|
608 case TS_CONNECTING: |
|
609 MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower lower is connecting."); |
|
610 break; |
|
611 |
|
612 case TS_OPEN: |
|
613 MOZ_MTLOG(ML_ERROR, |
|
614 LAYER_INFO << "Lower lower is now open; starting TLS"); |
|
615 Handshake(); |
|
616 break; |
|
617 |
|
618 case TS_CLOSED: |
|
619 MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower lower is now closed"); |
|
620 TL_SET_STATE(TS_CLOSED); |
|
621 break; |
|
622 |
|
623 case TS_ERROR: |
|
624 MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower lower experienced an error"); |
|
625 TL_SET_STATE(TS_ERROR); |
|
626 break; |
|
627 } |
|
628 } |
|
629 |
|
630 void TransportLayerDtls::Handshake() { |
|
631 TL_SET_STATE(TS_CONNECTING); |
|
632 |
|
633 // Clear the retransmit timer |
|
634 timer_->Cancel(); |
|
635 |
|
636 SECStatus rv = SSL_ForceHandshake(ssl_fd_); |
|
637 |
|
638 if (rv == SECSuccess) { |
|
639 MOZ_MTLOG(ML_NOTICE, |
|
640 LAYER_INFO << "****** SSL handshake completed ******"); |
|
641 if (!cert_ok_) { |
|
642 MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Certificate check never occurred"); |
|
643 TL_SET_STATE(TS_ERROR); |
|
644 return; |
|
645 } |
|
646 TL_SET_STATE(TS_OPEN); |
|
647 } else { |
|
648 int32_t err = PR_GetError(); |
|
649 switch(err) { |
|
650 case SSL_ERROR_RX_MALFORMED_HANDSHAKE: |
|
651 if (mode_ != DGRAM) { |
|
652 MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Malformed TLS message"); |
|
653 TL_SET_STATE(TS_ERROR); |
|
654 } else { |
|
655 MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Malformed DTLS message; ignoring"); |
|
656 } |
|
657 // Fall through |
|
658 case PR_WOULD_BLOCK_ERROR: |
|
659 MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Would have blocked"); |
|
660 if (mode_ == DGRAM) { |
|
661 PRIntervalTime timeout; |
|
662 rv = DTLS_GetHandshakeTimeout(ssl_fd_, &timeout); |
|
663 if (rv == SECSuccess) { |
|
664 uint32_t timeout_ms = PR_IntervalToMilliseconds(timeout); |
|
665 |
|
666 MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Setting DTLS timeout to " << |
|
667 timeout_ms); |
|
668 timer_->SetTarget(target_); |
|
669 timer_->InitWithFuncCallback(TimerCallback, |
|
670 this, timeout_ms, |
|
671 nsITimer::TYPE_ONE_SHOT); |
|
672 } |
|
673 } |
|
674 break; |
|
675 default: |
|
676 MOZ_MTLOG(ML_ERROR, LAYER_INFO << "SSL handshake error "<< err); |
|
677 TL_SET_STATE(TS_ERROR); |
|
678 break; |
|
679 } |
|
680 } |
|
681 } |
|
682 |
|
683 void TransportLayerDtls::PacketReceived(TransportLayer* layer, |
|
684 const unsigned char *data, |
|
685 size_t len) { |
|
686 CheckThread(); |
|
687 MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << len << ")"); |
|
688 |
|
689 if (state_ != TS_CONNECTING && state_ != TS_OPEN) { |
|
690 MOZ_MTLOG(ML_DEBUG, |
|
691 LAYER_INFO << "Discarding packet in inappropriate state"); |
|
692 return; |
|
693 } |
|
694 |
|
695 nspr_io_adapter_->PacketReceived(data, len); |
|
696 |
|
697 // If we're still connecting, try to handshake |
|
698 if (state_ == TS_CONNECTING) { |
|
699 Handshake(); |
|
700 } |
|
701 |
|
702 // Now try a recv if we're open, since there might be data left |
|
703 if (state_ == TS_OPEN) { |
|
704 unsigned char buf[2000]; |
|
705 |
|
706 int32_t rv = PR_Recv(ssl_fd_, buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT); |
|
707 if (rv > 0) { |
|
708 // We have data |
|
709 MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS"); |
|
710 SignalPacketReceived(this, buf, rv); |
|
711 } else if (rv == 0) { |
|
712 TL_SET_STATE(TS_CLOSED); |
|
713 } else { |
|
714 int32_t err = PR_GetError(); |
|
715 |
|
716 if (err == PR_WOULD_BLOCK_ERROR) { |
|
717 // This gets ignored |
|
718 MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Would have blocked"); |
|
719 } else { |
|
720 MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err); |
|
721 TL_SET_STATE(TS_ERROR); |
|
722 } |
|
723 } |
|
724 } |
|
725 } |
|
726 |
|
727 TransportResult TransportLayerDtls::SendPacket(const unsigned char *data, |
|
728 size_t len) { |
|
729 CheckThread(); |
|
730 if (state_ != TS_OPEN) { |
|
731 MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Can't call SendPacket() in state " |
|
732 << state_); |
|
733 return TE_ERROR; |
|
734 } |
|
735 |
|
736 int32_t rv = PR_Send(ssl_fd_, data, len, 0, PR_INTERVAL_NO_WAIT); |
|
737 |
|
738 if (rv > 0) { |
|
739 // We have data |
|
740 MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Wrote " << rv << " bytes to SSL Layer"); |
|
741 return rv; |
|
742 } |
|
743 |
|
744 if (rv == 0) { |
|
745 TL_SET_STATE(TS_CLOSED); |
|
746 return 0; |
|
747 } |
|
748 |
|
749 int32_t err = PR_GetError(); |
|
750 |
|
751 if (err == PR_WOULD_BLOCK_ERROR) { |
|
752 // This gets ignored |
|
753 MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Would have blocked"); |
|
754 return TE_WOULDBLOCK; |
|
755 } |
|
756 |
|
757 MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err); |
|
758 TL_SET_STATE(TS_ERROR); |
|
759 return TE_ERROR; |
|
760 } |
|
761 |
|
762 SECStatus TransportLayerDtls::GetClientAuthDataHook(void *arg, PRFileDesc *fd, |
|
763 CERTDistNames *caNames, |
|
764 CERTCertificate **pRetCert, |
|
765 SECKEYPrivateKey **pRetKey) { |
|
766 MOZ_MTLOG(ML_DEBUG, "Server requested client auth"); |
|
767 |
|
768 TransportLayerDtls *stream = reinterpret_cast<TransportLayerDtls *>(arg); |
|
769 stream->CheckThread(); |
|
770 |
|
771 if (!stream->identity_) { |
|
772 MOZ_MTLOG(ML_ERROR, "No identity available"); |
|
773 PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0); |
|
774 return SECFailure; |
|
775 } |
|
776 |
|
777 *pRetCert = CERT_DupCertificate(stream->identity_->cert()); |
|
778 if (!*pRetCert) { |
|
779 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
|
780 return SECFailure; |
|
781 } |
|
782 |
|
783 *pRetKey = SECKEY_CopyPrivateKey(stream->identity_->privkey()); |
|
784 if (!*pRetKey) { |
|
785 CERT_DestroyCertificate(*pRetCert); |
|
786 *pRetCert = nullptr; |
|
787 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); |
|
788 return SECFailure; |
|
789 } |
|
790 |
|
791 return SECSuccess; |
|
792 } |
|
793 |
|
794 nsresult TransportLayerDtls::SetSrtpCiphers(std::vector<uint16_t> ciphers) { |
|
795 // TODO: We should check these |
|
796 srtp_ciphers_ = ciphers; |
|
797 |
|
798 return NS_OK; |
|
799 } |
|
800 |
|
801 nsresult TransportLayerDtls::GetSrtpCipher(uint16_t *cipher) { |
|
802 CheckThread(); |
|
803 SECStatus rv = SSL_GetSRTPCipher(ssl_fd_, cipher); |
|
804 if (rv != SECSuccess) { |
|
805 MOZ_MTLOG(ML_DEBUG, "No SRTP cipher negotiated"); |
|
806 return NS_ERROR_FAILURE; |
|
807 } |
|
808 |
|
809 return NS_OK; |
|
810 } |
|
811 |
|
812 nsresult TransportLayerDtls::ExportKeyingMaterial(const std::string& label, |
|
813 bool use_context, |
|
814 const std::string& context, |
|
815 unsigned char *out, |
|
816 unsigned int outlen) { |
|
817 CheckThread(); |
|
818 SECStatus rv = SSL_ExportKeyingMaterial(ssl_fd_, |
|
819 label.c_str(), |
|
820 label.size(), |
|
821 use_context, |
|
822 reinterpret_cast<const unsigned char *>( |
|
823 context.c_str()), |
|
824 context.size(), |
|
825 out, |
|
826 outlen); |
|
827 if (rv != SECSuccess) { |
|
828 MOZ_MTLOG(ML_ERROR, "Couldn't export SSL keying material"); |
|
829 return NS_ERROR_FAILURE; |
|
830 } |
|
831 |
|
832 return NS_OK; |
|
833 } |
|
834 |
|
835 SECStatus TransportLayerDtls::AuthCertificateHook(void *arg, |
|
836 PRFileDesc *fd, |
|
837 PRBool checksig, |
|
838 PRBool isServer) { |
|
839 TransportLayerDtls *stream = reinterpret_cast<TransportLayerDtls *>(arg); |
|
840 stream->CheckThread(); |
|
841 return stream->AuthCertificateHook(fd, checksig, isServer); |
|
842 } |
|
843 |
|
844 SECStatus |
|
845 TransportLayerDtls::CheckDigest(const RefPtr<VerificationDigest>& |
|
846 digest, |
|
847 CERTCertificate *peer_cert) { |
|
848 unsigned char computed_digest[kMaxDigestLength]; |
|
849 size_t computed_digest_len; |
|
850 |
|
851 MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Checking digest, algorithm=" |
|
852 << digest->algorithm_); |
|
853 nsresult res = |
|
854 DtlsIdentity::ComputeFingerprint(peer_cert, |
|
855 digest->algorithm_, |
|
856 computed_digest, |
|
857 sizeof(computed_digest), |
|
858 &computed_digest_len); |
|
859 if (NS_FAILED(res)) { |
|
860 MOZ_MTLOG(ML_ERROR, "Could not compute peer fingerprint for digest " << |
|
861 digest->algorithm_); |
|
862 // Go to end |
|
863 PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0); |
|
864 return SECFailure; |
|
865 } |
|
866 |
|
867 if (computed_digest_len != digest->len_) { |
|
868 MOZ_MTLOG(ML_ERROR, "Digest is wrong length " << digest->len_ << |
|
869 " should be " << computed_digest_len << " for algorithm " << |
|
870 digest->algorithm_); |
|
871 PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0); |
|
872 return SECFailure; |
|
873 } |
|
874 |
|
875 if (memcmp(digest->value_, computed_digest, computed_digest_len) != 0) { |
|
876 MOZ_MTLOG(ML_ERROR, "Digest does not match"); |
|
877 PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0); |
|
878 return SECFailure; |
|
879 } |
|
880 |
|
881 return SECSuccess; |
|
882 } |
|
883 |
|
884 |
|
885 SECStatus TransportLayerDtls::AuthCertificateHook(PRFileDesc *fd, |
|
886 PRBool checksig, |
|
887 PRBool isServer) { |
|
888 CheckThread(); |
|
889 ScopedCERTCertificate peer_cert; |
|
890 peer_cert = SSL_PeerCertificate(fd); |
|
891 |
|
892 |
|
893 // We are not set up to take this being called multiple |
|
894 // times. Change this if we ever add renegotiation. |
|
895 MOZ_ASSERT(!auth_hook_called_); |
|
896 if (auth_hook_called_) { |
|
897 PR_SetError(PR_UNKNOWN_ERROR, 0); |
|
898 return SECFailure; |
|
899 } |
|
900 auth_hook_called_ = true; |
|
901 |
|
902 MOZ_ASSERT(verification_mode_ != VERIFY_UNSET); |
|
903 MOZ_ASSERT(peer_cert_ == nullptr); |
|
904 |
|
905 switch (verification_mode_) { |
|
906 case VERIFY_UNSET: |
|
907 // Break out to error exit |
|
908 PR_SetError(PR_UNKNOWN_ERROR, 0); |
|
909 break; |
|
910 |
|
911 case VERIFY_ALLOW_ALL: |
|
912 peer_cert_ = peer_cert.forget(); |
|
913 cert_ok_ = true; |
|
914 return SECSuccess; |
|
915 |
|
916 case VERIFY_DIGEST: |
|
917 { |
|
918 MOZ_ASSERT(digests_.size() != 0); |
|
919 // Check all the provided digests |
|
920 |
|
921 // Checking functions call PR_SetError() |
|
922 SECStatus rv = SECFailure; |
|
923 for (size_t i = 0; i < digests_.size(); i++) { |
|
924 RefPtr<VerificationDigest> digest = digests_[i]; |
|
925 rv = CheckDigest(digest, peer_cert); |
|
926 |
|
927 if (rv != SECSuccess) |
|
928 break; |
|
929 } |
|
930 |
|
931 if (rv == SECSuccess) { |
|
932 // Matches all digests, we are good to go |
|
933 cert_ok_ = true; |
|
934 peer_cert = peer_cert.forget(); |
|
935 return SECSuccess; |
|
936 } |
|
937 } |
|
938 break; |
|
939 default: |
|
940 MOZ_CRASH(); // Can't happen |
|
941 } |
|
942 |
|
943 return SECFailure; |
|
944 } |
|
945 |
|
946 void TransportLayerDtls::TimerCallback(nsITimer *timer, void *arg) { |
|
947 TransportLayerDtls *dtls = reinterpret_cast<TransportLayerDtls *>(arg); |
|
948 |
|
949 MOZ_MTLOG(ML_DEBUG, "DTLS timer expired"); |
|
950 |
|
951 dtls->Handshake(); |
|
952 } |
|
953 |
|
954 } // close namespace |