diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index ccb6a0cab966687feba8684d328589dd6474ebb5..857aea5bed933122ecd155afd777f2af8488ae99 100644 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -60,7 +60,8 @@ public: static constexpr auto TLS_TIMEOUT = std::chrono::seconds(20); Impl(ConnectedTurnTransport& tr, - dht::crypto::TrustList& trust_list) : turn {tr}, trustList {trust_list} {} + std::function<bool(const dht::crypto::Certificate&)>&& cert_check) + : turn {tr}, peerCertificateCheckFunc {std::move(cert_check)} {} ~Impl(); @@ -72,7 +73,7 @@ public: std::unique_ptr<tls::TlsSession> tls; ConnectedTurnTransport& turn; - dht::crypto::TrustList& trustList; + std::function<bool(const dht::crypto::Certificate&)> peerCertificateCheckFunc; dht::crypto::Certificate peerCertificate; std::promise<bool> connected; }; @@ -81,10 +82,7 @@ public: constexpr std::chrono::seconds TlsTurnEndpoint::Impl::TLS_TIMEOUT; TlsTurnEndpoint::Impl::~Impl() -{ - if (peerCertificate) - trustList.remove(peerCertificate); -} +{} int TlsTurnEndpoint::Impl::verifyCertificate(gnutls_session_t session) @@ -107,22 +105,18 @@ TlsTurnEndpoint::Impl::verifyCertificate(gnutls_session_t session) return GNUTLS_E_CERTIFICATE_ERROR; } - // Check if peer certificate is inside our list of permited certificate + // Check if received peer certificate is awaited std::vector<std::pair<uint8_t*, uint8_t*>> crt_data; crt_data.reserve(cert_list_size); for (unsigned i=0; i<cert_list_size; i++) crt_data.emplace_back(cert_list[i].data, cert_list[i].data + cert_list[i].size); auto crt = dht::crypto::Certificate {crt_data}; - auto verify_result = trustList.verify(crt); - if (!verify_result) { - RING_ERR() << "[TLS-TURN] Peer certificate verification failed: " << verify_result; - return verify_result.result; - } - // Store valid peer certificate for trust list removal during dtor + if (!peerCertificateCheckFunc(crt)) + return GNUTLS_E_CERTIFICATE_ERROR; + peerCertificate = std::move(crt); - // notify GnuTLS to continue handshake normally return GNUTLS_E_SUCCESS; } @@ -149,8 +143,8 @@ TlsTurnEndpoint::Impl::onTlsCertificatesUpdate(UNUSED const gnutls_datum_t* loca TlsTurnEndpoint::TlsTurnEndpoint(ConnectedTurnTransport& turn_ep, const Identity& local_identity, const std::shared_future<tls::DhParams>& dh_params, - dht::crypto::TrustList& trust_list) - : pimpl_ { std::make_unique<Impl>(turn_ep, trust_list) } + std::function<bool(const dht::crypto::Certificate&)>&& cert_check) + : pimpl_ { std::make_unique<Impl>(turn_ep, std::move(cert_check)) } { // Add TLS over TURN tls::TlsSession::TlsSessionCallbacks tls_cbs = { @@ -210,7 +204,7 @@ TlsTurnEndpoint::write(const ValueType* buf, std::size_t len, std::error_code& e return pimpl_->tls->write(buf, len, ec); } -dht::crypto::Certificate& +const dht::crypto::Certificate& TlsTurnEndpoint::peerCertificate() const { return pimpl_->peerCertificate; @@ -335,7 +329,6 @@ TlsSocketEndpoint::Impl::verifyCertificate(gnutls_session_t session) return GNUTLS_E_CERTIFICATE_ERROR; } - // notify GnuTLS to continue handshake normally return GNUTLS_E_SUCCESS; } diff --git a/src/peer_connection.h b/src/peer_connection.h index e7c437dbeeed6408cb6e38c71400395046340a62..8901d5837eb7e87a81e55be2f222f018f96b0b2d 100644 --- a/src/peer_connection.h +++ b/src/peer_connection.h @@ -81,7 +81,7 @@ public: TlsTurnEndpoint(ConnectedTurnTransport& turn, const Identity& local_identity, const std::shared_future<tls::DhParams>& dh_params, - dht::crypto::TrustList& trust_list); + std::function<bool(const dht::crypto::Certificate&)>&& cert_check); ~TlsTurnEndpoint(); void shutdown() override; @@ -100,7 +100,7 @@ public: void connect(); - dht::crypto::Certificate& peerCertificate() const; + const dht::crypto::Certificate& peerCertificate() const; private: class Impl; diff --git a/src/ringdht/p2p.cpp b/src/ringdht/p2p.cpp index 13f14b4e7498973c7125fc6db3bbf378cfc94158..b72176d7297f27fc238f8bea54846eb852c68f30 100644 --- a/src/ringdht/p2p.cpp +++ b/src/ringdht/p2p.cpp @@ -203,8 +203,11 @@ public: private: std::unique_ptr<ConnectedTurnTransport> turn_ep_; std::unique_ptr<TurnTransport> turn_; - std::map<std::shared_ptr<dht::crypto::Certificate>, dht::InfoHash> certMap_; - dht::crypto::TrustList trustedPeers_; + + // key: Stored certificate PublicKey id (normaly it's the DeviceId) + // value: pair of shared_ptr<Certificate> and associated RingId + std::map<dht::InfoHash, std::pair<std::shared_ptr<dht::crypto::Certificate>, dht::InfoHash>> certMap_; + std::map<IpAddr, dht::InfoHash> connectedPeers_; protected: std::map<IpAddr, std::unique_ptr<PeerConnection>> servers_; @@ -223,6 +226,7 @@ private: const std::function<void(PeerConnection*)>&); void turnConnect(); void eventLoop(); + bool validatePeerCertificate(const dht::crypto::Certificate&, dht::InfoHash&); std::future<void> loopFut_; // keep it last member }; @@ -390,6 +394,21 @@ DhtPeerConnector::Impl::turnConnect() } } +/// Find who is connected by using connection certificate +bool +DhtPeerConnector::Impl::validatePeerCertificate(const dht::crypto::Certificate& cert, + dht::InfoHash& peer_h) +{ + const auto& iter = certMap_.find(cert.getId()); + if (iter != std::cend(certMap_)) { + if (iter->second.first->getPacked() == cert.getPacked()) { + peer_h = iter->second.second; + return true; + } + } + return false; +} + /// Negotiate a TLS session over a TURN socket this method does [yoda]. /// At this stage both endpoints has a dedicated TCP connection on each other. void @@ -399,28 +418,23 @@ DhtPeerConnector::Impl::onTurnPeerConnection(const IpAddr& peer_addr) << peer_addr.toString(true, true); auto turn_ep = std::make_unique<ConnectedTurnTransport>(*turn_, peer_addr); - RING_DBG() << account << "[CNX] start TLS session over TURN socket"; - auto tls_ep = std::make_unique<TlsTurnEndpoint>(*turn_ep, account.identity(), - account.dhParams(), trustedPeers_); - tls_ep->connect(); // block until TLS negotiated (throw in case of error) - // Find who is connected by using connection certificate + RING_DBG() << account << "[CNX] start TLS session over TURN socket"; dht::InfoHash peer_h; - auto& connected_cert = tls_ep->peerCertificate(); - for (const auto& item : certMap_) { - if (item.first->getPacked() == connected_cert.getPacked()) { - peer_h = item.second; - break; - } - } + auto tls_ep = std::make_unique<TlsTurnEndpoint>( + *turn_ep, account.identity(), account.dhParams(), + [&, this] (const dht::crypto::Certificate& cert) { return validatePeerCertificate(cert, peer_h); }); - // Not found? Considering this case as an attack! - if (!peer_h) { - RING_WARN() << "[CNX] unknown certificate from ip " << peer_addr.toString(true, true); + // block until TLS is negotiated (must throw in case of error) + try { + tls_ep->connect(); + } catch (...) { + RING_WARN() << "[CNX] TLS connection failure from peer " << peer_addr.toString(true, true); return; } RING_DBG() << account << "[CNX] Accepted TLS-TURN connection from RingID " << peer_h; + connectedPeers_.emplace(peer_addr, tls_ep->peerCertificate().getId()); auto connection = std::make_unique<PeerConnection>(account, peer_addr.toString(), std::move(tls_ep)); connection->attachOutputStream(std::make_shared<FtpServer>(account.getAccountID(), peer_h.toString())); servers_.emplace(peer_addr, std::move(connection)); @@ -437,6 +451,7 @@ DhtPeerConnector::Impl::onTurnPeerDisconnection(const IpAddr& peer_addr) return; RING_WARN() << account << "[CNX] disconnection from peer " << peer_addr.toString(true, true); servers_.erase(iter); + connectedPeers_.erase(peer_addr); } void @@ -465,8 +480,7 @@ DhtPeerConnector::Impl::onTrustedRequestMsg(PeerConnectionMsg&& request, turnConnect(); // start a TURN client connection on first pass, next ones just add new peer cnx handlers // Save peer certificate for later TLS session (MUST BE DONE BEFORE TURN PEER AUTHORIZATION) - certMap_.insert(std::make_pair(cert, peer_h)); - trustedPeers_.add(*cert); + certMap_.emplace(cert->getId(), std::make_pair(cert, peer_h)); for (auto& ip: request.addresses) { try {