]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Implement published and unpublished dnskeys to allow algorith rollovers. 8177/head
authorRobin Geuze <robing@transip.nl>
Thu, 8 Aug 2019 18:03:28 +0000 (20:03 +0200)
committerRobin Geuze <robing@transip.nl>
Tue, 28 Jan 2020 13:16:30 +0000 (14:16 +0100)
62 files changed:
docs/backends/generic-sql.rst
docs/backends/lua2.rst
docs/backends/remote.rst
docs/http-api/swagger/authoritative-api-swagger.yaml
docs/manpages/pdnsutil.1.rst
modules/bindbackend/bindbackend2.hh
modules/bindbackend/binddnssec.cc
modules/geoipbackend/geoipbackend.cc
modules/geoipbackend/geoipbackend.hh
modules/gmysqlbackend/4.2.0_to_4.3.0_schema.mysql.sql [new file with mode: 0644]
modules/gmysqlbackend/gmysqlbackend.cc
modules/gmysqlbackend/schema.mysql.sql
modules/godbcbackend/4.2.0_to_4.3.0_schema.mssql.sql [new file with mode: 0644]
modules/godbcbackend/godbcbackend.cc
modules/godbcbackend/schema.mssql.sql
modules/gpgsqlbackend/4.2.0_to_4.3.0_schema.pgsql.sql [new file with mode: 0644]
modules/gpgsqlbackend/gpgsqlbackend.cc
modules/gpgsqlbackend/schema.pgsql.sql
modules/gsqlite3backend/4.2.0_to_4.3.0_schema.sqlite3.sql [new file with mode: 0644]
modules/gsqlite3backend/gsqlite3backend.cc
modules/gsqlite3backend/schema.sqlite3.sql
modules/lmdbbackend/lmdbbackend.cc
modules/lmdbbackend/lmdbbackend.hh
modules/lua2backend/lua2api2.hh
modules/remotebackend/example.rb
modules/remotebackend/httpconnector.cc
modules/remotebackend/regression-tests/backend.rb
modules/remotebackend/regression-tests/dnsbackend.rb
modules/remotebackend/regression-tests/test-schema.sql
modules/remotebackend/remotebackend.cc
modules/remotebackend/remotebackend.hh
modules/remotebackend/test-remotebackend-keys.hh
modules/remotebackend/test-remotebackend.cc
modules/remotebackend/unittest.rb
modules/remotebackend/unittest_http.rb
pdns/backends/gsql/gsqlbackend.cc
pdns/backends/gsql/gsqlbackend.hh
pdns/bind-dnssec.4.2.0_to_4.3.0_schema.sqlite3.sql [new file with mode: 0644]
pdns/bind-dnssec.schema.sqlite3.sql
pdns/dbdnsseckeeper.cc
pdns/dnsbackend.hh
pdns/dnsseckeeper.hh
pdns/packethandler.cc
pdns/pdnsutil.cc
pdns/tcpreceiver.cc
pdns/ueberbackend.cc
pdns/ueberbackend.hh
pdns/ws-auth.cc
regression-tests.api/test_Zones.py
regression-tests.api/test_cryptokeys.py
regression-tests.nobackend/tinydns-data-check/expected_result
regression-tests/backends/bind-master
regression-tests/backends/godbc_sqlite3-master
regression-tests/backends/gsql-common
regression-tests/backends/lmdb-master
regression-tests/named.conf
regression-tests/tests/cryptokeys/command [new file with mode: 0755]
regression-tests/tests/cryptokeys/description [new file with mode: 0644]
regression-tests/tests/cryptokeys/expected_result.dnssec [new file with mode: 0644]
regression-tests/tests/cryptokeys/skip.nodnssec [new file with mode: 0644]
regression-tests/tests/verify-dnssec-zone/command
regression-tests/zones/.gitignore

index 78491e5ac35ca24eea796852cf7eb6d131d34ff6..66d17725114d01339656b527f4d1925c0891e67f 100644 (file)
@@ -302,6 +302,8 @@ Domain and zone manipulation
 -  ``activate-domain-key-query``: Called to set a cryptokey to active.
 -  ``deactivate-domain-key-query``: Called to set a cryptokey to
    inactive.
+-  ``publish-domain-key-query``: Called to set a cryptokey to published.
+-  ``unpublish-domain-key-query``: Called to set a cryptokey to unpublished.
 -  ``clear-domain-all-keys-query``: Called to remove all DNSSEC keys for
    a zone.
 -  ``remove-domain-key-query``: Called to remove a crypto key.
index f08815de2f363e9c6d439e98b2a063ee0cdd9585..07019467cb6203d9bb1c3b5b439af8643e9e86d0 100644 (file)
@@ -156,6 +156,7 @@ OUTPUT:
  - int id - Key ID
  - int flags - Key flags
  - bool active - Is key active
+ - bool published - Is key published
  - string content - Key itself
 
 NOTES:
index 6cb2653cc10069508698a12cf90e540440dbc582..81c946ac55e6db8c31c37d3160beee8114c8c075 100644 (file)
@@ -467,14 +467,14 @@ Response:
 ~~~~~~~~~~~~~~~~~
 
 Retrieves any keys of kind. The id, flags are unsigned integers, and
-active is boolean. Content must be valid key record in format that
-PowerDNS understands. You are encouraged to implement :ref:`the section
-called "addDomainKey" <remote-adddomainkey>`, as you can use
+active and published are boolean. Content must be valid key record in format
+that PowerDNS understands. You are encouraged to implement :ref:`the
+section called "addDomainKey" <remote-adddomainkey>`, as you can use
 :doc:`../manpages/pdnsutil.1` to provision keys.
 
 -  Mandatory: for DNSSEC
 -  Parameters: name, kind
--  Reply: array of ``id, flags, active, content``
+-  Reply: array of ``id, flags, active, published, content``
 
 Example JSON/RPC
 ''''''''''''''''
@@ -489,7 +489,7 @@ Response:
 
 .. code-block:: json
 
-    {"result":[{"id":1,"flags":256,"active":true,"content":"Private-key-format: v1.2
+    {"result":[{"id":1,"flags":256,"active":true,"published":true,"content":"Private-key-format: v1.2
     Algorithm: 8 (RSASHA256)
     Modulus: r+vmQll38ndQqNSCx9eqRBUbSOLcH4PZFX824sGhY2NSQChqt1G4ZfndzRwgjXMUwiE7GkkqU2Vbt/g4iP67V/+MYecMV9YHkCRnEzb47nBXvs9JCf8AHMCnma567GQjPECh4HevPE9wmcOfpy/u7UN1oHKSKRWuZJadUwcjbp8=
     PublicExponent: AQAB
@@ -516,7 +516,7 @@ Response:
     HTTP/1.1 200 OK
     Content-Type: text/javascript; charset=utf-8
 
-    {"result":[{"id":1,"flags":256,"active":true,"content":"Private-key-format: v1.2
+    {"result":[{"id":1,"flags":256,"active":true,"published":true,"content":"Private-key-format: v1.2
     Algorithm: 8 (RSASHA256)
     Modulus: r+vmQll38ndQqNSCx9eqRBUbSOLcH4PZFX824sGhY2NSQChqt1G4ZfndzRwgjXMUwiE7GkkqU2Vbt/g4iP67V/+MYecMV9YHkCRnEzb47nBXvs9JCf8AHMCnma567GQjPECh4HevPE9wmcOfpy/u7UN1oHKSKRWuZJadUwcjbp8=
     PublicExponent: AQAB
@@ -535,7 +535,7 @@ Response:
 Adds key into local storage. See :ref:`remote-getdomainkeys` for more information.
 
 -  Mandatory: No
--  Parameters: name, key=\ ``<flags,active,content>``, id
+-  Parameters: name, key=\ ``<flags,active,published,content>``, id
 -  Reply: true for success, false for failure
 
 Example JSON/RPC
@@ -545,7 +545,7 @@ Query:
 
 .. code-block:: json
 
-    {"method":"adddomainkey", "parameters":{"key":{"id":1,"flags":256,"active":true,"content":"Private-key-format: v1.2
+    {"method":"adddomainkey", "parameters":{"key":{"id":1,"flags":256,"active":true,"published":true,"content":"Private-key-format: v1.2
     Algorithm: 8 (RSASHA256)
     Modulus: r+vmQll38ndQqNSCx9eqRBUbSOLcH4PZFX824sGhY2NSQChqt1G4ZfndzRwgjXMUwiE7GkkqU2Vbt/g4iP67V/+MYecMV9YHkCRnEzb47nBXvs9JCf8AHMCnma567GQjPECh4HevPE9wmcOfpy/u7UN1oHKSKRWuZJadUwcjbp8=
     PublicExponent: AQAB
@@ -573,7 +573,7 @@ Query:
     Content-Type: application/x-www-form-urlencoded
     Content-Length: 965
 
-    flags=256&active=1&content=Private-key-format: v1.2
+    flags=256&active=1&published=1&content=Private-key-format: v1.2
     Algorithm: 8 (RSASHA256)
     Modulus: r+vmQll38ndQqNSCx9eqRBUbSOLcH4PZFX824sGhY2NSQChqt1G4ZfndzRwgjXMUwiE7GkkqU2Vbt/g4iP67V/+MYecMV9YHkCRnEzb47nBXvs9JCf8AHMCnma567GQjPECh4HevPE9wmcOfpy/u7UN1oHKSKRWuZJadUwcjbp8=
     PublicExponent: AQAB
@@ -719,6 +719,92 @@ Response:
 
     {"result": true}
 
+``publishDomainKey``
+~~~~~~~~~~~~~~~~~~~~
+
+Publish key id for domain name.
+
+-  Mandatory: No
+-  Parameters: name, id
+-  Reply: true for success, false for failure
+
+Example JSON/RPC
+''''''''''''''''
+
+Query:
+
+.. code-block:: json
+
+    {"method":"publishdomainkey","parameters":{"name":"example.com","id":1}}
+
+Response:
+
+.. code-block:: json
+
+    {"result": true}
+
+Example HTTP/RPC
+''''''''''''''''
+
+Query:
+
+.. code-block:: http
+
+    POST /dnsapi/publishdomainkey/example.com/1 HTTP/1.1
+
+Response:
+
+.. code-block:: http
+
+    HTTP/1.1 200 OK
+    Content-Type: text/javascript; utf-8
+
+    {"result": true}
+
+
+``unpublishDomainKey``
+~~~~~~~~~~~~~~~~~~~~~~
+
+Unpublish key id for domain name.
+
+-  Mandatory: No
+-  Parameters: name, id
+-  Reply: true for success, false for failure
+
+Example JSON/RPC
+''''''''''''''''
+
+Query:
+
+.. code-block:: json
+
+    {"method":"unpublishdomainkey","parameters":{"name":"example.com","id":1}}
+
+Response:
+
+.. code-block:: json
+
+    {"result": true}
+
+Example HTTP/RPC
+''''''''''''''''
+
+Query:
+
+.. code-block:: http
+
+    POST /dnsapi/unpublishdomainkey/example.com/1 HTTP/1.1
+
+Response:
+
+.. code-block:: http
+
+    HTTP/1.1 200 OK
+    Content-Type: text/javascript; utf-8
+
+    {"result": true}
+
+
 ``getTSIGKey``
 ~~~~~~~~~~~~~~
 
index 86bc3921bee779abf6e40a5a0b5d310330f8aa5d..6183e83d9df927952e722d72a801a64d995c6a61 100644 (file)
@@ -1250,6 +1250,9 @@ definitions:
       active:
         type: boolean
         description: 'Whether or not the key is in active use'
+      published:
+        type: boolean
+        descriptioon: 'Whether or not the DNSKEY record is published in the zone'
       dnskey:
         type: string
         description: 'The DNSKEY record for this key'
index ac2ee9b985b11fe27d997027451b965f88fd4707..b8a3baa596134429841dd8e86f28bdd453f309b1 100644 (file)
@@ -48,11 +48,13 @@ algorithms are supported:
 
 activate-zone-key *ZONE* *KEY-ID*
     Activate a key with id *KEY-ID* within a zone called *ZONE*.
-add-zone-key *ZONE* {**KSK**,\ **ZSK**} [**active**,\ **inactive**] *KEYBITS* *ALGORITHM*
+add-zone-key *ZONE* {**KSK**,\ **ZSK**} [**active**,\ **inactive**] [**published**,\ **unpublished**] *KEYBITS* *ALGORITHM*
     Create a new key for zone *ZONE*, and make it a KSK or a ZSK, with
     the specified algorithm. The key is inactive by default, set it to
-    **active** to immediately use it to sign *ZONE*. Prints the id of
-    the added key.
+    **active** to immediately use it to sign *ZONE*. The key is published
+    in the zone by default, set it to **unpublished** to keep it from
+    being returned in a DNSKEY query, which is useful for algorithm
+    rollovers. Prints the id of the added key.
 create-bind-db *FILE*
     Create DNSSEC database (sqlite3) at *FILE* for the BIND backend.
     Remember to set ``bind-dnssec-db=*FILE*`` in your ``pdns.conf``.
@@ -81,6 +83,8 @@ import-zone-key *ZONE* *FILE* {**KSK**,\ **ZSK**}
     format used is compatible with BIND and NSD/LDNS. **KSK** or **ZSK**
     specifies the flags this key should have on import. Prints the id of
     the added key.
+publish-zone-key *ZONE* *KEY-ID*
+    Publish the key with id *KEY-ID* within a zone called *ZONE*.
 remove-zone-key *ZONE* *KEY-ID*
     Remove a key with id *KEY-ID* from a zone called *ZONE*.
 set-nsec3 *ZONE* ['*HASH-ALGORITHM* *FLAGS* *ITERATIONS* *SALT*'] [**narrow**]
@@ -103,6 +107,8 @@ set-nsec3 *ZONE* ['*HASH-ALGORITHM* *FLAGS* *ITERATIONS* *SALT*'] [**narrow**]
     commandline is: ``pdnsutil set-nsec3 powerdnssec.org '1 1 1 ab' narrow``.
     **WARNING**: If running in RSASHA1 mode (algorithm 5 or 7), switching
     from NSEC to NSEC3 will require a DS update in the parent zone.
+unpublish-zone-key *ZONE* *KEY-ID*
+    Unpublish the key with id *KEY-ID* within a zone called *ZONE*.
 unset-nsec3 *ZONE*
     Converts *ZONE* to NSEC operations. **WARNING**: If running in
     RSASHA1 mode (algorithm 5 or 7), switching from NSEC to NSEC3 will
index 5b6d32a770e008d323bec7d1830c6f169ad516b1..4140b0aa0ba2927370c3f4b8f1e137890398c102 100644 (file)
@@ -200,6 +200,8 @@ public:
   bool addDomainKey(const DNSName& name, const KeyData& key, int64_t& id) override;
   bool activateDomainKey(const DNSName& name, unsigned int id) override;
   bool deactivateDomainKey(const DNSName& name, unsigned int id) override;
+  bool publishDomainKey(const DNSName& name, unsigned int id) override;
+  bool unpublishDomainKey(const DNSName& name, unsigned int id) override;
   bool getTSIGKey(const DNSName& name, DNSName* algorithm, string* content) override;
   bool setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content) override;
   bool deleteTSIGKey(const DNSName& name) override;
@@ -272,6 +274,8 @@ private:
   unique_ptr<SSqlStatement> d_GetLastInsertedKeyIdQuery_stmt;
   unique_ptr<SSqlStatement> d_activateDomainKeyQuery_stmt;
   unique_ptr<SSqlStatement> d_deactivateDomainKeyQuery_stmt;
+  unique_ptr<SSqlStatement> d_publishDomainKeyQuery_stmt;
+  unique_ptr<SSqlStatement> d_unpublishDomainKeyQuery_stmt;
   unique_ptr<SSqlStatement> d_getTSIGKeyQuery_stmt;
   unique_ptr<SSqlStatement> d_setTSIGKeyQuery_stmt;
   unique_ptr<SSqlStatement> d_deleteTSIGKeyQuery_stmt;
index 5df886d56e77a532d9f40a76de091cc4a747474b..0733e8e1ba012ccea363a56d6cf7c2f9358c90c6 100644 (file)
@@ -65,6 +65,12 @@ bool Bind2Backend::activateDomainKey(const DNSName& name, unsigned int id)
 bool Bind2Backend::deactivateDomainKey(const DNSName& name, unsigned int id)
 { return false; }
 
+bool Bind2Backend::publishDomainKey(const DNSName& name, unsigned int id)
+{ return false; }
+
+bool Bind2Backend::unpublishDomainKey(const DNSName& name, unsigned int id)
+{ return false; }
+
 bool Bind2Backend::getTSIGKey(const DNSName& name, DNSName* algorithm, string* content)
 { return false; }
 
@@ -113,12 +119,14 @@ void Bind2Backend::setupStatements()
   d_getDomainMetadataQuery_stmt = d_dnssecdb->prepare("select content from domainmetadata where domain=:domain and kind=:kind",2);
   d_deleteDomainMetadataQuery_stmt = d_dnssecdb->prepare("delete from domainmetadata where domain=:domain and kind=:kind",2);
   d_insertDomainMetadataQuery_stmt = d_dnssecdb->prepare("insert into domainmetadata (domain, kind, content) values (:domain,:kind,:content)",3);
-  d_getDomainKeysQuery_stmt = d_dnssecdb->prepare("select id,flags, active, content from cryptokeys where domain=:domain",1);
+  d_getDomainKeysQuery_stmt = d_dnssecdb->prepare("select id,flags, active, published, content from cryptokeys where domain=:domain",1);
   d_deleteDomainKeyQuery_stmt = d_dnssecdb->prepare("delete from cryptokeys where domain=:domain and id=:key_id",2);
-  d_insertDomainKeyQuery_stmt = d_dnssecdb->prepare("insert into cryptokeys (domain, flags, active, content) values (:domain, :flags, :active, :content)", 4);
+  d_insertDomainKeyQuery_stmt = d_dnssecdb->prepare("insert into cryptokeys (domain, flags, active, published, content) values (:domain, :flags, :active, :published, :content)", 5);
   d_GetLastInsertedKeyIdQuery_stmt = d_dnssecdb->prepare("select last_insert_rowid()", 0);
   d_activateDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set active=1 where domain=:domain and id=:key_id", 2);
   d_deactivateDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set active=0 where domain=:domain and id=:key_id", 2);
+  d_publishDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set published=1 where domain=:domain and id=:key_id", 2);
+  d_unpublishDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set published=0 where domain=:domain and id=:key_id", 2);
   d_getTSIGKeyQuery_stmt = d_dnssecdb->prepare("select algorithm, secret from tsigkeys where name=:key_name", 1);
   d_setTSIGKeyQuery_stmt = d_dnssecdb->prepare("replace into tsigkeys (name,algorithm,secret) values(:key_name, :algorithm, :content)", 3);
   d_deleteTSIGKeyQuery_stmt = d_dnssecdb->prepare("delete from tsigkeys where name=:key_name", 1);
@@ -137,6 +145,8 @@ void Bind2Backend::freeStatements()
   d_GetLastInsertedKeyIdQuery_stmt.reset();
   d_activateDomainKeyQuery_stmt.reset();
   d_deactivateDomainKeyQuery_stmt.reset();
+  d_publishDomainKeyQuery_stmt.reset();
+  d_unpublishDomainKeyQuery_stmt.reset();
   d_getTSIGKeyQuery_stmt.reset();
   d_setTSIGKeyQuery_stmt.reset();
   d_deleteTSIGKeyQuery_stmt.reset();
@@ -274,7 +284,8 @@ bool Bind2Backend::getDomainKeys(const DNSName& name, std::vector<KeyData>& keys
       kd.id = pdns_stou(row[0]);
       kd.flags = pdns_stou(row[1]);
       kd.active = (row[2] == "1");
-      kd.content = row[3];
+      kd.published = (row[3] == "1");
+      kd.content = row[4];
       keys.push_back(kd);
     }
 
@@ -314,6 +325,7 @@ bool Bind2Backend::addDomainKey(const DNSName& name, const KeyData& key, int64_t
       bind("domain", name)->
       bind("flags", key.flags)->
       bind("active", key.active)->
+      bind("published", key.published)->
       bind("content", key.content)->
       execute()->
       reset();
@@ -379,6 +391,43 @@ bool Bind2Backend::deactivateDomainKey(const DNSName& name, unsigned int id)
   return true;
 }
 
+bool Bind2Backend::publishDomainKey(const DNSName& name, unsigned int id)
+{
+  if(!d_dnssecdb || d_hybrid)
+    return false;
+
+  try {
+    d_publishDomainKeyQuery_stmt->
+      bind("domain", name)->
+      bind("key_id", id)->
+      execute()->
+      reset();
+  }
+  catch(SSqlException& se) {
+    throw PDNSException("Error accessing DNSSEC database in BIND backend, publishDomainKey(): "+se.txtReason());
+  }
+  return true;
+}
+
+bool Bind2Backend::unpublishDomainKey(const DNSName& name, unsigned int id)
+{
+  if(!d_dnssecdb || d_hybrid)
+    return false;
+
+  try {
+    d_unpublishDomainKeyQuery_stmt->
+      bind("domain", name)->
+      bind("key_id", id)->
+      execute()->
+      reset();
+  }
+  catch(SSqlException& se) {
+    throw PDNSException("Error accessing DNSSEC database in BIND backend, unpublishDomainKey(): "+se.txtReason());
+  }
+  return true;
+}
+
+
 bool Bind2Backend::getTSIGKey(const DNSName& name, DNSName* algorithm, string* content)
 {
   if(!d_dnssecdb || d_hybrid)
index d3f7d900f6c3eca5de2d5c1e9ac7fcba31ac244c..2238ababdfd3a3ef776171e926acfbdf4e3b5ae1 100644 (file)
@@ -744,6 +744,7 @@ bool GeoIPBackend::getDomainKeys(const DNSName& name, std::vector<DNSBackend::Ke
             DNSBackend::KeyData kd;
             kd.id = pdns_stou(glob_result.gl_pathv[i]+regm[3].rm_so);
             kd.active = !strncmp(glob_result.gl_pathv[i]+regm[4].rm_so, "1", 1);
+            kd.published = true;
             kd.flags = pdns_stou(glob_result.gl_pathv[i]+regm[2].rm_so);
             ifstream ifs(glob_result.gl_pathv[i]);
             ostringstream content;
@@ -857,7 +858,7 @@ bool GeoIPBackend::activateDomainKey(const DNSName& name, unsigned int id) {
               ostringstream newpath;
               newpath << getArg("dnssec-keydir") << "/" << dom.domain.toStringNoDot() << "." << pdns_stou(glob_result.gl_pathv[i]+regm[2].rm_so) << "." << kid << ".1.key";
               if (rename(glob_result.gl_pathv[i], newpath.str().c_str())) {
-                cerr << "Cannot active key: " << strerror(errno) << endl;
+                cerr << "Cannot activate key: " << strerror(errno) << endl;
               }
             }
           }
@@ -904,6 +905,15 @@ bool GeoIPBackend::deactivateDomainKey(const DNSName& name, unsigned int id) {
   return false;
 }
 
+bool GeoIPBackend::publishDomainKey(const DNSName& name, unsigned int id) {
+  return false;
+}
+
+bool GeoIPBackend::unpublishDomainKey(const DNSName& name, unsigned int id) {
+  return false;
+}
+
+
 bool GeoIPBackend::hasDNSSECkey(const DNSName& name) {
   ostringstream pathname;
   pathname << getArg("dnssec-keydir") << "/" << name.toStringNoDot() << "*.key";
index c947478bf972ef107836983912c680a338e352bb..fcf301eda1f81e542ed6a362b8bc43accdb189d0 100644 (file)
@@ -65,6 +65,8 @@ public:
   bool addDomainKey(const DNSName& name, const KeyData& key, int64_t& id) override;
   bool activateDomainKey(const DNSName& name, unsigned int id) override;
   bool deactivateDomainKey(const DNSName& name, unsigned int id) override;
+  bool publishDomainKey(const DNSName& name, unsigned int id) override;
+  bool unpublishDomainKey(const DNSName& name, unsigned int id) override;
 
 private:
   static pthread_rwlock_t s_state_lock;
diff --git a/modules/gmysqlbackend/4.2.0_to_4.3.0_schema.mysql.sql b/modules/gmysqlbackend/4.2.0_to_4.3.0_schema.mysql.sql
new file mode 100644 (file)
index 0000000..c1f6cad
--- /dev/null
@@ -0,0 +1,2 @@
+ALTER TABLE cryptokeys ADD COLUMN published BOOL DEFAULT 1;
+
index 22bfeae93c52210d2585ef641e412e4e516965c5..ef80c9d88a55ec7c40da17db4ee301fcc61f64ba 100644 (file)
@@ -130,9 +130,9 @@ public:
     declare(suffix,"delete-rrset-query","","delete from records where domain_id=? and name=? and type=?");
     declare(suffix,"delete-names-query","","delete from records where domain_id=? and name=?");
 
-    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, content) select id, ?, ?, ? from domains where name=?");
+    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, published, content) select id, ?, ?, ?, ? from domains where name=?");
     declare(suffix,"get-last-inserted-key-id-query", "", "select LAST_INSERT_ID()");
-    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=?");
+    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, active, published, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=?");
     declare(suffix,"get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=?");
     declare(suffix,"get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=? and domainmetadata.kind=?");
     declare(suffix,"clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=?) and domainmetadata.kind=?");
@@ -140,6 +140,8 @@ public:
     declare(suffix,"set-domain-metadata-query","", "insert into domainmetadata (domain_id, kind, content) select id, ?, ? from domains where name=?");
     declare(suffix,"activate-domain-key-query","", "update cryptokeys set active=1 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?");
     declare(suffix,"deactivate-domain-key-query","", "update cryptokeys set active=0 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?");
+    declare(suffix,"publish-domain-key-query","", "update cryptokeys set published=1 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?");
+    declare(suffix,"unpublish-domain-key-query","", "update cryptokeys set published=0 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?");
     declare(suffix,"remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name=?) and cryptokeys.id=?");
     declare(suffix,"clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name=?)");
     declare(suffix,"get-tsig-key-query","", "select algorithm, secret from tsigkeys where name=?");
index 2dea1c24cc72ec5d33854fa27d3634f7b21eb0d9..c923035ba672389ee19a099156c20d5f967c70f8 100644 (file)
@@ -70,6 +70,7 @@ CREATE TABLE cryptokeys (
   domain_id             INT NOT NULL,
   flags                 INT NOT NULL,
   active                BOOL,
+  published             BOOL DEFAULT 1,
   content               TEXT,
   PRIMARY KEY(id)
 ) Engine=InnoDB CHARACTER SET 'latin1';
diff --git a/modules/godbcbackend/4.2.0_to_4.3.0_schema.mssql.sql b/modules/godbcbackend/4.2.0_to_4.3.0_schema.mssql.sql
new file mode 100644 (file)
index 0000000..8a9e75e
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE cryptokeys ADD COLUMN published BIT DEFAULT 1;
index 533f50f959b39c0bce226bd0430a208d647a4e79..02139c24c4b0cb7fc959b2b828d18cb37c777b39 100644 (file)
@@ -113,9 +113,9 @@ public:
     declare(suffix,"delete-rrset-query","","delete from records where domain_id=? and name=? and type=?");
     declare(suffix,"delete-names-query","","delete from records where domain_id=? and name=?");
 
-    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, content) select id, ?, ?, ? from domains where name=?");
+    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, published, content) select id, ?, ?, ?, ? from domains where name=?");
     declare(suffix,"get-last-inserted-key-id-query", "", "select ident_current('cryptokeys')");
-    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=?");
+    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, active, published, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=?");
     declare(suffix,"get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=?");
     declare(suffix,"get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=? and domainmetadata.kind=?");
     declare(suffix,"clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=?) and domainmetadata.kind=?");
@@ -123,6 +123,8 @@ public:
     declare(suffix,"set-domain-metadata-query","", "insert into domainmetadata (domain_id, kind, content) select id, ?, ? from domains where name=?");
     declare(suffix,"activate-domain-key-query","", "update cryptokeys set active=1 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?");
     declare(suffix,"deactivate-domain-key-query","", "update cryptokeys set active=0 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?");
+    declare(suffix,"publish-domain-key-query","", "update cryptokeys set published=1 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?");
+    declare(suffix,"unpublish-domain-key-query","", "update cryptokeys set published=0 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?");
     declare(suffix,"remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name=?) and cryptokeys.id=?");
     declare(suffix,"clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name=?)");
     declare(suffix,"get-tsig-key-query","", "select algorithm, secret from tsigkeys where name=?");
index 0bd56a012eb40a75a515dc39e610439499b44e56..d5922dc85289c2d96619ce2d9af321707c3d505d 100644 (file)
@@ -69,6 +69,7 @@ CREATE TABLE cryptokeys (
   domain_id             INT NOT NULL,
   flags                 INT NOT NULL,
   active                BIT,
+  published             BIT DEFAULT 1,
   content               VARCHAR(MAX),
   PRIMARY KEY(id)
 );
diff --git a/modules/gpgsqlbackend/4.2.0_to_4.3.0_schema.pgsql.sql b/modules/gpgsqlbackend/4.2.0_to_4.3.0_schema.pgsql.sql
new file mode 100644 (file)
index 0000000..f9fa3a8
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE cryptokeys ADD COLUMN published BOOL DEFAULT true;
index b909b98cb56e4b4026ca232fd979fe7c1eee2dd6..c8e38a86073717a4d7a7d8c6a4dd5cfa241727c4 100644 (file)
@@ -135,9 +135,9 @@ public:
     declare(suffix,"delete-rrset-query","","delete from records where domain_id=$1 and name=$2 and type=$3");
     declare(suffix,"delete-names-query","","delete from records where domain_id=$1 and name=$2");
 
-    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, content) select id, $1, $2, $3 from domains where name=$4");
+    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, published, content) select id, $1, $2, $3, $4 from domains where name=$5");
     declare(suffix,"get-last-inserted-key-id-query","", "select lastval()");
-    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, case when active then 1 else 0 end as active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=$1");
+    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, case when active then 1 else 0 end as active, case when published then 1 else 0 end as published, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=$1");
     declare(suffix,"get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=$1");
     declare(suffix,"get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=$1 and domainmetadata.kind=$2");
     declare(suffix,"clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=$1) and domainmetadata.kind=$2");
@@ -145,6 +145,8 @@ public:
     declare(suffix,"set-domain-metadata-query","", "insert into domainmetadata (domain_id, kind, content) select id, $1, $2 from domains where name=$3");
     declare(suffix,"activate-domain-key-query","", "update cryptokeys set active=true where domain_id=(select id from domains where name=$1) and  cryptokeys.id=$2");
     declare(suffix,"deactivate-domain-key-query","", "update cryptokeys set active=false where domain_id=(select id from domains where name=$1) and  cryptokeys.id=$2");
+    declare(suffix,"publish-domain-key-query","", "update cryptokeys set published=true where domain_id=(select id from domains where name=$1) and  cryptokeys.id=$2");
+    declare(suffix,"unpublish-domain-key-query","", "update cryptokeys set published=false where domain_id=(select id from domains where name=$1) and  cryptokeys.id=$2");
     declare(suffix,"remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name=$1) and cryptokeys.id=$2");    
     declare(suffix,"clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name=$1)");
     declare(suffix,"get-tsig-key-query","", "select algorithm, secret from tsigkeys where name=$1");
index 911dd373ec5ae34ce1691448a47589300b83b752..3a7534bb8166923ac4c6a35c5e7fd540ca0673b9 100644 (file)
@@ -77,6 +77,7 @@ CREATE TABLE cryptokeys (
   domain_id             INT REFERENCES domains(id) ON DELETE CASCADE,
   flags                 INT NOT NULL,
   active                BOOL,
+  published             BOOL DEFAULT true,
   content               TEXT
 );
 
diff --git a/modules/gsqlite3backend/4.2.0_to_4.3.0_schema.sqlite3.sql b/modules/gsqlite3backend/4.2.0_to_4.3.0_schema.sqlite3.sql
new file mode 100644 (file)
index 0000000..d334fec
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE cryptokeys ADD published BOOL DEFAULT 1;
index a188fc655e32cd54bc61ef50f74bb6be1ff5cc67..d697b83bd5cc5460bcad96c9945148a89b02494b 100644 (file)
@@ -126,9 +126,9 @@ public:
     declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=:domain_id and name=:qname and type=:qtype");
     declare(suffix, "delete-names-query", "", "delete from records where domain_id=:domain_id and name=:qname");
 
-    declare(suffix, "add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, content) select id, :flags,:active, :content from domains where name=:domain");
+    declare(suffix, "add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, published, content) select id, :flags, :active, :published, :content from domains where name=:domain");
     declare(suffix, "get-last-inserted-key-id-query", "", "select last_insert_rowid()");
-    declare(suffix, "list-domain-keys-query","", "select cryptokeys.id, flags, active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=:domain");
+    declare(suffix, "list-domain-keys-query","", "select cryptokeys.id, flags, active, published, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=:domain");
     declare(suffix, "get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=:domain");
     declare(suffix, "get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=:domain and domainmetadata.kind=:kind");
     declare(suffix, "clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=:domain) and domainmetadata.kind=:kind");
@@ -136,6 +136,8 @@ public:
     declare(suffix, "set-domain-metadata-query","", "insert into domainmetadata (domain_id, kind, content) select id, :kind, :content from domains where name=:domain");
     declare(suffix, "activate-domain-key-query","", "update cryptokeys set active=1 where domain_id=(select id from domains where name=:domain) and  cryptokeys.id=:key_id");
     declare(suffix, "deactivate-domain-key-query","", "update cryptokeys set active=0 where domain_id=(select id from domains where name=:domain) and  cryptokeys.id=:key_id");
+    declare(suffix, "publish-domain-key-query","", "update cryptokeys set published=1 where domain_id=(select id from domains where name=:domain) and  cryptokeys.id=:key_id");
+    declare(suffix, "unpublish-domain-key-query","", "update cryptokeys set published=0 where domain_id=(select id from domains where name=:domain) and  cryptokeys.id=:key_id");
     declare(suffix, "remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name=:domain) and cryptokeys.id=:key_id");
     declare(suffix, "clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name=:domain)");
     declare(suffix, "get-tsig-key-query","", "select algorithm, secret from tsigkeys where name=:key_name");
index ed3a23de5a40abaf42eab5798814b93b29d64430..64a7da0d20f36fe2fd124fc591d023664cea6e12 100644 (file)
@@ -74,6 +74,7 @@ CREATE TABLE cryptokeys (
  domain_id              INT NOT NULL,
  flags                  INT NOT NULL,
  active                 BOOL,
+ published              BOOL DEFAULT 1,
  content                TEXT,
  FOREIGN KEY(domain_id) REFERENCES domains(id) ON DELETE CASCADE ON UPDATE CASCADE
 );
index dfa28a7e09e2566f7e9f9edea07ce29f12dd01bc..80fc6b5ab592ff7af4384a10f47c373d7a674b15 100644 (file)
 
 #include "lmdbbackend.hh"
 
-#define SCHEMAVERSION 1
+#define SCHEMAVERSION 2
+#define SCHEMAVERSION_TEXT "2"
+// List the class version here. Default is 0
+BOOST_CLASS_VERSION(LMDBBackend::KeyDataDB, 1)
 
 LMDBBackend::LMDBBackend(const std::string& suffix)
 {
@@ -72,16 +75,19 @@ LMDBBackend::LMDBBackend(const std::string& suffix)
   
   auto pdnsdbi = d_tdomains->getEnv()->openDB("pdns", MDB_CREATE);
   auto txn = d_tdomains->getEnv()->getRWTransaction();
+  uint32_t schemaversion = 1;
   MDBOutVal _schemaversion;
   if(!txn->get(pdnsdbi, "schemaversion", _schemaversion)) {
-    auto schemaversion = _schemaversion.get<uint32_t>();
-    if (schemaversion != SCHEMAVERSION) {
+    schemaversion = _schemaversion.get<uint32_t>();
+  }
+
+  if (schemaversion != SCHEMAVERSION) {
+    if (getArgAsNum("schema-version") != SCHEMAVERSION) {
       throw std::runtime_error("Expected LMDB schema version "+std::to_string(SCHEMAVERSION)+" but got "+std::to_string(schemaversion));
     }
-  }
-  else {
     txn->put(pdnsdbi, "schemaversion", SCHEMAVERSION);
   }
+
   MDBOutVal shards;
   if(!txn->get(pdnsdbi, "shards", shards)) {
     
@@ -160,11 +166,23 @@ void serialize(Archive & ar, LMDBBackend::DomainMeta& g, const unsigned int vers
 }
 
 template<class Archive>
-void serialize(Archive & ar, LMDBBackend::KeyDataDB& g, const unsigned int version)
+void save(Archive & ar, const LMDBBackend::KeyDataDB& g, const unsigned int version)
+{
+  ar & g.domain & g.content & g.flags & g.active & g.published;
+}
+
+template<class Archive>
+void load(Archive & ar, LMDBBackend::KeyDataDB& g, const unsigned int version)
 {
   ar & g.domain & g.content & g.flags & g.active;
+  if (version >= 1) {
+    ar & g.published;
+  } else {
+    g.published = true;
+  }
 }
 
+
 template<class Archive>
 void serialize(Archive & ar, TSIGKey& g, const unsigned int version)
 {
@@ -180,6 +198,7 @@ void serialize(Archive & ar, TSIGKey& g, const unsigned int version)
 
 BOOST_SERIALIZATION_SPLIT_FREE(DNSName);
 BOOST_SERIALIZATION_SPLIT_FREE(QType);
+BOOST_SERIALIZATION_SPLIT_FREE(LMDBBackend::KeyDataDB);
 BOOST_IS_BITWISE_SERIALIZABLE(ComboAddress);
 
 template<>
@@ -887,7 +906,7 @@ bool LMDBBackend::getDomainKeys(const DNSName& name, std::vector<KeyData>& keys)
   auto txn = d_tkdb->getROTransaction();
   auto range = txn.equal_range<0>(name);
   for(auto& iter = range.first; iter != range.second; ++iter) {
-    KeyData kd{iter->content, iter.getID(), iter->flags, iter->active};
+    KeyData kd{iter->content, iter.getID(), iter->flags, iter->active, iter->published};
     keys.push_back(kd);
   }
 
@@ -912,7 +931,7 @@ bool LMDBBackend::removeDomainKey(const DNSName& name, unsigned int id)
 bool LMDBBackend::addDomainKey(const DNSName& name, const KeyData& key, int64_t& id)
 {
   auto txn = d_tkdb->getRWTransaction();
-  KeyDataDB kdb{name, key.content, key.flags, key.active};
+  KeyDataDB kdb{name, key.content, key.flags, key.active, key.published};
   id = txn.put(kdb);
   txn.commit();
     
@@ -952,10 +971,48 @@ bool LMDBBackend::deactivateDomainKey(const DNSName& name, unsigned int id)
       return true;
     }
   }
-  // cout << "??? wanted to activate domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
+  // cout << "??? wanted to deactivate domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
   return true;
 }
 
+bool LMDBBackend::publishDomainKey(const DNSName& name, unsigned int id)
+{
+  auto txn = d_tkdb->getRWTransaction();
+  KeyDataDB kdb;
+  if(txn.get(id, kdb)) {
+    if(kdb.domain == name) {
+      txn.modify(id, [](KeyDataDB& kdbarg)
+                 {
+                   kdbarg.published = true;
+                 });
+      txn.commit();
+      return true;
+    }
+  }
+
+  // cout << "??? wanted to hide domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
+  return true;
+}
+
+bool LMDBBackend::unpublishDomainKey(const DNSName& name, unsigned int id)
+{
+  auto txn = d_tkdb->getRWTransaction();
+  KeyDataDB kdb;
+  if(txn.get(id, kdb)) {
+    if(kdb.domain == name) {
+      txn.modify(id, [](KeyDataDB& kdbarg)
+                 {
+                   kdbarg.published = false;
+                 });
+      txn.commit();
+      return true;
+    }
+  }
+  // cout << "??? wanted to unhide domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
+  return true;
+}
+
+
 bool LMDBBackend::getBeforeAndAfterNamesAbsolute(uint32_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after) 
 {
   //  cout << __PRETTY_FUNCTION__<< ": "<<id <<", "<<qname << " " << unhashed<<endl;
@@ -1523,7 +1580,8 @@ public:
     declare(suffix,"filename","Filename for lmdb","./pdns.lmdb");
     declare(suffix,"sync-mode","Synchronisation mode: nosync, nometasync, mapasync, sync","mapasync");
     // there just is no room for more on 32 bit
-    declare(suffix,"shards","Records database will be split into this number of shards", (sizeof(long) == 4) ? "2" : "64"); 
+    declare(suffix,"shards","Records database will be split into this number of shards", (sizeof(long) == 4) ? "2" : "64");
+    declare(suffix,"schema-version","Maximum allowed schema version to run on this DB. If a lower version is found, auto update is performed", SCHEMAVERSION_TEXT); 
   }
   DNSBackend *make(const string &suffix="")
   {
index b01a849981378a0cb43cac75f7c2ee87d93b72bb..6199f6fdc26d8574018220677f06992262cb207f 100644 (file)
@@ -88,6 +88,8 @@ public:
   bool addDomainKey(const DNSName& name, const KeyData& key, int64_t& id) override;
   bool activateDomainKey(const DNSName& name, unsigned int id) override;
   bool deactivateDomainKey(const DNSName& name, unsigned int id) override;
+  bool publishDomainKey(const DNSName& name, unsigned int id) override;
+  bool unpublishDomainKey(const DNSName& name, unsigned int id) override;
 
   // TSIG
   bool getTSIGKey(const DNSName& name, DNSName* algorithm, string* content) override;
@@ -194,6 +196,7 @@ public:
     std::string content;
     unsigned int flags;
     bool active;
+    bool published;
   };
 
 private:
index dff22a7c94954f7145e8fbbc1cc27c64d2c91293..1f56fc29fcd30de9ed1bffcc35f25bd02a32f2a4 100644 (file)
@@ -337,6 +337,7 @@ public:
 
     for(const auto& row: boost::get<vector<pair<int, keydata_result_t> > >(result)) {
       DNSBackend::KeyData key;
+      key.published = true;
       for(const auto& item: row.second) {
         if (item.first == "content")
           key.content = boost::get<string>(item.second);
@@ -346,10 +347,12 @@ public:
           key.flags = static_cast<unsigned int>(boost::get<int>(item.second));
         else if (item.first == "active")
           key.active = boost::get<bool>(item.second);
+        else if (item.first == "published")
+          key.published = boost::get<bool>(item.second);
         else
           g_log<<Logger::Warning<<"["<<getPrefix()<<"] Unsupported key '"<<item.first<<"' in keydata result"<<endl;
       }
-      logResult("id="<<key.id<<",flags="<<key.flags<<",active="<<(key.active ? "true" : "false"));
+      logResult("id="<<key.id<<",flags="<<key.flags<<",active="<<(key.active ? "true" : "false")<<",published="<<(key.published ? "true" : "false"));
       keys.push_back(key);
     }
 
index 3471bc46b281cc297f103237c82d9175459da2ea..8d8b2af0603afd5276720fbec33e7d095678bc6f 100644 (file)
@@ -66,6 +66,7 @@ protected
              "id" => 1,
              "flags" => 257,
              "active" => true,
+             "published" => true,
              "content" => "Private-key-format: v1.2
 Algorithm: 8 (RSASHA256)
 Modulus: ovvzf1fHdptdXsBrBLSqmGqdEKwR2B9st/KBgh8xQKoQzTGUG00CsPjF/J59IBU+EU/IIInMn0MxLLTyUKa2DJUkR6i7UKif5jKX1c7yvWzrFKLGOHjugUX2++r+o789biUte1qpWp3Kc2RYL18oPco4zpo6JcsPmhOK3aUCDJXmuWgHl1KudCQIiPkISArXVn4oOp+skQq+mUBl1Pysc4D+6sl77ERR2fW6xJ4ZRPOIKr445RJJmKgoMG8yRrR3it1RmV49hZlvMosQjBUoNcqhqOI0n4l8HOLyna7KIzoNKG62GtUCZh8uy8IjdUiWPYGEtkZ9zE0bnnF+R7HGvQ==
@@ -81,6 +82,7 @@ Coefficient: vG8tLZBE4s3bftN5INv2/o3knEcaoUAPfakSsjM2uLwQCGiUbBOOlp3QSdTU4MiLjDs
              "id" => 2,
              "flags" => 256,
              "active" => true,
+             "published" => true,
              "content" => "Private-key-format: v1.2
 Algorithm: 8 (RSASHA256)
 Modulus: wKPNcDwkCd2DKxfdkMqTFOV2ITdgxIDaOd4vQ2QtphMBY9yYwmEkNsVdVFz7VVuQHdls20JUe+brFUhs1zEMMbokulFP/qVAItAeEWcqtkPULT+mmX5HsexpFVAZ5+UXuerObk/HMiIMt1CvkIWhmjSIkAI6dFRlf/93zTjy0+vwrNWZPXSzLccK5TfJmxdYdGPcsHkg6UmqEFPQuyZpmlmpg3IwjL5YddTDobAoABz/BrH7WsW0q/PyVubITo8JuFiBI5Fmw+3ef3PVUt1jtUCGASvtqNXW4wtWrgqvQKg/odthpceQ4QagV9XSlOdml527thnf9cMpm0Gh4Ox5HQ==
index bf74849590e59fffc1905c427732fee045db66f2..940bb1739e6e3d9c7c2c27fd71b273540006d285 100644 (file)
@@ -127,7 +127,7 @@ void HTTPConnector::restful_requestbuilder(const std::string &method, const Json
     addUrlComponent(parameters, "qtype", ss);
 
     // set the correct type of request based on method
-    if (method == "activateDomainKey" || method == "deactivateDomainKey") {
+    if (method == "activateDomainKey" || method == "deactivateDomainKey" || method == "publishDomainKey" || method == "unpublishDomainKey") {
         // create an empty post
         req.preparePost();
         verb = "POST";
@@ -142,6 +142,7 @@ void HTTPConnector::restful_requestbuilder(const std::string &method, const Json
         const Json& param = parameters["key"];
         req.POST()["flags"] = asString(param["flags"]);
         req.POST()["active"] = (param["active"].bool_value() ? "1" : "0");
+        req.POST()["published"] = (param["published"].bool_value() ? "1" : "0");
         req.POST()["content"] = param["content"].string_value();
         req.preparePost();
         verb = "PUT";
index 3da5d4ef6a62bdfe47dbac67aba7d828e0125489..0c4bf479584b2075868d3b428658356c59321bcb 100755 (executable)
@@ -62,8 +62,8 @@ class Handler
 
    def do_getdomainkeys(args)
        ret = []
-       db.execute("SELECT cryptokeys.id,flags,active,content FROM domains JOIN cryptokeys ON domains.id = cryptokeys.domain_id WHERE domains.name = ?", [args["name"]]) do |row|
-          ret << {:id => row[0].to_i, :flags => row[1].to_i, :active => !(row[2].to_i.zero?), :content => row[3]}
+       db.execute("SELECT cryptokeys.id,flags,active, published, content FROM domains JOIN cryptokeys ON domains.id = cryptokeys.domain_id WHERE domains.name = ?", [args["name"]]) do |row|
+          ret << {:id => row[0].to_i, :flags => row[1].to_i, :active => !(row[2].to_i.zero?), :published => row[3], :content => row[4]}
        end 
        return false if ret.empty?
        return [ret,nil]
@@ -150,15 +150,21 @@ class Handler
    def do_adddomainkey(args)
      d_id = db.get_first_value("SELECT id FROM domains WHERE name = ?", args["name"])
      return false if d_id.nil?
-     sql = "INSERT INTO cryptokeys (domain_id, flags, active, content) VALUES(?,?,?,?)"
+     sql = "INSERT INTO cryptokeys (domain_id, flags, active, published, content) VALUES(?,?,?,?,?)"
      active = args["key"]["active"]
      if (active) 
         active = 1
      else
         active = 0
      end
+     published = args["key"]["published"]
+     if (published)
+         published = 1
+     else
+         published = 0
+     end
      db do |tx|
-        tx.execute(sql, [d_id, args["key"]["flags"].to_i, active, args["key"]["content"]])
+        tx.execute(sql, [d_id, args["key"]["flags"].to_i, active, published, args["key"]["content"]])
      end
      return db.get_first_value("SELECT last_insert_rowid()").to_i
    end
@@ -181,6 +187,24 @@ class Handler
      return true
    end
 
+   def do_unpublishdomainkey(args)
+     d_id = db.get_first_value("SELECT id FROM domains WHERE name = ?", args["name"])
+     return false if d_id.nil?
+     db do |tx|
+       tx.execute("UPDATE cryptokeys SET published = 0 WHERE domain_id = ? AND id = ?", [d_id, args["id"]])
+     end
+     return true
+   end
+
+   def do_publishdomainkey(args)
+     d_id = db.get_first_value("SELECT id FROM domains WHERE name = ?", args["name"])
+     return false if d_id.nil?
+     db do |tx|
+       db.execute("UPDATE cryptokeys SET published = 1 WHERE domain_id = ? AND id = ?", [d_id, args["id"]])
+     end
+     return true
+   end
+
    def do_getdomainmetadata(args) 
        ret = []
         sql = "SELECT content FROM domainmetadata JOIN domains WHERE name = :name AND kind = :kind"
index 83a90cc021e71f034e93d621879ce29fcf3f31be..2e36fa5d861d5bc7f4d056a7f08cd32f6799dd47 100644 (file)
@@ -101,6 +101,7 @@ class DNSBackendHandler < WEBrick::HTTPServlet::AbstractServlet
         args["key"] = {
            "flags" => args.delete("flags").to_i,
            "active" => args.delete("active").to_i,
+           "published" => args.delete("published").to_i,
            "content" => args.delete("content")
         }
      end
index b7d9ba8b4cd6536f96cb1b560894b0e9c6de82c7..6faa1e049ed4ea1290426c0c7fd379b371ef35a5 100644 (file)
@@ -49,6 +49,7 @@ create table cryptokeys (
  domain_id      INT NOT NULL,
  flags        INT NOT NULL,
  active        BOOL,
+ published     BOOL,
  content    TEXT
 );         
 
index 2c20cd306a678bddea867ba3ea95a118b5542b21..b330eafd48fbb8fdb7e48e8918fe29845cb8329f 100644 (file)
@@ -374,6 +374,7 @@ bool RemoteBackend::getDomainKeys(const DNSName& name, std::vector<DNSBackend::K
      key.id = intFromJson(jsonKey, "id");
      key.flags = intFromJson(jsonKey, "flags");
      key.active = asBool(jsonKey["active"]);
+     key.published = boolFromJson(jsonKey, "published", true);
      key.content = stringFromJson(jsonKey, "content");
      keys.push_back(key);
    }
@@ -411,6 +412,7 @@ bool RemoteBackend::addDomainKey(const DNSName& name, const KeyData& key, int64_
        { "key", Json::object {
          { "flags", static_cast<int>(key.flags) },
          { "active", key.active },
+         { "published", key.published },
          { "content", key.content }
        }}
      }}
@@ -462,6 +464,45 @@ bool RemoteBackend::deactivateDomainKey(const DNSName& name, unsigned int id) {
    return true;
 }
 
+bool RemoteBackend::publishDomainKey(const DNSName& name, unsigned int id) {
+   // no point doing dnssec if it's not supported
+   if (d_dnssec == false) return false;
+
+   Json query = Json::object{
+     { "method", "publishDomainKey" },
+     { "parameters", Json::object {
+       { "name", name.toString() },
+       { "id", static_cast<int>(id) }
+     }}
+   };
+
+   Json answer;
+   if (this->send(query) == false || this->recv(answer) == false)
+     return false;
+
+   return true;
+}
+
+bool RemoteBackend::unpublishDomainKey(const DNSName& name, unsigned int id) {
+   // no point doing dnssec if it's not supported
+   if (d_dnssec == false) return false;
+
+   Json query = Json::object{
+     { "method", "unpublishDomainKey" },
+     { "parameters", Json::object {
+       { "name", name.toString() },
+       { "id", static_cast<int>(id) }
+     }}
+   };
+
+   Json answer;
+   if (this->send(query) == false || this->recv(answer) == false)
+     return false;
+
+   return true;
+}
+
+
 bool RemoteBackend::doesDNSSEC() {
    return d_dnssec;
 }
index 647da8cdd953744bf60cc6d9f883471aeff465a9..0509c99fc527ec197abc74b8d6de3cacf960d994 100644 (file)
@@ -169,6 +169,8 @@ class RemoteBackend : public DNSBackend
   bool addDomainKey(const DNSName& name, const KeyData& key, int64_t& id) override;
   bool activateDomainKey(const DNSName& name, unsigned int id) override;
   bool deactivateDomainKey(const DNSName& name, unsigned int id) override;
+  bool publishDomainKey(const DNSName& name, unsigned int id) override;
+  bool unpublishDomainKey(const DNSName& name, unsigned int id) override;
   bool getDomainInfo(const DNSName& domain, DomainInfo& di, bool getSerial=true ) override;
   void setNotified(uint32_t id, uint32_t serial) override;
   bool doesDNSSEC() override;
index 5543574cbff00cd2ebe5246a567abeda02650919..cc51fb0e6894da732565906f29b6e4b413cb8e95 100644 (file)
@@ -19,6 +19,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
-DNSBackend::KeyData k1 = {std::string("Private-key-format: v1.2\nAlgorithm: 5 (RSASHA1)\nModulus: qpe9fxlN4dBT38cLPWtqljZhcJjbqRprj9XsYmf2/uFu4kA5sHYrlQY7H9lpzGJPRfOAfxShBpKs1AVaVInfJQ==\nPublicExponent: AQAB\nPrivateExponent: Ad3YogzXvVDLsWuAfioY571QlolbdTbzVlhLEMLD6dSRx+xcZgw6c27ak2HAH00iSKTvqK3AyeaK8Eqy/oJ5QQ==\nPrime1: wo8LZrdU2y0xLGCeLhwziQDDtTMi18NEIwlx8tUPnhs=\nPrime2: 4HcuFqgo7NOiXFvN+V2PT+QaIt2+oi6D2m8/qtTDS78=\nExponent1: GUdCoPbi9JM7l1t6Ud1iKMPLqchaF5SMTs0UXAuous8=\nExponent2: nzgKqimX9f1corTAEw0pddrwKyEtcu8ZuhzFhZCsAxM=\nCoefficient: YGNxbulf5GTNiIu0oNKmAF0khNtx9layjOPEI0R4/RY="), 1, 257, true };
+DNSBackend::KeyData k1 = {std::string("Private-key-format: v1.2\nAlgorithm: 5 (RSASHA1)\nModulus: qpe9fxlN4dBT38cLPWtqljZhcJjbqRprj9XsYmf2/uFu4kA5sHYrlQY7H9lpzGJPRfOAfxShBpKs1AVaVInfJQ==\nPublicExponent: AQAB\nPrivateExponent: Ad3YogzXvVDLsWuAfioY571QlolbdTbzVlhLEMLD6dSRx+xcZgw6c27ak2HAH00iSKTvqK3AyeaK8Eqy/oJ5QQ==\nPrime1: wo8LZrdU2y0xLGCeLhwziQDDtTMi18NEIwlx8tUPnhs=\nPrime2: 4HcuFqgo7NOiXFvN+V2PT+QaIt2+oi6D2m8/qtTDS78=\nExponent1: GUdCoPbi9JM7l1t6Ud1iKMPLqchaF5SMTs0UXAuous8=\nExponent2: nzgKqimX9f1corTAEw0pddrwKyEtcu8ZuhzFhZCsAxM=\nCoefficient: YGNxbulf5GTNiIu0oNKmAF0khNtx9layjOPEI0R4/RY="), 1, 257, true, true };
 
-DNSBackend::KeyData k2 = {std::string("Private-key-format: v1.2\nAlgorithm: 5 (RSASHA1)\nModulus: tY2TAMgL/whZdSbn2aci4wcMqohO24KQAaq5RlTRwQ33M8FYdW5fZ3DMdMsSLQUkjGnKJPKEdN3Qd4Z5b18f+w==\nPublicExponent: AQAB\nPrivateExponent: BB6xibPNPrBV0PUp3CQq0OdFpk9v9EZ2NiBFrA7osG5mGIZICqgOx/zlHiHKmX4OLmL28oU7jPKgogeuONXJQQ==\nPrime1: yjxe/iHQ4IBWpvCmuGqhxApWF+DY9LADIP7bM3Ejf3M=\nPrime2: 5dGWTyYEQRBVK74q1a64iXgaNuYm1pbClvvZ6ccCq1k=\nExponent1: TwM5RebmWeAqerzJFoIqw5IaQugJO8hM4KZR9A4/BTs=\nExponent2: bpV2HSmu3Fvuj7jWxbFoDIXlH0uJnrI2eg4/4hSnvSk=\nCoefficient: e2uDDWN2zXwYa2P6VQBWQ4mR1ZZjFEtO/+YqOJZun1Y="), 2, 256, true };
+DNSBackend::KeyData k2 = {std::string("Private-key-format: v1.2\nAlgorithm: 5 (RSASHA1)\nModulus: tY2TAMgL/whZdSbn2aci4wcMqohO24KQAaq5RlTRwQ33M8FYdW5fZ3DMdMsSLQUkjGnKJPKEdN3Qd4Z5b18f+w==\nPublicExponent: AQAB\nPrivateExponent: BB6xibPNPrBV0PUp3CQq0OdFpk9v9EZ2NiBFrA7osG5mGIZICqgOx/zlHiHKmX4OLmL28oU7jPKgogeuONXJQQ==\nPrime1: yjxe/iHQ4IBWpvCmuGqhxApWF+DY9LADIP7bM3Ejf3M=\nPrime2: 5dGWTyYEQRBVK74q1a64iXgaNuYm1pbClvvZ6ccCq1k=\nExponent1: TwM5RebmWeAqerzJFoIqw5IaQugJO8hM4KZR9A4/BTs=\nExponent2: bpV2HSmu3Fvuj7jWxbFoDIXlH0uJnrI2eg4/4hSnvSk=\nCoefficient: e2uDDWN2zXwYa2P6VQBWQ4mR1ZZjFEtO/+YqOJZun1Y="), 2, 256, true, true };
index 13d803b560450d5378d6bd17a09752235a000a91..ef783f4ea8b2df7b6f76a0ce15c2fc1ab3aeb947 100644 (file)
@@ -145,6 +145,7 @@ BOOST_AUTO_TEST_CASE(test_method_getDomainKeys) {
         BOOST_CHECK(kd.id > 0);
         BOOST_CHECK(kd.flags == 256 || kd.flags == 257);
         BOOST_CHECK(kd.active == true);
+        BOOST_CHECK(kd.published == true);
         BOOST_CHECK(kd.content.size() > 500);
       }
    }
index 7fb40ec33345f1cf7c3c1cc7a81aaadacb331e01..4984c5da7943564857d32dd7ea8304af34c4d5be 100644 (file)
@@ -128,6 +128,28 @@ class Handler
      [false]
    end
 
+   def do_publishdomainkey(args)
+     args["id"] = args["id"].to_i
+     if $keys.has_key? args["name"]
+      if $keys[args["name"]][args["id"]-1]
+         $keys[args["name"]][args["id"]-1]["published"] = true
+         return [true]
+      end
+     end
+     [false]
+   end
+
+   def do_unpublishdomainkey(args)
+     args["id"] = args["id"].to_i
+     if $keys.has_key? args["name"]
+      if $keys[args["name"]][args["id"]-1]
+         $keys[args["name"]][args["id"]-1]["published"] = false
+         return [true]
+      end
+     end
+     [false]
+   end
+
    def do_removedomainkey(args)
      args["id"] = args["id"].to_i
      if $keys.has_key? args["name"]
index 21beaa3f26ab0aaffc2451996e12dfb01215e440..dc94a1071dc13e78c6123d4af543789e52349668 100755 (executable)
@@ -138,6 +138,7 @@ class DNSBackendHandler < WEBrick::HTTPServlet::AbstractServlet
         args["key"] = {
            "flags" => args.delete("flags").to_i,
            "active" => args.delete("active").to_i,
+           "published" => args.delete("published").to_i,
            "content" => args.delete("content")
         }
      end
index 8ab7d9916c7d15160b26bd42b5ec3e1984a66005..d223a0618bb28684830496d50321c916b9d9ece6 100644 (file)
@@ -110,6 +110,8 @@ GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
 
   d_ActivateDomainKeyQuery = getArg("activate-domain-key-query");
   d_DeactivateDomainKeyQuery = getArg("deactivate-domain-key-query");
+  d_PublishDomainKeyQuery = getArg("publish-domain-key-query");
+  d_UnpublishDomainKeyQuery = getArg("unpublish-domain-key-query");
   d_RemoveDomainKeyQuery = getArg("remove-domain-key-query");
   d_ClearDomainAllKeysQuery = getArg("clear-domain-all-keys-query");
 
@@ -166,6 +168,8 @@ GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
   d_RemoveDomainKeyQuery_stmt = NULL;
   d_ActivateDomainKeyQuery_stmt = NULL;
   d_DeactivateDomainKeyQuery_stmt = NULL;
+  d_PublishDomainKeyQuery_stmt = NULL;
+  d_UnpublishDomainKeyQuery_stmt = NULL;
   d_ClearDomainAllKeysQuery_stmt = NULL;
   d_getTSIGKeyQuery_stmt = NULL;
   d_setTSIGKeyQuery_stmt = NULL;
@@ -712,6 +716,7 @@ bool GSQLBackend::addDomainKey(const DNSName& name, const KeyData& key, int64_t&
     d_AddDomainKeyQuery_stmt->
       bind("flags", key.flags)->
       bind("active", key.active)->
+      bind("published", key.published)->
       bind("content", key.content)->
       bind("domain", name)->
       execute()->
@@ -784,6 +789,48 @@ bool GSQLBackend::deactivateDomainKey(const DNSName& name, unsigned int id)
   return true;
 }
 
+bool GSQLBackend::publishDomainKey(const DNSName& name, unsigned int id)
+{
+  if(!d_dnssecQueries)
+    return false;
+
+  try {
+    reconnectIfNeeded();
+
+    d_PublishDomainKeyQuery_stmt->
+      bind("domain", name)->
+      bind("key_id", id)->
+      execute()->
+      reset();
+  }
+  catch (SSqlException &e) {
+    throw PDNSException("GSQLBackend unable to publish key with id "+ std::to_string(id) + " for domain '" + name.toLogString() + "': "+e.txtReason());
+  }
+  return true;
+}
+
+bool GSQLBackend::unpublishDomainKey(const DNSName& name, unsigned int id)
+{
+  if(!d_dnssecQueries)
+    return false;
+
+  try {
+    reconnectIfNeeded();
+
+    d_UnpublishDomainKeyQuery_stmt->
+      bind("domain", name)->
+      bind("key_id", id)->
+      execute()->
+      reset();
+  }
+  catch (SSqlException &e) {
+    throw PDNSException("GSQLBackend unable to unpublish key with id "+ std::to_string(id) + " for domain '" + name.toLogString() + "': "+e.txtReason());
+  }
+  return true;
+}
+
+
+
 bool GSQLBackend::removeDomainKey(const DNSName& name, unsigned int id)
 {
   if(!d_dnssecQueries)
@@ -919,14 +966,15 @@ bool GSQLBackend::getDomainKeys(const DNSName& name, std::vector<KeyData>& keys)
     KeyData kd;
     while(d_ListDomainKeysQuery_stmt->hasNextRow()) {
       d_ListDomainKeysQuery_stmt->nextRow(row);
-      ASSERT_ROW_COLUMNS("list-domain-keys-query", row, 4);
+      ASSERT_ROW_COLUMNS("list-domain-keys-query", row, 5);
       //~ for(const auto& val: row) {
         //~ cerr<<"'"<<val<<"'"<<endl;
       //~ }
       kd.id = pdns_stou(row[0]);
       kd.flags = pdns_stou(row[1]);
       kd.active = row[2] == "1";
-      kd.content = row[3];
+      kd.published = row[3] == "1";
+      kd.content = row[4];
       keys.push_back(kd);
     }
 
index 656b099c0ce4a53733d426aac02e8a9b6cf69555..aa9eb2d6267d66a3173c175fe5417a4cddb1e647 100644 (file)
@@ -91,7 +91,7 @@ public:
       d_nullifyOrderNameAndUpdateAuthTypeQuery_stmt = d_db->prepare(d_nullifyOrderNameAndUpdateAuthTypeQuery, 4);
       d_RemoveEmptyNonTerminalsFromZoneQuery_stmt = d_db->prepare(d_RemoveEmptyNonTerminalsFromZoneQuery, 1);
       d_DeleteEmptyNonTerminalQuery_stmt = d_db->prepare(d_DeleteEmptyNonTerminalQuery, 2);
-      d_AddDomainKeyQuery_stmt = d_db->prepare(d_AddDomainKeyQuery, 4);
+      d_AddDomainKeyQuery_stmt = d_db->prepare(d_AddDomainKeyQuery, 5);
       d_GetLastInsertedKeyIdQuery_stmt = d_db->prepare(d_GetLastInsertedKeyIdQuery, 0);
       d_ListDomainKeysQuery_stmt = d_db->prepare(d_ListDomainKeysQuery, 1);
       d_GetAllDomainMetadataQuery_stmt = d_db->prepare(d_GetAllDomainMetadataQuery, 1);
@@ -102,6 +102,8 @@ public:
       d_RemoveDomainKeyQuery_stmt = d_db->prepare(d_RemoveDomainKeyQuery, 2);
       d_ActivateDomainKeyQuery_stmt = d_db->prepare(d_ActivateDomainKeyQuery, 2);
       d_DeactivateDomainKeyQuery_stmt = d_db->prepare(d_DeactivateDomainKeyQuery, 2);
+      d_PublishDomainKeyQuery_stmt = d_db->prepare(d_PublishDomainKeyQuery, 2);
+      d_UnpublishDomainKeyQuery_stmt = d_db->prepare(d_UnpublishDomainKeyQuery, 2);
       d_ClearDomainAllKeysQuery_stmt = d_db->prepare(d_ClearDomainAllKeysQuery, 1);
       d_getTSIGKeyQuery_stmt = d_db->prepare(d_getTSIGKeyQuery, 1);
       d_setTSIGKeyQuery_stmt = d_db->prepare(d_setTSIGKeyQuery, 3);
@@ -163,6 +165,8 @@ public:
     d_RemoveDomainKeyQuery_stmt.reset();
     d_ActivateDomainKeyQuery_stmt.reset();
     d_DeactivateDomainKeyQuery_stmt.reset();
+    d_PublishDomainKeyQuery_stmt.reset();
+    d_UnpublishDomainKeyQuery_stmt.reset();
     d_ClearDomainAllKeysQuery_stmt.reset();
     d_getTSIGKeyQuery_stmt.reset();
     d_setTSIGKeyQuery_stmt.reset();
@@ -220,6 +224,8 @@ public:
   bool removeDomainKey(const DNSName& name, unsigned int id) override;
   bool activateDomainKey(const DNSName& name, unsigned int id) override;
   bool deactivateDomainKey(const DNSName& name, unsigned int id) override;
+  bool publishDomainKey(const DNSName& name, unsigned int id) override;
+  bool unpublishDomainKey(const DNSName& name, unsigned int id) override;
   
   bool getTSIGKey(const DNSName& name, DNSName* algorithm, string* content) override;
   bool setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content) override;
@@ -319,6 +325,8 @@ private:
   string d_RemoveDomainKeyQuery;
   string d_ActivateDomainKeyQuery;
   string d_DeactivateDomainKeyQuery;
+  string d_PublishDomainKeyQuery;
+  string d_UnpublishDomainKeyQuery;
   string d_ClearDomainAllKeysQuery;
 
   string d_getTSIGKeyQuery;
@@ -383,6 +391,8 @@ private:
   unique_ptr<SSqlStatement> d_RemoveDomainKeyQuery_stmt;
   unique_ptr<SSqlStatement> d_ActivateDomainKeyQuery_stmt;
   unique_ptr<SSqlStatement> d_DeactivateDomainKeyQuery_stmt;
+  unique_ptr<SSqlStatement> d_PublishDomainKeyQuery_stmt;
+  unique_ptr<SSqlStatement> d_UnpublishDomainKeyQuery_stmt;
   unique_ptr<SSqlStatement> d_ClearDomainAllKeysQuery_stmt;
   unique_ptr<SSqlStatement> d_getTSIGKeyQuery_stmt;
   unique_ptr<SSqlStatement> d_setTSIGKeyQuery_stmt;
diff --git a/pdns/bind-dnssec.4.2.0_to_4.3.0_schema.sqlite3.sql b/pdns/bind-dnssec.4.2.0_to_4.3.0_schema.sqlite3.sql
new file mode 100644 (file)
index 0000000..d334fec
--- /dev/null
@@ -0,0 +1 @@
+ALTER TABLE cryptokeys ADD published BOOL DEFAULT 1;
index e4f75d6707a28b62acfdb3678e954f964a9bee5d..d9046bc55c35a9b36b39a6cc85f6791d486b2c6a 100644 (file)
@@ -12,6 +12,7 @@ create table cryptokeys (
  domain     VARCHAR(255) COLLATE NOCASE,
  flags      INT NOT NULL,
  active     BOOL,
+ published  BOOL,
  content    TEXT
 );
 
index 8500eae5a8f5466a6416dbe1d01adb5e565a630b..5f97bec6d21f01e1bc3ce6568a42a8647ac28773 100644 (file)
@@ -79,7 +79,7 @@ bool DNSSECKeeper::isPresigned(const DNSName& name)
   return meta=="1";
 }
 
-bool DNSSECKeeper::addKey(const DNSName& name, bool setSEPBit, int algorithm, int64_t& id, int bits, bool active)
+bool DNSSECKeeper::addKey(const DNSName& name, bool setSEPBit, int algorithm, int64_t& id, int bits, bool active, bool published)
 {
   if(!bits) {
     if(algorithm <= 10)
@@ -106,7 +106,7 @@ bool DNSSECKeeper::addKey(const DNSName& name, bool setSEPBit, int algorithm, in
   dspk.setKey(dpk);
   dspk.d_algorithm = algorithm;
   dspk.d_flags = setSEPBit ? 257 : 256;
-  return addKey(name, dspk, id, active);
+  return addKey(name, dspk, id, active, published);
 }
 
 void DNSSECKeeper::clearAllCaches() {
@@ -131,12 +131,13 @@ void DNSSECKeeper::clearCaches(const DNSName& name)
 }
 
 
-bool DNSSECKeeper::addKey(const DNSName& name, const DNSSECPrivateKey& dpk, int64_t& id, bool active)
+bool DNSSECKeeper::addKey(const DNSName& name, const DNSSECPrivateKey& dpk, int64_t& id, bool active, bool published)
 {
   clearCaches(name);
   DNSBackend::KeyData kd;
   kd.flags = dpk.d_flags; // the dpk doesn't get stored, only they key part
   kd.active = active;
+  kd.published = published;
   kd.content = dpk.getKey()->convertToISC();
  // now store it
   return d_keymetadb->addDomainKey(name, kd, id);
@@ -191,6 +192,18 @@ bool DNSSECKeeper::activateKey(const DNSName& zname, unsigned int id)
   return d_keymetadb->activateDomainKey(zname, id);
 }
 
+bool DNSSECKeeper::unpublishKey(const DNSName& zname, unsigned int id)
+{
+  clearCaches(zname);
+  return d_keymetadb->unpublishDomainKey(zname, id);
+}
+
+bool DNSSECKeeper::publishKey(const DNSName& zname, unsigned int id)
+{
+  clearCaches(zname);
+  return d_keymetadb->publishDomainKey(zname, id);
+}
+
 void DNSSECKeeper::getFromMetaOrDefault(const DNSName& zname, const std::string& key, std::string& value, const std::string& defaultvalue)
 {
   if (getFromMeta(zname, key, value))
@@ -515,6 +528,7 @@ DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const DNSName& zone, bool useCache)
     KeyMetaData kmd;
 
     kmd.active = kd.active;
+    kmd.published = kd.published;
     kmd.hasSEPBit = (kd.flags == 257);
     kmd.id = kd.id;
 
index 6fd4c1626001558eda217b858f214125148c43f4..321463fc45e68f7c8ecf2bdeba3fde785e2182aa 100644 (file)
@@ -183,6 +183,7 @@ public:
     unsigned int id;
     unsigned int flags;
     bool active;
+    bool published;
   };
 
   virtual bool getDomainKeys(const DNSName& name, std::vector<KeyData>& keys) { return false;}
@@ -190,6 +191,8 @@ public:
   virtual bool addDomainKey(const DNSName& name, const KeyData& key, int64_t& id){ return false; }
   virtual bool activateDomainKey(const DNSName& name, unsigned int id) { return false; }
   virtual bool deactivateDomainKey(const DNSName& name, unsigned int id) { return false; }
+  virtual bool publishDomainKey(const DNSName& name, unsigned int id) { return false; }
+  virtual bool unpublishDomainKey(const DNSName& name, unsigned int id) { return false; }
 
   virtual bool getTSIGKey(const DNSName& name, DNSName* algorithm, string* content) { return false; }
   virtual bool setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content) { return false; }
index 6007d4bc35c46a447749f045390511d8ee3bf476..d30e327f86b7756e40d02b62282a1add3d6f9d9e 100644 (file)
@@ -69,6 +69,7 @@ public:
     bool active;
     keytype_t keyType;
     bool hasSEPBit;
+    bool published;
   };
   typedef std::pair<DNSSECPrivateKey, KeyMetaData> keymeta_t;
   typedef std::vector<keymeta_t > keyset_t;
@@ -191,11 +192,13 @@ public:
   keyset_t getEntryPoints(const DNSName& zname);
   keyset_t getKeys(const DNSName& zone, bool useCache = true);
   DNSSECPrivateKey getKeyById(const DNSName& zone, unsigned int id);
-  bool addKey(const DNSName& zname, bool setSEPBit, int algorithm, int64_t& id, int bits=0, bool active=true);
-  bool addKey(const DNSName& zname, const DNSSECPrivateKey& dpk, int64_t& id, bool active=true);
+  bool addKey(const DNSName& zname, bool setSEPBit, int algorithm, int64_t& id, int bits=0, bool active=true, bool published=true);
+  bool addKey(const DNSName& zname, const DNSSECPrivateKey& dpk, int64_t& id, bool active=true, bool published=true);
   bool removeKey(const DNSName& zname, unsigned int id);
   bool activateKey(const DNSName& zname, unsigned int id);
   bool deactivateKey(const DNSName& zname, unsigned int id);
+  bool publishKey(const DNSName& zname, unsigned int id);
+  bool unpublishKey(const DNSName& zname, unsigned int id);
   bool checkKeys(const DNSName& zname, vector<string>* errorMessages = nullptr);
 
   bool getNSEC3PARAM(const DNSName& zname, NSEC3PARAMRecordContent* n3p=0, bool* narrow=0);
index 3a447b7d934bf2dca400cd6cba3215bd3c94ea2f..872b08a6b817de2dece26854f9c723d462cd7851 100644 (file)
@@ -117,6 +117,9 @@ bool PacketHandler::addCDNSKEY(DNSPacket& p, std::unique_ptr<DNSPacket>& r, cons
 
   DNSSECKeeper::keyset_t entryPoints = d_dk.getEntryPoints(p.qdomain);
   for(const auto& value: entryPoints) {
+    if (!value.second.published) {
+      continue;
+    }
     rr.dr.d_type=QType::CDNSKEY;
     rr.dr.d_ttl=sd.default_ttl;
     rr.dr.d_name=p.qdomain;
@@ -153,6 +156,9 @@ bool PacketHandler::addDNSKEY(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const
 
   DNSSECKeeper::keyset_t keyset = d_dk.getKeys(p.qdomain);
   for(const auto& value: keyset) {
+    if (!value.second.published) {
+      continue;
+    }
     rr.dr.d_type=QType::DNSKEY;
     rr.dr.d_ttl=sd.default_ttl;
     rr.dr.d_name=p.qdomain;
@@ -205,6 +211,9 @@ bool PacketHandler::addCDS(DNSPacket& p, std::unique_ptr<DNSPacket>& r, const SO
   DNSSECKeeper::keyset_t keyset = d_dk.getEntryPoints(p.qdomain);
 
   for(auto const &value : keyset) {
+    if (!value.second.published) {
+      continue;
+    }
     for(auto const &digestAlgo : digestAlgos){
       rr.dr.d_content=std::make_shared<DSRecordContent>(makeDSFromDNSKey(p.qdomain, value.first.getDNSKEY(), pdns_stou(digestAlgo)));
       r->addRecord(rr);
index f3e7fd65a4313a167e98936dd5ebdabf13adc1ec..f62277449c88bcd35fa767a2a9a569e9b9aa40fd 100644 (file)
@@ -1687,7 +1687,7 @@ bool showZone(DNSSECKeeper& dk, const DNSName& zone, bool exportDS = false)
       if (!exportDS) {
         cout<<", flags = "<<std::to_string(value.first.d_flags);
         cout<<", tag = "<<value.first.getDNSKEY().getTag();
-        cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.getKey()->getBits()<<"\t"<<((int)value.second.active == 1 ? "  A" : "Ina")<<"ctive ( " + algname + " ) "<<endl;
+        cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.getKey()->getBits()<<"\t"<<((int)value.second.active == 1 ? "  A" : "Ina")<<"ctive\t"<<(value.second.published ? " Published" : " Unpublished")<<"  ( " + algname + " ) "<<endl;
       }
 
       if (!exportDS) {
@@ -1771,7 +1771,7 @@ bool secureZone(DNSSECKeeper& dk, const DNSName& zone)
 
     int k_real_algo = DNSSECKeeper::shorthand2algorithm(k_algo);
 
-    if (!dk.addKey(zone, true, k_real_algo, id, k_size, true)) {
+    if (!dk.addKey(zone, true, k_real_algo, id, k_size, true, true)) {
       cerr<<"No backend was able to secure '"<<zone<<"', most likely because no DNSSEC"<<endl;
       cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl;
       cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl;
@@ -1785,7 +1785,7 @@ bool secureZone(DNSSECKeeper& dk, const DNSName& zone)
 
     int z_real_algo = DNSSECKeeper::shorthand2algorithm(z_algo);
 
-    if (!dk.addKey(zone, false, z_real_algo, id, z_size, true)) {
+    if (!dk.addKey(zone, false, z_real_algo, id, z_size, true, true)) {
       cerr<<"No backend was able to secure '"<<zone<<"', most likely because no DNSSEC"<<endl;
       cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl;
       cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl;
@@ -1997,7 +1997,7 @@ try
     cout<<"activate-zone-key ZONE KEY-ID      Activate the key with key id KEY-ID in ZONE"<<endl;
     cout<<"add-record ZONE NAME TYPE [ttl] content"<<endl;
     cout<<"             [content..]           Add one or more records to ZONE"<<endl;
-    cout<<"add-zone-key ZONE {zsk|ksk} [BITS] [active|inactive]"<<endl;
+    cout<<"add-zone-key ZONE {zsk|ksk} [BITS] [active|inactive] [published|unpublished]"<<endl;
     cout<<"             [rsasha1|rsasha1-nsec3-sha1|rsasha256|rsasha512|ecdsa256|ecdsa384";
 #if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519)
     cout<<"|ed25519";
@@ -2045,7 +2045,7 @@ try
     cout<<"increase-serial ZONE               Increases the SOA-serial by 1. Uses SOA-EDIT"<<endl;
     cout<<"import-tsig-key NAME ALGORITHM KEY Import TSIG key"<<endl;
     cout<<"import-zone-key ZONE FILE          Import from a file a private key, ZSK or KSK"<<endl;
-    cout<<"       [active|inactive] [ksk|zsk] Defaults to KSK and active"<<endl;
+    cout<<"       [active|inactive] [ksk|zsk] [published|unpublished] Defaults to KSK, active and published"<<endl;
     cout<<"ipdecrypt IP passphrase/key [key]  Encrypt IP address using passphrase or base64 key"<<endl;
     cout<<"ipencrypt IP passphrase/key [key]  Encrypt IP address using passphrase or base64 key"<<endl;
     cout<<"load-zone ZONE FILE                Load ZONE from FILE, possibly creating zone or atomically"<<endl;
@@ -2056,6 +2056,7 @@ try
     cout<<"list-all-zones [master|slave|native]"<<endl;
     cout<<"                                   List all zone names"<<endl;;
     cout<<"list-tsig-keys                     List all TSIG keys"<<endl;
+    cout<<"publish-zone-key ZONE KEY-ID       Publish the zone key with key id KEY-ID in ZONE"<<endl;
     cout<<"rectify-zone ZONE [ZONE ..]        Fix up DNSSEC fields (order, auth)"<<endl;
     cout<<"rectify-all-zones [quiet]          Rectify all zones. Optionally quiet output with errors only"<<endl;
     cout<<"remove-zone-key ZONE KEY-ID        Remove key with KEY-ID from ZONE"<<endl;
@@ -2075,6 +2076,7 @@ try
     cout<<"set-meta ZONE KIND [VALUE] [VALUE] Set zone metadata, optionally providing a value. *No* value clears meta"<<endl;
     cout<<"                                   Note - this will replace all metadata records of KIND!"<<endl;
     cout<<"show-zone ZONE                     Show DNSSEC (public) key details about a zone"<<endl;
+    cout<<"unpublish-zone-key ZONE KEY-ID     Unpublish the zone key with key id KEY-ID in ZONE"<<endl;
     cout<<"unset-nsec3 ZONE                   Switch back to NSEC"<<endl;
     cout<<"unset-presigned ZONE               No longer use presigned RRSIGs"<<endl;
     cout<<"unset-publish-cdnskey ZONE         Disable sending CDNSKEY responses for ZONE"<<endl;
@@ -2310,6 +2312,43 @@ try
     }
     return 0;
   }
+  else if(cmds[0] == "publish-zone-key") {
+    if(cmds.size() != 3) {
+      cerr << "Syntax: pdnsutil publish-zone-key ZONE KEY-ID"<<endl;
+      return 0;
+    }
+    DNSName zone(cmds[1]);
+    unsigned int id=atoi(cmds[2].c_str()); // if you make this pdns_stou, the error gets worse
+    if(!id)
+    {
+      cerr<<"Invalid KEY-ID '"<<cmds[2]<<"'"<<endl;
+      return 1;
+    }
+    if (!dk.publishKey(zone, id)) {
+      cerr<<"Publishing of key failed"<<endl;
+      return 1;
+    }
+    return 0;
+  }
+  else if(cmds[0] == "unpublish-zone-key") {
+    if(cmds.size() != 3) {
+      cerr << "Syntax: pdnsutil unpublish-zone-key ZONE KEY-ID"<<endl;
+      return 0;
+    }
+    DNSName zone(cmds[1]);
+    unsigned int id=atoi(cmds[2].c_str()); // if you make this pdns_stou, the error gets worse
+    if(!id)
+    {
+      cerr<<"Invalid KEY-ID '"<<cmds[2]<<"'"<<endl;
+      return 1;
+    }
+    if (!dk.unpublishKey(zone, id)) {
+      cerr<<"Unpublishing of key failed"<<endl;
+      return 1;
+    }
+    return 0;
+  }
+
   else if(cmds[0] == "add-zone-key") {
     if(cmds.size() < 3 ) {
       cerr << "Syntax: pdnsutil add-zone-key ZONE zsk|ksk [BITS] [active|inactive] [rsasha1|rsasha1-nsec3-sha1|rsasha256|rsasha512|ecdsa256|ecdsa384";
@@ -2338,6 +2377,7 @@ try
     int bits=0;
     int algorithm=DNSSECKeeper::ECDSA256;
     bool active=false;
+    bool published=true;
     for(unsigned int n=2; n < cmds.size(); ++n) {
       if(pdns_iequals(cmds[n], "zsk"))
         keyOrZone = false;
@@ -2349,6 +2389,10 @@ try
         active=true;
       } else if(pdns_iequals(cmds[n], "inactive") || pdns_iequals(cmds[n], "passive")) { // 'passive' eventually needs to be removed
         active=false;
+      } else if(pdns_iequals(cmds[n], "published")) {
+        published = true;
+      } else if(pdns_iequals(cmds[n], "unpublished")) {
+        published = false;
       } else if(pdns_stou(cmds[n])) {
         bits = pdns_stou(cmds[n]);
       } else {
@@ -2357,7 +2401,7 @@ try
       }
     }
     int64_t id;
-    if (!dk.addKey(zone, keyOrZone, algorithm, id, bits, active)) {
+    if (!dk.addKey(zone, keyOrZone, algorithm, id, bits, active, published)) {
       cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
       exit(1);
     } else {
@@ -2790,6 +2834,7 @@ try
 
     dpk.d_flags = 257;
     bool active=true;
+    bool published=true;
 
     for(unsigned int n = 3; n < cmds.size(); ++n) {
       if(pdns_iequals(cmds[n], "ZSK"))
@@ -2800,13 +2845,17 @@ try
         active = 1;
       else if(pdns_iequals(cmds[n], "passive") || pdns_iequals(cmds[n], "inactive")) // passive eventually needs to be removed
         active = 0;
+      else if(pdns_iequals(cmds[n], "published"))
+        published = 1;
+      else if(pdns_iequals(cmds[n], "unpublished"))
+        published = 0;
       else {
         cerr<<"Unknown key flag '"<<cmds[n]<<"'"<<endl;
         exit(1);
       }
     }
     int64_t id;
-    if (!dk.addKey(DNSName(zone), dpk, id, active)) {
+    if (!dk.addKey(DNSName(zone), dpk, id, active, published)) {
       cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
       exit(1);
     }
index d82c2925aad44ad250ad8c680d7b6733b59c6ef1..17d4527a6ce892bd04eddfda473fa2525b7c5a73 100644 (file)
@@ -688,6 +688,9 @@ int TCPNameserver::doAXFR(const DNSName &target, std::unique_ptr<DNSPacket>& q,
     entryPointIds.insert(value.second.id);
 
   for(const DNSSECKeeper::keyset_t::value_type& value :  keys) {
+    if (!value.second.published) {
+      continue;
+    }
     zrr.dr.d_type = QType::DNSKEY;
     zrr.dr.d_content = std::make_shared<DNSKEYRecordContent>(value.first.getDNSKEY());
     DNSName keyname = NSEC3Zone ? DNSName(toBase32Hex(hashQNameWithSalt(ns3pr, zrr.dr.d_name))) : zrr.dr.d_name;
index 4e792aa4c6ee694bdc46233291a85e077c087a24..266b25be13f452c05d2abbe1665743ddd90e2900 100644 (file)
@@ -190,6 +190,25 @@ bool UeberBackend::deactivateDomainKey(const DNSName& name, unsigned int id)
   return false;
 }
 
+bool UeberBackend::publishDomainKey(const DNSName& name, unsigned int id)
+{
+  for(DNSBackend* db :  backends) {
+    if(db->publishDomainKey(name, id))
+      return true;
+  }
+  return false;
+}
+
+bool UeberBackend::unpublishDomainKey(const DNSName& name, unsigned int id)
+{
+  for(DNSBackend* db :  backends) {
+    if(db->unpublishDomainKey(name, id))
+      return true;
+  }
+  return false;
+}
+
+
 bool UeberBackend::removeDomainKey(const DNSName& name, unsigned int id)
 {
   for(DNSBackend* db :  backends) {
index ffff7f1b9fae7f41588e5202937123fd4807a003..062645d4442a62582c2507ef73a86c05b0bb00d5 100644 (file)
@@ -122,6 +122,8 @@ public:
   bool removeDomainKey(const DNSName& name, unsigned int id);
   bool activateDomainKey(const DNSName& name, unsigned int id);
   bool deactivateDomainKey(const DNSName& name, unsigned int id);
+  bool publishDomainKey(const DNSName& name, unsigned int id);
+  bool unpublishDomainKey(const DNSName& name, unsigned int id);
 
   bool getTSIGKey(const DNSName& name, DNSName* algorithm, string* content);
   bool setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content);
index e68a3b3a9e19cedb07d281cadcd9c5d5f842c7e2..dd966f3ea024956a85c0db4c049b472db8a994da 100644 (file)
@@ -1055,6 +1055,7 @@ static void apiZoneCryptokeysGET(DNSName zonename, int inquireKeyId, HttpRespons
         { "type", "Cryptokey" },
         { "id", (int)value.second.id },
         { "active", value.second.active },
+        { "published", value.second.published },
         { "keytype", keyType },
         { "flags", (uint16_t)value.first.d_flags },
         { "dnskey", value.first.getDNSKEY().getZoneRepresentation() },
@@ -1153,6 +1154,7 @@ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpRespon
     privatekey_fieldname = "content";
   }
   bool active = boolFromJson(document, "active", false);
+  bool published = boolFromJson(document, "published", true);
   bool keyOrZone;
 
   if (stringFromJson(document, "keytype") == "ksk" || stringFromJson(document, "keytype") == "csk") {
@@ -1188,7 +1190,7 @@ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpRespon
     }
 
     try {
-      if (!dk->addKey(zonename, keyOrZone, algorithm, insertedId, bits, active)) {
+      if (!dk->addKey(zonename, keyOrZone, algorithm, insertedId, bits, active, published)) {
         throw ApiException("Adding key failed, perhaps DNSSEC not enabled in configuration?");
       }
     } catch (std::runtime_error& error) {
@@ -1217,7 +1219,7 @@ static void apiZoneCryptokeysPOST(DNSName zonename, HttpRequest *req, HttpRespon
     catch (std::runtime_error& error) {
       throw ApiException("Key could not be parsed. Make sure your key format is correct.");
     } try {
-      if (!dk->addKey(zonename, dpk,insertedId, active)) {
+      if (!dk->addKey(zonename, dpk,insertedId, active, published)) {
         throw ApiException("Adding key failed, perhaps DNSSEC not enabled in configuration?");
       }
     } catch (std::runtime_error& error) {
@@ -1248,6 +1250,7 @@ static void apiZoneCryptokeysPUT(DNSName zonename, int inquireKeyId, HttpRequest
   auto document = req->json();
   //throws an exception if the key does not exist or is not a bool
   bool active = boolFromJson(document, "active");
+  bool published = boolFromJson(document, "published", true);
   if (active) {
     if (!dk->activateKey(zonename, inquireKeyId)) {
       resp->setErrorResult("Could not activate Key: " + req->parameters["key_id"] + " in Zone: " + zonename.toString(), 422);
@@ -1259,6 +1262,19 @@ static void apiZoneCryptokeysPUT(DNSName zonename, int inquireKeyId, HttpRequest
       return;
     }
   }
+
+  if (published) {
+    if (!dk->publishKey(zonename, inquireKeyId)) {
+      resp->setErrorResult("Could not publish Key: " + req->parameters["key_id"] + " in Zone: " + zonename.toString(), 422);
+      return;
+    }
+  } else {
+    if (!dk->unpublishKey(zonename, inquireKeyId)) {
+      resp->setErrorResult("Could not unpublish Key: " + req->parameters["key_id"] + " in Zone: " + zonename.toString(), 422);
+      return;
+    }
+  }
+
   resp->body = "";
   resp->status = 204;
   return;
index c7fbe2758bb46eff529dd706813a5ebaa87e0a28..7db391f8c293c919184a504737ee8a3c2ba2baff 100644 (file)
@@ -2110,6 +2110,7 @@ class AuthZoneKeys(ApiTestCase, AuthZonesHelperMixin):
             u'type': u'Cryptokey',
             u'keytype': u'csk',
             u'flags': 257,
+            u'published': True,
             u'id': 1}
         self.assertEquals(key0, expected)
 
index 8a4c874d6459bff564b647ffd3a6e8ecf974691c..24102c5f3a98a981d7ec38ec3d24ad48d7c719c4 100644 (file)
@@ -30,8 +30,8 @@ class Cryptokeys(ApiTestCase):
         self.remove_zone_key(self.keyid)
 
     # Adding a key to self.zone using the pdnsutil command
-    def add_zone_key(self, status='inactive'):
-        return pdnsutil("add-zone-key", self.zone_nodot, "ksk", status)
+    def add_zone_key(self, status=['inactive']):
+        return pdnsutil("add-zone-key", self.zone_nodot, "ksk", *status)
 
     # Removes a key from self.zone by id using the pdnsutil command
     def remove_zone_key(self, key_id):
@@ -191,7 +191,8 @@ class Cryptokeys(ApiTestCase):
         self.keyid = self.add_zone_key()
 
         payload = {
-            'active': True
+            'active': True,
+            'published': True,
         }
         r = self.session.put(
             self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
@@ -205,10 +206,11 @@ class Cryptokeys(ApiTestCase):
         self.assertIn("Active", out)
 
     def test_put_deactivate_key(self):
-        self.keyid = self.add_zone_key(status='active')
+        self.keyid = self.add_zone_key(status=['active'])
         # deactivate key
         payload2 = {
-            'active': False
+            'active': False,
+            'published': True,
         }
 
         r = self.session.put(
@@ -227,7 +229,8 @@ class Cryptokeys(ApiTestCase):
 
         # deactivate key
         payload = {
-            'active': False
+            'active': False,
+            'published': True,
         }
 
         r = self.session.put(
@@ -242,11 +245,12 @@ class Cryptokeys(ApiTestCase):
         self.assertIn("Inactive", out)
 
     def test_put_activate_active_key(self):
-        self.keyid =self.add_zone_key(status='active')
+        self.keyid = self.add_zone_key(status=['active'])
 
         # activate key
         payload2 = {
-            'active': True
+            'active': True,
+            'published': True,
         }
         r = self.session.put(
             self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
@@ -258,3 +262,79 @@ class Cryptokeys(ApiTestCase):
         # check if key is activated
         out = pdnsutil("show-zone", self.zone_nodot)
         self.assertIn("Active", out)
+
+    def test_put_unpublish_key(self):
+        self.keyid = self.add_zone_key(status=['active'])
+
+        payload = {
+            'active': True,
+            'published': False,
+        }
+        r = self.session.put(
+            self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+            data=json.dumps(payload),
+            headers={'content-type': 'application/json'})
+        self.assertEquals(r.status_code, 204)
+        self.assertEquals(r.content, b"")
+
+        # check if key is activated
+        out = pdnsutil("show-zone", self.zone_nodot)
+        self.assertIn("Unpublished", out)
+
+    def test_put_publish_key(self):
+        self.keyid = self.add_zone_key(status=['active', 'unpublished'])
+        # deactivate key
+        payload2 = {
+            'active': True,
+            'published': True,
+        }
+
+        r = self.session.put(
+            self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+            data=json.dumps(payload2),
+            headers={'content-type': 'application/json'})
+        self.assertEquals(r.status_code, 204)
+        self.assertEquals(r.content, b"")
+
+        # check if key is deactivated
+        out = pdnsutil("show-zone", self.zone_nodot)
+        self.assertIn("Published", out)
+
+    def test_put_publish_published_key(self):
+        self.keyid = self.add_zone_key(status=['active'])
+
+        # deactivate key
+        payload = {
+            'active': True,
+            'published': True,
+        }
+
+        r = self.session.put(
+            self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+            data=json.dumps(payload),
+            headers={'content-type': 'application/json'})
+        self.assertEquals(r.status_code, 204)
+        self.assertEquals(r.content, b"")
+
+        # check if key is still deactivated
+        out = pdnsutil("show-zone", self.zone_nodot)
+        self.assertIn("Published", out)
+
+    def test_put_unpublish_unpublished_key(self):
+        self.keyid = self.add_zone_key(status=['active', 'unpublished'])
+
+        # activate key
+        payload2 = {
+            'active': True,
+            'published': False,
+        }
+        r = self.session.put(
+            self.url("/api/v1/servers/localhost/zones/"+self.zone+"/cryptokeys/"+self.keyid),
+            data=json.dumps(payload2),
+            headers={'content-type': 'application/json'})
+        self.assertEquals(r.status_code, 204)
+        self.assertEquals(r.content, b"")
+
+        # check if key is activated
+        out = pdnsutil("show-zone", self.zone_nodot)
+        self.assertIn("Unpublished", out)
index d0086b5e1bf14dc394e1bfb1df98c357f09ae90d..0ec4367daeaabddc181d338f5a7440bf44ae86db 100644 (file)
@@ -13,4 +13,5 @@ a63dc120391d9df0003f2ec4f461a6af  ../regression-tests/zones/secure-delegated.dns
 b1f775045fa2cf0a3b91aa834af06e49  ../regression-tests/zones/stest.com
 a98864b315f16bcf49ce577426063c42  ../regression-tests/zones/cdnskey-cds-test.com
 9aeed2c26d0c3ba3baf22dfa9568c451  ../regression-tests/zones/2.0.192.in-addr.arpa
+99c73e8b5db5781fec1ac3fa6a2662a9  ../regression-tests/zones/cryptokeys.org
 52a95993ada0b4ed986a2fe6463a27e0  ../modules/tinydnsbackend/data.cdb
index 579935bfb803a4c84987878618990a50c6b3e68b..c2b577015600971ce246b2347d9e4812bf1ec4d3 100644 (file)
@@ -67,6 +67,12 @@ __EOF__
                                then
                                        $PDNSUTIL --config-dir=. --config-name=bind set-nsec3 $zone '1 1 1 abcd' narrow 2>&1
                                fi
+                if [ $zone = cryptokeys.org ]
+                then
+                    $PDNSUTIL --config-dir=. --config-name=bind add-zone-key $zone zsk 384 active unpublished ecdsa384
+                    $PDNSUTIL --config-dir=. --config-name=bind add-zone-key $zone zsk 2048 inactive published rsasha512
+                    $PDNSUTIL --config-dir=. --config-name=bind add-zone-key $zone zsk 2048 inactive unpublished rsasha256
+                fi
                        fi
                        if [ "$zone" = "tsig.com" ]; then
                                $PDNSUTIL --config-dir=. --config-name=bind import-tsig-key test $ALGORITHM $KEY
index 4099c5ca10ae167057abf7b43a65deecadea580c..e17a3bd134981a90327cd18031b9755264731fa6 100644 (file)
@@ -13,7 +13,7 @@ godbc-datasource=$GODBC_SQLITE3_DSN
 
 # ../pdns/pdns_server --module-dir=./modules/ --launch=gsqlite3 --config | grep gsqlite3 | grep query | grep = | cut -c3- | perl -pe 's/^gsqlite3/godbc/; s/:\w+/?/g'
 godbc-activate-domain-key-query=update cryptokeys set active=1 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?
-godbc-add-domain-key-query=insert into cryptokeys (domain_id, flags, active, content) select id, ?,?, ? from domains where name=?
+godbc-add-domain-key-query=insert into cryptokeys (domain_id, flags, active, published, content) select id, ?, ?, ?, ? from domains where name=?
 godbc-any-id-query=SELECT content,ttl,prio,type,domain_id,disabled,name,auth FROM records WHERE disabled=0 and name=? and domain_id=?
 godbc-any-query=SELECT content,ttl,prio,type,domain_id,disabled,name,auth FROM records WHERE disabled=0 and name=?
 godbc-basic-query=SELECT content,ttl,prio,type,domain_id,disabled,name,auth FROM records WHERE disabled=0 and type=? and name=?
@@ -39,6 +39,7 @@ godbc-get-order-first-query=select ordername from records where disabled=0 and d
 godbc-get-order-last-query=select ordername, name from records where disabled=0 and ordername != '' and domain_id=? and ordername is not null order by 1 desc limit 1
 godbc-get-tsig-key-query=select algorithm, secret from tsigkeys where name=?
 godbc-get-tsig-keys-query=select name,algorithm, secret from tsigkeys
+godbc-publish-domain-key-query=update cryptokeys set published=1 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?
 godbc-id-query=SELECT content,ttl,prio,type,domain_id,disabled,name,auth FROM records WHERE disabled=0 and type=? and name=? and domain_id=?
 godbc-info-all-master-query=select id,name,master,last_check,notified_serial,type from domains where type='MASTER'
 godbc-info-all-slaves-query=select id,name,master,last_check from domains where type='SLAVE'
@@ -48,7 +49,7 @@ godbc-insert-empty-non-terminal-order-query=insert into records (type,domain_id,
 godbc-insert-record-query=insert into records (content,ttl,prio,type,domain_id,disabled,name,ordername,auth) values (?,?,?,?,?,?,?,?,?)
 godbc-insert-zone-query=insert into domains (type,name,master,account,last_check,notified_serial) values(?, ?, ?, ?, null, null)
 godbc-list-comments-query=SELECT domain_id,name,type,modified_at,account,comment FROM comments WHERE domain_id=?
-godbc-list-domain-keys-query=select cryptokeys.id, flags, active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=?
+godbc-list-domain-keys-query=select cryptokeys.id, flags, active, published, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=?
 godbc-list-query=SELECT content,ttl,prio,type,domain_id,disabled,name,auth FROM records WHERE (disabled=0 OR ?) and domain_id=? order by name, type
 godbc-list-subzone-query=SELECT content,ttl,prio,type,domain_id,disabled,name,auth FROM records WHERE disabled=0 and (name=? OR name like ?) and domain_id=?
 godbc-nullify-ordername-and-update-auth-query=update records set ordername=NULL,auth=? where domain_id=? and name=? and disabled=0
@@ -60,6 +61,7 @@ godbc-search-records-query=SELECT content,ttl,prio,type,domain_id,disabled,name,
 godbc-set-domain-metadata-query=insert into domainmetadata (domain_id, kind, content) select id, ?, ? from domains where name=?
 godbc-set-tsig-key-query=replace into tsigkeys (name,algorithm,secret) values(?,?,?)
 godbc-supermaster-query=select account from supermasters where ip=? and nameserver=?
+godbc-unpublish-domain-key-query=update cryptokeys set published=0 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?
 godbc-update-account-query=update domains set account=? where name=?
 godbc-update-kind-query=update domains set type=? where name=?
 godbc-update-lastcheck-query=update domains set last_check=? where id=?
index 99eff8ecf457f5cb33b731d73ec970f687d3d5a0..86840ff1d576a91fb5fa43f74e50e869e1dce783 100644 (file)
@@ -25,6 +25,12 @@ gsql_master()
                                $PDNSUTIL --config-dir=. --config-name=$backend set-nsec3 $zone '1 1 1 abcd' narrow 2>&1
                        fi
                        securezone $zone ${backend}
+            if [ $zone = cryptokeys.org ]
+            then
+                $PDNSUTIL --config-dir=. --config-name=$backend add-zone-key $zone zsk 384 active unpublished ecdsa384
+                $PDNSUTIL --config-dir=. --config-name=$backend add-zone-key $zone zsk 2048 inactive published rsasha512
+                $PDNSUTIL --config-dir=. --config-name=$backend add-zone-key $zone zsk 2048 inactive unpublished rsasha256
+            fi
                else
                        $PDNSUTIL --config-dir=. --config-name=$backend rectify-zone $zone 2>&1
                fi
index 9f37e57725c598b6362f5d03ddaa7d0d35d652fc..b557b6a88c9c63feeb20f60e02f162d93b4e548e 100644 (file)
@@ -29,6 +29,12 @@ __EOF__
                         $PDNSUTIL --config-dir=. --config-name=lmdb set-nsec3 $zone '1 1 1 abcd' narrow 2>&1
                     fi
                     securezone $zone lmdb
+                    if [ $zone = cryptokeys.org ]
+                    then
+                        $PDNSUTIL --config-dir=. --config-name=lmdb add-zone-key $zone zsk 384 active unpublished ecdsa384
+                        $PDNSUTIL --config-dir=. --config-name=lmdb add-zone-key $zone zsk 2048 inactive published rsasha512
+                        $PDNSUTIL --config-dir=. --config-name=lmdb add-zone-key $zone zsk 2048 inactive unpublished rsasha256
+                    fi
                 fi
             else
                 $PDNSUTIL --config-dir=. --config-name=lmdb rectify-zone $zone 2>&1
index 2a1a754da754ef671aea68e074dfa15d1f36778b..52c383f94b0ab73620b35505a7e9e63ecb6838c3 100644 (file)
@@ -87,3 +87,9 @@ zone "2.0.192.in-addr.arpa"{
        type master;
        file "2.0.192.in-addr.arpa";
 };
+
+zone "cryptokeys.org"{
+    type master;
+    file "cryptokeys.org";
+};
+
diff --git a/regression-tests/tests/cryptokeys/command b/regression-tests/tests/cryptokeys/command
new file mode 100755 (executable)
index 0000000..1529298
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+cleandig cryptokeys.org DNSKEY dnssec
diff --git a/regression-tests/tests/cryptokeys/description b/regression-tests/tests/cryptokeys/description
new file mode 100644 (file)
index 0000000..cee275f
--- /dev/null
@@ -0,0 +1 @@
+Test all possible combinations between active, inactive, published and unpublished keys.
diff --git a/regression-tests/tests/cryptokeys/expected_result.dnssec b/regression-tests/tests/cryptokeys/expected_result.dnssec
new file mode 100644 (file)
index 0000000..409f965
--- /dev/null
@@ -0,0 +1,7 @@
+0      cryptokeys.org. IN      DNSKEY  3600    256 3 10 ...
+0      cryptokeys.org. IN      DNSKEY  3600    257 3 13 ...
+0      cryptokeys.org. IN      RRSIG   3600    DNSKEY 13 2 3600 [expiry] [inception] [keytag] cryptokeys.org. ...
+0      cryptokeys.org. IN      RRSIG   3600    DNSKEY 14 2 3600 [expiry] [inception] [keytag] cryptokeys.org. ...
+2      .       IN      OPT     32768   
+Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='cryptokeys.org.', qtype=DNSKEY
diff --git a/regression-tests/tests/cryptokeys/skip.nodnssec b/regression-tests/tests/cryptokeys/skip.nodnssec
new file mode 100644 (file)
index 0000000..e69de29
index 30dbe195566e720d5e52dfece41b69c834e775eb..e57cbd01828dab9a09da4c6f01b1e82d005b42ce 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/env bash
-for zone in $(grep 'zone ' named.conf  | cut -f2 -d\" | grep -v '^\(example.com\|nztest.com\|insecure.dnssec-parent.com\)$')
+for zone in $(grep 'zone ' named.conf  | cut -f2 -d\" | grep -v '^\(cryptokeys.org\|example.com\|nztest.com\|insecure.dnssec-parent.com\)$')
 do
        TFILE=$(mktemp tmp.XXXXXXXXXX)
        drill -p $port axfr $zone @$nameserver | ldns-read-zone -z -u CDS -u CDNSKEY > $TFILE
index 936d3b67bd7cae20b9b884f2ba892316fbffd5b9..a9b5df69cbc6b88345098868358001135edf510f 100644 (file)
@@ -3,6 +3,7 @@
 /*.bind
 /*.com-slave
 /*.dyndns-slave
+/*.org-slave
 /2.0.192.in-addr.arpa-slave
 /*.signed
 /*.nsd