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