| 1 | /* |
|---|
| 2 | PowerDNS Versatile Database Driven Nameserver |
|---|
| 3 | Copyright (C) 2001 - 2011 PowerDNS.COM BV |
|---|
| 4 | |
|---|
| 5 | This program is free software; you can redistribute it and/or modify |
|---|
| 6 | it under the terms of the GNU General Public License version 2 as |
|---|
| 7 | published by the Free Software Foundation |
|---|
| 8 | |
|---|
| 9 | This program is distributed in the hope that it will be useful, |
|---|
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 12 | GNU General Public License for more details. |
|---|
| 13 | |
|---|
| 14 | You should have received a copy of the GNU General Public License |
|---|
| 15 | along with this program; if not, write to the Free Software |
|---|
| 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
|---|
| 17 | */ |
|---|
| 18 | #include "dnssecinfra.hh" |
|---|
| 19 | #include "namespaces.hh" |
|---|
| 20 | #include <boost/foreach.hpp> |
|---|
| 21 | #include "dnsseckeeper.hh" |
|---|
| 22 | #include "lock.hh" |
|---|
| 23 | |
|---|
| 24 | /* this is where the RRSIGs begin, keys are retrieved, |
|---|
| 25 | but the actual signing happens in fillOutRRSIG */ |
|---|
| 26 | int getRRSIGsForRRSET(DNSSECKeeper& dk, const std::string& signer, const std::string signQName, uint16_t signQType, uint32_t signTTL, |
|---|
| 27 | vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent>& rrcs, bool ksk) |
|---|
| 28 | { |
|---|
| 29 | if(toSign.empty()) |
|---|
| 30 | return -1; |
|---|
| 31 | RRSIGRecordContent rrc; |
|---|
| 32 | rrc.d_type=signQType; |
|---|
| 33 | |
|---|
| 34 | rrc.d_labels=countLabels(signQName); |
|---|
| 35 | rrc.d_originalttl=signTTL; |
|---|
| 36 | rrc.d_siginception=getCurrentInception();; |
|---|
| 37 | rrc.d_sigexpire = rrc.d_siginception + 14*86400; // XXX should come from zone metadata |
|---|
| 38 | rrc.d_signer = signer; |
|---|
| 39 | rrc.d_tag = 0; |
|---|
| 40 | |
|---|
| 41 | // we sign the RRSET in toSign + the rrc w/o hash |
|---|
| 42 | |
|---|
| 43 | DNSSECKeeper::keyset_t keys = dk.getKeys(rrc.d_signer); |
|---|
| 44 | vector<DNSSECPrivateKey> KSKs, ZSKs; |
|---|
| 45 | vector<DNSSECPrivateKey>* signingKeys; |
|---|
| 46 | |
|---|
| 47 | // if ksk==1, only get KSKs |
|---|
| 48 | // if ksk==0, get ZSKs, unless there is no ZSK, then get KSK |
|---|
| 49 | BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type& keymeta, keys) { |
|---|
| 50 | rrc.d_algorithm = keymeta.first.d_algorithm; |
|---|
| 51 | if(!keymeta.second.active) |
|---|
| 52 | continue; |
|---|
| 53 | |
|---|
| 54 | if(keymeta.second.keyOrZone) |
|---|
| 55 | KSKs.push_back(keymeta.first); |
|---|
| 56 | else if(!ksk) |
|---|
| 57 | ZSKs.push_back(keymeta.first); |
|---|
| 58 | } |
|---|
| 59 | if(ksk) |
|---|
| 60 | signingKeys = &KSKs; |
|---|
| 61 | else { |
|---|
| 62 | if(ZSKs.empty()) |
|---|
| 63 | signingKeys = &KSKs; |
|---|
| 64 | else |
|---|
| 65 | signingKeys =&ZSKs; |
|---|
| 66 | } |
|---|
| 67 | |
|---|
| 68 | BOOST_FOREACH(DNSSECPrivateKey& dpk, *signingKeys) { |
|---|
| 69 | fillOutRRSIG(dpk, signQName, rrc, toSign); |
|---|
| 70 | rrcs.push_back(rrc); |
|---|
| 71 | } |
|---|
| 72 | return 0; |
|---|
| 73 | } |
|---|
| 74 | |
|---|
| 75 | // this is the entrypoint from DNSPacket |
|---|
| 76 | void addSignature(DNSSECKeeper& dk, const std::string& signer, const std::string signQName, const std::string& wildcardname, uint16_t signQType, |
|---|
| 77 | uint32_t signTTL, DNSPacketWriter::Place signPlace, |
|---|
| 78 | vector<shared_ptr<DNSRecordContent> >& toSign, vector<DNSResourceRecord>& outsigned) |
|---|
| 79 | { |
|---|
| 80 | // cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n"; |
|---|
| 81 | if(toSign.empty()) |
|---|
| 82 | return; |
|---|
| 83 | vector<RRSIGRecordContent> rrcs; |
|---|
| 84 | if(dk.isPresigned(signer)) { |
|---|
| 85 | dk.getPreRRSIGs(signer, signQName, QType(signQType), signPlace, outsigned); // does it all |
|---|
| 86 | } |
|---|
| 87 | else { |
|---|
| 88 | if(getRRSIGsForRRSET(dk, signer, wildcardname.empty() ? signQName : wildcardname, signQType, signTTL, toSign, rrcs, signQType == QType::DNSKEY) < 0) { |
|---|
| 89 | // cerr<<"Error signing a record!"<<endl; |
|---|
| 90 | return; |
|---|
| 91 | } |
|---|
| 92 | |
|---|
| 93 | DNSResourceRecord rr; |
|---|
| 94 | rr.qname=signQName; |
|---|
| 95 | rr.qtype=QType::RRSIG; |
|---|
| 96 | rr.ttl=signTTL; |
|---|
| 97 | rr.auth=false; |
|---|
| 98 | rr.d_place = (DNSResourceRecord::Place) signPlace; |
|---|
| 99 | BOOST_FOREACH(RRSIGRecordContent& rrc, rrcs) { |
|---|
| 100 | rr.content = rrc.getZoneRepresentation(); |
|---|
| 101 | outsigned.push_back(rr); |
|---|
| 102 | } |
|---|
| 103 | } |
|---|
| 104 | toSign.clear(); |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | static pthread_mutex_t g_signatures_lock = PTHREAD_MUTEX_INITIALIZER; |
|---|
| 108 | static map<pair<string, string>, string> g_signatures; |
|---|
| 109 | |
|---|
| 110 | void fillOutRRSIG(DNSSECPrivateKey& dpk, const std::string& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign) |
|---|
| 111 | { |
|---|
| 112 | DNSKEYRecordContent drc = dpk.getDNSKEY(); |
|---|
| 113 | const DNSPrivateKey* rc = dpk.getKey(); |
|---|
| 114 | rrc.d_tag = drc.getTag(); |
|---|
| 115 | rrc.d_algorithm = drc.d_algorithm; |
|---|
| 116 | string realhash=getHashForRRSET(signQName, rrc, toSign); // this is what we sign |
|---|
| 117 | |
|---|
| 118 | pair<string, string> lookup(rc->getPubKeyHash(), realhash); |
|---|
| 119 | |
|---|
| 120 | { |
|---|
| 121 | Lock l(&g_signatures_lock); |
|---|
| 122 | if(g_signatures.count(lookup)) { |
|---|
| 123 | // cerr<<"Hit!"<<endl; |
|---|
| 124 | rrc.d_signature=g_signatures[lookup]; |
|---|
| 125 | return; |
|---|
| 126 | } |
|---|
| 127 | else |
|---|
| 128 | ; // cerr<<"Miss!"<<endl; |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | rrc.d_signature = rc->sign(realhash); |
|---|
| 132 | |
|---|
| 133 | Lock l(&g_signatures_lock); |
|---|
| 134 | g_signatures[lookup] = rrc.d_signature; |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | static bool rrsigncomp(const DNSResourceRecord& a, const DNSResourceRecord& b) |
|---|
| 138 | { |
|---|
| 139 | return a.d_place < b.d_place; |
|---|
| 140 | } |
|---|
| 141 | |
|---|
| 142 | void addRRSigs(DNSSECKeeper& dk, const std::string& signer, DNSPacket& p) |
|---|
| 143 | { |
|---|
| 144 | vector<DNSResourceRecord>& rrs=p.getRRS(); |
|---|
| 145 | |
|---|
| 146 | stable_sort(rrs.begin(), rrs.end(), rrsigncomp); |
|---|
| 147 | |
|---|
| 148 | string signQName, wildcardQName; |
|---|
| 149 | uint16_t signQType=0; |
|---|
| 150 | uint32_t signTTL=0; |
|---|
| 151 | |
|---|
| 152 | DNSPacketWriter::Place signPlace=DNSPacketWriter::ANSWER; |
|---|
| 153 | vector<shared_ptr<DNSRecordContent> > toSign; |
|---|
| 154 | |
|---|
| 155 | vector<DNSResourceRecord> signedRecords; |
|---|
| 156 | |
|---|
| 157 | for(vector<DNSResourceRecord>::const_iterator pos = rrs.begin(); pos != rrs.end(); ++pos) { |
|---|
| 158 | signedRecords.push_back(*pos); |
|---|
| 159 | if(pos != rrs.begin() && (signQType != pos->qtype.getCode() || signQName != pos->qname)) { |
|---|
| 160 | addSignature(dk, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords); |
|---|
| 161 | } |
|---|
| 162 | signQName= pos->qname; |
|---|
| 163 | wildcardQName = pos->wildcardname; |
|---|
| 164 | signQType = pos ->qtype.getCode(); |
|---|
| 165 | signTTL = pos->ttl; |
|---|
| 166 | signPlace = (DNSPacketWriter::Place) pos->d_place; |
|---|
| 167 | if(pos->auth || pos->qtype.getCode() == QType::DS) { |
|---|
| 168 | string content = pos->content; |
|---|
| 169 | if(pos->qtype.getCode()==QType::MX || pos->qtype.getCode() == QType::SRV) { |
|---|
| 170 | content = lexical_cast<string>(pos->priority) + " " + pos->content; |
|---|
| 171 | } |
|---|
| 172 | if(!pos->content.empty() && pos->qtype.getCode()==QType::TXT && pos->content[0]!='"') { |
|---|
| 173 | content="\""+pos->content+"\""; |
|---|
| 174 | } |
|---|
| 175 | if(pos->content.empty()) // empty contents confuse the MOADNS setup |
|---|
| 176 | content="."; |
|---|
| 177 | |
|---|
| 178 | shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(pos->qtype.getCode(), 1, content)); |
|---|
| 179 | toSign.push_back(drc); |
|---|
| 180 | } |
|---|
| 181 | } |
|---|
| 182 | addSignature(dk, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords); |
|---|
| 183 | |
|---|
| 184 | rrs.swap(signedRecords); |
|---|
| 185 | } |
|---|