From: Remi Gacogne Date: Mon, 8 Jul 2019 13:48:09 +0000 (+0200) Subject: dnsdist: Accept more than one certificate in `addDNSCryptBind()` X-Git-Tag: dnsdist-1.4.0-rc1~50^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=37b6d73dfd798d667f1eff6192c85e64dae3d2c7;p=pdns dnsdist: Accept more than one certificate in `addDNSCryptBind()` --- diff --git a/pdns/dnscrypt.cc b/pdns/dnscrypt.cc index 4cf126ce3..c18d29e2c 100644 --- a/pdns/dnscrypt.cc +++ b/pdns/dnscrypt.cc @@ -123,11 +123,11 @@ DNSCryptQuery::~DNSCryptQuery() } #endif /* HAVE_CRYPTO_BOX_EASY_AFTERNM */ -DNSCryptContext::DNSCryptContext(const std::string& pName, const std::string& certFile, const std::string& keyFile): providerName(pName) +DNSCryptContext::DNSCryptContext(const std::string& pName, const std::vector& certKeys): d_certKeyPaths(certKeys), providerName(pName) { pthread_rwlock_init(&d_lock, 0); - loadNewCertificate(certFile, keyFile); + reloadCertificates(); } DNSCryptContext::DNSCryptContext(const std::string& pName, const DNSCryptCert& certificate, const DNSCryptPrivateKey& pKey): providerName(pName) @@ -283,12 +283,12 @@ std::string DNSCryptContext::certificateDateToStr(uint32_t date) return string(buf); } -void DNSCryptContext::addNewCertificate(const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, bool active, bool reload) +void DNSCryptContext::addNewCertificate(std::shared_ptr& newCert, bool reload) { WriteLock w(&d_lock); - for (auto pair : certs) { - if (pair->cert.getSerial() == newCert.getSerial()) { + for (auto pair : d_certs) { + if (pair->cert.getSerial() == newCert->cert.getSerial()) { if (reload) { /* on reload we just assume that this is the same certificate */ return; @@ -299,37 +299,56 @@ void DNSCryptContext::addNewCertificate(const DNSCryptCert& newCert, const DNSCr } } + d_certs.push_back(newCert); +} + +void DNSCryptContext::addNewCertificate(const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, bool active, bool reload) +{ auto pair = std::make_shared(); pair->cert = newCert; pair->privateKey = newKey; computePublicKeyFromPrivate(pair->privateKey, pair->publicKey); pair->active = active; - certs.push_back(pair); + + addNewCertificate(pair, reload); } -void DNSCryptContext::loadNewCertificate(const std::string& certFile, const std::string& keyFile, bool active, bool reload) +std::shared_ptr DNSCryptContext::loadCertificatePair(const std::string& certFile, const std::string& keyFile) { - DNSCryptCert newCert; - DNSCryptPrivateKey newPrivateKey; - - loadCertFromFile(certFile, newCert); - newPrivateKey.loadFromFile(keyFile); + auto pair = std::make_shared(); + loadCertFromFile(certFile, pair->cert); + pair->privateKey.loadFromFile(keyFile); + pair->active = true; + computePublicKeyFromPrivate(pair->privateKey, pair->publicKey); + return pair; +} - addNewCertificate(newCert, newPrivateKey, active, reload); - certificatePath = certFile; - keyPath = keyFile; +void DNSCryptContext::loadNewCertificate(const std::string& certFile, const std::string& keyFile, bool active, bool reload) +{ + auto newPair = DNSCryptContext::loadCertificatePair(certFile, keyFile); + newPair->active = active; + addNewCertificate(newPair, reload); + d_certKeyPaths.push_back({certFile, keyFile}); } -void DNSCryptContext::reloadCertificate() +void DNSCryptContext::reloadCertificates() { - loadNewCertificate(certificatePath, keyPath, true, true); + std::vector> newCerts; + for (const auto& pair : d_certKeyPaths) { + newCerts.push_back(DNSCryptContext::loadCertificatePair(pair.cert, pair.key)); + } + + { + WriteLock w(&d_lock); + d_certs = std::move(newCerts); + } } void DNSCryptContext::markActive(uint32_t serial) { WriteLock w(&d_lock); - for (auto pair : certs) { + for (auto pair : d_certs) { if (pair->active == false && pair->cert.getSerial() == serial) { pair->active = true; return; @@ -342,7 +361,7 @@ void DNSCryptContext::markInactive(uint32_t serial) { WriteLock w(&d_lock); - for (auto pair : certs) { + for (auto pair : d_certs) { if (pair->active == true && pair->cert.getSerial() == serial) { pair->active = false; return; @@ -355,9 +374,9 @@ void DNSCryptContext::removeInactiveCertificate(uint32_t serial) { WriteLock w(&d_lock); - for (auto it = certs.begin(); it != certs.end(); ) { + for (auto it = d_certs.begin(); it != d_certs.end(); ) { if ((*it)->active == false && (*it)->cert.getSerial() == serial) { - it = certs.erase(it); + it = d_certs.erase(it); return; } else { it++; @@ -406,7 +425,7 @@ void DNSCryptContext::getCertificateResponse(time_t now, const DNSName& qname, u dh->rcode = RCode::NoError; ReadLock r(&d_lock); - for (const auto pair : certs) { + for (const auto pair : d_certs) { if (!pair->active || !pair->cert.isValid(now)) { continue; } @@ -427,7 +446,7 @@ bool DNSCryptContext::magicMatchesAPublicKey(DNSCryptQuery& query, time_t now) const unsigned char* magic = query.getClientMagic(); ReadLock r(&d_lock); - for (const auto& pair : certs) { + for (const auto& pair : d_certs) { if (pair->cert.isValid(now) && memcmp(magic, pair->cert.signedData.clientMagic, DNSCRYPT_CLIENT_MAGIC_SIZE) == 0) { query.setCertificatePair(pair); return true; diff --git a/pdns/dnscrypt.hh b/pdns/dnscrypt.hh index 86ddcd201..3026ec178 100644 --- a/pdns/dnscrypt.hh +++ b/pdns/dnscrypt.hh @@ -254,16 +254,23 @@ public: static DNSCryptExchangeVersion getExchangeVersion(const unsigned char esVersion[sizeof(DNSCryptCert::esVersion)]); static DNSCryptExchangeVersion getExchangeVersion(const DNSCryptCert& cert); - DNSCryptContext(const std::string& pName, const std::string& certFile, const std::string& keyFile); + struct CertKeyPaths + { + std::string cert; + std::string key; + }; + + DNSCryptContext(const std::string& pName, const std::vector& certKeys); DNSCryptContext(const std::string& pName, const DNSCryptCert& certificate, const DNSCryptPrivateKey& pKey); - void reloadCertificate(); + void reloadCertificates(); void loadNewCertificate(const std::string& certFile, const std::string& keyFile, bool active=true, bool reload=false); void addNewCertificate(const DNSCryptCert& newCert, const DNSCryptPrivateKey& newKey, bool active=true, bool reload=false); + void markActive(uint32_t serial); void markInactive(uint32_t serial); void removeInactiveCertificate(uint32_t serial); - std::vector> getCertificates() { return certs; }; + std::vector> getCertificates() { return d_certs; }; const DNSName& getProviderName() const { return providerName; } int encryptQuery(char* query, uint16_t queryLen, uint16_t querySize, const unsigned char clientPublicKey[DNSCRYPT_PUBLIC_KEY_SIZE], const DNSCryptPrivateKey& clientPrivateKey, const unsigned char clientNonce[DNSCRYPT_NONCE_SIZE / 2], bool tcp, uint16_t* encryptedResponseLen, const std::shared_ptr& cert) const; @@ -273,12 +280,14 @@ public: private: static void computePublicKeyFromPrivate(const DNSCryptPrivateKey& privK, unsigned char pubK[DNSCRYPT_PUBLIC_KEY_SIZE]); static void loadCertFromFile(const std::string&filename, DNSCryptCert& dest); + static std::shared_ptr loadCertificatePair(const std::string& certFile, const std::string& keyFile); + + void addNewCertificate(std::shared_ptr& newCert, bool reload=false); pthread_rwlock_t d_lock; - std::vector> certs; + std::vector> d_certs; + std::vector d_certKeyPaths; DNSName providerName; - std::string certificatePath; - std::string keyPath; }; bool generateDNSCryptCertificate(const std::string& providerPrivateKeyFile, uint32_t serial, time_t begin, time_t end, DNSCryptExchangeVersion version, DNSCryptCert& certOut, DNSCryptPrivateKey& keyOut); diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index 322ae318e..e642f929a 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -537,7 +537,7 @@ void setupLuaBindings(bool client) if (ctx != nullptr) { size_t idx = 1; - boost::format fmt("%1$-3d %|5t|%2$-8d %|10t|%3$-2d %|20t|%4$-21.21s %|41t|%5$-21.21s"); + boost::format fmt("%1$-3d %|5t|%2$-8d %|10t|%3$-7d %|20t|%4$-21.21s %|41t|%5$-21.21s"); ret << (fmt % "#" % "Serial" % "Version" % "From" % "To" ) << endl; for (auto pair : ctx->getCertificates()) { diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index 883e5194b..10f9b4212 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -1090,7 +1090,7 @@ void setupLuaConfig(bool client) } }); - g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, const std::string& certFile, const std::string keyFile, boost::optional vars) { + g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, boost::variant>> certFiles, boost::variant>> keyFiles, boost::optional vars) { if (g_configurationDone) { g_outputBuffer="addDNSCryptBind cannot be used at runtime!\n"; return; @@ -1100,11 +1100,37 @@ void setupLuaConfig(bool client) int tcpFastOpenQueueSize = 0; std::string interface; std::set cpus; + std::vector certKeys; parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus); + if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) { + auto certFile = boost::get(certFiles); + auto keyFile = boost::get(keyFiles); + certKeys.push_back({certFile, keyFile}); + } + else if (certFiles.type() == typeid(std::vector>) && keyFiles.type() == typeid(std::vector>)) { + auto certFilesVect = boost::get>>(certFiles); + auto keyFilesVect = boost::get>>(keyFiles); + if (certFilesVect.size() == keyFilesVect.size()) { + for (size_t idx = 0; idx < certFilesVect.size(); idx++) { + certKeys.push_back({certFilesVect.at(idx).second, keyFilesVect.at(idx).second}); + } + } + else { + errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind!"); + g_outputBuffer="Error, mismatching number of certificates and keys in call to addDNSCryptBind()!"; + return; + } + } + else { + errlog("Error, mismatching number of certificates and keys in call to addDNSCryptBind()!"); + g_outputBuffer="Error, mismatching number of certificates and keys in call to addDNSCryptBind()!"; + return; + } + try { - auto ctx = std::make_shared(providerName, certFile, keyFile); + auto ctx = std::make_shared(providerName, certKeys); /* UDP */ auto cs = std::unique_ptr(new ClientState(ComboAddress(addr, 443), false, reusePort, tcpFastOpenQueueSize, interface, cpus)); @@ -1939,7 +1965,7 @@ void setupLuaConfig(bool client) try { #ifdef HAVE_DNSCRYPT if (frontend->dnscryptCtx) { - frontend->dnscryptCtx->reloadCertificate(); + frontend->dnscryptCtx->reloadCertificates(); } #endif /* HAVE_DNSCRYPT */ #ifdef HAVE_DNS_OVER_TLS diff --git a/pdns/dnsdistdist/docs/reference/dnscrypt.rst b/pdns/dnsdistdist/docs/reference/dnscrypt.rst index d25c6cfce..c012445dc 100644 --- a/pdns/dnsdistdist/docs/reference/dnscrypt.rst +++ b/pdns/dnsdistdist/docs/reference/dnscrypt.rst @@ -1,20 +1,21 @@ DNSCrypt objects and functions ============================== -.. function:: addDNSCryptBind(address, provider, certificate, keyfile[, options]) +.. function:: addDNSCryptBind(address, provider, certFile(s), keyFile(s) [, options]) .. versionchanged:: 1.3.0 ``cpus`` option added. .. versionchanged:: 1.4.0 Removed ``doTCP`` from the options. A listen socket on TCP is always created. + ``certFile(s)`` and ``keyFile(s)`` now accept a list of files. Adds a DNSCrypt listen socket on ``address``. :param string address: The address and port to listen on :param string provider: The provider name for this bind - :param string certificate: Path to the certificate file - :param string keyfile: Path to the key file of the certificate + :param str certFile(s): The path to a X.509 certificate file in PEM format, or a list of paths to such files. + :param str keyFile(s): The path to the private key file corresponding to the certificate, or a list of paths to such files, whose order should match the certFile(s) ones. :param table options: A table with key: value pairs with options (see below) Options: