From 3391829938b4544a59c93c4734532ce2fdc311bf Mon Sep 17 00:00:00 2001 From: Robin Geuze Date: Thu, 8 Aug 2019 20:03:28 +0200 Subject: [PATCH] Implement published and unpublished dnskeys to allow algorith rollovers. --- docs/backends/generic-sql.rst | 2 + docs/backends/lua2.rst | 1 + docs/backends/remote.rst | 104 ++++++++++++++++-- .../swagger/authoritative-api-swagger.yaml | 3 + docs/manpages/pdnsutil.1.rst | 12 +- modules/bindbackend/bindbackend2.hh | 4 + modules/bindbackend/binddnssec.cc | 55 ++++++++- modules/geoipbackend/geoipbackend.cc | 12 +- modules/geoipbackend/geoipbackend.hh | 2 + .../4.2.0_to_4.3.0_schema.mysql.sql | 2 + modules/gmysqlbackend/gmysqlbackend.cc | 6 +- modules/gmysqlbackend/schema.mysql.sql | 1 + .../4.2.0_to_4.3.0_schema.mssql.sql | 1 + modules/godbcbackend/godbcbackend.cc | 6 +- modules/godbcbackend/schema.mssql.sql | 1 + .../4.2.0_to_4.3.0_schema.pgsql.sql | 1 + modules/gpgsqlbackend/gpgsqlbackend.cc | 6 +- modules/gpgsqlbackend/schema.pgsql.sql | 1 + .../4.2.0_to_4.3.0_schema.sqlite3.sql | 1 + modules/gsqlite3backend/gsqlite3backend.cc | 6 +- modules/gsqlite3backend/schema.sqlite3.sql | 1 + modules/lmdbbackend/lmdbbackend.cc | 78 +++++++++++-- modules/lmdbbackend/lmdbbackend.hh | 3 + modules/lua2backend/lua2api2.hh | 5 +- modules/remotebackend/example.rb | 2 + modules/remotebackend/httpconnector.cc | 3 +- .../remotebackend/regression-tests/backend.rb | 32 +++++- .../regression-tests/dnsbackend.rb | 1 + .../regression-tests/test-schema.sql | 1 + modules/remotebackend/remotebackend.cc | 41 +++++++ modules/remotebackend/remotebackend.hh | 2 + .../remotebackend/test-remotebackend-keys.hh | 4 +- modules/remotebackend/test-remotebackend.cc | 1 + modules/remotebackend/unittest.rb | 22 ++++ modules/remotebackend/unittest_http.rb | 1 + pdns/backends/gsql/gsqlbackend.cc | 52 ++++++++- pdns/backends/gsql/gsqlbackend.hh | 12 +- ...d-dnssec.4.2.0_to_4.3.0_schema.sqlite3.sql | 1 + pdns/bind-dnssec.schema.sqlite3.sql | 1 + pdns/dbdnsseckeeper.cc | 20 +++- pdns/dnsbackend.hh | 3 + pdns/dnsseckeeper.hh | 7 +- pdns/packethandler.cc | 9 ++ pdns/pdnsutil.cc | 63 +++++++++-- pdns/tcpreceiver.cc | 3 + pdns/ueberbackend.cc | 19 ++++ pdns/ueberbackend.hh | 2 + pdns/ws-auth.cc | 20 +++- regression-tests.api/test_Zones.py | 1 + regression-tests.api/test_cryptokeys.py | 96 ++++++++++++++-- .../tinydns-data-check/expected_result | 1 + regression-tests/backends/bind-master | 6 + .../backends/godbc_sqlite3-master | 6 +- regression-tests/backends/gsql-common | 6 + regression-tests/backends/lmdb-master | 6 + regression-tests/named.conf | 6 + regression-tests/tests/cryptokeys/command | 2 + regression-tests/tests/cryptokeys/description | 1 + .../tests/cryptokeys/expected_result.dnssec | 7 ++ .../tests/cryptokeys/skip.nodnssec | 0 .../tests/verify-dnssec-zone/command | 2 +- regression-tests/zones/.gitignore | 1 + 62 files changed, 707 insertions(+), 70 deletions(-) create mode 100644 modules/gmysqlbackend/4.2.0_to_4.3.0_schema.mysql.sql create mode 100644 modules/godbcbackend/4.2.0_to_4.3.0_schema.mssql.sql create mode 100644 modules/gpgsqlbackend/4.2.0_to_4.3.0_schema.pgsql.sql create mode 100644 modules/gsqlite3backend/4.2.0_to_4.3.0_schema.sqlite3.sql create mode 100644 pdns/bind-dnssec.4.2.0_to_4.3.0_schema.sqlite3.sql create mode 100755 regression-tests/tests/cryptokeys/command create mode 100644 regression-tests/tests/cryptokeys/description create mode 100644 regression-tests/tests/cryptokeys/expected_result.dnssec create mode 100644 regression-tests/tests/cryptokeys/skip.nodnssec diff --git a/docs/backends/generic-sql.rst b/docs/backends/generic-sql.rst index 78491e5ac3..66d1772511 100644 --- a/docs/backends/generic-sql.rst +++ b/docs/backends/generic-sql.rst @@ -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. diff --git a/docs/backends/lua2.rst b/docs/backends/lua2.rst index f08815de2f..07019467cb 100644 --- a/docs/backends/lua2.rst +++ b/docs/backends/lua2.rst @@ -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: diff --git a/docs/backends/remote.rst b/docs/backends/remote.rst index 6cb2653cc1..81c946ac55 100644 --- a/docs/backends/remote.rst +++ b/docs/backends/remote.rst @@ -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" `, 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" `, 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=\ ````, id +- Parameters: name, key=\ ````, 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`` ~~~~~~~~~~~~~~ diff --git a/docs/http-api/swagger/authoritative-api-swagger.yaml b/docs/http-api/swagger/authoritative-api-swagger.yaml index 86bc3921be..6183e83d9d 100644 --- a/docs/http-api/swagger/authoritative-api-swagger.yaml +++ b/docs/http-api/swagger/authoritative-api-swagger.yaml @@ -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' diff --git a/docs/manpages/pdnsutil.1.rst b/docs/manpages/pdnsutil.1.rst index ac2ee9b985..b8a3baa596 100644 --- a/docs/manpages/pdnsutil.1.rst +++ b/docs/manpages/pdnsutil.1.rst @@ -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 diff --git a/modules/bindbackend/bindbackend2.hh b/modules/bindbackend/bindbackend2.hh index 5b6d32a770..4140b0aa0b 100644 --- a/modules/bindbackend/bindbackend2.hh +++ b/modules/bindbackend/bindbackend2.hh @@ -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 d_GetLastInsertedKeyIdQuery_stmt; unique_ptr d_activateDomainKeyQuery_stmt; unique_ptr d_deactivateDomainKeyQuery_stmt; + unique_ptr d_publishDomainKeyQuery_stmt; + unique_ptr d_unpublishDomainKeyQuery_stmt; unique_ptr d_getTSIGKeyQuery_stmt; unique_ptr d_setTSIGKeyQuery_stmt; unique_ptr d_deleteTSIGKeyQuery_stmt; diff --git a/modules/bindbackend/binddnssec.cc b/modules/bindbackend/binddnssec.cc index 5df886d56e..0733e8e1ba 100644 --- a/modules/bindbackend/binddnssec.cc +++ b/modules/bindbackend/binddnssec.cc @@ -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& 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) diff --git a/modules/geoipbackend/geoipbackend.cc b/modules/geoipbackend/geoipbackend.cc index d3f7d900f6..2238ababdf 100644 --- a/modules/geoipbackend/geoipbackend.cc +++ b/modules/geoipbackend/geoipbackend.cc @@ -744,6 +744,7 @@ bool GeoIPBackend::getDomainKeys(const DNSName& name, std::vectorgetEnv()->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(); - if (schemaversion != SCHEMAVERSION) { + schemaversion = _schemaversion.get(); + } + + 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 -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 +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 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& 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 "<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 "<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 "< > >(result)) { DNSBackend::KeyData key; + key.published = true; for(const auto& item: row.second) { if (item.first == "content") key.content = boost::get(item.second); @@ -346,10 +347,12 @@ public: key.flags = static_cast(boost::get(item.second)); else if (item.first == "active") key.active = boost::get(item.second); + else if (item.first == "published") + key.published = boost::get(item.second); else g_log< 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== diff --git a/modules/remotebackend/httpconnector.cc b/modules/remotebackend/httpconnector.cc index bf74849590..940bb1739e 100644 --- a/modules/remotebackend/httpconnector.cc +++ b/modules/remotebackend/httpconnector.cc @@ -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"; diff --git a/modules/remotebackend/regression-tests/backend.rb b/modules/remotebackend/regression-tests/backend.rb index 3da5d4ef6a..0c4bf47958 100755 --- a/modules/remotebackend/regression-tests/backend.rb +++ b/modules/remotebackend/regression-tests/backend.rb @@ -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" diff --git a/modules/remotebackend/regression-tests/dnsbackend.rb b/modules/remotebackend/regression-tests/dnsbackend.rb index 83a90cc021..2e36fa5d86 100644 --- a/modules/remotebackend/regression-tests/dnsbackend.rb +++ b/modules/remotebackend/regression-tests/dnsbackend.rb @@ -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 diff --git a/modules/remotebackend/regression-tests/test-schema.sql b/modules/remotebackend/regression-tests/test-schema.sql index b7d9ba8b4c..6faa1e049e 100644 --- a/modules/remotebackend/regression-tests/test-schema.sql +++ b/modules/remotebackend/regression-tests/test-schema.sql @@ -49,6 +49,7 @@ create table cryptokeys ( domain_id INT NOT NULL, flags INT NOT NULL, active BOOL, + published BOOL, content TEXT ); diff --git a/modules/remotebackend/remotebackend.cc b/modules/remotebackend/remotebackend.cc index 2c20cd306a..b330eafd48 100644 --- a/modules/remotebackend/remotebackend.cc +++ b/modules/remotebackend/remotebackend.cc @@ -374,6 +374,7 @@ bool RemoteBackend::getDomainKeys(const DNSName& name, std::vector(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(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(id) } + }} + }; + + Json answer; + if (this->send(query) == false || this->recv(answer) == false) + return false; + + return true; +} + + bool RemoteBackend::doesDNSSEC() { return d_dnssec; } diff --git a/modules/remotebackend/remotebackend.hh b/modules/remotebackend/remotebackend.hh index 647da8cdd9..0509c99fc5 100644 --- a/modules/remotebackend/remotebackend.hh +++ b/modules/remotebackend/remotebackend.hh @@ -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; diff --git a/modules/remotebackend/test-remotebackend-keys.hh b/modules/remotebackend/test-remotebackend-keys.hh index 5543574cbf..cc51fb0e68 100644 --- a/modules/remotebackend/test-remotebackend-keys.hh +++ b/modules/remotebackend/test-remotebackend-keys.hh @@ -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 }; diff --git a/modules/remotebackend/test-remotebackend.cc b/modules/remotebackend/test-remotebackend.cc index 13d803b560..ef783f4ea8 100644 --- a/modules/remotebackend/test-remotebackend.cc +++ b/modules/remotebackend/test-remotebackend.cc @@ -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); } } diff --git a/modules/remotebackend/unittest.rb b/modules/remotebackend/unittest.rb index 7fb40ec333..4984c5da79 100644 --- a/modules/remotebackend/unittest.rb +++ b/modules/remotebackend/unittest.rb @@ -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"] diff --git a/modules/remotebackend/unittest_http.rb b/modules/remotebackend/unittest_http.rb index 21beaa3f26..dc94a1071d 100755 --- a/modules/remotebackend/unittest_http.rb +++ b/modules/remotebackend/unittest_http.rb @@ -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 diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc index 8ab7d9916c..d223a0618b 100644 --- a/pdns/backends/gsql/gsqlbackend.cc +++ b/pdns/backends/gsql/gsqlbackend.cc @@ -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& 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<<"'"<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 d_RemoveDomainKeyQuery_stmt; unique_ptr d_ActivateDomainKeyQuery_stmt; unique_ptr d_DeactivateDomainKeyQuery_stmt; + unique_ptr d_PublishDomainKeyQuery_stmt; + unique_ptr d_UnpublishDomainKeyQuery_stmt; unique_ptr d_ClearDomainAllKeysQuery_stmt; unique_ptr d_getTSIGKeyQuery_stmt; unique_ptr 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 index 0000000000..d334fecdfc --- /dev/null +++ b/pdns/bind-dnssec.4.2.0_to_4.3.0_schema.sqlite3.sql @@ -0,0 +1 @@ +ALTER TABLE cryptokeys ADD published BOOL DEFAULT 1; diff --git a/pdns/bind-dnssec.schema.sqlite3.sql b/pdns/bind-dnssec.schema.sqlite3.sql index e4f75d6707..d9046bc55c 100644 --- a/pdns/bind-dnssec.schema.sqlite3.sql +++ b/pdns/bind-dnssec.schema.sqlite3.sql @@ -12,6 +12,7 @@ create table cryptokeys ( domain VARCHAR(255) COLLATE NOCASE, flags INT NOT NULL, active BOOL, + published BOOL, content TEXT ); diff --git a/pdns/dbdnsseckeeper.cc b/pdns/dbdnsseckeeper.cc index 8500eae5a8..5f97bec6d2 100644 --- a/pdns/dbdnsseckeeper.cc +++ b/pdns/dbdnsseckeeper.cc @@ -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; diff --git a/pdns/dnsbackend.hh b/pdns/dnsbackend.hh index 6fd4c16260..321463fc45 100644 --- a/pdns/dnsbackend.hh +++ b/pdns/dnsbackend.hh @@ -183,6 +183,7 @@ public: unsigned int id; unsigned int flags; bool active; + bool published; }; virtual bool getDomainKeys(const DNSName& name, std::vector& 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; } diff --git a/pdns/dnsseckeeper.hh b/pdns/dnsseckeeper.hh index 6007d4bc35..d30e327f86 100644 --- a/pdns/dnsseckeeper.hh +++ b/pdns/dnsseckeeper.hh @@ -69,6 +69,7 @@ public: bool active; keytype_t keyType; bool hasSEPBit; + bool published; }; typedef std::pair keymeta_t; typedef std::vector 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* errorMessages = nullptr); bool getNSEC3PARAM(const DNSName& zname, NSEC3PARAMRecordContent* n3p=0, bool* narrow=0); diff --git a/pdns/packethandler.cc b/pdns/packethandler.cc index 3a447b7d93..872b08a6b8 100644 --- a/pdns/packethandler.cc +++ b/pdns/packethandler.cc @@ -117,6 +117,9 @@ bool PacketHandler::addCDNSKEY(DNSPacket& p, std::unique_ptr& 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& 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& 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(makeDSFromDNSKey(p.qdomain, value.first.getDNSKEY(), pdns_stou(digestAlgo))); r->addRecord(rr); diff --git a/pdns/pdnsutil.cc b/pdns/pdnsutil.cc index f3e7fd65a4..f62277449c 100644 --- a/pdns/pdnsutil.cc +++ b/pdns/pdnsutil.cc @@ -1687,7 +1687,7 @@ bool showZone(DNSSECKeeper& dk, const DNSName& zone, bool exportDS = false) if (!exportDS) { cout<<", flags = "<& 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(value.first.getDNSKEY()); DNSName keyname = NSEC3Zone ? DNSName(toBase32Hex(hashQNameWithSalt(ns3pr, zrr.dr.d_name))) : zrr.dr.d_name; diff --git a/pdns/ueberbackend.cc b/pdns/ueberbackend.cc index 4e792aa4c6..266b25be13 100644 --- a/pdns/ueberbackend.cc +++ b/pdns/ueberbackend.cc @@ -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) { diff --git a/pdns/ueberbackend.hh b/pdns/ueberbackend.hh index ffff7f1b9f..062645d444 100644 --- a/pdns/ueberbackend.hh +++ b/pdns/ueberbackend.hh @@ -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); diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index e68a3b3a9e..dd966f3ea0 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -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; diff --git a/regression-tests.api/test_Zones.py b/regression-tests.api/test_Zones.py index c7fbe2758b..7db391f8c2 100644 --- a/regression-tests.api/test_Zones.py +++ b/regression-tests.api/test_Zones.py @@ -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) diff --git a/regression-tests.api/test_cryptokeys.py b/regression-tests.api/test_cryptokeys.py index 8a4c874d64..24102c5f3a 100644 --- a/regression-tests.api/test_cryptokeys.py +++ b/regression-tests.api/test_cryptokeys.py @@ -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) diff --git a/regression-tests.nobackend/tinydns-data-check/expected_result b/regression-tests.nobackend/tinydns-data-check/expected_result index d0086b5e1b..0ec4367dae 100644 --- a/regression-tests.nobackend/tinydns-data-check/expected_result +++ b/regression-tests.nobackend/tinydns-data-check/expected_result @@ -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 diff --git a/regression-tests/backends/bind-master b/regression-tests/backends/bind-master index 579935bfb8..c2b5770156 100644 --- a/regression-tests/backends/bind-master +++ b/regression-tests/backends/bind-master @@ -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 diff --git a/regression-tests/backends/godbc_sqlite3-master b/regression-tests/backends/godbc_sqlite3-master index 4099c5ca10..e17a3bd134 100644 --- a/regression-tests/backends/godbc_sqlite3-master +++ b/regression-tests/backends/godbc_sqlite3-master @@ -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=? diff --git a/regression-tests/backends/gsql-common b/regression-tests/backends/gsql-common index 99eff8ecf4..86840ff1d5 100644 --- a/regression-tests/backends/gsql-common +++ b/regression-tests/backends/gsql-common @@ -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 diff --git a/regression-tests/backends/lmdb-master b/regression-tests/backends/lmdb-master index 9f37e57725..b557b6a88c 100644 --- a/regression-tests/backends/lmdb-master +++ b/regression-tests/backends/lmdb-master @@ -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 diff --git a/regression-tests/named.conf b/regression-tests/named.conf index 2a1a754da7..52c383f94b 100644 --- a/regression-tests/named.conf +++ b/regression-tests/named.conf @@ -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 index 0000000000..1529298c63 --- /dev/null +++ b/regression-tests/tests/cryptokeys/command @@ -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 index 0000000000..cee275f865 --- /dev/null +++ b/regression-tests/tests/cryptokeys/description @@ -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 index 0000000000..409f965e24 --- /dev/null +++ b/regression-tests/tests/cryptokeys/expected_result.dnssec @@ -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 index 0000000000..e69de29bb2 diff --git a/regression-tests/tests/verify-dnssec-zone/command b/regression-tests/tests/verify-dnssec-zone/command index 30dbe19556..e57cbd0182 100755 --- a/regression-tests/tests/verify-dnssec-zone/command +++ b/regression-tests/tests/verify-dnssec-zone/command @@ -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 diff --git a/regression-tests/zones/.gitignore b/regression-tests/zones/.gitignore index 936d3b67bd..a9b5df69cb 100644 --- a/regression-tests/zones/.gitignore +++ b/regression-tests/zones/.gitignore @@ -3,6 +3,7 @@ /*.bind /*.com-slave /*.dyndns-slave +/*.org-slave /2.0.192.in-addr.arpa-slave /*.signed /*.nsd -- 2.47.2