]> granicus.if.org Git - pdns/commitdiff
dnsdist: Accept more than one certificate in `addDNSCryptBind()`
authorRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 8 Jul 2019 13:48:09 +0000 (15:48 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Mon, 8 Jul 2019 13:48:09 +0000 (15:48 +0200)
pdns/dnscrypt.cc
pdns/dnscrypt.hh
pdns/dnsdist-lua-bindings.cc
pdns/dnsdist-lua.cc
pdns/dnsdistdist/docs/reference/dnscrypt.rst

index 4cf126ce389d6afc7f8bb979670e34c461e458e9..c18d29e2c5e5a14bcc775399b7f254eec17a44ba 100644 (file)
@@ -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<CertKeyPaths>& 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<DNSCryptCertificatePair>& 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<DNSCryptCertificatePair>();
   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<DNSCryptCertificatePair> 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<DNSCryptCertificatePair>();
+  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<std::shared_ptr<DNSCryptCertificatePair>> 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;
index 86ddcd20159fdad28b485fd3b97bafa7dab323bc..3026ec17853d6fbe2403b53a893ee93ca663ff5b 100644 (file)
@@ -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<CertKeyPaths>& 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<std::shared_ptr<DNSCryptCertificatePair>> getCertificates() { return certs; };
+  std::vector<std::shared_ptr<DNSCryptCertificatePair>> 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<DNSCryptCert>& 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<DNSCryptCertificatePair> loadCertificatePair(const std::string& certFile, const std::string& keyFile);
+
+  void addNewCertificate(std::shared_ptr<DNSCryptCertificatePair>& newCert, bool reload=false);
 
   pthread_rwlock_t d_lock;
-  std::vector<std::shared_ptr<DNSCryptCertificatePair>> certs;
+  std::vector<std::shared_ptr<DNSCryptCertificatePair>> d_certs;
+  std::vector<CertKeyPaths> 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);
index 322ae318e3244fb60bcab8d646e175deb3e42345..e642f929a8e69efbe1b83ad9e37a26b0e7fab246 100644 (file)
@@ -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()) {
index 883e5194b9b7c4f0100638dc446bd7e78f0c22f9..10f9b4212c9d4936a26da014c2d444be0b537adf 100644 (file)
@@ -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<localbind_t> vars) {
+  g_lua.writeFunction("addDNSCryptBind", [](const std::string& addr, const std::string& providerName, boost::variant<std::string, std::vector<std::pair<int, std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles, boost::optional<localbind_t> 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<int> cpus;
+      std::vector<DNSCryptContext::CertKeyPaths> certKeys;
 
       parseLocalBindVars(vars, reusePort, tcpFastOpenQueueSize, interface, cpus);
 
+      if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) {
+        auto certFile = boost::get<std::string>(certFiles);
+        auto keyFile = boost::get<std::string>(keyFiles);
+        certKeys.push_back({certFile, keyFile});
+      }
+      else if (certFiles.type() == typeid(std::vector<std::pair<int,std::string>>) && keyFiles.type() == typeid(std::vector<std::pair<int,std::string>>)) {
+        auto certFilesVect = boost::get<std::vector<std::pair<int,std::string>>>(certFiles);
+        auto keyFilesVect = boost::get<std::vector<std::pair<int,std::string>>>(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<DNSCryptContext>(providerName, certFile, keyFile);
+        auto ctx = std::make_shared<DNSCryptContext>(providerName, certKeys);
 
         /* UDP */
         auto cs = std::unique_ptr<ClientState>(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
index d25c6cfce888d57acf0b9727a807e88648b1560d..c012445dc35d41e029d2a8688d1eb7f4a5c15146 100644 (file)
@@ -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: