TransportResult TransportLayerDtls::SendPacket(const unsigned char *data, size_t len) { CheckThread(); if (state_ != TS_OPEN) { MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Can't call SendPacket() in state " << state_); return TE_ERROR; } int32_t rv = PR_Send(ssl_fd_, data, len, 0, PR_INTERVAL_NO_WAIT); if (rv > 0) { // We have data MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Wrote " << rv << " bytes to SSL Layer"); return rv; } if (rv == 0) { TL_SET_STATE(TS_CLOSED); return 0; } int32_t err = PR_GetError(); if (err == PR_WOULD_BLOCK_ERROR) { // This gets ignored MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Send would have blocked"); return TE_WOULDBLOCK; } MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err); TL_SET_STATE(TS_ERROR); return TE_ERROR; }
void TransportLayerDtls::Handshake() { // Clear the retransmit timer timer_->Cancel(); SECStatus rv = SSL_ForceHandshake(ssl_fd_); if (rv == SECSuccess) { MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "****** SSL handshake completed ******"); if (!cert_ok_) { MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Certificate check never occurred"); TL_SET_STATE(TS_ERROR); return; } if (!CheckAlpn()) { // Despite connecting, the connection doesn't have a valid ALPN label. // Forcibly close the connection so that the peer isn't left hanging // (assuming the close_notify isn't dropped). ssl_fd_ = nullptr; TL_SET_STATE(TS_ERROR); return; } TL_SET_STATE(TS_OPEN); } else { int32_t err = PR_GetError(); switch(err) { case SSL_ERROR_RX_MALFORMED_HANDSHAKE: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Malformed DTLS message; ignoring"); // If this were TLS (and not DTLS), this would be fatal, but // here we're required to ignore bad messages, so fall through MOZ_FALLTHROUGH; case PR_WOULD_BLOCK_ERROR: MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "Handshake would have blocked"); PRIntervalTime timeout; rv = DTLS_GetHandshakeTimeout(ssl_fd_, &timeout); if (rv == SECSuccess) { uint32_t timeout_ms = PR_IntervalToMilliseconds(timeout); MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Setting DTLS timeout to " << timeout_ms); timer_->SetTarget(target_); timer_->InitWithFuncCallback(TimerCallback, this, timeout_ms, nsITimer::TYPE_ONE_SHOT); } break; default: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "SSL handshake error "<< err); TL_SET_STATE(TS_ERROR); break; } } }
void TransportLayerDtls::PacketReceived(TransportLayer* layer, const unsigned char *data, size_t len) { CheckThread(); MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "PacketReceived(" << len << ")"); if (state_ != TS_CONNECTING && state_ != TS_OPEN) { MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Discarding packet in inappropriate state"); return; } nspr_io_adapter_->PacketReceived(data, len); // If we're still connecting, try to handshake if (state_ == TS_CONNECTING) { Handshake(); } // Now try a recv if we're open, since there might be data left if (state_ == TS_OPEN) { // nICEr uses a 9216 bytes buffer to allow support for jumbo frames unsigned char buf[9216]; int32_t rv; // One packet might contain several DTLS packets do { rv = PR_Recv(ssl_fd_, buf, sizeof(buf), 0, PR_INTERVAL_NO_WAIT); if (rv > 0) { // We have data MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Read " << rv << " bytes from NSS"); SignalPacketReceived(this, buf, rv); } else if (rv == 0) { TL_SET_STATE(TS_CLOSED); } else { int32_t err = PR_GetError(); if (err == PR_WOULD_BLOCK_ERROR) { // This gets ignored MOZ_MTLOG(ML_DEBUG, LAYER_INFO << "Receive would have blocked"); } else { MOZ_MTLOG(ML_NOTICE, LAYER_INFO << "NSS Error " << err); TL_SET_STATE(TS_ERROR); } } } while (rv > 0); } }
void TransportLayerDtls::StateChange(TransportLayer *layer, State state) { if (state <= state_) { MOZ_MTLOG(ML_ERROR, "Lower layer state is going backwards from ours"); TL_SET_STATE(TS_ERROR); return; } switch (state) { case TS_NONE: MOZ_ASSERT(false); // Can't happen break; case TS_INIT: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "State change of lower layer to INIT forbidden"); TL_SET_STATE(TS_ERROR); break; case TS_CONNECTING: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer is connecting."); break; case TS_OPEN: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer is now open; starting TLS"); // Async, since the ICE layer might need to send a STUN response, and we // don't want the handshake to start until that is sent. TL_SET_STATE(TS_CONNECTING); timer_->Cancel(); timer_->SetTarget(target_); timer_->InitWithFuncCallback(TimerCallback, this, 0, nsITimer::TYPE_ONE_SHOT); break; case TS_CLOSED: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer is now closed"); TL_SET_STATE(TS_CLOSED); break; case TS_ERROR: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer experienced an error"); TL_SET_STATE(TS_ERROR); break; } }
void TransportLayerIce::PostSetup() { stream_->SignalReady.connect(this, &TransportLayerIce::IceReady); stream_->SignalFailed.connect(this, &TransportLayerIce::IceFailed); stream_->SignalPacketReceived.connect(this, &TransportLayerIce::IcePacketReceived); if (stream_->state() == NrIceMediaStream::ICE_OPEN) { TL_SET_STATE(TS_OPEN); } }
void TransportLayerIce::IceFailed(NrIceMediaStream *stream) { CheckThread(); // only handle the current stream (not the old stream during restart) if (stream != stream_) { return; } MOZ_MTLOG(ML_INFO, LAYER_INFO << "ICE Failed(" << stream->name() << "," << component_ << ")"); TL_SET_STATE(TS_ERROR); }
void TransportLayerDtls::StateChange(TransportLayer *layer, State state) { if (state <= state_) { MOZ_MTLOG(ML_ERROR, "Lower layer state is going backwards from ours"); TL_SET_STATE(TS_ERROR); return; } switch (state) { case TS_NONE: MOZ_ASSERT(false); // Can't happen break; case TS_INIT: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "State change of lower layer to INIT forbidden"); TL_SET_STATE(TS_ERROR); break; case TS_CONNECTING: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer is connecting."); break; case TS_OPEN: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer is now open; starting TLS"); Handshake(); break; case TS_CLOSED: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer is now closed"); TL_SET_STATE(TS_CLOSED); break; case TS_ERROR: MOZ_MTLOG(ML_ERROR, LAYER_INFO << "Lower layer experienced an error"); TL_SET_STATE(TS_ERROR); break; } }
// Connect to the other side void TransportLayerLoopback::Connect(TransportLayerLoopback* peer) { peer_ = peer; TL_SET_STATE(TS_OPEN); }
void TransportLayerDtls::WasInserted() { // Connect to the lower layers if (!Setup()) { TL_SET_STATE(TS_ERROR); } }
// TODO: make sure this is called from STS. Otherwise // we have thread safety issues bool TransportLayerDtls::Setup() { CheckThread(); SECStatus rv; if (!downward_) { MOZ_MTLOG(ML_ERROR, "DTLS layer with nothing below. This is useless"); return false; } nspr_io_adapter_ = new TransportLayerNSPRAdapter(downward_); if (!identity_) { MOZ_MTLOG(ML_ERROR, "Can't start DTLS without an identity"); return false; } if (verification_mode_ == VERIFY_UNSET) { MOZ_MTLOG(ML_ERROR, "Can't start DTLS without specifying a verification mode"); return false; } if (transport_layer_identity == PR_INVALID_IO_LAYER) { transport_layer_identity = PR_GetUniqueIdentity("nssstreamadapter"); } ScopedPRFileDesc pr_fd(PR_CreateIOLayerStub(transport_layer_identity, &TransportLayerMethods)); MOZ_ASSERT(pr_fd != nullptr); if (!pr_fd) return false; pr_fd->secret = reinterpret_cast<PRFilePrivate *>(nspr_io_adapter_.get()); ScopedPRFileDesc ssl_fd(DTLS_ImportFD(nullptr, pr_fd)); MOZ_ASSERT(ssl_fd != nullptr); // This should never happen if (!ssl_fd) { return false; } pr_fd.forget(); // ownership transfered to ssl_fd; if (role_ == CLIENT) { MOZ_MTLOG(ML_INFO, "Setting up DTLS as client"); rv = SSL_GetClientAuthDataHook(ssl_fd, GetClientAuthDataHook, this); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set identity"); return false; } } else { MOZ_MTLOG(ML_INFO, "Setting up DTLS as server"); // Server side rv = SSL_ConfigSecureServer(ssl_fd, identity_->cert(), identity_->privkey(), identity_->auth_type()); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set identity"); return false; } // Insist on a certificate from the client rv = SSL_OptionSet(ssl_fd, SSL_REQUEST_CERTIFICATE, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't request certificate"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_REQUIRE_CERTIFICATE, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't require certificate"); return false; } } // Require TLS 1.1 or 1.2. Perhaps some day in the future we will allow TLS // 1.0 for stream modes. SSLVersionRange version_range = { SSL_LIBRARY_VERSION_TLS_1_1, SSL_LIBRARY_VERSION_TLS_1_2 }; rv = SSL_VersionRangeSet(ssl_fd, &version_range); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Can't disable SSLv3"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_SESSION_TICKETS, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable session tickets"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_NO_CACHE, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable session caching"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_DEFLATE, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable deflate"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_RENEGOTIATION, SSL_RENEGOTIATE_NEVER); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable renegotiation"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_ENABLE_FALSE_START, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable false start"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_NO_LOCKS, PR_TRUE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable locks"); return false; } rv = SSL_OptionSet(ssl_fd, SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't disable ECDHE key reuse"); return false; } if (!SetupCipherSuites(ssl_fd)) { return false; } // Certificate validation rv = SSL_AuthCertificateHook(ssl_fd, AuthCertificateHook, reinterpret_cast<void *>(this)); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't set certificate validation hook"); return false; } if (!SetupAlpn(ssl_fd)) { return false; } // Now start the handshake rv = SSL_ResetHandshake(ssl_fd, role_ == SERVER ? PR_TRUE : PR_FALSE); if (rv != SECSuccess) { MOZ_MTLOG(ML_ERROR, "Couldn't reset handshake"); return false; } ssl_fd_ = ssl_fd.forget(); // Finally, get ready to receive data downward_->SignalStateChange.connect(this, &TransportLayerDtls::StateChange); downward_->SignalPacketReceived.connect(this, &TransportLayerDtls::PacketReceived); if (downward_->state() == TS_OPEN) { TL_SET_STATE(TS_CONNECTING); Handshake(); } return true; }