diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp index dad52a690ac3cdb3c3110dc97da7c865adfedafc..6cc5c2ca83d287522e2c0b72ab1bd6938df1882a 100644 --- a/src/ringdht/ringaccount.cpp +++ b/src/ringdht/ringaccount.cpp @@ -1899,7 +1899,7 @@ RingAccount::doRegister_() dht_.put(h, announce_, dht::DoneCallback{}, {}, true); dht_.listen<DeviceAnnouncement>(h, [shared](DeviceAnnouncement&& dev) { shared->findCertificate(dev.dev, [shared](const std::shared_ptr<dht::crypto::Certificate> crt) { - shared->foundKnownDevice(crt); + shared->foundAccountDevice(crt); }); return true; }); @@ -1919,44 +1919,16 @@ RingAccount::doRegister_() if (msg.from == this_.dht_.getId()) return true; - RING_WARN("ICE candidate from %s.", msg.from.toString().c_str()); - - // quick check in case we already explicilty banned this public key - auto trustStatus = this_.trust_.getCertificateStatus(msg.from.toString()); - if (trustStatus == tls::TrustStore::PermissionStatus::BANNED) { - RING_WARN("Discarding incoming DHT call request from banned peer %s", msg.from.toString().c_str()); - return true; - } - auto res = this_.treatedCalls_.insert(msg.id); this_.saveTreatedCalls(); if (!res.second) return true; - this_.findCertificate( msg.from, - [shared, msg, trustStatus](const std::shared_ptr<dht::crypto::Certificate> cert) mutable { - auto& this_ = *shared; - if (not this_.dhtPublicInCalls_ and trustStatus != tls::TrustStore::PermissionStatus::ALLOWED) { - if (!cert or cert->getId() != msg.from) { - RING_WARN("Can't find certificate of %s for incoming call.", - msg.from.toString().c_str()); - return; - } - - tls::CertificateStore::instance().pinCertificate(cert); - - auto& this_ = *shared; - if (!this_.trust_.isAllowed(*cert)) { - RING_WARN("Discarding incoming DHT call from untrusted peer %s.", - msg.from.toString().c_str()); - return; - } - } else if (not this_.dhtPublicInCalls_ - or trustStatus == tls::TrustStore::PermissionStatus::BANNED) { - return; - } + RING_WARN("ICE candidate from %s.", msg.from.toString().c_str()); - // Peer certificate trusted... go ahead + this_.onPeerMessage(msg.from, [shared, msg](const std::shared_ptr<dht::crypto::Certificate>& cert, + const dht::InfoHash& /*account*/) mutable + { shared->incomingCall(std::move(msg), cert); }); return true; @@ -2007,7 +1979,7 @@ RingAccount::doRegister_() RING_WARN("Can't find certificate for device %s", sync.from.toString().c_str()); return; } - if (not shared->foundKnownDevice(cert)) + if (not shared->foundAccountDevice(cert)) return; shared->onReceiveDeviceSync(std::move(sync)); }); @@ -2025,43 +1997,15 @@ RingAccount::doRegister_() if (!res.second) return true; this_.saveTreatedMessages(); - - // quick check in case we already explicilty banned this public key - auto trustStatus = this_.trust_.getCertificateStatus(v.from.toString()); - if (trustStatus == tls::TrustStore::PermissionStatus::BANNED) { - RING_WARN("Discarding incoming DHT message from banned peer %s", v.from.toString().c_str()); - return true; - } - - this_.findCertificate( v.from, - [shared, v, trustStatus, inboxDeviceKey](const std::shared_ptr<dht::crypto::Certificate> cert) mutable { - auto& this_ = *shared; - if (not this_.dhtPublicInCalls_ and trustStatus != tls::TrustStore::PermissionStatus::ALLOWED) { - if (!cert or cert->getId() != v.from) { - RING_WARN("Can't find certificate of %s for incoming message.", v.from.toString().c_str()); - return; - } - - tls::CertificateStore::instance().pinCertificate(cert); - - auto& this_ = *shared; - if (!this_.trust_.isAllowed(*cert)) { - RING_WARN("Discarding incoming DHT message from untrusted peer %s.", v.from.toString().c_str()); - return; - } - } else if (not this_.dhtPublicInCalls_ or trustStatus == tls::TrustStore::PermissionStatus::BANNED) { - RING_WARN("Discarding incoming DHT message from untrusted or banned peer %s.", v.from.toString().c_str()); - return; - } - - auto from_acc_id = cert ? (cert->issuer ? cert->issuer->getId().toString() : cert->getId().toString()) : v.from.toString(); - + this_.onPeerMessage(v.from, [shared, v, inboxDeviceKey](const std::shared_ptr<dht::crypto::Certificate>&, + const dht::InfoHash& peer_account) + { auto now = system_clock::to_time_t(system_clock::now()); std::map<std::string, std::string> payloads = {{"text/plain", utf8_make_valid(v.msg)}}; - shared->onTextMessage(from_acc_id, payloads); + shared->onTextMessage(peer_account.toString(), payloads); RING_DBG("Sending message confirmation %" PRIu64, v.id); - this_.dht_.putEncrypted(inboxDeviceKey, + shared->dht_.putEncrypted(inboxDeviceKey, v.from, dht::ImMessage(v.id, std::string(), now)); }); @@ -2075,12 +2019,52 @@ RingAccount::doRegister_() } } + +void +RingAccount::onPeerMessage(const dht::InfoHash& peer_device, std::function<void(const std::shared_ptr<dht::crypto::Certificate>& crt, const dht::InfoHash& peer_account)> cb) +{ + // quick check in case we already explicilty banned this public key + auto trustStatus = trust_.getCertificateStatus(peer_device.toString()); + if (trustStatus == tls::TrustStore::PermissionStatus::BANNED) { + RING_WARN("Discarding message from banned peer %s", peer_device.toString().c_str()); + return; + } + + auto shared = std::static_pointer_cast<RingAccount>(shared_from_this()); + findCertificate(peer_device, + [shared, peer_device, trustStatus, cb](const std::shared_ptr<dht::crypto::Certificate>& cert) { + auto& this_ = *shared; + + dht::InfoHash peer_account_id; + if (not this_.foundPeerDevice(cert, peer_account_id)) { + RING_WARN("Discarding message from invalid peer certificate %s.", peer_device.toString().c_str()); + return; + } + + if (not this_.dhtPublicInCalls_ and trustStatus != tls::TrustStore::PermissionStatus::ALLOWED) { + if (!cert or cert->getId() != peer_device) { + RING_WARN("Can't find certificate of %s for incoming message.", peer_device.toString().c_str()); + return; + } + + auto& this_ = *shared; + if (!this_.trust_.isAllowed(*cert)) { + RING_WARN("Discarding message from untrusted peer %s.", peer_device.toString().c_str()); + return; + } + } else if (not this_.dhtPublicInCalls_ or trustStatus == tls::TrustStore::PermissionStatus::BANNED) { + RING_WARN("Discarding message from untrusted or banned peer %s.", peer_device.toString().c_str()); + return; + } + + cb(cert, peer_account_id); + }); +} + void RingAccount::incomingCall(dht::IceCandidates&& msg, std::shared_ptr<dht::crypto::Certificate> from_cert) { - auto from = msg.from.toString(); - RING_WARN("ICE incoming from DHT peer %s\n%s", from.c_str(), - std::string(msg.ice_data.cbegin(), msg.ice_data.cend()).c_str()); + RING_WARN("ICE incoming from DHT peer %s", msg.from.toString().c_str()); auto call = Manager::instance().callFactory.newCall<SIPCall, RingAccount>(*this, Manager::instance().getNewCallID(), Call::CallType::INCOMING); auto ice = createIceTransport(("sip:"+call->getCallId()).c_str(), ICE_COMPONENTS, false, getIceOptions()); @@ -2111,7 +2095,7 @@ RingAccount::incomingCall(dht::IceCandidates&& msg, std::shared_ptr<dht::crypto: } bool -RingAccount::foundKnownDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, const std::string& name) +RingAccount::foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, const std::string& name) { if (not crt) return false; @@ -2147,6 +2131,36 @@ RingAccount::foundKnownDevice(const std::shared_ptr<dht::crypto::Certificate>& c return true; } +bool +RingAccount::foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, dht::InfoHash& account_id) +{ + if (not crt) + return false; + + auto top_issuer = crt; + while (top_issuer->issuer) + top_issuer = top_issuer->issuer; + + // Device certificate can't be self-signed + if (top_issuer == crt) { + RING_WARN("[Account %s] Found invalid peer device: %s", getAccountID().c_str(), crt->getId().toString().c_str()); + return false; + } + + // Check peer certificate chain + // Trust store with top issuer as the only CA + tls::TrustStore peer_trust; + peer_trust.setCertificateStatus(top_issuer, tls::TrustStore::PermissionStatus::ALLOWED, false); + + if (not peer_trust.isAllowed(*crt)) { + RING_WARN("[Account %s] Found invalid peer device: %s", getAccountID().c_str(), crt->getId().toString().c_str()); + return false; + } + + account_id = crt->issuer->getId(); + return true; +} + void RingAccount::replyToIncomingIceMsg(std::shared_ptr<SIPCall> call, std::shared_ptr<IceTransport> ice, @@ -2688,7 +2702,7 @@ RingAccount::onReceiveDeviceSync(DeviceSync&& sync) findCertificate(d.first, [shared,d](const std::shared_ptr<dht::crypto::Certificate> crt) { if (not crt) return; - shared->foundKnownDevice(crt, d.second); + shared->foundAccountDevice(crt, d.second); }); } it->second.last_sync = sync_date; diff --git a/src/ringdht/ringaccount.h b/src/ringdht/ringaccount.h index a03e9086efa908e1c58096eeadf32df2bfec81fa..26680b58ee4131b03caf5a0a61ba734a6aacd356 100644 --- a/src/ringdht/ringaccount.h +++ b/src/ringdht/ringaccount.h @@ -370,7 +370,26 @@ class RingAccount : public SIPAccountBase { */ bool SIPStartCall(const std::shared_ptr<SIPCall>& call, IpAddr target); - bool foundKnownDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, const std::string& name = {}); + /** + * Inform that a potential account device have been found. + * Returns true if the device have been validated to be part of this account + */ + bool foundAccountDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, const std::string& name = {}); + + /** + * Inform that a potential peer device have been found. + * Returns true only if the device certificate is a valid Ring device certificate. + * In that case (true is returned) the account_id parameter is set to the peer account ID. + */ + bool foundPeerDevice(const std::shared_ptr<dht::crypto::Certificate>& crt, dht::InfoHash& account_id); + + /** + * Check that a peer is authorised to talk to us. + * If everything is in order, calls the callback with the + * peer certificate chain (down to the peer device certificate), + * and the peer account id. + */ + void onPeerMessage(const dht::InfoHash& peer_device, std::function<void(const std::shared_ptr<dht::crypto::Certificate>& crt, const dht::InfoHash& account_id)>); /** * Maps require port via UPnP