media/mtransport/transportlayerdtls.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial