diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index 663bd4033d29ab321819287d038db71e32a30aeb..bc3163de2ee28dc57a8f81a38f41916b97c5327e 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -208,7 +208,7 @@ struct JamiAccount::DiscoveredPeer static constexpr int ICE_COMPONENTS {1}; static constexpr int ICE_COMP_SIP_TRANSPORT {0}; static constexpr auto ICE_NEGOTIATION_TIMEOUT = std::chrono::seconds(60); -static constexpr auto TLS_TIMEOUT = std::chrono::seconds(30); +static constexpr auto TLS_TIMEOUT = std::chrono::seconds(40); const constexpr auto EXPORT_KEY_RENEWAL_TIME = std::chrono::minutes(20); static constexpr const char* const RING_URI_PREFIX = "ring:"; @@ -1667,7 +1667,7 @@ JamiAccount::handlePendingCall(PendingCall& pc, bool incoming) /*.cert = */ id.second, /*.cert_key = */ id.first, /*.dh_params = */ dhParams_, - /*.timeout = */ std::chrono::duration_cast<decltype(tls::TlsParams::timeout)>(TLS_TIMEOUT), + /*.timeout = */ TLS_TIMEOUT, /*.cert_check = */ [waccount, wcall, remote_device, remote_account](unsigned status, const gnutls_datum_t* cert_list, diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 5d11a7024deaf2b284d53a247a37f0351fd5b08e..33f6e243f153562a54a80045e426d9399fdff9c6 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -28,6 +28,7 @@ #include "security/tls_session.h" #include <algorithm> +#include <chrono> #include <future> #include <vector> #include <atomic> @@ -115,8 +116,7 @@ IceSocketEndpoint::waitForData(std::chrono::milliseconds timeout, std::error_cod if (ice_) { if (!ice_->isRunning()) return -1; - return iceIsSender ? ice_->isDataAvailable(compId_) - : ice_->waitForData(compId_, timeout, ec); + return ice_->waitForData(compId_, timeout, ec); } return -1; } @@ -161,7 +161,7 @@ IceSocketEndpoint::write(const ValueType* buf, std::size_t len, std::error_code& class TlsSocketEndpoint::Impl { public: - static constexpr auto TLS_TIMEOUT = std::chrono::seconds(20); + static constexpr auto TLS_TIMEOUT = std::chrono::seconds(40); Impl(std::unique_ptr<IceSocketEndpoint>&& ep, const dht::crypto::Certificate& peer_cert, @@ -187,7 +187,7 @@ public: /*.cert = */ local_identity.second, /*.cert_key = */ local_identity.first, /*.dh_params = */ dh_params, - /*.timeout = */ Impl::TLS_TIMEOUT, + /*.timeout = */ TLS_TIMEOUT, /*.cert_check = */ nullptr, }; tls = std::make_unique<tls::TlsSession>(std::move(ep), tls_param, tls_cbs); @@ -220,7 +220,7 @@ public: /*.cert = */ local_identity.second, /*.cert_key = */ local_identity.first, /*.dh_params = */ dh_params, - /*.timeout = */ Impl::TLS_TIMEOUT, + /*.timeout = */ std::chrono::duration_cast<decltype(tls::TlsParams::timeout)>(TLS_TIMEOUT), /*.cert_check = */ nullptr, }; tls = std::make_unique<tls::TlsSession>(std::move(ep), tls_param, tls_cbs); @@ -259,9 +259,6 @@ public: const IceSocketEndpoint* ep_; }; -// Declaration at namespace scope is necessary (until C++17) -constexpr std::chrono::seconds TlsSocketEndpoint::Impl::TLS_TIMEOUT; - int TlsSocketEndpoint::Impl::verifyCertificate(gnutls_session_t session) { diff --git a/src/security/tls_session.cpp b/src/security/tls_session.cpp index 9c737eb555ed95dac99d9a80a16d92cd076d68a4..df935d2d0122a9d8210e578a04093c759ce9f1af 100644 --- a/src/security/tls_session.cpp +++ b/src/security/tls_session.cpp @@ -108,8 +108,10 @@ static constexpr auto RX_OOO_TIMEOUT = std::chrono::milliseconds(1500); static constexpr int ASYMETRIC_TRANSPORT_MTU_OFFSET = 20; // when client, if your local IP is IPV4 and server is IPV6; you must reduce your MTU to // avoid packet too big error on server side. the offset is the difference in size of IP headers -static constexpr auto OCSP_VERIFICATION_TIMEOUT = std::chrono::seconds(2); // Time to wait for an OCSP verification -static constexpr auto OCSP_REQUEST_TIMEOUT = std::chrono::seconds(2); // Time to wait for an ocsp-request +static constexpr auto OCSP_VERIFICATION_TIMEOUT = std::chrono::seconds( + 2); // Time to wait for an OCSP verification +static constexpr auto OCSP_REQUEST_TIMEOUT = std::chrono::seconds( + 2); // Time to wait for an ocsp-request // Helper to cast any duration into an integer number of milliseconds template<class Rep, class Period> @@ -300,12 +302,17 @@ public: /* * Verify OCSP (Online Certificate Service Protocol): */ - void verifyOcsp(const std::string& url, dht::crypto::Certificate& cert, gnutls_x509_crt_t issuer, OcspVerification cb); + void verifyOcsp(const std::string& url, + dht::crypto::Certificate& cert, + gnutls_x509_crt_t issuer, + OcspVerification cb); /* * Send OCSP Request to the specified URI. */ - void sendOcspRequest(const std::string& uri, std::string body, - std::chrono::seconds timeout, HttpResponse cb = {}); + void sendOcspRequest(const std::string& uri, + std::string body, + std::chrono::seconds timeout, + HttpResponse cb = {}); // FSM thread (TLS states) ThreadLoop thread_; // ctor init. @@ -610,7 +617,9 @@ TlsSession::TlsSessionImpl::commonSessionInit() return this_->waitForRawData( std::chrono::milliseconds(ms)); }); - + // TODO -1 = default else set value + if (transport_->isReliable()) + gnutls_handshake_set_timeout(session_, duration2ms(params_.timeout)); return true; } @@ -623,13 +632,12 @@ getOcspUrl(gnutls_x509_crt_t cert) do { // Extracts the Authority Information Access (AIA) extension, see RFC 5280 section 4.2.2.1 ret = gnutls_x509_crt_get_authority_info_access(cert, seq++, GNUTLS_IA_OCSP_URI, &aia, NULL); - } - while (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + } while (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); // could also try the issuer if we include ocsp uri into there if (ret < 0) { return {}; } - std::string url((const char*)aia.data,(size_t)aia.size); + std::string url((const char*) aia.data, (size_t) aia.size); gnutls_free(aia.data); return url; } @@ -639,8 +647,7 @@ TlsSession::TlsSessionImpl::verifyCertificateWrapper(gnutls_session_t session) { // Perform user-set verification first to avoid flooding with ocsp-requests if peer is denied int verified; - if (callbacks_.verifyCertificate) - { + if (callbacks_.verifyCertificate) { auto this_ = reinterpret_cast<TlsSessionImpl*>(gnutls_session_get_ptr(session)); verified = this_->callbacks_.verifyCertificate(session); if (verified != GNUTLS_E_SUCCESS) @@ -680,16 +687,18 @@ TlsSession::TlsSessionImpl::verifyCertificateWrapper(gnutls_session_t session) std::mutex cv_m; std::condition_variable cv; std::unique_lock<std::mutex> cv_lk(cv_m); - + gnutls_x509_crt_t issuer_crt = cert.issuer ? cert.issuer->cert : nullptr; verifyOcsp(ocspUrl, cert, issuer_crt, [&](const int status) { - if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE){ + if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { // OCSP URI is absent, don't fail the verification by overwritting the user-set one. JAMI_WARN("Skipping OCSP verification %s: request failed", cert.getUID().c_str()); } else { if (status != GNUTLS_E_SUCCESS) { JAMI_ERR("OCSP verification failed for %s: %s (%i)", - cert.getUID().c_str(), gnutls_strerror(status), status); + cert.getUID().c_str(), + gnutls_strerror(status), + status); } verified = status; } @@ -697,14 +706,19 @@ TlsSession::TlsSessionImpl::verifyCertificateWrapper(gnutls_session_t session) ocsp_done = true; cv.notify_all(); }); - cv.wait_for(cv_lk, std::chrono::seconds(OCSP_VERIFICATION_TIMEOUT),[&ocsp_done]{return ocsp_done;}); + cv.wait_for(cv_lk, std::chrono::seconds(OCSP_VERIFICATION_TIMEOUT), [&ocsp_done] { + return ocsp_done; + }); // } OCSP return verified; } void -TlsSession::TlsSessionImpl::verifyOcsp(const std::string& aia_uri, dht::crypto::Certificate& cert, gnutls_x509_crt_t issuer, OcspVerification cb) +TlsSession::TlsSessionImpl::verifyOcsp(const std::string& aia_uri, + dht::crypto::Certificate& cert, + gnutls_x509_crt_t issuer, + OcspVerification cb) { JAMI_DBG("Certificate's AIA URI: %s", aia_uri.c_str()); @@ -712,95 +726,102 @@ TlsSession::TlsSessionImpl::verifyOcsp(const std::string& aia_uri, dht::crypto:: std::pair<std::string, dht::Blob> ocsp_req; try { ocsp_req = cert.generateOcspRequest(issuer); - } - catch (dht::crypto::CryptoException& e){ + } catch (dht::crypto::CryptoException& e) { JAMI_ERR("Failed to generate OCSP request: %s", e.what()); if (cb) cb(GNUTLS_E_INVALID_REQUEST); return; } - sendOcspRequest(aia_uri, std::move(ocsp_req.first), OCSP_REQUEST_TIMEOUT, - [this, cb = std::move(cb), &cert, nonce=std::move(ocsp_req.second)](const dht::http::Response& r){ - // Prepare response data - // Verify response validity - if (r.status_code != 200) { - JAMI_WARN("HTTP OCSP Request Failed with code %i", r.status_code); - if (cb) - cb(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); - return; - } - JAMI_DBG("HTTP OCSP Request done!"); - unsigned int verify = 0; - try { - cert.ocspResponse = std::make_shared<dht::crypto::OcspResponse>((const uint8_t*)r.body.data(), r.body.size()); - JAMI_DBG("%s", cert.ocspResponse->toString().c_str()); - verify = cert.ocspResponse->verifyDirect(cert, nonce); - } - catch (dht::crypto::CryptoException& e) { - JAMI_ERR("Failed to verify OCSP response: %s", e.what()); - if (cb) - cb(GNUTLS_E_INVALID_REQUEST); - return; - } - if (verify == 0) - JAMI_DBG("OCSP verification success!"); - else - JAMI_ERR("OCSP verification error!"); - if (verify & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND) - JAMI_ERR("Signer cert not found"); - if (verify & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR) - JAMI_ERR("Signer cert keyusage error"); - if (verify & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER) - JAMI_ERR("Signer cert is not trusted"); - if (verify & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) - JAMI_ERR("Insecure algorithm"); - if (verify & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE) - JAMI_ERR("Signature failure"); - if (verify & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED) - JAMI_ERR("Signer cert not yet activated"); - if (verify & GNUTLS_OCSP_VERIFY_CERT_EXPIRED) - JAMI_ERR("Signer cert expired"); - // Save response into the certificate store - tls::CertificateStore::instance().pinOcspResponse(cert); - if (cb) - cb(verify); - }); + sendOcspRequest(aia_uri, + std::move(ocsp_req.first), + OCSP_REQUEST_TIMEOUT, + [this, cb = std::move(cb), &cert, nonce = std::move(ocsp_req.second)]( + const dht::http::Response& r) { + // Prepare response data + // Verify response validity + if (r.status_code != 200) { + JAMI_WARN("HTTP OCSP Request Failed with code %i", r.status_code); + if (cb) + cb(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + return; + } + JAMI_DBG("HTTP OCSP Request done!"); + unsigned int verify = 0; + try { + cert.ocspResponse = std::make_shared<dht::crypto::OcspResponse>( + (const uint8_t*) r.body.data(), r.body.size()); + JAMI_DBG("%s", cert.ocspResponse->toString().c_str()); + verify = cert.ocspResponse->verifyDirect(cert, nonce); + } catch (dht::crypto::CryptoException& e) { + JAMI_ERR("Failed to verify OCSP response: %s", e.what()); + if (cb) + cb(GNUTLS_E_INVALID_REQUEST); + return; + } + if (verify == 0) + JAMI_DBG("OCSP verification success!"); + else + JAMI_ERR("OCSP verification error!"); + if (verify & GNUTLS_OCSP_VERIFY_SIGNER_NOT_FOUND) + JAMI_ERR("Signer cert not found"); + if (verify & GNUTLS_OCSP_VERIFY_SIGNER_KEYUSAGE_ERROR) + JAMI_ERR("Signer cert keyusage error"); + if (verify & GNUTLS_OCSP_VERIFY_UNTRUSTED_SIGNER) + JAMI_ERR("Signer cert is not trusted"); + if (verify & GNUTLS_OCSP_VERIFY_INSECURE_ALGORITHM) + JAMI_ERR("Insecure algorithm"); + if (verify & GNUTLS_OCSP_VERIFY_SIGNATURE_FAILURE) + JAMI_ERR("Signature failure"); + if (verify & GNUTLS_OCSP_VERIFY_CERT_NOT_ACTIVATED) + JAMI_ERR("Signer cert not yet activated"); + if (verify & GNUTLS_OCSP_VERIFY_CERT_EXPIRED) + JAMI_ERR("Signer cert expired"); + // Save response into the certificate store + tls::CertificateStore::instance().pinOcspResponse(cert); + if (cb) + cb(verify); + }); } void -TlsSession::TlsSessionImpl::sendOcspRequest(const std::string& uri, std::string body, - std::chrono::seconds timeout, HttpResponse cb) +TlsSession::TlsSessionImpl::sendOcspRequest(const std::string& uri, + std::string body, + std::chrono::seconds timeout, + HttpResponse cb) { using namespace dht; - auto request = std::make_shared<http::Request>(*Manager::instance().ioContext(), uri);//, logger); + auto request = std::make_shared<http::Request>(*Manager::instance().ioContext(), + uri); //, logger); request->set_method(restinio::http_method_post()); request->set_header_field(restinio::http_field_t::user_agent, "Jami"); request->set_header_field(restinio::http_field_t::accept, "*/*"); request->set_header_field(restinio::http_field_t::content_type, "application/ocsp-request"); request->set_body(std::move(body)); request->set_connection_type(restinio::http_connection_header_t::close); - request->add_on_state_change_callback([this, cb = std::move(cb), timeout] - (const http::Request::State state, const http::Response response) - { - JAMI_DBG("HTTP OCSP Request state=%i status_code=%i", (unsigned int) state, response.status_code); - if (state == http::Request::State::SENDING){ - auto request = response.request.lock(); - request->get_connection()->timeout(timeout, [request](const asio::error_code& ec){ - if (ec and ec != asio::error::operation_aborted) - JAMI_ERR("HTTP OCSP Request timeout with error: %s", ec.message().c_str()); - request->cancel(); - }); - } - if (state != http::Request::State::DONE) - return; - if (cb) - cb(response); - if (auto request = response.request.lock()) { - std::lock_guard<std::mutex> lock(requestsMtx_); - requests_.erase(request); - } - }); + request->add_on_state_change_callback( + [this, cb = std::move(cb), timeout](const http::Request::State state, + const http::Response response) { + JAMI_DBG("HTTP OCSP Request state=%i status_code=%i", + (unsigned int) state, + response.status_code); + if (state == http::Request::State::SENDING) { + auto request = response.request.lock(); + request->get_connection()->timeout(timeout, [request](const asio::error_code& ec) { + if (ec and ec != asio::error::operation_aborted) + JAMI_ERR("HTTP OCSP Request timeout with error: %s", ec.message().c_str()); + request->cancel(); + }); + } + if (state != http::Request::State::DONE) + return; + if (cb) + cb(response); + if (auto request = response.request.lock()) { + std::lock_guard<std::mutex> lock(requestsMtx_); + requests_.erase(request); + } + }); { std::lock_guard<std::mutex> lock(requestsMtx_); requests_.emplace(request); diff --git a/src/security/tls_session.h b/src/security/tls_session.h index 5951a4008f9382af3f9efa9b96ebb30b1e2dd568..48105a04b6530a53acaa0bdfecf21df9bc5833e8 100644 --- a/src/security/tls_session.h +++ b/src/security/tls_session.h @@ -75,7 +75,7 @@ struct TlsParams // Diffie-Hellman computed by gnutls_dh_params_init/gnutls_dh_params_generateX std::shared_future<DhParams> dh_params; - // DTLS timeout + // handshake timeout duration timeout; // Callback for certificate checkings