| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442 |
- /**
- ISC License
- Copyright © 2015, Iñaki Baz Castillo <ibc@aliax.net>
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #define MS_CLASS "RTC::DtlsTransport"
- // #define MS_LOG_DEV_LEVEL 3
- #include "DtlsTransport.hpp"
- #include "logger.h"
- #include <openssl/asn1.h>
- #include <openssl/bn.h>
- #include <openssl/err.h>
- #include <openssl/evp.h>
- #include <openssl/rsa.h>
- #include <cstdio> // std::sprintf(), std::fopen()
- #include <cstring> // std::memcpy(), std::strcmp()
- #include "Util/util.h"
- #include "Util/SSLBox.h"
- #include "Util/SSLUtil.h"
- using namespace std;
- #define LOG_OPENSSL_ERROR(desc) \
- do \
- { \
- if (ERR_peek_error() == 0) \
- MS_ERROR("OpenSSL error [desc:'%s']", desc); \
- else \
- { \
- int64_t err; \
- while ((err = ERR_get_error()) != 0) \
- { \
- MS_ERROR("OpenSSL error [desc:'%s', error:'%s']", desc, ERR_error_string(err, nullptr)); \
- } \
- ERR_clear_error(); \
- } \
- } while (false)
- /* Static methods for OpenSSL callbacks. */
- inline static int onSslCertificateVerify(int /*preverifyOk*/, X509_STORE_CTX* /*ctx*/)
- {
- MS_TRACE();
- // Always valid since DTLS certificates are self-signed.
- return 1;
- }
- inline static unsigned int onSslDtlsTimer(SSL* /*ssl*/, unsigned int timerUs)
- {
- if (timerUs == 0)
- return 100000;
- else if (timerUs >= 4000000)
- return 4000000;
- else
- return 2 * timerUs;
- }
- namespace RTC
- {
- /* Static. */
- // clang-format off
- static constexpr int DtlsMtu{ 1350 };
- // AES-HMAC: http://tools.ietf.org/html/rfc3711
- static constexpr size_t SrtpMasterKeyLength{ 16 };
- static constexpr size_t SrtpMasterSaltLength{ 14 };
- static constexpr size_t SrtpMasterLength{ SrtpMasterKeyLength + SrtpMasterSaltLength };
- // AES-GCM: http://tools.ietf.org/html/rfc7714
- static constexpr size_t SrtpAesGcm256MasterKeyLength{ 32 };
- static constexpr size_t SrtpAesGcm256MasterSaltLength{ 12 };
- static constexpr size_t SrtpAesGcm256MasterLength{ SrtpAesGcm256MasterKeyLength + SrtpAesGcm256MasterSaltLength };
- static constexpr size_t SrtpAesGcm128MasterKeyLength{ 16 };
- static constexpr size_t SrtpAesGcm128MasterSaltLength{ 12 };
- static constexpr size_t SrtpAesGcm128MasterLength{ SrtpAesGcm128MasterKeyLength + SrtpAesGcm128MasterSaltLength };
- // clang-format on
- /* Class variables. */
- // clang-format off
- std::map<std::string, DtlsTransport::FingerprintAlgorithm> DtlsTransport::string2FingerprintAlgorithm =
- {
- { "sha-1", DtlsTransport::FingerprintAlgorithm::SHA1 },
- { "sha-224", DtlsTransport::FingerprintAlgorithm::SHA224 },
- { "sha-256", DtlsTransport::FingerprintAlgorithm::SHA256 },
- { "sha-384", DtlsTransport::FingerprintAlgorithm::SHA384 },
- { "sha-512", DtlsTransport::FingerprintAlgorithm::SHA512 }
- };
- std::map<DtlsTransport::FingerprintAlgorithm, std::string> DtlsTransport::fingerprintAlgorithm2String =
- {
- { DtlsTransport::FingerprintAlgorithm::SHA1, "sha-1" },
- { DtlsTransport::FingerprintAlgorithm::SHA224, "sha-224" },
- { DtlsTransport::FingerprintAlgorithm::SHA256, "sha-256" },
- { DtlsTransport::FingerprintAlgorithm::SHA384, "sha-384" },
- { DtlsTransport::FingerprintAlgorithm::SHA512, "sha-512" }
- };
- std::map<std::string, DtlsTransport::Role> DtlsTransport::string2Role =
- {
- { "auto", DtlsTransport::Role::AUTO },
- { "client", DtlsTransport::Role::CLIENT },
- { "server", DtlsTransport::Role::SERVER }
- };
- std::vector<DtlsTransport::SrtpCryptoSuiteMapEntry> DtlsTransport::srtpCryptoSuites =
- {
- { RTC::SrtpSession::CryptoSuite::AEAD_AES_256_GCM, "SRTP_AEAD_AES_256_GCM" },
- { RTC::SrtpSession::CryptoSuite::AEAD_AES_128_GCM, "SRTP_AEAD_AES_128_GCM" },
- { RTC::SrtpSession::CryptoSuite::AES_CM_128_HMAC_SHA1_80, "SRTP_AES128_CM_SHA1_80" },
- { RTC::SrtpSession::CryptoSuite::AES_CM_128_HMAC_SHA1_32, "SRTP_AES128_CM_SHA1_32" }
- };
- // clang-format on
- INSTANCE_IMP(DtlsTransport::DtlsEnvironment);
- /* Class methods. */
- DtlsTransport::DtlsEnvironment::DtlsEnvironment()
- {
- MS_TRACE();
- // Generate a X509 certificate and private key (unless PEM files are provided).
- auto ssl = toolkit::SSL_Initor::Instance().getSSLCtx("", true);
- if (!ssl || !ReadCertificateAndPrivateKeyFromContext(ssl.get())) {
- GenerateCertificateAndPrivateKey();
- }
- // Create a global SSL_CTX.
- CreateSslCtx();
- // Generate certificate fingerprints.
- GenerateFingerprints();
- }
- DtlsTransport::DtlsEnvironment::~DtlsEnvironment()
- {
- MS_TRACE();
- if (privateKey)
- EVP_PKEY_free(privateKey);
- if (certificate)
- X509_free(certificate);
- if (sslCtx)
- SSL_CTX_free(sslCtx);
- }
- void DtlsTransport::DtlsEnvironment::GenerateCertificateAndPrivateKey()
- {
- MS_TRACE();
- int ret{ 0 };
- EC_KEY* ecKey{ nullptr };
- X509_NAME* certName{ nullptr };
- std::string subject =
- std::string("mediasoup") + to_string(rand() % 999999 + 100000);
- // Create key with curve.
- ecKey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- if (!ecKey)
- {
- LOG_OPENSSL_ERROR("EC_KEY_new_by_curve_name() failed");
- goto error;
- }
- EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
- // NOTE: This can take some time.
- ret = EC_KEY_generate_key(ecKey);
- if (ret == 0)
- {
- LOG_OPENSSL_ERROR("EC_KEY_generate_key() failed");
- goto error;
- }
- // Create a private key object.
- privateKey = EVP_PKEY_new();
- if (!privateKey)
- {
- LOG_OPENSSL_ERROR("EVP_PKEY_new() failed");
- goto error;
- }
- // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
- ret = EVP_PKEY_assign_EC_KEY(privateKey, ecKey);
- if (ret == 0)
- {
- LOG_OPENSSL_ERROR("EVP_PKEY_assign_EC_KEY() failed");
- goto error;
- }
- // The EC key now belongs to the private key, so don't clean it up separately.
- ecKey = nullptr;
- // Create the X509 certificate.
- certificate = X509_new();
- if (!certificate)
- {
- LOG_OPENSSL_ERROR("X509_new() failed");
- goto error;
- }
- // Set version 3 (note that 0 means version 1).
- X509_set_version(certificate, 2);
- // Set serial number (avoid default 0).
- ASN1_INTEGER_set(
- X509_get_serialNumber(certificate),
- static_cast<uint64_t>(rand() % 999999 + 100000));
- // Set valid period.
- X509_gmtime_adj(X509_get_notBefore(certificate), -315360000); // -10 years.
- X509_gmtime_adj(X509_get_notAfter(certificate), 315360000); // 10 years.
- // Set the public key for the certificate using the key.
- ret = X509_set_pubkey(certificate, privateKey);
- if (ret == 0)
- {
- LOG_OPENSSL_ERROR("X509_set_pubkey() failed");
- goto error;
- }
- // Set certificate fields.
- certName = X509_get_subject_name(certificate);
- if (!certName)
- {
- LOG_OPENSSL_ERROR("X509_get_subject_name() failed");
- goto error;
- }
- X509_NAME_add_entry_by_txt(
- certName, "O", MBSTRING_ASC, reinterpret_cast<const uint8_t*>(subject.c_str()), -1, -1, 0);
- X509_NAME_add_entry_by_txt(
- certName, "CN", MBSTRING_ASC, reinterpret_cast<const uint8_t*>(subject.c_str()), -1, -1, 0);
- // It is self-signed so set the issuer name to be the same as the subject.
- ret = X509_set_issuer_name(certificate, certName);
- if (ret == 0)
- {
- LOG_OPENSSL_ERROR("X509_set_issuer_name() failed");
- goto error;
- }
- // Sign the certificate with its own private key.
- ret = X509_sign(certificate, privateKey, EVP_sha1());
- if (ret == 0)
- {
- LOG_OPENSSL_ERROR("X509_sign() failed");
- goto error;
- }
- return;
- error:
- if (ecKey)
- EC_KEY_free(ecKey);
- if (privateKey)
- EVP_PKEY_free(privateKey); // NOTE: This also frees the EC key.
- if (certificate)
- X509_free(certificate);
- MS_THROW_ERROR("DTLS certificate and private key generation failed");
- }
- bool DtlsTransport::DtlsEnvironment::ReadCertificateAndPrivateKeyFromContext(SSL_CTX *ctx)
- {
- MS_TRACE();
- certificate = SSL_CTX_get0_certificate(ctx);
- if (!certificate) {
- return false;
- }
- X509_up_ref(certificate);
- privateKey = SSL_CTX_get0_privatekey(ctx);
- if (!privateKey) {
- return false;
- }
- EVP_PKEY_up_ref(privateKey);
- InfoL << "Load webrtc dtls certificate: " << toolkit::SSLUtil::getServerName(certificate);
- return true;
- }
- void DtlsTransport::DtlsEnvironment::CreateSslCtx()
- {
- MS_TRACE();
- std::string dtlsSrtpCryptoSuites;
- int ret;
- /* Set the global DTLS context. */
- // Both DTLS 1.0 and 1.2 (requires OpenSSL >= 1.1.0).
- sslCtx = SSL_CTX_new(DTLS_method());
- if (!sslCtx)
- {
- LOG_OPENSSL_ERROR("SSL_CTX_new() failed");
- goto error;
- }
- ret = SSL_CTX_use_certificate(sslCtx, certificate);
- if (ret == 0)
- {
- LOG_OPENSSL_ERROR("SSL_CTX_use_certificate() failed");
- goto error;
- }
- ret = SSL_CTX_use_PrivateKey(sslCtx, privateKey);
- if (ret == 0)
- {
- LOG_OPENSSL_ERROR("SSL_CTX_use_PrivateKey() failed");
- goto error;
- }
- ret = SSL_CTX_check_private_key(sslCtx);
- if (ret == 0)
- {
- LOG_OPENSSL_ERROR("SSL_CTX_check_private_key() failed");
- goto error;
- }
- // Set options.
- SSL_CTX_set_options(
- sslCtx,
- SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_TICKET | SSL_OP_SINGLE_ECDH_USE |
- SSL_OP_NO_QUERY_MTU);
- // Don't use sessions cache.
- SSL_CTX_set_session_cache_mode(sslCtx, SSL_SESS_CACHE_OFF);
- // Read always as much into the buffer as possible.
- // NOTE: This is the default for DTLS, but a bug in non latest OpenSSL
- // versions makes this call required.
- SSL_CTX_set_read_ahead(sslCtx, 1);
- SSL_CTX_set_verify_depth(sslCtx, 4);
- // Require certificate from peer.
- SSL_CTX_set_verify(
- sslCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, onSslCertificateVerify);
- // Set SSL info callback.
- SSL_CTX_set_info_callback(sslCtx, [](const SSL* ssl, int where, int ret){
- static_cast<RTC::DtlsTransport*>(SSL_get_ex_data(ssl, 0))->OnSslInfo(where, ret);
- });
- // Set ciphers.
- ret = SSL_CTX_set_cipher_list(
- sslCtx, "DEFAULT:!NULL:!aNULL:!SHA256:!SHA384:!aECDH:!AESGCM+AES256:!aPSK:!RC4");
- if (ret == 0)
- {
- LOG_OPENSSL_ERROR("SSL_CTX_set_cipher_list() failed");
- goto error;
- }
- // Enable ECDH ciphers.
- // DOC: http://en.wikibooks.org/wiki/OpenSSL/Diffie-Hellman_parameters
- // NOTE: https://code.google.com/p/chromium/issues/detail?id=406458
- // NOTE: https://bugs.ruby-lang.org/issues/12324
- // For OpenSSL >= 1.0.2.
- SSL_CTX_set_ecdh_auto(sslCtx, 1);
- // Set the "use_srtp" DTLS extension.
- for (auto it = DtlsTransport::srtpCryptoSuites.begin();
- it != DtlsTransport::srtpCryptoSuites.end();
- ++it)
- {
- if (it != DtlsTransport::srtpCryptoSuites.begin())
- dtlsSrtpCryptoSuites += ":";
- SrtpCryptoSuiteMapEntry* cryptoSuiteEntry = std::addressof(*it);
- dtlsSrtpCryptoSuites += cryptoSuiteEntry->name;
- }
- MS_DEBUG_2TAGS(dtls, srtp, "setting SRTP cryptoSuites for DTLS: %s", dtlsSrtpCryptoSuites.c_str());
- // NOTE: This function returns 0 on success.
- ret = SSL_CTX_set_tlsext_use_srtp(sslCtx, dtlsSrtpCryptoSuites.c_str());
- if (ret != 0)
- {
- MS_ERROR(
- "SSL_CTX_set_tlsext_use_srtp() failed when entering '%s'", dtlsSrtpCryptoSuites.c_str());
- LOG_OPENSSL_ERROR("SSL_CTX_set_tlsext_use_srtp() failed");
- goto error;
- }
- return;
- error:
- if (sslCtx)
- {
- SSL_CTX_free(sslCtx);
- sslCtx = nullptr;
- }
- MS_THROW_ERROR("SSL context creation failed");
- }
- void DtlsTransport::DtlsEnvironment::GenerateFingerprints()
- {
- MS_TRACE();
- for (auto& kv : DtlsTransport::string2FingerprintAlgorithm)
- {
- const std::string& algorithmString = kv.first;
- FingerprintAlgorithm algorithm = kv.second;
- uint8_t binaryFingerprint[EVP_MAX_MD_SIZE];
- unsigned int size{ 0 };
- char hexFingerprint[(EVP_MAX_MD_SIZE * 3) + 1];
- const EVP_MD* hashFunction;
- int ret;
- switch (algorithm)
- {
- case FingerprintAlgorithm::SHA1:
- hashFunction = EVP_sha1();
- break;
- case FingerprintAlgorithm::SHA224:
- hashFunction = EVP_sha224();
- break;
- case FingerprintAlgorithm::SHA256:
- hashFunction = EVP_sha256();
- break;
- case FingerprintAlgorithm::SHA384:
- hashFunction = EVP_sha384();
- break;
- case FingerprintAlgorithm::SHA512:
- hashFunction = EVP_sha512();
- break;
- default:
- MS_THROW_ERROR("unknown algorithm");
- }
- ret = X509_digest(certificate, hashFunction, binaryFingerprint, &size);
- if (ret == 0)
- {
- MS_ERROR("X509_digest() failed");
- MS_THROW_ERROR("Fingerprints generation failed");
- }
- // Convert to hexadecimal format in uppercase with colons.
- for (unsigned int i{ 0 }; i < size; ++i)
- {
- std::sprintf(hexFingerprint + (i * 3), "%.2X:", binaryFingerprint[i]);
- }
- hexFingerprint[(size * 3) - 1] = '\0';
- MS_DEBUG_TAG(dtls, "%-7s fingerprint: %s", algorithmString.c_str(), hexFingerprint);
- // Store it in the vector.
- DtlsTransport::Fingerprint fingerprint;
- fingerprint.algorithm = DtlsTransport::GetFingerprintAlgorithm(algorithmString);
- fingerprint.value = hexFingerprint;
- localFingerprints.push_back(fingerprint);
- }
- }
- /* Instance methods. */
- DtlsTransport::DtlsTransport(EventPoller::Ptr poller,Listener* listener) : poller(std::move(poller)), listener(listener)
- {
- MS_TRACE();
- env = DtlsEnvironment::Instance().shared_from_this();
- /* Set SSL. */
- this->ssl = SSL_new(env->sslCtx);
- if (!this->ssl)
- {
- LOG_OPENSSL_ERROR("SSL_new() failed");
- goto error;
- }
- // Set this as custom data.
- SSL_set_ex_data(this->ssl, 0, static_cast<void*>(this));
- this->sslBioFromNetwork = BIO_new(BIO_s_mem());
- if (!this->sslBioFromNetwork)
- {
- LOG_OPENSSL_ERROR("BIO_new() failed");
- SSL_free(this->ssl);
- goto error;
- }
- this->sslBioToNetwork = BIO_new(BIO_s_mem());
- if (!this->sslBioToNetwork)
- {
- LOG_OPENSSL_ERROR("BIO_new() failed");
- BIO_free(this->sslBioFromNetwork);
- SSL_free(this->ssl);
- goto error;
- }
- SSL_set_bio(this->ssl, this->sslBioFromNetwork, this->sslBioToNetwork);
- // Set the MTU so that we don't send packets that are too large with no fragmentation.
- SSL_set_mtu(this->ssl, DtlsMtu);
- DTLS_set_link_mtu(this->ssl, DtlsMtu);
- // Set callback handler for setting DTLS timer interval.
- DTLS_set_timer_cb(this->ssl, onSslDtlsTimer);
- return;
- error:
- // NOTE: At this point SSL_set_bio() was not called so we must free BIOs as
- // well.
- if (this->sslBioFromNetwork)
- BIO_free(this->sslBioFromNetwork);
- if (this->sslBioToNetwork)
- BIO_free(this->sslBioToNetwork);
- if (this->ssl)
- SSL_free(this->ssl);
- // NOTE: If this is not catched by the caller the program will abort, but
- // this should never happen.
- MS_THROW_ERROR("DtlsTransport instance creation failed");
- }
- DtlsTransport::~DtlsTransport()
- {
- MS_TRACE();
- if (IsRunning())
- {
- // Send close alert to the peer.
- SSL_shutdown(this->ssl);
- SendPendingOutgoingDtlsData();
- }
- if (this->ssl)
- {
- SSL_free(this->ssl);
- this->ssl = nullptr;
- this->sslBioFromNetwork = nullptr;
- this->sslBioToNetwork = nullptr;
- }
- // Close the DTLS timer.
- this->timer = nullptr;
- }
- void DtlsTransport::Dump() const
- {
- MS_TRACE();
- std::string state{ "new" };
- std::string role{ "none " };
- switch (this->state)
- {
- case DtlsState::CONNECTING:
- state = "connecting";
- break;
- case DtlsState::CONNECTED:
- state = "connected";
- break;
- case DtlsState::FAILED:
- state = "failed";
- break;
- case DtlsState::CLOSED:
- state = "closed";
- break;
- default:;
- }
- switch (this->localRole)
- {
- case Role::AUTO:
- role = "auto";
- break;
- case Role::SERVER:
- role = "server";
- break;
- case Role::CLIENT:
- role = "client";
- break;
- default:;
- }
- MS_DUMP("<DtlsTransport>");
- MS_DUMP(" state : %s", state.c_str());
- MS_DUMP(" role : %s", role.c_str());
- MS_DUMP(" handshake done: : %s", this->handshakeDone ? "yes" : "no");
- MS_DUMP("</DtlsTransport>");
- }
- void DtlsTransport::Run(Role localRole)
- {
- MS_TRACE();
- MS_ASSERT(
- localRole == Role::CLIENT || localRole == Role::SERVER,
- "local DTLS role must be 'client' or 'server'");
- Role previousLocalRole = this->localRole;
- if (localRole == previousLocalRole)
- {
- MS_ERROR("same local DTLS role provided, doing nothing");
- return;
- }
- // If the previous local DTLS role was 'client' or 'server' do reset.
- if (previousLocalRole == Role::CLIENT || previousLocalRole == Role::SERVER)
- {
- MS_DEBUG_TAG(dtls, "resetting DTLS due to local role change");
- Reset();
- }
- // Update local role.
- this->localRole = localRole;
- // Set state and notify the listener.
- this->state = DtlsState::CONNECTING;
- this->listener->OnDtlsTransportConnecting(this);
- switch (this->localRole)
- {
- case Role::CLIENT:
- {
- MS_DEBUG_TAG(dtls, "running [role:client]");
- SSL_set_connect_state(this->ssl);
- SSL_do_handshake(this->ssl);
- SendPendingOutgoingDtlsData();
- SetTimeout();
- break;
- }
- case Role::SERVER:
- {
- MS_DEBUG_TAG(dtls, "running [role:server]");
- SSL_set_accept_state(this->ssl);
- SSL_do_handshake(this->ssl);
- break;
- }
- default:
- {
- MS_ABORT("invalid local DTLS role");
- }
- }
- }
- bool DtlsTransport::SetRemoteFingerprint(Fingerprint fingerprint)
- {
- MS_TRACE();
- MS_ASSERT(
- fingerprint.algorithm != FingerprintAlgorithm::NONE, "no fingerprint algorithm provided");
- this->remoteFingerprint = fingerprint;
- // The remote fingerpring may have been set after DTLS handshake was done,
- // so we may need to process it now.
- if (this->handshakeDone && this->state != DtlsState::CONNECTED)
- {
- MS_DEBUG_TAG(dtls, "handshake already done, processing it right now");
- return ProcessHandshake();
- }
- return true;
- }
- void DtlsTransport::ProcessDtlsData(const uint8_t* data, size_t len)
- {
- MS_TRACE();
- int written;
- int read;
- if (!IsRunning())
- {
- MS_WARN_TAG(nullptr,"cannot process data while not running");
- return;
- }
- // Write the received DTLS data into the sslBioFromNetwork.
- written =
- BIO_write(this->sslBioFromNetwork, static_cast<const void*>(data), static_cast<int>(len));
- if (written != static_cast<int>(len))
- {
- MS_WARN_TAG(
- dtls,
- "OpenSSL BIO_write() wrote less (%zu bytes) than given data (%zu bytes)",
- static_cast<size_t>(written),
- len);
- }
- // Must call SSL_read() to process received DTLS data.
- read = SSL_read(this->ssl, static_cast<void*>(DtlsTransport::sslReadBuffer), SslReadBufferSize);
- // Send data if it's ready.
- SendPendingOutgoingDtlsData();
- // Check SSL status and return if it is bad/closed.
- if (!CheckStatus(read))
- return;
- // Set/update the DTLS timeout.
- if (!SetTimeout())
- return;
- // Application data received. Notify to the listener.
- if (read > 0)
- {
- // It is allowed to receive DTLS data even before validating remote fingerprint.
- if (!this->handshakeDone)
- {
- MS_WARN_TAG(dtls, "ignoring application data received while DTLS handshake not done");
- return;
- }
- // Notify the listener.
- this->listener->OnDtlsTransportApplicationDataReceived(
- this, (uint8_t*)DtlsTransport::sslReadBuffer, static_cast<size_t>(read));
- }
- }
- void DtlsTransport::SendApplicationData(const uint8_t* data, size_t len)
- {
- MS_TRACE();
- // We cannot send data to the peer if its remote fingerprint is not validated.
- if (this->state != DtlsState::CONNECTED)
- {
- MS_WARN_TAG(dtls, "cannot send application data while DTLS is not fully connected");
- return;
- }
- if (len == 0)
- {
- MS_WARN_TAG(dtls, "ignoring 0 length data");
- return;
- }
- int written;
- written = SSL_write(this->ssl, static_cast<const void*>(data), static_cast<int>(len));
- if (written < 0)
- {
- LOG_OPENSSL_ERROR("SSL_write() failed");
- if (!CheckStatus(written))
- return;
- }
- else if (written != static_cast<int>(len))
- {
- MS_WARN_TAG(
- dtls, "OpenSSL SSL_write() wrote less (%d bytes) than given data (%zu bytes)", written, len);
- }
- // Send data.
- SendPendingOutgoingDtlsData();
- }
- void DtlsTransport::Reset()
- {
- MS_TRACE();
- int ret;
- if (!IsRunning())
- return;
- MS_WARN_TAG(dtls, "resetting DTLS transport");
- // Stop the DTLS timer.
- this->timer = nullptr;
- // We need to reset the SSL instance so we need to "shutdown" it, but we
- // don't want to send a Close Alert to the peer, so just don't call
- // SendPendingOutgoingDTLSData().
- SSL_shutdown(this->ssl);
- this->localRole = Role::NONE;
- this->state = DtlsState::NEW;
- this->handshakeDone = false;
- this->handshakeDoneNow = false;
- // Reset SSL status.
- // NOTE: For this to properly work, SSL_shutdown() must be called before.
- // NOTE: This may fail if not enough DTLS handshake data has been received,
- // but we don't care so just clear the error queue.
- ret = SSL_clear(this->ssl);
- if (ret == 0)
- ERR_clear_error();
- }
- inline bool DtlsTransport::CheckStatus(int returnCode)
- {
- MS_TRACE();
- int err;
- bool wasHandshakeDone = this->handshakeDone;
- err = SSL_get_error(this->ssl, returnCode);
- switch (err)
- {
- case SSL_ERROR_NONE:
- break;
- case SSL_ERROR_SSL:
- LOG_OPENSSL_ERROR("SSL status: SSL_ERROR_SSL");
- break;
- case SSL_ERROR_WANT_READ:
- break;
- case SSL_ERROR_WANT_WRITE:
- MS_WARN_TAG(dtls, "SSL status: SSL_ERROR_WANT_WRITE");
- break;
- case SSL_ERROR_WANT_X509_LOOKUP:
- MS_DEBUG_TAG(dtls, "SSL status: SSL_ERROR_WANT_X509_LOOKUP");
- break;
- case SSL_ERROR_SYSCALL:
- LOG_OPENSSL_ERROR("SSL status: SSL_ERROR_SYSCALL");
- break;
- case SSL_ERROR_ZERO_RETURN:
- break;
- case SSL_ERROR_WANT_CONNECT:
- MS_WARN_TAG(dtls, "SSL status: SSL_ERROR_WANT_CONNECT");
- break;
- case SSL_ERROR_WANT_ACCEPT:
- MS_WARN_TAG(dtls, "SSL status: SSL_ERROR_WANT_ACCEPT");
- break;
- default:
- MS_WARN_TAG(dtls, "SSL status: unknown error");
- }
- // Check if the handshake (or re-handshake) has been done right now.
- if (this->handshakeDoneNow)
- {
- this->handshakeDoneNow = false;
- this->handshakeDone = true;
- // Stop the timer.
- this->timer = nullptr;
- // Process the handshake just once (ignore if DTLS renegotiation).
- if (!wasHandshakeDone && this->remoteFingerprint.algorithm != FingerprintAlgorithm::NONE)
- return ProcessHandshake();
- return true;
- }
- // Check if the peer sent close alert or a fatal error happened.
- else if (((SSL_get_shutdown(this->ssl) & SSL_RECEIVED_SHUTDOWN) != 0) || err == SSL_ERROR_SSL || err == SSL_ERROR_SYSCALL)
- {
- if (this->state == DtlsState::CONNECTED)
- {
- MS_DEBUG_TAG(dtls, "disconnected");
- Reset();
- // Set state and notify the listener.
- this->state = DtlsState::CLOSED;
- this->listener->OnDtlsTransportClosed(this);
- }
- else
- {
- MS_WARN_TAG(dtls, "connection failed");
- Reset();
- // Set state and notify the listener.
- this->state = DtlsState::FAILED;
- this->listener->OnDtlsTransportFailed(this);
- }
- return false;
- }
- else
- {
- return true;
- }
- }
- inline void DtlsTransport::SendPendingOutgoingDtlsData()
- {
- MS_TRACE();
- if (BIO_eof(this->sslBioToNetwork))
- return;
- int64_t read;
- char* data{ nullptr };
- read = BIO_get_mem_data(this->sslBioToNetwork, &data); // NOLINT
- if (read <= 0)
- return;
- MS_DEBUG_DEV("%" PRIu64 " bytes of DTLS data ready to sent to the peer", read);
- // Notify the listener.
- this->listener->OnDtlsTransportSendData(
- this, reinterpret_cast<uint8_t*>(data), static_cast<size_t>(read));
- // Clear the BIO buffer.
- // NOTE: the (void) avoids the -Wunused-value warning.
- (void)BIO_reset(this->sslBioToNetwork);
- }
- inline bool DtlsTransport::SetTimeout()
- {
- MS_TRACE();
- MS_ASSERT(
- this->state == DtlsState::CONNECTING || this->state == DtlsState::CONNECTED,
- "invalid DTLS state");
- int64_t ret;
- struct timeval dtlsTimeout{ 0, 0 };
- uint64_t timeoutMs;
- // NOTE: If ret == 0 then ignore the value in dtlsTimeout.
- // NOTE: No DTLSv_1_2_get_timeout() or DTLS_get_timeout() in OpenSSL 1.1.0-dev.
- ret = DTLSv1_get_timeout(this->ssl, static_cast<void*>(&dtlsTimeout)); // NOLINT
- if (ret == 0)
- return true;
- timeoutMs = (dtlsTimeout.tv_sec * static_cast<uint64_t>(1000)) + (dtlsTimeout.tv_usec / 1000);
- if (timeoutMs == 0)
- {
- return true;
- }
- else if (timeoutMs < 30000)
- {
- MS_DEBUG_DEV("DTLS timer set in %" PRIu64 "ms", timeoutMs);
- weak_ptr<DtlsTransport> weak_self = shared_from_this();
- this->timer = std::make_shared<Timer>(timeoutMs / 1000.0f, [weak_self](){
- auto strong_self = weak_self.lock();
- if(strong_self){
- strong_self->OnTimer();
- }
- return true;
- }, this->poller);
- return true;
- }
- // NOTE: Don't start the timer again if the timeout is greater than 30 seconds.
- else
- {
- MS_WARN_TAG(dtls, "DTLS timeout too high (%" PRIu64 "ms), resetting DLTS", timeoutMs);
- Reset();
- // Set state and notify the listener.
- this->state = DtlsState::FAILED;
- this->listener->OnDtlsTransportFailed(this);
- return false;
- }
- }
- inline bool DtlsTransport::ProcessHandshake()
- {
- MS_TRACE();
- MS_ASSERT(this->handshakeDone, "handshake not done yet");
- MS_ASSERT(
- this->remoteFingerprint.algorithm != FingerprintAlgorithm::NONE, "remote fingerprint not set");
- // Validate the remote fingerprint.
- if (!CheckRemoteFingerprint())
- {
- Reset();
- // Set state and notify the listener.
- this->state = DtlsState::FAILED;
- this->listener->OnDtlsTransportFailed(this);
- return false;
- }
- // Get the negotiated SRTP crypto suite.
- RTC::SrtpSession::CryptoSuite srtpCryptoSuite = GetNegotiatedSrtpCryptoSuite();
- if (srtpCryptoSuite != RTC::SrtpSession::CryptoSuite::NONE)
- {
- // Extract the SRTP keys (will notify the listener with them).
- ExtractSrtpKeys(srtpCryptoSuite);
- return true;
- }
- // NOTE: We assume that "use_srtp" DTLS extension is required even if
- // there is no audio/video.
- MS_WARN_2TAGS(dtls, srtp, "SRTP crypto suite not negotiated");
- Reset();
- // Set state and notify the listener.
- this->state = DtlsState::FAILED;
- this->listener->OnDtlsTransportFailed(this);
- return false;
- }
- inline bool DtlsTransport::CheckRemoteFingerprint()
- {
- MS_TRACE();
- MS_ASSERT(
- this->remoteFingerprint.algorithm != FingerprintAlgorithm::NONE, "remote fingerprint not set");
- X509* certificate;
- uint8_t binaryFingerprint[EVP_MAX_MD_SIZE];
- unsigned int size{ 0 };
- char hexFingerprint[(EVP_MAX_MD_SIZE * 3) + 1];
- const EVP_MD* hashFunction;
- int ret;
- certificate = SSL_get_peer_certificate(this->ssl);
- if (!certificate)
- {
- MS_WARN_TAG(dtls, "no certificate was provided by the peer");
- return false;
- }
- switch (this->remoteFingerprint.algorithm)
- {
- case FingerprintAlgorithm::SHA1:
- hashFunction = EVP_sha1();
- break;
- case FingerprintAlgorithm::SHA224:
- hashFunction = EVP_sha224();
- break;
- case FingerprintAlgorithm::SHA256:
- hashFunction = EVP_sha256();
- break;
- case FingerprintAlgorithm::SHA384:
- hashFunction = EVP_sha384();
- break;
- case FingerprintAlgorithm::SHA512:
- hashFunction = EVP_sha512();
- break;
- default:
- MS_ABORT("unknown algorithm");
- }
- // Compare the remote fingerprint with the value given via signaling.
- ret = X509_digest(certificate, hashFunction, binaryFingerprint, &size);
- if (ret == 0)
- {
- MS_ERROR("X509_digest() failed");
- X509_free(certificate);
- return false;
- }
- // Convert to hexadecimal format in uppercase with colons.
- for (unsigned int i{ 0 }; i < size; ++i)
- {
- std::sprintf(hexFingerprint + (i * 3), "%.2X:", binaryFingerprint[i]);
- }
- hexFingerprint[(size * 3) - 1] = '\0';
- if (this->remoteFingerprint.value != hexFingerprint)
- {
- MS_WARN_TAG(
- dtls,
- "fingerprint in the remote certificate (%s) does not match the announced one (%s)",
- hexFingerprint,
- this->remoteFingerprint.value.c_str());
- X509_free(certificate);
- return false;
- }
- MS_DEBUG_TAG(dtls, "valid remote fingerprint");
- // Get the remote certificate in PEM format.
- BIO* bio = BIO_new(BIO_s_mem());
- // Ensure the underlying BUF_MEM structure is also freed.
- // NOTE: Avoid stupid "warning: value computed is not used [-Wunused-value]" since
- // BIO_set_close() always returns 1.
- (void)BIO_set_close(bio, BIO_CLOSE);
- ret = PEM_write_bio_X509(bio, certificate);
- if (ret != 1)
- {
- LOG_OPENSSL_ERROR("PEM_write_bio_X509() failed");
- X509_free(certificate);
- BIO_free(bio);
- return false;
- }
- BUF_MEM* mem;
- BIO_get_mem_ptr(bio, &mem); // NOLINT[cppcoreguidelines-pro-type-cstyle-cast]
- if (!mem || !mem->data || mem->length == 0u)
- {
- LOG_OPENSSL_ERROR("BIO_get_mem_ptr() failed");
- X509_free(certificate);
- BIO_free(bio);
- return false;
- }
- this->remoteCert = std::string(mem->data, mem->length);
- X509_free(certificate);
- BIO_free(bio);
- return true;
- }
- inline void DtlsTransport::ExtractSrtpKeys(RTC::SrtpSession::CryptoSuite srtpCryptoSuite)
- {
- MS_TRACE();
- size_t srtpKeyLength{ 0 };
- size_t srtpSaltLength{ 0 };
- size_t srtpMasterLength{ 0 };
- switch (srtpCryptoSuite)
- {
- case RTC::SrtpSession::CryptoSuite::AES_CM_128_HMAC_SHA1_80:
- case RTC::SrtpSession::CryptoSuite::AES_CM_128_HMAC_SHA1_32:
- {
- srtpKeyLength = SrtpMasterKeyLength;
- srtpSaltLength = SrtpMasterSaltLength;
- srtpMasterLength = SrtpMasterLength;
- break;
- }
- case RTC::SrtpSession::CryptoSuite::AEAD_AES_256_GCM:
- {
- srtpKeyLength = SrtpAesGcm256MasterKeyLength;
- srtpSaltLength = SrtpAesGcm256MasterSaltLength;
- srtpMasterLength = SrtpAesGcm256MasterLength;
- break;
- }
- case RTC::SrtpSession::CryptoSuite::AEAD_AES_128_GCM:
- {
- srtpKeyLength = SrtpAesGcm128MasterKeyLength;
- srtpSaltLength = SrtpAesGcm128MasterSaltLength;
- srtpMasterLength = SrtpAesGcm128MasterLength;
- break;
- }
- default:
- {
- MS_ABORT("unknown SRTP crypto suite");
- }
- }
- auto* srtpMaterial = new uint8_t[srtpMasterLength * 2];
- uint8_t* srtpLocalKey{ nullptr };
- uint8_t* srtpLocalSalt{ nullptr };
- uint8_t* srtpRemoteKey{ nullptr };
- uint8_t* srtpRemoteSalt{ nullptr };
- auto* srtpLocalMasterKey = new uint8_t[srtpMasterLength];
- auto* srtpRemoteMasterKey = new uint8_t[srtpMasterLength];
- int ret;
- ret = SSL_export_keying_material(
- this->ssl, srtpMaterial, srtpMasterLength * 2, "EXTRACTOR-dtls_srtp", 19, nullptr, 0, 0);
- MS_ASSERT(ret != 0, "SSL_export_keying_material() failed");
- switch (this->localRole)
- {
- case Role::SERVER:
- {
- srtpRemoteKey = srtpMaterial;
- srtpLocalKey = srtpRemoteKey + srtpKeyLength;
- srtpRemoteSalt = srtpLocalKey + srtpKeyLength;
- srtpLocalSalt = srtpRemoteSalt + srtpSaltLength;
- break;
- }
- case Role::CLIENT:
- {
- srtpLocalKey = srtpMaterial;
- srtpRemoteKey = srtpLocalKey + srtpKeyLength;
- srtpLocalSalt = srtpRemoteKey + srtpKeyLength;
- srtpRemoteSalt = srtpLocalSalt + srtpSaltLength;
- break;
- }
- default:
- {
- MS_ABORT("no DTLS role set");
- }
- }
- // Create the SRTP local master key.
- std::memcpy(srtpLocalMasterKey, srtpLocalKey, srtpKeyLength);
- std::memcpy(srtpLocalMasterKey + srtpKeyLength, srtpLocalSalt, srtpSaltLength);
- // Create the SRTP remote master key.
- std::memcpy(srtpRemoteMasterKey, srtpRemoteKey, srtpKeyLength);
- std::memcpy(srtpRemoteMasterKey + srtpKeyLength, srtpRemoteSalt, srtpSaltLength);
- // Set state and notify the listener.
- this->state = DtlsState::CONNECTED;
- this->listener->OnDtlsTransportConnected(
- this,
- srtpCryptoSuite,
- srtpLocalMasterKey,
- srtpMasterLength,
- srtpRemoteMasterKey,
- srtpMasterLength,
- this->remoteCert);
- delete[] srtpMaterial;
- delete[] srtpLocalMasterKey;
- delete[] srtpRemoteMasterKey;
- }
- inline RTC::SrtpSession::CryptoSuite DtlsTransport::GetNegotiatedSrtpCryptoSuite()
- {
- MS_TRACE();
- RTC::SrtpSession::CryptoSuite negotiatedSrtpCryptoSuite = RTC::SrtpSession::CryptoSuite::NONE;
- // Ensure that the SRTP crypto suite has been negotiated.
- // NOTE: This is a OpenSSL type.
- SRTP_PROTECTION_PROFILE* sslSrtpCryptoSuite = SSL_get_selected_srtp_profile(this->ssl);
- if (!sslSrtpCryptoSuite)
- return negotiatedSrtpCryptoSuite;
- // Get the negotiated SRTP crypto suite.
- for (auto& srtpCryptoSuite : DtlsTransport::srtpCryptoSuites)
- {
- SrtpCryptoSuiteMapEntry* cryptoSuiteEntry = std::addressof(srtpCryptoSuite);
- if (std::strcmp(sslSrtpCryptoSuite->name, cryptoSuiteEntry->name) == 0)
- {
- MS_DEBUG_2TAGS(dtls, srtp, "chosen SRTP crypto suite: %s", cryptoSuiteEntry->name);
- negotiatedSrtpCryptoSuite = cryptoSuiteEntry->cryptoSuite;
- }
- }
- MS_ASSERT(
- negotiatedSrtpCryptoSuite != RTC::SrtpSession::CryptoSuite::NONE,
- "chosen SRTP crypto suite is not an available one");
- return negotiatedSrtpCryptoSuite;
- }
- inline void DtlsTransport::OnSslInfo(int where, int ret)
- {
- MS_TRACE();
- int w = where & -SSL_ST_MASK;
- const char* role;
- if ((w & SSL_ST_CONNECT) != 0)
- role = "client";
- else if ((w & SSL_ST_ACCEPT) != 0)
- role = "server";
- else
- role = "undefined";
- if ((where & SSL_CB_LOOP) != 0)
- {
- MS_DEBUG_TAG(dtls, "[role:%s, action:'%s']", role, SSL_state_string_long(this->ssl));
- }
- else if ((where & SSL_CB_ALERT) != 0)
- {
- const char* alertType;
- switch (*SSL_alert_type_string(ret))
- {
- case 'W':
- alertType = "warning";
- break;
- case 'F':
- alertType = "fatal";
- break;
- default:
- alertType = "undefined";
- }
- if ((where & SSL_CB_READ) != 0)
- {
- MS_WARN_TAG(dtls, "received DTLS %s alert: %s", alertType, SSL_alert_desc_string_long(ret));
- }
- else if ((where & SSL_CB_WRITE) != 0)
- {
- MS_DEBUG_TAG(dtls, "sending DTLS %s alert: %s", alertType, SSL_alert_desc_string_long(ret));
- }
- else
- {
- MS_DEBUG_TAG(dtls, "DTLS %s alert: %s", alertType, SSL_alert_desc_string_long(ret));
- }
- }
- else if ((where & SSL_CB_EXIT) != 0)
- {
- if (ret == 0)
- MS_DEBUG_TAG(dtls, "[role:%s, failed:'%s']", role, SSL_state_string_long(this->ssl));
- else if (ret < 0)
- MS_DEBUG_TAG(dtls, "role: %s, waiting:'%s']", role, SSL_state_string_long(this->ssl));
- }
- else if ((where & SSL_CB_HANDSHAKE_START) != 0)
- {
- MS_DEBUG_TAG(dtls, "DTLS handshake start");
- }
- else if ((where & SSL_CB_HANDSHAKE_DONE) != 0)
- {
- MS_DEBUG_TAG(dtls, "DTLS handshake done");
- this->handshakeDoneNow = true;
- }
- // NOTE: checking SSL_get_shutdown(this->ssl) & SSL_RECEIVED_SHUTDOWN here upon
- // receipt of a close alert does not work (the flag is set after this callback).
- }
- inline void DtlsTransport::OnTimer()
- {
- MS_TRACE();
- // Workaround for https://github.com/openssl/openssl/issues/7998.
- if (this->handshakeDone)
- {
- // MS_DEBUG_DEV("handshake is done so return");
- return;
- }
- DTLSv1_handle_timeout(this->ssl);
- // If required, send DTLS data.
- SendPendingOutgoingDtlsData();
- // Set the DTLS timer again.
- SetTimeout();
- }
- } // namespace RTC
|