]> git.ipfire.org Git - thirdparty/pdns.git/blob - modules/bindbackend/binddnssec.cc
Logging: have a global g_log
[thirdparty/pdns.git] / modules / bindbackend / binddnssec.cc
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
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
38 bool Bind2Backend::doesDNSSEC()
39 { return d_hybrid; }
40
41 bool Bind2Backend::getNSEC3PARAM(const DNSName& name, NSEC3PARAMRecordContent* ns3p)
42 { return false; }
43
44 bool Bind2Backend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta)
45 { return false; }
46
47 bool Bind2Backend::getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta)
48 { return false; }
49
50 bool Bind2Backend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta)
51 { return false; }
52
53 bool Bind2Backend::getDomainKeys(const DNSName& name, std::vector<KeyData>& keys)
54 { return false; }
55
56 bool Bind2Backend::removeDomainKey(const DNSName& name, unsigned int id)
57 { return false; }
58
59 bool Bind2Backend::addDomainKey(const DNSName& name, const KeyData& key, int64_t& id)
60 { return false; }
61
62 bool Bind2Backend::activateDomainKey(const DNSName& name, unsigned int id)
63 { return false; }
64
65 bool Bind2Backend::deactivateDomainKey(const DNSName& name, unsigned int id)
66 { return false; }
67
68 bool Bind2Backend::getTSIGKey(const DNSName& name, DNSName* algorithm, string* content)
69 { return false; }
70
71 bool Bind2Backend::setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content)
72 { return false; }
73
74 bool Bind2Backend::deleteTSIGKey(const DNSName& name)
75 { return false; }
76
77 bool Bind2Backend::getTSIGKeys(std::vector<struct TSIGKey> &keys)
78 { return false; }
79
80 void Bind2Backend::setupStatements()
81 { return; }
82
83 void Bind2Backend::freeStatements()
84 { return; }
85
86 #else
87
88 #include "pdns/bind-dnssec.schema.sqlite3.sql.h"
89 #include "pdns/logger.hh"
90 #include "pdns/ssqlite3.hh"
91
92 #define ASSERT_ROW_COLUMNS(query, row, num) { if (row.size() != num) { throw PDNSException(std::string(query) + " returned wrong number of columns, expected " #num ", got " + std::to_string(row.size())); } }
93
94 void Bind2Backend::setupDNSSEC()
95 {
96 if(getArg("dnssec-db").empty() || d_hybrid)
97 return;
98 try {
99 d_dnssecdb = shared_ptr<SSQLite3>(new SSQLite3(getArg("dnssec-db")));
100 setupStatements();
101 }
102 catch(SSqlException& se) {
103 // this error is meant to kill the server dead - it makes no sense to continue..
104 throw runtime_error("Error opening DNSSEC database in BIND backend: "+se.txtReason());
105 }
106
107 d_dnssecdb->setLog(::arg().mustDo("query-logging"));
108 }
109
110 void Bind2Backend::setupStatements()
111 {
112 d_getAllDomainMetadataQuery_stmt = d_dnssecdb->prepare("select kind, content from domainmetadata where domain=:domain",1);
113 d_getDomainMetadataQuery_stmt = d_dnssecdb->prepare("select content from domainmetadata where domain=:domain and kind=:kind",2);
114 d_deleteDomainMetadataQuery_stmt = d_dnssecdb->prepare("delete from domainmetadata where domain=:domain and kind=:kind",2);
115 d_insertDomainMetadataQuery_stmt = d_dnssecdb->prepare("insert into domainmetadata (domain, kind, content) values (:domain,:kind,:content)",3);
116 d_getDomainKeysQuery_stmt = d_dnssecdb->prepare("select id,flags, active, content from cryptokeys where domain=:domain",1);
117 d_deleteDomainKeyQuery_stmt = d_dnssecdb->prepare("delete from cryptokeys where domain=:domain and id=:key_id",2);
118 d_insertDomainKeyQuery_stmt = d_dnssecdb->prepare("insert into cryptokeys (domain, flags, active, content) values (:domain, :flags, :active, :content)", 4);
119 d_GetLastInsertedKeyIdQuery_stmt = d_dnssecdb->prepare("select last_insert_rowid()", 0);
120 d_activateDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set active=1 where domain=:domain and id=:key_id", 2);
121 d_deactivateDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set active=0 where domain=:domain and id=:key_id", 2);
122 d_getTSIGKeyQuery_stmt = d_dnssecdb->prepare("select algorithm, secret from tsigkeys where name=:key_name", 1);
123 d_setTSIGKeyQuery_stmt = d_dnssecdb->prepare("replace into tsigkeys (name,algorithm,secret) values(:key_name, :algorithm, :content)", 3);
124 d_deleteTSIGKeyQuery_stmt = d_dnssecdb->prepare("delete from tsigkeys where name=:key_name", 1);
125 d_getTSIGKeysQuery_stmt = d_dnssecdb->prepare("select name,algorithm,secret from tsigkeys", 0);
126 }
127
128 void Bind2Backend::release(SSqlStatement** stmt) {
129 delete *stmt;
130 *stmt = NULL;
131 }
132
133 void Bind2Backend::freeStatements()
134 {
135 d_getAllDomainMetadataQuery_stmt.reset();
136 d_getDomainMetadataQuery_stmt.reset();
137 d_deleteDomainMetadataQuery_stmt.reset();
138 d_insertDomainMetadataQuery_stmt.reset();
139 d_getDomainKeysQuery_stmt.reset();
140 d_deleteDomainKeyQuery_stmt.reset();
141 d_insertDomainKeyQuery_stmt.reset();
142 d_GetLastInsertedKeyIdQuery_stmt.reset();
143 d_activateDomainKeyQuery_stmt.reset();
144 d_deactivateDomainKeyQuery_stmt.reset();
145 d_getTSIGKeyQuery_stmt.reset();
146 d_setTSIGKeyQuery_stmt.reset();
147 d_deleteTSIGKeyQuery_stmt.reset();
148 d_getTSIGKeysQuery_stmt.reset();
149 }
150
151 bool Bind2Backend::doesDNSSEC()
152 {
153 return d_dnssecdb || d_hybrid;
154 }
155
156 bool Bind2Backend::getNSEC3PARAM(const DNSName& name, NSEC3PARAMRecordContent* ns3p)
157 {
158 if(!d_dnssecdb || d_hybrid)
159 return false;
160
161 string value;
162 vector<string> meta;
163 getDomainMetadata(name, "NSEC3PARAM", meta);
164 if(!meta.empty())
165 value=*meta.begin();
166 else
167 return false; // No NSEC3 zone
168
169 static int maxNSEC3Iterations=::arg().asNum("max-nsec3-iterations");
170 if(ns3p) {
171 auto tmp=std::dynamic_pointer_cast<NSEC3PARAMRecordContent>(DNSRecordContent::mastermake(QType::NSEC3PARAM, 1, value));
172 *ns3p = *tmp;
173
174 if (ns3p->d_iterations > maxNSEC3Iterations) {
175 ns3p->d_iterations = maxNSEC3Iterations;
176 g_log<<Logger::Error<<"Number of NSEC3 iterations for zone '"<<name<<"' is above 'max-nsec3-iterations'. Value adjusted to: "<<maxNSEC3Iterations<<endl;
177 }
178
179 if (ns3p->d_algorithm != 1) {
180 g_log<<Logger::Error<<"Invalid hash algorithm for NSEC3: '"<<std::to_string(ns3p->d_algorithm)<<"', setting to 1 for zone '"<<name<<"'."<<endl;
181 ns3p->d_algorithm = 1;
182 }
183 }
184
185 return true;
186 }
187
188 bool Bind2Backend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta)
189 {
190 if(!d_dnssecdb || d_hybrid)
191 return false;
192
193 try {
194 d_getAllDomainMetadataQuery_stmt->
195 bind("domain", name)->
196 execute();
197
198 SSqlStatement::row_t row;
199 while(d_getAllDomainMetadataQuery_stmt->hasNextRow()) {
200 d_getAllDomainMetadataQuery_stmt->nextRow(row);
201 meta[row[0]].push_back(row[1]);
202 }
203
204 d_getAllDomainMetadataQuery_stmt->reset();
205 }
206 catch(SSqlException& se) {
207 throw PDNSException("Error accessing DNSSEC database in BIND backend, getAllDomainMetadata(): "+se.txtReason());
208 }
209 return true;
210 }
211
212 bool Bind2Backend::getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta)
213 {
214 if(!d_dnssecdb || d_hybrid)
215 return false;
216
217 try {
218 d_getDomainMetadataQuery_stmt->
219 bind("domain", name)->
220 bind("kind", kind)->
221 execute();
222
223 SSqlStatement::row_t row;
224 while(d_getDomainMetadataQuery_stmt->hasNextRow()) {
225 d_getDomainMetadataQuery_stmt->nextRow(row);
226 meta.push_back(row[0]);
227 }
228
229 d_getDomainMetadataQuery_stmt->reset();
230 }
231 catch(SSqlException& se) {
232 throw PDNSException("Error accessing DNSSEC database in BIND backend, getDomainMetadata(): "+se.txtReason());
233 }
234 return true;
235 }
236
237 bool Bind2Backend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta)
238 {
239 if(!d_dnssecdb || d_hybrid)
240 return false;
241
242 try {
243 d_deleteDomainMetadataQuery_stmt->
244 bind("domain", name)->
245 bind("kind", kind)->
246 execute()->
247 reset();
248 if(!meta.empty()) {
249 for(const auto& value: meta) {
250 d_insertDomainMetadataQuery_stmt->
251 bind("domain", name)->
252 bind("kind", kind)->
253 bind("content", value)->
254 execute()->
255 reset();
256 }
257 }
258 }
259 catch(SSqlException& se) {
260 throw PDNSException("Error accessing DNSSEC database in BIND backend, setDomainMetadata(): "+se.txtReason());
261 }
262 return true;
263 }
264
265 bool Bind2Backend::getDomainKeys(const DNSName& name, std::vector<KeyData>& keys)
266 {
267 if(!d_dnssecdb || d_hybrid)
268 return false;
269
270 try {
271 d_getDomainKeysQuery_stmt->
272 bind("domain", name)->
273 execute();
274
275 KeyData kd;
276 SSqlStatement::row_t row;
277 while(d_getDomainKeysQuery_stmt->hasNextRow()) {
278 d_getDomainKeysQuery_stmt->nextRow(row);
279 kd.id = pdns_stou(row[0]);
280 kd.flags = pdns_stou(row[1]);
281 kd.active = (row[2] == "1");
282 kd.content = row[3];
283 keys.push_back(kd);
284 }
285
286 d_getDomainKeysQuery_stmt->reset();
287 }
288 catch(SSqlException& se) {
289 throw PDNSException("Error accessing DNSSEC database in BIND backend, getDomainKeys(): "+se.txtReason());
290 }
291 return true;
292 }
293
294 bool Bind2Backend::removeDomainKey(const DNSName& name, unsigned int id)
295 {
296 if(!d_dnssecdb || d_hybrid)
297 return false;
298
299 try {
300 d_deleteDomainKeyQuery_stmt->
301 bind("domain", name)->
302 bind("key_id", id)->
303 execute()->
304 reset();
305 }
306 catch(SSqlException& se) {
307 throw PDNSException("Error accessing DNSSEC database in BIND backend, removeDomainKeys(): "+se.txtReason());
308 }
309 return true;
310 }
311
312 bool Bind2Backend::addDomainKey(const DNSName& name, const KeyData& key, int64_t& id)
313 {
314 if(!d_dnssecdb || d_hybrid)
315 return false;
316
317 try {
318 d_insertDomainKeyQuery_stmt->
319 bind("domain", name)->
320 bind("flags", key.flags)->
321 bind("active", key.active)->
322 bind("content", key.content)->
323 execute()->
324 reset();
325 }
326 catch(SSqlException& se) {
327 throw PDNSException("Error accessing DNSSEC database in BIND backend, addDomainKey(): "+se.txtReason());
328 }
329
330 try {
331 d_GetLastInsertedKeyIdQuery_stmt->execute();
332 if (!d_GetLastInsertedKeyIdQuery_stmt->hasNextRow()) {
333 id = -2;
334 return true;
335 }
336 SSqlStatement::row_t row;
337 d_GetLastInsertedKeyIdQuery_stmt->nextRow(row);
338 ASSERT_ROW_COLUMNS("get-last-inserted-key-id-query", row, 1);
339 id = std::stoi(row[0]);
340 d_GetLastInsertedKeyIdQuery_stmt->reset();
341 return true;
342 }
343 catch (SSqlException &e) {
344 id = -2;
345 return true;
346 }
347
348 return false;
349 }
350
351 bool Bind2Backend::activateDomainKey(const DNSName& name, unsigned int id)
352 {
353 if(!d_dnssecdb || d_hybrid)
354 return false;
355
356 try {
357 d_activateDomainKeyQuery_stmt->
358 bind("domain", name)->
359 bind("key_id", id)->
360 execute()->
361 reset();
362 }
363 catch(SSqlException& se) {
364 throw PDNSException("Error accessing DNSSEC database in BIND backend, activateDomainKey(): "+se.txtReason());
365 }
366 return true;
367 }
368
369 bool Bind2Backend::deactivateDomainKey(const DNSName& name, unsigned int id)
370 {
371 if(!d_dnssecdb || d_hybrid)
372 return false;
373
374 try {
375 d_deactivateDomainKeyQuery_stmt->
376 bind("domain", name)->
377 bind("key_id", id)->
378 execute()->
379 reset();
380 }
381 catch(SSqlException& se) {
382 throw PDNSException("Error accessing DNSSEC database in BIND backend, deactivateDomainKey(): "+se.txtReason());
383 }
384 return true;
385 }
386
387 bool Bind2Backend::getTSIGKey(const DNSName& name, DNSName* algorithm, string* content)
388 {
389 if(!d_dnssecdb || d_hybrid)
390 return false;
391
392 try {
393 d_getTSIGKeyQuery_stmt->
394 bind("key_name", name)->
395 execute();
396
397 SSqlStatement::row_t row;
398 content->clear();
399 while(d_getTSIGKeyQuery_stmt->hasNextRow()) {
400 d_getTSIGKeyQuery_stmt->nextRow(row);
401 if(row.size() >= 2 && (algorithm->empty() || *algorithm == DNSName(row[0]))) {
402 *algorithm = DNSName(row[0]);
403 *content = row[1];
404 }
405 }
406
407 d_getTSIGKeyQuery_stmt->reset();
408 }
409 catch (SSqlException &e) {
410 throw PDNSException("Error accessing DNSSEC database in BIND backend, getTSIGKey(): "+e.txtReason());
411 }
412 return !content->empty();
413 }
414
415 bool Bind2Backend::setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content)
416 {
417 if(!d_dnssecdb || d_hybrid)
418 return false;
419
420 try {
421 d_setTSIGKeyQuery_stmt->
422 bind("key_name", name)->
423 bind("algorithm", algorithm)->
424 bind("content", content)->
425 execute()->
426 reset();
427 }
428 catch (SSqlException &e) {
429 throw PDNSException("Error accessing DNSSEC database in BIND backend, setTSIGKey(): "+e.txtReason());
430 }
431 return true;
432 }
433
434 bool Bind2Backend::deleteTSIGKey(const DNSName& name)
435 {
436 if(!d_dnssecdb || d_hybrid)
437 return false;
438
439 try {
440 d_deleteTSIGKeyQuery_stmt->
441 bind("key_name", name)->
442 execute()->
443 reset();
444 }
445 catch (SSqlException &e) {
446 throw PDNSException("Error accessing DNSSEC database in BIND backend, deleteTSIGKey(): "+e.txtReason());
447 }
448 return true;
449 }
450
451 bool Bind2Backend::getTSIGKeys(std::vector< struct TSIGKey > &keys)
452 {
453 if(!d_dnssecdb || d_hybrid)
454 return false;
455
456 try {
457 d_getTSIGKeysQuery_stmt->
458 execute();
459
460 SSqlStatement::row_t row;
461 while(d_getTSIGKeysQuery_stmt->hasNextRow()) {
462 d_getTSIGKeysQuery_stmt->nextRow(row);
463 struct TSIGKey key;
464 key.name = DNSName(row[0]);
465 key.algorithm = DNSName(row[1]);
466 key.key = row[2];
467 keys.push_back(key);
468 }
469
470 d_getTSIGKeysQuery_stmt->reset();
471 }
472 catch (SSqlException &e) {
473 throw PDNSException("Error accessing DNSSEC database in BIND backend, getTSIGKeys(): "+e.txtReason());
474 }
475 return !keys.empty();
476 }
477
478 #endif