1 /*
2  * This file is part of PowerDNS or dnsdist.
3  * Copyright -- PowerDNS.COM B.V. and its contributors
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of version 2 of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * In addition, for the avoidance of any doubt, permission is granted to
10  * link this program with OpenSSL and to (re)distribute the binaries
11  * produced as the result of such linking.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include "bindbackend2.hh"
27 #include "pdns/arguments.hh"
28 #include "pdns/dnsrecords.hh"
29 
30 #ifndef HAVE_SQLITE3
31 
setupDNSSEC()32 void Bind2Backend::setupDNSSEC()
33 {
34   if (!getArg("dnssec-db").empty())
35     throw runtime_error("bind-dnssec-db requires building PowerDNS with SQLite3");
36 }
37 
doesDNSSEC()38 bool Bind2Backend::doesDNSSEC()
39 {
40   return d_hybrid;
41 }
42 
getNSEC3PARAM(const DNSName & name,NSEC3PARAMRecordContent * ns3p)43 bool Bind2Backend::getNSEC3PARAM(const DNSName& name, NSEC3PARAMRecordContent* ns3p)
44 {
45   return false;
46 }
47 
getNSEC3PARAMuncached(const DNSName & name,NSEC3PARAMRecordContent * ns3p)48 bool Bind2Backend::getNSEC3PARAMuncached(const DNSName& name, NSEC3PARAMRecordContent* ns3p)
49 {
50   return false;
51 }
52 
getAllDomainMetadata(const DNSName & name,std::map<std::string,std::vector<std::string>> & meta)53 bool Bind2Backend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string>>& meta)
54 {
55   return false;
56 }
57 
getDomainMetadata(const DNSName & name,const std::string & kind,std::vector<std::string> & meta)58 bool Bind2Backend::getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta)
59 {
60   return false;
61 }
62 
setDomainMetadata(const DNSName & name,const std::string & kind,const std::vector<std::string> & meta)63 bool Bind2Backend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta)
64 {
65   return false;
66 }
67 
getDomainKeys(const DNSName & name,std::vector<KeyData> & keys)68 bool Bind2Backend::getDomainKeys(const DNSName& name, std::vector<KeyData>& keys)
69 {
70   return false;
71 }
72 
removeDomainKey(const DNSName & name,unsigned int id)73 bool Bind2Backend::removeDomainKey(const DNSName& name, unsigned int id)
74 {
75   return false;
76 }
77 
addDomainKey(const DNSName & name,const KeyData & key,int64_t & id)78 bool Bind2Backend::addDomainKey(const DNSName& name, const KeyData& key, int64_t& id)
79 {
80   return false;
81 }
82 
activateDomainKey(const DNSName & name,unsigned int id)83 bool Bind2Backend::activateDomainKey(const DNSName& name, unsigned int id)
84 {
85   return false;
86 }
87 
deactivateDomainKey(const DNSName & name,unsigned int id)88 bool Bind2Backend::deactivateDomainKey(const DNSName& name, unsigned int id)
89 {
90   return false;
91 }
92 
publishDomainKey(const DNSName & name,unsigned int id)93 bool Bind2Backend::publishDomainKey(const DNSName& name, unsigned int id)
94 {
95   return false;
96 }
97 
unpublishDomainKey(const DNSName & name,unsigned int id)98 bool Bind2Backend::unpublishDomainKey(const DNSName& name, unsigned int id)
99 {
100   return false;
101 }
102 
getTSIGKey(const DNSName & name,DNSName * algorithm,string * content)103 bool Bind2Backend::getTSIGKey(const DNSName& name, DNSName* algorithm, string* content)
104 {
105   return false;
106 }
107 
setTSIGKey(const DNSName & name,const DNSName & algorithm,const string & content)108 bool Bind2Backend::setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content)
109 {
110   return false;
111 }
112 
deleteTSIGKey(const DNSName & name)113 bool Bind2Backend::deleteTSIGKey(const DNSName& name)
114 {
115   return false;
116 }
117 
getTSIGKeys(std::vector<struct TSIGKey> & keys)118 bool Bind2Backend::getTSIGKeys(std::vector<struct TSIGKey>& keys)
119 {
120   return false;
121 }
122 
setupStatements()123 void Bind2Backend::setupStatements()
124 {
125   return;
126 }
127 
freeStatements()128 void Bind2Backend::freeStatements()
129 {
130   return;
131 }
132 
133 #else
134 
135 #include "pdns/logger.hh"
136 #include "pdns/ssqlite3.hh"
137 
138 #define ASSERT_ROW_COLUMNS(query, row, num)                                                                                                \
139   {                                                                                                                                        \
140     if (row.size() != num) {                                                                                                               \
141       throw PDNSException(std::string(query) + " returned wrong number of columns, expected " #num ", got " + std::to_string(row.size())); \
142     }                                                                                                                                      \
143   }
144 
setupDNSSEC()145 void Bind2Backend::setupDNSSEC()
146 {
147   if (getArg("dnssec-db").empty() || d_hybrid)
148     return;
149   try {
150     d_dnssecdb = shared_ptr<SSQLite3>(new SSQLite3(getArg("dnssec-db"), getArg("dnssec-db-journal-mode")));
151     setupStatements();
152   }
153   catch (SSqlException& se) {
154     // this error is meant to kill the server dead - it makes no sense to continue..
155     throw runtime_error("Error opening DNSSEC database in BIND backend: " + se.txtReason());
156   }
157 
158   d_dnssecdb->setLog(::arg().mustDo("query-logging"));
159 }
160 
setupStatements()161 void Bind2Backend::setupStatements()
162 {
163   d_getAllDomainMetadataQuery_stmt = d_dnssecdb->prepare("select kind, content from domainmetadata where domain=:domain", 1);
164   d_getDomainMetadataQuery_stmt = d_dnssecdb->prepare("select content from domainmetadata where domain=:domain and kind=:kind", 2);
165   d_deleteDomainMetadataQuery_stmt = d_dnssecdb->prepare("delete from domainmetadata where domain=:domain and kind=:kind", 2);
166   d_insertDomainMetadataQuery_stmt = d_dnssecdb->prepare("insert into domainmetadata (domain, kind, content) values (:domain,:kind,:content)", 3);
167   d_getDomainKeysQuery_stmt = d_dnssecdb->prepare("select id,flags, active, published, content from cryptokeys where domain=:domain", 1);
168   d_deleteDomainKeyQuery_stmt = d_dnssecdb->prepare("delete from cryptokeys where domain=:domain and id=:key_id", 2);
169   d_insertDomainKeyQuery_stmt = d_dnssecdb->prepare("insert into cryptokeys (domain, flags, active, published, content) values (:domain, :flags, :active, :published, :content)", 5);
170   d_GetLastInsertedKeyIdQuery_stmt = d_dnssecdb->prepare("select last_insert_rowid()", 0);
171   d_activateDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set active=1 where domain=:domain and id=:key_id", 2);
172   d_deactivateDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set active=0 where domain=:domain and id=:key_id", 2);
173   d_publishDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set published=1 where domain=:domain and id=:key_id", 2);
174   d_unpublishDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set published=0 where domain=:domain and id=:key_id", 2);
175   d_getTSIGKeyQuery_stmt = d_dnssecdb->prepare("select algorithm, secret from tsigkeys where name=:key_name", 1);
176   d_setTSIGKeyQuery_stmt = d_dnssecdb->prepare("replace into tsigkeys (name,algorithm,secret) values(:key_name, :algorithm, :content)", 3);
177   d_deleteTSIGKeyQuery_stmt = d_dnssecdb->prepare("delete from tsigkeys where name=:key_name", 1);
178   d_getTSIGKeysQuery_stmt = d_dnssecdb->prepare("select name,algorithm,secret from tsigkeys", 0);
179 }
180 
freeStatements()181 void Bind2Backend::freeStatements()
182 {
183   d_getAllDomainMetadataQuery_stmt.reset();
184   d_getDomainMetadataQuery_stmt.reset();
185   d_deleteDomainMetadataQuery_stmt.reset();
186   d_insertDomainMetadataQuery_stmt.reset();
187   d_getDomainKeysQuery_stmt.reset();
188   d_deleteDomainKeyQuery_stmt.reset();
189   d_insertDomainKeyQuery_stmt.reset();
190   d_GetLastInsertedKeyIdQuery_stmt.reset();
191   d_activateDomainKeyQuery_stmt.reset();
192   d_deactivateDomainKeyQuery_stmt.reset();
193   d_publishDomainKeyQuery_stmt.reset();
194   d_unpublishDomainKeyQuery_stmt.reset();
195   d_getTSIGKeyQuery_stmt.reset();
196   d_setTSIGKeyQuery_stmt.reset();
197   d_deleteTSIGKeyQuery_stmt.reset();
198   d_getTSIGKeysQuery_stmt.reset();
199 }
200 
doesDNSSEC()201 bool Bind2Backend::doesDNSSEC()
202 {
203   return d_dnssecdb || d_hybrid;
204 }
205 
getNSEC3PARAM(const DNSName & name,NSEC3PARAMRecordContent * ns3p)206 bool Bind2Backend::getNSEC3PARAM(const DNSName& name, NSEC3PARAMRecordContent* ns3p)
207 {
208   BB2DomainInfo bbd;
209   if (!safeGetBBDomainInfo(name, &bbd))
210     return false;
211 
212   if (ns3p) {
213     *ns3p = bbd.d_nsec3param;
214   }
215 
216   return bbd.d_nsec3zone;
217 }
218 
getNSEC3PARAMuncached(const DNSName & name,NSEC3PARAMRecordContent * ns3p)219 bool Bind2Backend::getNSEC3PARAMuncached(const DNSName& name, NSEC3PARAMRecordContent* ns3p)
220 {
221   if (!d_dnssecdb || d_hybrid)
222     return false;
223 
224   string value;
225   vector<string> meta;
226   getDomainMetadata(name, "NSEC3PARAM", meta);
227   if (!meta.empty())
228     value = *meta.begin();
229   else
230     return false; // No NSEC3 zone
231 
232   static int maxNSEC3Iterations = ::arg().asNum("max-nsec3-iterations");
233   if (ns3p) {
234     auto tmp = std::dynamic_pointer_cast<NSEC3PARAMRecordContent>(DNSRecordContent::mastermake(QType::NSEC3PARAM, 1, value));
235     *ns3p = *tmp;
236 
237     if (ns3p->d_iterations > maxNSEC3Iterations) {
238       ns3p->d_iterations = maxNSEC3Iterations;
239       g_log << Logger::Error << "Number of NSEC3 iterations for zone '" << name << "' is above 'max-nsec3-iterations'. Value adjusted to: " << maxNSEC3Iterations << endl;
240     }
241 
242     if (ns3p->d_algorithm != 1) {
243       g_log << Logger::Error << "Invalid hash algorithm for NSEC3: '" << std::to_string(ns3p->d_algorithm) << "', setting to 1 for zone '" << name << "'." << endl;
244       ns3p->d_algorithm = 1;
245     }
246   }
247 
248   return true;
249 }
250 
getAllDomainMetadata(const DNSName & name,std::map<std::string,std::vector<std::string>> & meta)251 bool Bind2Backend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string>>& meta)
252 {
253   if (!d_dnssecdb || d_hybrid)
254     return false;
255 
256   try {
257     d_getAllDomainMetadataQuery_stmt->bind("domain", name)->execute();
258 
259     SSqlStatement::row_t row;
260     while (d_getAllDomainMetadataQuery_stmt->hasNextRow()) {
261       d_getAllDomainMetadataQuery_stmt->nextRow(row);
262       meta[row[0]].push_back(row[1]);
263     }
264 
265     d_getAllDomainMetadataQuery_stmt->reset();
266   }
267   catch (SSqlException& se) {
268     throw PDNSException("Error accessing DNSSEC database in BIND backend, getAllDomainMetadata(): " + se.txtReason());
269   }
270   return true;
271 }
272 
getDomainMetadata(const DNSName & name,const std::string & kind,std::vector<std::string> & meta)273 bool Bind2Backend::getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta)
274 {
275   if (!d_dnssecdb || d_hybrid)
276     return false;
277 
278   try {
279     d_getDomainMetadataQuery_stmt->bind("domain", name)->bind("kind", kind)->execute();
280 
281     SSqlStatement::row_t row;
282     while (d_getDomainMetadataQuery_stmt->hasNextRow()) {
283       d_getDomainMetadataQuery_stmt->nextRow(row);
284       meta.push_back(row[0]);
285     }
286 
287     d_getDomainMetadataQuery_stmt->reset();
288   }
289   catch (SSqlException& se) {
290     throw PDNSException("Error accessing DNSSEC database in BIND backend, getDomainMetadata(): " + se.txtReason());
291   }
292   return true;
293 }
294 
setDomainMetadata(const DNSName & name,const std::string & kind,const std::vector<std::string> & meta)295 bool Bind2Backend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta)
296 {
297   if (!d_dnssecdb || d_hybrid)
298     return false;
299 
300   try {
301     d_deleteDomainMetadataQuery_stmt->bind("domain", name)->bind("kind", kind)->execute()->reset();
302     if (!meta.empty()) {
303       for (const auto& value : meta) {
304         d_insertDomainMetadataQuery_stmt->bind("domain", name)->bind("kind", kind)->bind("content", value)->execute()->reset();
305       }
306     }
307   }
308   catch (SSqlException& se) {
309     throw PDNSException("Error accessing DNSSEC database in BIND backend, setDomainMetadata(): " + se.txtReason());
310   }
311   return true;
312 }
313 
getDomainKeys(const DNSName & name,std::vector<KeyData> & keys)314 bool Bind2Backend::getDomainKeys(const DNSName& name, std::vector<KeyData>& keys)
315 {
316   if (!d_dnssecdb || d_hybrid)
317     return false;
318 
319   try {
320     d_getDomainKeysQuery_stmt->bind("domain", name)->execute();
321 
322     KeyData kd;
323     SSqlStatement::row_t row;
324     while (d_getDomainKeysQuery_stmt->hasNextRow()) {
325       d_getDomainKeysQuery_stmt->nextRow(row);
326       kd.id = pdns_stou(row[0]);
327       kd.flags = pdns_stou(row[1]);
328       kd.active = (row[2] == "1");
329       kd.published = (row[3] == "1");
330       kd.content = row[4];
331       keys.push_back(kd);
332     }
333 
334     d_getDomainKeysQuery_stmt->reset();
335   }
336   catch (SSqlException& se) {
337     throw PDNSException("Error accessing DNSSEC database in BIND backend, getDomainKeys(): " + se.txtReason());
338   }
339   return true;
340 }
341 
removeDomainKey(const DNSName & name,unsigned int id)342 bool Bind2Backend::removeDomainKey(const DNSName& name, unsigned int id)
343 {
344   if (!d_dnssecdb || d_hybrid)
345     return false;
346 
347   try {
348     d_deleteDomainKeyQuery_stmt->bind("domain", name)->bind("key_id", id)->execute()->reset();
349   }
350   catch (SSqlException& se) {
351     throw PDNSException("Error accessing DNSSEC database in BIND backend, removeDomainKeys(): " + se.txtReason());
352   }
353   return true;
354 }
355 
addDomainKey(const DNSName & name,const KeyData & key,int64_t & id)356 bool Bind2Backend::addDomainKey(const DNSName& name, const KeyData& key, int64_t& id)
357 {
358   if (!d_dnssecdb || d_hybrid)
359     return false;
360 
361   try {
362     d_insertDomainKeyQuery_stmt->bind("domain", name)->bind("flags", key.flags)->bind("active", key.active)->bind("published", key.published)->bind("content", key.content)->execute()->reset();
363   }
364   catch (SSqlException& se) {
365     throw PDNSException("Error accessing DNSSEC database in BIND backend, addDomainKey(): " + se.txtReason());
366   }
367 
368   try {
369     d_GetLastInsertedKeyIdQuery_stmt->execute();
370     if (!d_GetLastInsertedKeyIdQuery_stmt->hasNextRow()) {
371       id = -2;
372       return true;
373     }
374     SSqlStatement::row_t row;
375     d_GetLastInsertedKeyIdQuery_stmt->nextRow(row);
376     ASSERT_ROW_COLUMNS("get-last-inserted-key-id-query", row, 1);
377     id = std::stoi(row[0]);
378     d_GetLastInsertedKeyIdQuery_stmt->reset();
379     return true;
380   }
381   catch (SSqlException& e) {
382     id = -2;
383     return true;
384   }
385 
386   return false;
387 }
388 
activateDomainKey(const DNSName & name,unsigned int id)389 bool Bind2Backend::activateDomainKey(const DNSName& name, unsigned int id)
390 {
391   if (!d_dnssecdb || d_hybrid)
392     return false;
393 
394   try {
395     d_activateDomainKeyQuery_stmt->bind("domain", name)->bind("key_id", id)->execute()->reset();
396   }
397   catch (SSqlException& se) {
398     throw PDNSException("Error accessing DNSSEC database in BIND backend, activateDomainKey(): " + se.txtReason());
399   }
400   return true;
401 }
402 
deactivateDomainKey(const DNSName & name,unsigned int id)403 bool Bind2Backend::deactivateDomainKey(const DNSName& name, unsigned int id)
404 {
405   if (!d_dnssecdb || d_hybrid)
406     return false;
407 
408   try {
409     d_deactivateDomainKeyQuery_stmt->bind("domain", name)->bind("key_id", id)->execute()->reset();
410   }
411   catch (SSqlException& se) {
412     throw PDNSException("Error accessing DNSSEC database in BIND backend, deactivateDomainKey(): " + se.txtReason());
413   }
414   return true;
415 }
416 
publishDomainKey(const DNSName & name,unsigned int id)417 bool Bind2Backend::publishDomainKey(const DNSName& name, unsigned int id)
418 {
419   if (!d_dnssecdb || d_hybrid)
420     return false;
421 
422   try {
423     d_publishDomainKeyQuery_stmt->bind("domain", name)->bind("key_id", id)->execute()->reset();
424   }
425   catch (SSqlException& se) {
426     throw PDNSException("Error accessing DNSSEC database in BIND backend, publishDomainKey(): " + se.txtReason());
427   }
428   return true;
429 }
430 
unpublishDomainKey(const DNSName & name,unsigned int id)431 bool Bind2Backend::unpublishDomainKey(const DNSName& name, unsigned int id)
432 {
433   if (!d_dnssecdb || d_hybrid)
434     return false;
435 
436   try {
437     d_unpublishDomainKeyQuery_stmt->bind("domain", name)->bind("key_id", id)->execute()->reset();
438   }
439   catch (SSqlException& se) {
440     throw PDNSException("Error accessing DNSSEC database in BIND backend, unpublishDomainKey(): " + se.txtReason());
441   }
442   return true;
443 }
444 
getTSIGKey(const DNSName & name,DNSName * algorithm,string * content)445 bool Bind2Backend::getTSIGKey(const DNSName& name, DNSName* algorithm, string* content)
446 {
447   if (!d_dnssecdb || d_hybrid)
448     return false;
449 
450   try {
451     d_getTSIGKeyQuery_stmt->bind("key_name", name)->execute();
452 
453     SSqlStatement::row_t row;
454     content->clear();
455     while (d_getTSIGKeyQuery_stmt->hasNextRow()) {
456       d_getTSIGKeyQuery_stmt->nextRow(row);
457       if (row.size() >= 2 && (algorithm->empty() || *algorithm == DNSName(row[0]))) {
458         *algorithm = DNSName(row[0]);
459         *content = row[1];
460       }
461     }
462 
463     d_getTSIGKeyQuery_stmt->reset();
464   }
465   catch (SSqlException& e) {
466     throw PDNSException("Error accessing DNSSEC database in BIND backend, getTSIGKey(): " + e.txtReason());
467   }
468   return !content->empty();
469 }
470 
setTSIGKey(const DNSName & name,const DNSName & algorithm,const string & content)471 bool Bind2Backend::setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content)
472 {
473   if (!d_dnssecdb || d_hybrid)
474     return false;
475 
476   try {
477     d_setTSIGKeyQuery_stmt->bind("key_name", name)->bind("algorithm", algorithm)->bind("content", content)->execute()->reset();
478   }
479   catch (SSqlException& e) {
480     throw PDNSException("Error accessing DNSSEC database in BIND backend, setTSIGKey(): " + e.txtReason());
481   }
482   return true;
483 }
484 
deleteTSIGKey(const DNSName & name)485 bool Bind2Backend::deleteTSIGKey(const DNSName& name)
486 {
487   if (!d_dnssecdb || d_hybrid)
488     return false;
489 
490   try {
491     d_deleteTSIGKeyQuery_stmt->bind("key_name", name)->execute()->reset();
492   }
493   catch (SSqlException& e) {
494     throw PDNSException("Error accessing DNSSEC database in BIND backend, deleteTSIGKey(): " + e.txtReason());
495   }
496   return true;
497 }
498 
getTSIGKeys(std::vector<struct TSIGKey> & keys)499 bool Bind2Backend::getTSIGKeys(std::vector<struct TSIGKey>& keys)
500 {
501   if (!d_dnssecdb || d_hybrid)
502     return false;
503 
504   try {
505     d_getTSIGKeysQuery_stmt->execute();
506 
507     SSqlStatement::row_t row;
508     while (d_getTSIGKeysQuery_stmt->hasNextRow()) {
509       d_getTSIGKeysQuery_stmt->nextRow(row);
510       struct TSIGKey key;
511       key.name = DNSName(row[0]);
512       key.algorithm = DNSName(row[1]);
513       key.key = row[2];
514       keys.push_back(key);
515     }
516 
517     d_getTSIGKeysQuery_stmt->reset();
518   }
519   catch (SSqlException& e) {
520     throw PDNSException("Error accessing DNSSEC database in BIND backend, getTSIGKeys(): " + e.txtReason());
521   }
522   return !keys.empty();
523 }
524 
525 #endif
526