| 1 | #include "dnsparser.hh" |
|---|
| 2 | #include "sstuff.hh" |
|---|
| 3 | #include "misc.hh" |
|---|
| 4 | #include "dnswriter.hh" |
|---|
| 5 | #include "dnsrecords.hh" |
|---|
| 6 | #include "statbag.hh" |
|---|
| 7 | #include "iputils.hh" |
|---|
| 8 | #include <netinet/sctp.h> |
|---|
| 9 | #include <boost/foreach.hpp> |
|---|
| 10 | #include <boost/algorithm/string.hpp> |
|---|
| 11 | #include <polarssl/rsa.h> |
|---|
| 12 | #include <polarssl/base64.h> |
|---|
| 13 | #include <polarssl/sha1.h> |
|---|
| 14 | #include <polarssl/sha2.h> |
|---|
| 15 | #include "dnssecinfra.hh" |
|---|
| 16 | #include "dnsseckeeper.hh" |
|---|
| 17 | |
|---|
| 18 | using namespace boost; |
|---|
| 19 | using namespace std; |
|---|
| 20 | |
|---|
| 21 | DNSKEYRecordContent getRSAKeyFromISC(rsa_context* rsa, const char* fname) |
|---|
| 22 | { |
|---|
| 23 | char line[1024]; |
|---|
| 24 | |
|---|
| 25 | string sline; |
|---|
| 26 | string key,value; |
|---|
| 27 | map<string, mpi*> places; |
|---|
| 28 | |
|---|
| 29 | FILE *fp=fopen(fname, "r"); |
|---|
| 30 | if(!fp) |
|---|
| 31 | unixDie("opening file '"+string(fname)+"'"); |
|---|
| 32 | |
|---|
| 33 | rsa_init(rsa, RSA_PKCS_V15, 0, NULL, NULL ); |
|---|
| 34 | |
|---|
| 35 | places["Modulus"]=&rsa->N; |
|---|
| 36 | places["PublicExponent"]=&rsa->E; |
|---|
| 37 | places["PrivateExponent"]=&rsa->D; |
|---|
| 38 | places["Prime1"]=&rsa->P; |
|---|
| 39 | places["Prime2"]=&rsa->Q; |
|---|
| 40 | places["Exponent1"]=&rsa->DP; |
|---|
| 41 | places["Exponent2"]=&rsa->DQ; |
|---|
| 42 | places["Coefficient"]=&rsa->QP; |
|---|
| 43 | |
|---|
| 44 | unsigned char decoded[1024]; |
|---|
| 45 | DNSKEYRecordContent drc; |
|---|
| 46 | string modulus, exponent; |
|---|
| 47 | while(fgets(line, sizeof(line),fp)) { |
|---|
| 48 | sline.assign(line); |
|---|
| 49 | tie(key,value)=splitField(line, ':'); |
|---|
| 50 | trim(value); |
|---|
| 51 | |
|---|
| 52 | if(places.count(key)) { |
|---|
| 53 | if(places[key]) { |
|---|
| 54 | |
|---|
| 55 | int len=sizeof(decoded); |
|---|
| 56 | if(base64_decode(decoded, &len, (unsigned char*)value.c_str(), value.length()) < 0) { |
|---|
| 57 | cerr<<"Error base64 decoding '"<<value<<"'\n"; |
|---|
| 58 | exit(1); |
|---|
| 59 | } |
|---|
| 60 | // B64Decode(value, decoded); |
|---|
| 61 | // cerr<<key<<" decoded.length(): "<<8*len<<endl; |
|---|
| 62 | mpi_read_binary(places[key], decoded, len); |
|---|
| 63 | if(key=="Modulus") |
|---|
| 64 | modulus.assign((const char*)decoded,len); |
|---|
| 65 | if(key=="PublicExponent") |
|---|
| 66 | exponent.assign((const char*)decoded,len); |
|---|
| 67 | } |
|---|
| 68 | } |
|---|
| 69 | else { |
|---|
| 70 | if(key != "Private-key-format" && key != "Algorithm") |
|---|
| 71 | cerr<<"Unknown field '"<<key<<"'\n"; |
|---|
| 72 | } |
|---|
| 73 | } |
|---|
| 74 | rsa->len = ( mpi_msb( &rsa->N ) + 7 ) >> 3; // no clue what this does |
|---|
| 75 | |
|---|
| 76 | if(exponent.length() < 255) |
|---|
| 77 | drc.d_key.assign(1, (char) (unsigned int) exponent.length()); |
|---|
| 78 | else { |
|---|
| 79 | drc.d_key.assign(1, 0); |
|---|
| 80 | uint16_t len=htons(exponent.length()); |
|---|
| 81 | drc.d_key.append((char*)&len, 2); |
|---|
| 82 | } |
|---|
| 83 | drc.d_key.append(exponent); |
|---|
| 84 | drc.d_key.append(modulus); |
|---|
| 85 | drc.d_protocol=3; |
|---|
| 86 | drc.d_algorithm = 5; |
|---|
| 87 | fclose(fp); |
|---|
| 88 | return drc; |
|---|
| 89 | } |
|---|
| 90 | |
|---|
| 91 | |
|---|
| 92 | void makeRSAPublicKeyFromDNS(rsa_context* rc, const DNSKEYRecordContent& dkrc) |
|---|
| 93 | { |
|---|
| 94 | rsa_init(rc, RSA_PKCS_V15, 0, NULL, NULL ); |
|---|
| 95 | |
|---|
| 96 | mpi_read_binary(&rc->E, (unsigned char*)dkrc.getExponent().c_str(), dkrc.getExponent().length()); // exponent |
|---|
| 97 | mpi_read_binary(&rc->N, (unsigned char*)dkrc.getModulus().c_str(), dkrc.getModulus().length()); // modulus |
|---|
| 98 | rc->len = ( mpi_msb( &rc->N ) + 7 ) >> 3; // no clue what this does |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | bool sharedDNSSECCompare(const shared_ptr<DNSRecordContent>& a, const shared_ptr<DNSRecordContent>& b) |
|---|
| 102 | { |
|---|
| 103 | return a->serialize("", true) < b->serialize("", true); |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | string getSHA1HashForRRSET(const std::string& qname, const RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& signRecords) |
|---|
| 107 | { |
|---|
| 108 | sort(signRecords.begin(), signRecords.end(), sharedDNSSECCompare); |
|---|
| 109 | |
|---|
| 110 | string toHash; |
|---|
| 111 | toHash.append(const_cast<RRSIGRecordContent&>(rrc).serialize("", true)); |
|---|
| 112 | toHash.resize(toHash.size() - rrc.d_signature.length()); // chop off the end; |
|---|
| 113 | // cerr<<"toHash start size: "<<toHash.size()<<", signature length: "<<rrc.d_signature.length()<<endl; |
|---|
| 114 | |
|---|
| 115 | |
|---|
| 116 | BOOST_FOREACH(shared_ptr<DNSRecordContent>& add, signRecords) { |
|---|
| 117 | // cerr<<"\t IN "<<rrc.d_originalttl<<"\t"<<add->getZoneRepresentation()<<"\n"; |
|---|
| 118 | toHash.append(toLower(simpleCompress(qname, ""))); |
|---|
| 119 | uint16_t tmp=htons(rrc.d_type); |
|---|
| 120 | toHash.append((char*)&tmp, 2); |
|---|
| 121 | tmp=htons(1); // class |
|---|
| 122 | toHash.append((char*)&tmp, 2); |
|---|
| 123 | uint32_t ttl=htonl(rrc.d_originalttl); |
|---|
| 124 | toHash.append((char*)&ttl, 4); |
|---|
| 125 | string rdata=add->serialize("", true); // case issues hiding here.. |
|---|
| 126 | tmp=htons(rdata.length()); |
|---|
| 127 | toHash.append((char*)&tmp, 2); |
|---|
| 128 | toHash.append(rdata); |
|---|
| 129 | } |
|---|
| 130 | // cerr<<"toHash: "<<makeHexDump(toHash)<<endl; |
|---|
| 131 | unsigned char hash[20]; |
|---|
| 132 | sha1((unsigned char*)toHash.c_str(), toHash.length(), hash); |
|---|
| 133 | return string((char*)hash, 20); |
|---|
| 134 | } |
|---|
| 135 | |
|---|
| 136 | DSRecordContent makeDSFromDNSKey(const std::string& qname, const DNSKEYRecordContent& drc, int digest) |
|---|
| 137 | { |
|---|
| 138 | string toHash; |
|---|
| 139 | toHash.assign(toLower(simpleCompress(qname))); |
|---|
| 140 | toHash.append(const_cast<DNSKEYRecordContent&>(drc).serialize("", true)); |
|---|
| 141 | |
|---|
| 142 | unsigned char hash[32]; |
|---|
| 143 | if(digest==1) |
|---|
| 144 | sha1((unsigned char*)toHash.c_str(), toHash.length(), hash); |
|---|
| 145 | else |
|---|
| 146 | sha2((unsigned char*)toHash.c_str(), toHash.length(), hash, 0); |
|---|
| 147 | |
|---|
| 148 | DSRecordContent dsrc; |
|---|
| 149 | dsrc.d_algorithm=5; |
|---|
| 150 | dsrc.d_digesttype=digest; |
|---|
| 151 | dsrc.d_tag=const_cast<DNSKEYRecordContent&>(drc).getTag(); |
|---|
| 152 | dsrc.d_digest.assign((const char*)hash, digest == 1 ? 20 : 32); |
|---|
| 153 | return dsrc; |
|---|
| 154 | } |
|---|
| 155 | |
|---|
| 156 | DNSKEYRecordContent makeDNSKEYFromRSAKey(rsa_context* rc) |
|---|
| 157 | { |
|---|
| 158 | DNSKEYRecordContent drc; |
|---|
| 159 | char tmp[256]; |
|---|
| 160 | |
|---|
| 161 | // cerr<<"in makeDNSKEY rsa_check_pubkey: "<<rsa_check_pubkey(rc)<<", bits="<<mpi_size(&rc->N)*8<<endl; |
|---|
| 162 | |
|---|
| 163 | mpi_write_binary(&rc->E, (unsigned char*)tmp, mpi_size(&rc->E) ); |
|---|
| 164 | string exponent((char*)tmp, mpi_size(&rc->E)); |
|---|
| 165 | |
|---|
| 166 | mpi_write_binary(&rc->N, (unsigned char*)tmp, mpi_size(&rc->N) ); |
|---|
| 167 | string modulus((char*)tmp, mpi_size(&rc->N)); |
|---|
| 168 | |
|---|
| 169 | if(exponent.length() < 255) |
|---|
| 170 | drc.d_key.assign(1, (char) (unsigned int) exponent.length()); |
|---|
| 171 | else { |
|---|
| 172 | drc.d_key.assign(1, 0); |
|---|
| 173 | uint16_t len=htons(exponent.length()); |
|---|
| 174 | drc.d_key.append((char*)&len, 2); |
|---|
| 175 | } |
|---|
| 176 | drc.d_key.append(exponent); |
|---|
| 177 | drc.d_key.append(modulus); |
|---|
| 178 | |
|---|
| 179 | drc.d_protocol=3; |
|---|
| 180 | drc.d_algorithm = 5; |
|---|
| 181 | |
|---|
| 182 | drc.d_flags=256 + (modulus.length()>128); // oops, I just made this up.. |
|---|
| 183 | |
|---|
| 184 | return drc; |
|---|
| 185 | } |
|---|
| 186 | |
|---|
| 187 | bool getSignerFor(const std::string& keyRepositoryDir, const std::string& qname, std::string &signer) |
|---|
| 188 | { |
|---|
| 189 | DNSSECKeeper dk(keyRepositoryDir); |
|---|
| 190 | |
|---|
| 191 | signer=qname; |
|---|
| 192 | do { |
|---|
| 193 | if(dk.haveKSKFor(signer)) |
|---|
| 194 | return true; |
|---|
| 195 | } while(chopOff(signer)); |
|---|
| 196 | return false; |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | int countLabels(const std::string& signQName) |
|---|
| 200 | { |
|---|
| 201 | int count =1; |
|---|
| 202 | for(string::const_iterator pos = signQName.begin(); pos != signQName.end() ; ++pos) |
|---|
| 203 | if(*pos == '.' && pos+1 != signQName.end()) |
|---|
| 204 | count++; |
|---|
| 205 | |
|---|
| 206 | if(starts_with(signQName, "*.")) |
|---|
| 207 | count--; |
|---|
| 208 | return count; |
|---|
| 209 | } |
|---|
| 210 | |
|---|
| 211 | DNSKEYRecordContent getDNSKEYFor(const std::string& keyRepositoryDir, const std::string& qname, bool withKSK, RSAContext* rc) |
|---|
| 212 | { |
|---|
| 213 | DNSSECKeeper dk(keyRepositoryDir); |
|---|
| 214 | cerr<<"Asked for a DNSKEY for '"<<qname<<"', withKSK="<<withKSK<<"\n"; |
|---|
| 215 | DNSSECPrivateKey dpk; |
|---|
| 216 | |
|---|
| 217 | if(!withKSK) { |
|---|
| 218 | DNSSECKeeper::zskset_t zskset=dk.getZSKsFor(qname); |
|---|
| 219 | BOOST_FOREACH(DNSSECKeeper::zskset_t::value_type value, zskset) { |
|---|
| 220 | if(value.second) { |
|---|
| 221 | cerr<<"Found a ZSK for '"<<qname<<"', key tag = "<<value.first.getDNSKEY().getTag()<<endl; |
|---|
| 222 | *rc=value.first.d_key; |
|---|
| 223 | return value.first.getDNSKEY(); |
|---|
| 224 | } |
|---|
| 225 | else |
|---|
| 226 | cerr<<"Found an expired ZSK for '"<<qname<<"', key tag = "<<value.first.getDNSKEY().getTag()<<endl; |
|---|
| 227 | } |
|---|
| 228 | cerr<<"withKSK was not true, but found nothing!"<<endl; |
|---|
| 229 | exit(1); |
|---|
| 230 | } |
|---|
| 231 | else if(dk.haveKSKFor(qname, &dpk)) { |
|---|
| 232 | cerr<<"Found something"<<endl; |
|---|
| 233 | *rc=dpk.d_key; |
|---|
| 234 | return dpk.getDNSKEY(); |
|---|
| 235 | } else { |
|---|
| 236 | cerr<<"DID NOT FIND A ZSK!"<<endl; |
|---|
| 237 | exit(1); |
|---|
| 238 | } |
|---|
| 239 | } |
|---|
| 240 | |
|---|
| 241 | |
|---|
| 242 | map<pair<string, uint16_t>, RRSIGRecordContent> g_rrsigs; |
|---|
| 243 | |
|---|
| 244 | void fillOutRRSIG(const std::string& keyrepodir, const std::string& signQName, RRSIGRecordContent& rrc, const std::string& hash, vector<shared_ptr<DNSRecordContent> >& toSign, bool withKSK) |
|---|
| 245 | { |
|---|
| 246 | RSAContext rc; |
|---|
| 247 | |
|---|
| 248 | DNSKEYRecordContent drc =getDNSKEYFor(keyrepodir, rrc.d_signer, withKSK, &rc); |
|---|
| 249 | rrc.d_tag = drc.getTag(); |
|---|
| 250 | |
|---|
| 251 | if(g_rrsigs.count(make_pair(hash, rrc.d_tag))) { |
|---|
| 252 | cerr<<"RRSIG cache hit !"<<endl; |
|---|
| 253 | rrc = g_rrsigs[make_pair(hash, rrc.d_tag)]; |
|---|
| 254 | return; |
|---|
| 255 | } |
|---|
| 256 | |
|---|
| 257 | string realhash=getSHA1HashForRRSET(signQName, rrc, toSign); |
|---|
| 258 | |
|---|
| 259 | unsigned char signature[mpi_size(&rc.getContext().N)]; |
|---|
| 260 | |
|---|
| 261 | int ret=rsa_pkcs1_sign(&rc.getContext(), RSA_PRIVATE, SIG_RSA_SHA1, 20, (unsigned char*) realhash.c_str(), signature); |
|---|
| 262 | |
|---|
| 263 | if(ret!=0) { |
|---|
| 264 | cerr<<"signing returned: "<<ret<<endl; |
|---|
| 265 | exit(1); |
|---|
| 266 | } |
|---|
| 267 | |
|---|
| 268 | rrc.d_signature.assign((char*)signature, sizeof(signature)); |
|---|
| 269 | |
|---|
| 270 | g_rrsigs[make_pair(hash, rrc.d_tag)] = rrc; |
|---|
| 271 | |
|---|
| 272 | } |
|---|
| 273 | |
|---|
| 274 | uint32_t getCurrentInception() |
|---|
| 275 | { |
|---|
| 276 | uint32_t now = time(0); |
|---|
| 277 | now -= (now % (7*86400)); |
|---|
| 278 | return now; |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | |
|---|
| 282 | int getRRSIGForRRSET(const std::string& keyrepodir, const std::string signQName, uint16_t signQType, uint32_t signTTL, |
|---|
| 283 | vector<shared_ptr<DNSRecordContent> >& toSign, RRSIGRecordContent& rrc, bool ksk) |
|---|
| 284 | { |
|---|
| 285 | if(toSign.empty()) |
|---|
| 286 | return -1; |
|---|
| 287 | |
|---|
| 288 | rrc.d_type=signQType; |
|---|
| 289 | rrc.d_algorithm=5; // rsasha1 |
|---|
| 290 | rrc.d_labels=countLabels(signQName); |
|---|
| 291 | rrc.d_originalttl=signTTL; |
|---|
| 292 | rrc.d_siginception=getCurrentInception();; |
|---|
| 293 | rrc.d_sigexpire = rrc.d_siginception + 14*86400; |
|---|
| 294 | |
|---|
| 295 | rrc.d_tag=0; |
|---|
| 296 | if(!getSignerFor(keyrepodir, signQName, rrc.d_signer)) { |
|---|
| 297 | cerr<<"No signer known for '"<<signQName<<"'\n"; |
|---|
| 298 | return -1; |
|---|
| 299 | } |
|---|
| 300 | |
|---|
| 301 | string hash= getSHA1HashForRRSET(signQName, rrc, toSign); |
|---|
| 302 | fillOutRRSIG(keyrepodir, signQName, rrc, hash, toSign, ksk); |
|---|
| 303 | return 0; |
|---|
| 304 | } |
|---|
| 305 | |
|---|
| 306 | void addSignature(const std::string& keyrepodir, const std::string signQName, const std::string& wildcardname, uint16_t signQType, uint32_t signTTL, DNSPacketWriter::Place signPlace, vector<shared_ptr<DNSRecordContent> >& toSign, DNSPacketWriter& pw) |
|---|
| 307 | { |
|---|
| 308 | cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n"; |
|---|
| 309 | |
|---|
| 310 | RRSIGRecordContent rrc; |
|---|
| 311 | if(toSign.empty()) |
|---|
| 312 | return; |
|---|
| 313 | |
|---|
| 314 | for(int ksk = 0; ksk < 2; ++ksk) { |
|---|
| 315 | if(getRRSIGForRRSET(keyrepodir, wildcardname.empty() ? signQName : wildcardname, signQType, signTTL, toSign, rrc, ksk) < 0) { |
|---|
| 316 | cerr<<"Error signing a record!"<<endl; |
|---|
| 317 | return; |
|---|
| 318 | } |
|---|
| 319 | |
|---|
| 320 | pw.startRecord(signQName, QType::RRSIG, 3600, 1, |
|---|
| 321 | signQType==QType::DNSKEY ? DNSPacketWriter:: ANSWER : signPlace); |
|---|
| 322 | rrc.toPacket(pw); |
|---|
| 323 | |
|---|
| 324 | pw.commit(); |
|---|
| 325 | if(signQType != QType::DNSKEY) |
|---|
| 326 | break; |
|---|
| 327 | } |
|---|
| 328 | |
|---|
| 329 | toSign.clear(); |
|---|
| 330 | } |
|---|
| 331 | |
|---|
| 332 | |
|---|
| 333 | std::string hashQNameWithSalt(unsigned int times, const std::string& salt, const std::string& qname) |
|---|
| 334 | { |
|---|
| 335 | string toHash; |
|---|
| 336 | toHash.assign(simpleCompress(toLower(qname))); |
|---|
| 337 | toHash.append(salt); |
|---|
| 338 | |
|---|
| 339 | cerr<<makeHexDump(toHash)<<endl; |
|---|
| 340 | unsigned char hash[20]; |
|---|
| 341 | for(;;) { |
|---|
| 342 | sha1((unsigned char*)toHash.c_str(), toHash.length(), hash); |
|---|
| 343 | if(!times--) |
|---|
| 344 | break; |
|---|
| 345 | toHash.assign((char*)hash, 20); |
|---|
| 346 | toHash.append(salt); |
|---|
| 347 | } |
|---|
| 348 | return string((char*)hash, 20); |
|---|
| 349 | } |
|---|