]>
Commit | Line | Data |
---|---|---|
c0273500 BH |
1 | #include "dnsseckeeper.hh" |
2 | #include "dnssecinfra.hh" | |
3 | #include "ueberbackend.hh" | |
4 | #include "statbag.hh" | |
5 | #include <iostream> | |
6 | #include <boost/filesystem/operations.hpp> | |
7 | #include <boost/filesystem/path.hpp> | |
8 | #include <boost/foreach.hpp> | |
9 | #include <sys/stat.h> | |
10 | #include <sys/types.h> | |
11 | #include <fstream> | |
12 | #include <boost/algorithm/string.hpp> | |
13 | #include <boost/format.hpp> | |
14 | #include <boost/assign/std/vector.hpp> // for 'operator+=()' | |
15 | #include <boost/assign/list_inserter.hpp> | |
16 | using namespace boost::assign; | |
17 | namespace fs = boost::filesystem; | |
18 | ||
19 | using namespace std; | |
20 | using namespace boost; | |
21 | ||
e0d84497 | 22 | bool DNSSECKeeper::haveActiveKSKFor(const std::string& zone, DNSSECPrivateKey* dpk) |
c0273500 BH |
23 | { |
24 | keyset_t keys = getKeys(zone, true); | |
25 | // need to get an *active* one! | |
26 | //cerr<<__FUNCTION__<<"Got "<<keys.size()<<" keys"<<endl; | |
27 | if(dpk && !keys.empty()) { | |
28 | *dpk = keys.begin()->first; | |
29 | } | |
30 | return !keys.empty(); | |
31 | } | |
32 | ||
33 | ||
34 | void DNSSECKeeper::addKey(const std::string& name, bool keyOrZone, int algorithm, int bits, bool active) | |
35 | { | |
36 | if(!bits) | |
37 | bits = keyOrZone ? 2048 : 1024; | |
38 | DNSSECPrivateKey dpk; | |
39 | dpk.d_key.create(bits); | |
f7bcc763 BH |
40 | addKey(name, keyOrZone, dpk, active); |
41 | } | |
42 | ||
43 | void DNSSECKeeper::addKey(const std::string& name, bool keyOrZone, const DNSSECPrivateKey& dpk, bool active) | |
44 | { | |
c0273500 BH |
45 | DNSBackend::KeyData kd; |
46 | kd.flags = 256 + keyOrZone; | |
47 | kd.active = active; | |
f7bcc763 | 48 | kd.content = dpk.d_key.convertToISC(5); |
c0273500 BH |
49 | |
50 | // now store it | |
e0d84497 | 51 | d_db.addDomainKey(name, kd); |
c0273500 BH |
52 | } |
53 | ||
54 | ||
55 | static bool keyCompareByKindAndID(const DNSSECKeeper::keyset_t::value_type& a, const DNSSECKeeper::keyset_t::value_type& b) | |
56 | { | |
57 | return make_pair(!a.second.keyOrZone, a.second.id) < | |
58 | make_pair(!b.second.keyOrZone, b.second.id); | |
59 | } | |
60 | ||
61 | DNSSECPrivateKey DNSSECKeeper::getKeyById(const std::string& zname, unsigned int id) | |
62 | { | |
c0273500 | 63 | vector<DNSBackend::KeyData> keys; |
e0d84497 | 64 | d_db.getDomainKeys(zname, 0, keys); |
c0273500 BH |
65 | BOOST_FOREACH(const DNSBackend::KeyData& kd, keys) { |
66 | if(kd.id != id) | |
67 | continue; | |
68 | ||
69 | DNSSECPrivateKey dpk; | |
70 | ||
71 | getRSAKeyFromISCString(&dpk.d_key.getContext(), kd.content); | |
72 | dpk.d_flags = kd.flags; | |
73 | dpk.d_algorithm = 5 + 2*getNSEC3PARAM(zname); | |
74 | ||
75 | KeyMetaData kmd; | |
76 | ||
77 | kmd.active = kd.active; | |
78 | kmd.keyOrZone = (kd.flags == 257); | |
79 | kmd.id = kd.id; | |
80 | ||
81 | return dpk; | |
82 | } | |
83 | throw runtime_error("Can't find a key with id "+lexical_cast<string>(id)+" for zone '"+zname+"'"); | |
c0273500 BH |
84 | } |
85 | ||
86 | ||
87 | void DNSSECKeeper::removeKey(const std::string& zname, unsigned int id) | |
88 | { | |
e0d84497 | 89 | d_db.removeDomainKey(zname, id); |
c0273500 BH |
90 | } |
91 | ||
92 | void DNSSECKeeper::deactivateKey(const std::string& zname, unsigned int id) | |
93 | { | |
e0d84497 | 94 | d_db.deactivateDomainKey(zname, id); |
c0273500 BH |
95 | } |
96 | ||
97 | void DNSSECKeeper::activateKey(const std::string& zname, unsigned int id) | |
98 | { | |
e0d84497 | 99 | d_db.activateDomainKey(zname, id); |
c0273500 BH |
100 | } |
101 | ||
102 | bool DNSSECKeeper::getNSEC3PARAM(const std::string& zname, NSEC3PARAMRecordContent* ns3p) | |
103 | { | |
e0d84497 | 104 | |
c0273500 | 105 | vector<string> meta; |
e0d84497 | 106 | d_db.getDomainMetadata(zname, "NSEC3PARAM", meta); |
c0273500 BH |
107 | |
108 | if(meta.empty()) | |
109 | return false; | |
110 | ||
111 | if(ns3p) { | |
112 | string descr = *meta.begin(); | |
113 | reportAllTypes(); | |
114 | NSEC3PARAMRecordContent* tmp=dynamic_cast<NSEC3PARAMRecordContent*>(DNSRecordContent::mastermake(QType::NSEC3PARAM, 1, descr)); | |
115 | if(!tmp) { | |
116 | cerr<<"descr: '"<<descr<<"'\n"; | |
117 | return false; | |
118 | } | |
119 | *ns3p = *tmp; | |
120 | delete tmp; | |
121 | } | |
122 | return true; | |
123 | } | |
124 | ||
125 | void DNSSECKeeper::setNSEC3PARAM(const std::string& zname, const NSEC3PARAMRecordContent& ns3p) | |
126 | { | |
127 | string descr = ns3p.getZoneRepresentation(); | |
128 | vector<string> meta; | |
129 | meta.push_back(descr); | |
e0d84497 | 130 | d_db.setDomainMetadata(zname, "NSEC3PARAM", meta); |
c0273500 BH |
131 | } |
132 | ||
133 | void DNSSECKeeper::unsetNSEC3PARAM(const std::string& zname) | |
134 | { | |
e0d84497 | 135 | d_db.setDomainMetadata(zname, "NSEC3PARAM", vector<string>()); |
c0273500 BH |
136 | } |
137 | ||
138 | ||
e0d84497 | 139 | DNSSECKeeper::keyset_t DNSSECKeeper::getKeys(const std::string& zone, boost::tribool allOrKeyOrZone) |
c0273500 BH |
140 | { |
141 | keyset_t keyset; | |
c0273500 BH |
142 | vector<UeberBackend::KeyData> dbkeyset; |
143 | ||
e0d84497 | 144 | d_db.getDomainKeys(zone, 0, dbkeyset); |
c0273500 BH |
145 | // do db thing |
146 | //cerr<<"Here: received " <<dbkeyset.size()<<" keys"<<endl; | |
147 | BOOST_FOREACH(UeberBackend::KeyData& kd, dbkeyset) | |
148 | { | |
149 | DNSSECPrivateKey dpk; | |
150 | ||
151 | getRSAKeyFromISCString(&dpk.d_key.getContext(), kd.content); | |
152 | dpk.d_flags = kd.flags; | |
153 | dpk.d_algorithm = 5 + 2*getNSEC3PARAM(zone); | |
154 | ||
155 | KeyMetaData kmd; | |
156 | ||
157 | kmd.active = kd.active; | |
158 | kmd.keyOrZone = (kd.flags == 257); | |
159 | kmd.id = kd.id; | |
160 | ||
161 | if(boost::indeterminate(allOrKeyOrZone) || allOrKeyOrZone == kmd.keyOrZone) | |
162 | keyset.push_back(make_pair(dpk, kmd)); | |
163 | } | |
164 | sort(keyset.begin(), keyset.end(), keyCompareByKindAndID); | |
165 | return keyset; | |
166 | } | |
167 | ||
168 | void DNSSECKeeper::secureZone(const std::string& name, int algorithm) | |
169 | { | |
170 | addKey(name, true, algorithm); | |
171 | } | |
172 | ||
e0d84497 | 173 | bool getSignerFor(DNSSECKeeper& dk, const std::string& qname, std::string &signer) |
f7bcc763 | 174 | { |
f7bcc763 BH |
175 | signer=qname; |
176 | do { | |
177 | if(dk.haveActiveKSKFor(signer)) | |
178 | return true; | |
179 | } while(chopOff(signer)); | |
180 | return false; | |
181 | } | |
182 | ||
e0d84497 BH |
183 | // this should be able to answer with multiple keys, in case of multiple active ZSKs XXX |
184 | DNSKEYRecordContent getDNSKEYFor(DNSSECKeeper& dk, const std::string& qname, bool withKSK, RSAContext* rc) | |
f7bcc763 | 185 | { |
e0d84497 | 186 | // cerr<<"Asked for a DNSKEY for '"<<qname<<"', withKSK="<<withKSK<<"\n"; |
f7bcc763 | 187 | DNSSECPrivateKey dpk; |
c0273500 | 188 | |
f7bcc763 BH |
189 | if(!withKSK) { |
190 | DNSSECKeeper::keyset_t zskset=dk.getKeys(qname, false); | |
191 | BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, zskset) { | |
192 | if(value.second.active) { | |
e0d84497 | 193 | // cerr<<"Found a ZSK for '"<<qname<<"', key tag = "<<value.first.getDNSKEY().getTag()<<endl; |
f7bcc763 BH |
194 | *rc=value.first.d_key; |
195 | return value.first.getDNSKEY(); | |
196 | } | |
197 | else | |
198 | cerr<<"Found an inactive ZSK for '"<<qname<<"', key tag = "<<value.first.getDNSKEY().getTag()<<endl; | |
199 | } | |
200 | cerr<<"Could not find an active ZSK for '"<<qname<<"'"<<endl; | |
201 | exit(1); | |
202 | } | |
203 | else if(dk.haveActiveKSKFor(qname, &dpk)) { | |
204 | cerr<<"Found a KSK for '"<<qname<<"'"<<endl; | |
205 | *rc=dpk.d_key; | |
206 | return dpk.getDNSKEY(); | |
207 | } else { | |
208 | cerr<<"DID NOT FIND A ZSK for '"<<qname<<"'"<<endl; | |
209 | exit(1); | |
210 | } | |
211 | } | |
212 | ||
e0d84497 | 213 | int getRRSIGForRRSET(DNSSECKeeper& dk, const std::string signQName, uint16_t signQType, uint32_t signTTL, |
f7bcc763 BH |
214 | vector<shared_ptr<DNSRecordContent> >& toSign, RRSIGRecordContent& rrc, bool ksk) |
215 | { | |
216 | if(toSign.empty()) | |
217 | return -1; | |
218 | ||
219 | rrc.d_type=signQType; | |
220 | ||
221 | // d_algorithm gets filled out by fillOutRRSIG, since it gets the key | |
222 | rrc.d_labels=countLabels(signQName); | |
223 | rrc.d_originalttl=signTTL; | |
224 | rrc.d_siginception=getCurrentInception();; | |
e0d84497 | 225 | rrc.d_sigexpire = rrc.d_siginception + 14*86400; // XXX should come from zone metadata |
f7bcc763 BH |
226 | |
227 | rrc.d_tag=0; | |
e0d84497 | 228 | if(!getSignerFor(dk, signQName, rrc.d_signer)) { |
f7bcc763 BH |
229 | cerr<<"No signer known for '"<<signQName<<"'\n"; |
230 | return -1; | |
231 | } | |
232 | ||
233 | string hash= getSHA1HashForRRSET(signQName, rrc, toSign); | |
e0d84497 | 234 | fillOutRRSIG(dk, signQName, rrc, hash, toSign, ksk); |
f7bcc763 BH |
235 | return 0; |
236 | } | |
237 | ||
e0d84497 | 238 | void addSignature(DNSSECKeeper& dk, const std::string signQName, const std::string& wildcardname, uint16_t signQType, uint32_t signTTL, DNSPacketWriter::Place signPlace, vector<shared_ptr<DNSRecordContent> >& toSign, DNSPacketWriter& pw) |
f7bcc763 BH |
239 | { |
240 | // cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n"; | |
241 | ||
242 | RRSIGRecordContent rrc; | |
243 | if(toSign.empty()) | |
244 | return; | |
245 | ||
246 | for(int ksk = 0; ksk < 2; ++ksk) { | |
e0d84497 | 247 | if(getRRSIGForRRSET(dk, wildcardname.empty() ? signQName : wildcardname, signQType, signTTL, toSign, rrc, ksk) < 0) { |
f7bcc763 BH |
248 | cerr<<"Error signing a record!"<<endl; |
249 | return; | |
250 | } | |
251 | ||
252 | pw.startRecord(signQName, QType::RRSIG, 3600, 1, | |
253 | signQType==QType::DNSKEY ? DNSPacketWriter:: ANSWER : signPlace); | |
254 | rrc.toPacket(pw); | |
255 | ||
256 | pw.commit(); | |
257 | if(signQType != QType::DNSKEY) | |
258 | break; | |
259 | } | |
260 | ||
261 | toSign.clear(); | |
262 | } | |
263 | ||
104f67e9 BH |
264 | pthread_mutex_t g_rrsigs_lock = PTHREAD_MUTEX_INITIALIZER; |
265 | ||
f7bcc763 BH |
266 | map<pair<string, uint16_t>, RRSIGRecordContent> g_rrsigs; |
267 | ||
e0d84497 | 268 | void fillOutRRSIG(DNSSECKeeper& dk, const std::string& signQName, RRSIGRecordContent& rrc, const std::string& hash, vector<shared_ptr<DNSRecordContent> >& toSign, bool withKSK) |
f7bcc763 BH |
269 | { |
270 | RSAContext rc; | |
271 | ||
e0d84497 | 272 | DNSKEYRecordContent drc=getDNSKEYFor(dk, rrc.d_signer, withKSK, &rc); |
f7bcc763 BH |
273 | rrc.d_tag = drc.getTag(); |
274 | rrc.d_algorithm = drc.d_algorithm; | |
104f67e9 BH |
275 | |
276 | { | |
277 | Lock l(&g_rrsigs_lock); | |
278 | if(g_rrsigs.count(make_pair(hash, rrc.d_tag))) { | |
279 | // cerr<<"RRSIG cache hit !"<<endl; | |
280 | rrc = g_rrsigs[make_pair(hash, rrc.d_tag)]; | |
281 | return; | |
282 | } | |
f7bcc763 BH |
283 | } |
284 | ||
285 | string realhash=getSHA1HashForRRSET(signQName, rrc, toSign); | |
286 | ||
287 | unsigned char signature[mpi_size(&rc.getContext().N)]; | |
288 | ||
289 | int ret=rsa_pkcs1_sign(&rc.getContext(), RSA_PRIVATE, SIG_RSA_SHA1, 20, (unsigned char*) realhash.c_str(), signature); | |
290 | ||
291 | if(ret!=0) { | |
292 | cerr<<"signing returned: "<<ret<<endl; | |
293 | exit(1); | |
294 | } | |
295 | ||
296 | rrc.d_signature.assign((char*)signature, sizeof(signature)); | |
297 | ||
104f67e9 | 298 | Lock l(&g_rrsigs_lock); |
f7bcc763 | 299 | g_rrsigs[make_pair(hash, rrc.d_tag)] = rrc; |
f7bcc763 | 300 | } |