using namespace std;
using namespace boost;
-bool DNSSECKeeper::haveActiveKSKFor(const std::string& zone, DNSSECPrivateKey* dpk)
+bool DNSSECKeeper::haveActiveKSKFor(const std::string& zone)
{
keyset_t keys = getKeys(zone, true);
// need to get an *active* one!
//cerr<<__FUNCTION__<<"Got "<<keys.size()<<" keys"<<endl;
- if(dpk && !keys.empty()) {
- *dpk = keys.begin()->first;
+ BOOST_FOREACH(keyset_t::value_type& val, keys) {
+ if(val.second.active) {
+ return true;
+ }
}
- return !keys.empty();
+ return false;
}
{
addKey(name, true, algorithm);
}
-
-bool getSignerFor(DNSSECKeeper& dk, const std::string& qname, std::string &signer, uint8_t& algorithm)
+
+// nobody should ever call this function, you know the SOA/auth already!
+bool getSignerApexFor(DNSSECKeeper& dk, const std::string& qname, std::string &signer)
{
+ // cerr<<"getSignerApexFor: called, and should not be, should go away!"<<endl;
signer=qname;
- DNSSECPrivateKey dpk;
do {
- if(dk.haveActiveKSKFor(signer, &dpk)) {
- algorithm = dpk.d_algorithm;
+ if(dk.haveActiveKSKFor(signer)) {
return true;
}
} while(chopOff(signer));
return false;
}
-// this should be able to answer with multiple keys, in case of multiple active ZSKs XXX
-DNSKEYRecordContent getDNSKEYFor(DNSSECKeeper& dk, const std::string& qname, bool withKSK, RSAContext* rc)
-{
- // cerr<<"Asked for a DNSKEY for '"<<qname<<"', withKSK="<<withKSK<<"\n";
- DNSSECPrivateKey dpk;
-
- if(!withKSK) {
- DNSSECKeeper::keyset_t zskset=dk.getKeys(qname, false);
- BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, zskset) {
- if(value.second.active) {
- // cerr<<"Found a ZSK for '"<<qname<<"', key tag = "<<value.first.getDNSKEY().getTag()<<endl;
- *rc=value.first.d_key;
- return value.first.getDNSKEY();
- }
- else
- cerr<<"Found an inactive ZSK for '"<<qname<<"', key tag = "<<value.first.getDNSKEY().getTag()<<endl;
- }
- cerr<<"Could not find an active ZSK for '"<<qname<<"'"<<endl;
- exit(1);
- }
- else if(dk.haveActiveKSKFor(qname, &dpk)) {
-// cerr<<"Found a KSK for '"<<qname<<"'"<<endl;
- *rc=dpk.d_key;
- return dpk.getDNSKEY();
- } else {
- cerr<<"DID NOT FIND A ZSK for '"<<qname<<"'"<<endl;
- exit(1);
- }
-}
-
-int getRRSIGForRRSET(DNSSECKeeper& dk, const std::string signQName, uint16_t signQType, uint32_t signTTL,
- vector<shared_ptr<DNSRecordContent> >& toSign, RRSIGRecordContent& rrc, bool ksk)
+/* this is where the RRSIG gets filled out, the hashing gets done, key apex *name* gets found,
+ but the actual signing happens in fillOutRRSIG */
+int getRRSIGsForRRSET(DNSSECKeeper& dk, const std::string signQName, uint16_t signQType, uint32_t signTTL,
+ vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent>& rrcs, bool ksk)
{
if(toSign.empty())
return -1;
-
+ RRSIGRecordContent rrc;
rrc.d_type=signQType;
- // d_algorithm gets filled out by fillOutRRSIG, since it gets the key
+ // d_algorithm gets filled out by getSignerAPEX, since only it looks up the key
rrc.d_labels=countLabels(signQName);
rrc.d_originalttl=signTTL;
rrc.d_siginception=getCurrentInception();;
rrc.d_sigexpire = rrc.d_siginception + 14*86400; // XXX should come from zone metadata
+ rrc.d_tag = 0;
- rrc.d_tag=0;
- if(!getSignerFor(dk, signQName, rrc.d_signer, rrc.d_algorithm)) {
+ // XXX we know the apex already.. is is the SOA name which we determined earlier
+ if(!getSignerApexFor(dk, signQName, rrc.d_signer)) {
cerr<<"No signer known for '"<<signQName<<"'\n";
return -1;
}
-
- string hash= getSHA1HashForRRSET(signQName, rrc, toSign);
- fillOutRRSIG(dk, signQName, rrc, hash, toSign, ksk);
+ // we sign the RRSET in toSign + the rrc w/o key
+
+ DNSSECKeeper::keyset_t keys = dk.getKeys(rrc.d_signer);
+ vector<DNSSECPrivateKey> KSKs, ZSKs;
+ vector<DNSSECPrivateKey>* signingKeys;
+
+ // if ksk==1, only get KSKs
+ // if ksk==0, get ZSKs, unless there is no ZSK, then get KSK
+ BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type& keymeta, keys) {
+ if(!keymeta.second.active)
+ continue;
+
+ if(keymeta.second.keyOrZone)
+ KSKs.push_back(keymeta.first);
+ else if(!ksk)
+ ZSKs.push_back(keymeta.first);
+ }
+ if(ksk)
+ signingKeys = &KSKs;
+ else {
+ if(ZSKs.empty())
+ signingKeys = &KSKs;
+ else
+ signingKeys =&ZSKs;
+ }
+
+ BOOST_FOREACH(DNSSECPrivateKey& dpk, *signingKeys) {
+ fillOutRRSIG(dpk, signQName, rrc, toSign);
+ rrcs.push_back(rrc);
+ }
return 0;
}
+// this is the entrypoint from DNSPacket
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, uint16_t maxReplyLen, DNSPacketWriter& pw)
{
// cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
- RRSIGRecordContent rrc;
+ vector<RRSIGRecordContent> rrcs;
if(toSign.empty())
return;
- for(int ksk = 0; ksk < 2; ++ksk) {
- if(getRRSIGForRRSET(dk, wildcardname.empty() ? signQName : wildcardname, signQType, signTTL, toSign, rrc, ksk) < 0) {
- cerr<<"Error signing a record!"<<endl;
- return;
- }
-
+ if(getRRSIGsForRRSET(dk, wildcardname.empty() ? signQName : wildcardname, signQType, signTTL, toSign, rrcs, signQType == QType::DNSKEY) < 0) {
+ cerr<<"Error signing a record!"<<endl;
+ return;
+ }
+ BOOST_FOREACH(RRSIGRecordContent& rrc, rrcs) {
pw.startRecord(signQName, QType::RRSIG, 3600, 1,
- signQType==QType::DNSKEY ? DNSPacketWriter:: ANSWER : signPlace);
+ signQType==QType::DNSKEY ? DNSPacketWriter:: ANSWER : signPlace);
rrc.toPacket(pw);
if(maxReplyLen && (pw.size() + 20) > maxReplyLen) {
pw.rollback();
pw.getHeader()->tc=1;
return;
}
-
- pw.commit();
- if(signQType != QType::DNSKEY)
- break;
}
+ pw.commit();
toSign.clear();
}
-pthread_mutex_t g_rrsigs_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t g_signatures_lock = PTHREAD_MUTEX_INITIALIZER;
+static map<pair<RSAContext, string>, string> g_signatures;
-map<pair<string, uint16_t>, RRSIGRecordContent> g_rrsigs;
-
-void fillOutRRSIG(DNSSECKeeper& dk, const std::string& signQName, RRSIGRecordContent& rrc, const std::string& hash, vector<shared_ptr<DNSRecordContent> >& toSign, bool withKSK)
+void fillOutRRSIG(DNSSECPrivateKey& dpk, const std::string& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign)
{
- RSAContext rc;
-
- DNSKEYRecordContent drc=getDNSKEYFor(dk, rrc.d_signer, withKSK, &rc);
+ DNSKEYRecordContent drc= dpk.getDNSKEY();
+ RSAContext& rc = dpk.d_key;
rrc.d_tag = drc.getTag();
rrc.d_algorithm = drc.d_algorithm;
+ string realhash=getHashForRRSET(signQName, rrc, toSign); // this is what we sign
- {
- Lock l(&g_rrsigs_lock);
- if(g_rrsigs.count(make_pair(hash, rrc.d_tag))) {
- // cerr<<"RRSIG cache hit !"<<endl;
- rrc = g_rrsigs[make_pair(hash, rrc.d_tag)];
+ unsigned char signature[mpi_size(&rc.getContext().N)];
+
+ {
+ Lock l(&g_signatures_lock);
+ if(g_signatures.count(make_pair(rc, realhash))) {
+ rrc.d_signature=g_signatures[make_pair(rc, realhash)];
return;
}
}
-
- string realhash=getSHA1HashForRRSET(signQName, rrc, toSign);
-
- unsigned char signature[mpi_size(&rc.getContext().N)];
-
+
int ret=rsa_pkcs1_sign(&rc.getContext(), RSA_PRIVATE,
rrc.d_algorithm < 8 ? SIG_RSA_SHA1 : SIG_RSA_SHA256,
rrc.d_algorithm < 8 ? 20 : 32,
}
rrc.d_signature.assign((char*)signature, sizeof(signature));
-
- Lock l(&g_rrsigs_lock);
- g_rrsigs[make_pair(hash, rrc.d_tag)] = rrc;
+
+ Lock l(&g_signatures_lock);
+ g_signatures[make_pair(rc, realhash)] = rrc.d_signature;
}
return a->serialize("", true, true) < b->serialize("", true, true);
}
-string getSHA1HashForRRSET(const std::string& qname, const RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& signRecords)
+string getHashForRRSET(const std::string& qname, const RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& signRecords)
{
sort(signRecords.begin(), signRecords.end(), sharedDNSSECCompare);
string toHash;
toHash.append(const_cast<RRSIGRecordContent&>(rrc).serialize("", true, true));
- toHash.resize(toHash.size() - rrc.d_signature.length()); // chop off the end;
- // cerr<<"toHash start size: "<<toHash.size()<<", signature length: "<<rrc.d_signature.length()<<endl;
-
+ toHash.resize(toHash.size() - rrc.d_signature.length()); // chop off the end, don't sign the signature!
BOOST_FOREACH(shared_ptr<DNSRecordContent>& add, signRecords) {
- // cerr<<"\t IN "<<rrc.d_originalttl<<"\t"<<add->getZoneRepresentation()<<"\n";
toHash.append(toLower(simpleCompress(qname, "")));
uint16_t tmp=htons(rrc.d_type);
toHash.append((char*)&tmp, 2);
toHash.append((char*)&tmp, 2);
toHash.append(rdata);
}
- // cerr<<"toHash: "<<makeHexDump(toHash)<<endl;
if(rrc.d_algorithm <= 7 ) {
unsigned char hash[20];
DNSKEYRecordContent getRSAKeyFromISCString(rsa_context* rsa, const std::string& content);
void makeRSAPublicKeyFromDNS(rsa_context* rc, const DNSKEYRecordContent& dkrc);
bool sharedDNSSECCompare(const boost::shared_ptr<DNSRecordContent>& a, const shared_ptr<DNSRecordContent>& b);
-string getSHA1HashForRRSET(const std::string& qname, const RRSIGRecordContent& rrc, std::vector<boost::shared_ptr<DNSRecordContent> >& signRecords);
+string getHashForRRSET(const std::string& qname, const RRSIGRecordContent& rrc, std::vector<boost::shared_ptr<DNSRecordContent> >& signRecords);
DNSKEYRecordContent makeDNSKEYFromRSAKey(const rsa_context* rc, uint8_t algorithm, uint16_t flags);
DSRecordContent makeDSFromDNSKey(const std::string& qname, const DNSKEYRecordContent& drc, int digest=1);
class RSAContext;
class DNSSECKeeper;
+struct DNSSECPrivateKey;
-bool getSignerFor(DNSSECKeeper& dk, const std::string& keyrepodir, const std::string& qname, std::string &signer, uint8_t& algorithm);
-DNSKEYRecordContent getDNSKEYFor(DNSSECKeeper& dk, const std::string& keyrepodir, const std::string& qname, bool withKSK, RSAContext* rc);
-void fillOutRRSIG(DNSSECKeeper& dk, const std::string& signQName, RRSIGRecordContent& rrc, const std::string& hash, vector<shared_ptr<DNSRecordContent> >& toSign, bool withKSK=false);
+bool getSignerApexFor(DNSSECKeeper& dk, const std::string& keyrepodir, const std::string& qname, std::string &signer);
+void fillOutRRSIG(DNSSECPrivateKey& dpk, const std::string& signQName, RRSIGRecordContent& rrc, vector<shared_ptr<DNSRecordContent> >& toSign);
uint32_t getCurrentInception();
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,
uint16_t maxReplyLength, DNSPacketWriter& pw);
-int getRRSIGForRRSET(DNSSECKeeper& dk, const std::string signQName, uint16_t signQType, uint32_t signTTL,
- vector<shared_ptr<DNSRecordContent> >& toSign, RRSIGRecordContent &rrc, bool ksk);
+int getRRSIGsForRRSET(DNSSECKeeper& dk, const std::string signQName, uint16_t signQType, uint32_t signTTL,
+ vector<shared_ptr<DNSRecordContent> >& toSign, vector<RRSIGRecordContent> &rrc, bool ksk);
std::string hashQNameWithSalt(unsigned int times, const std::string& salt, const std::string& qname);
#define PDNSSEC_MC(x) PDNSSEC_MI(x); mpi_copy(&d_context.x, const_cast<mpi*>(&orig.d_context.x))
#define PDNSSEC_MF(x) mpi_free(&d_context.x, 0)
+inline bool operator<(const mpi& a, const mpi& b)
+{
+ return mpi_cmp_mpi(&a, &b) < 0;
+}
+
class RSAContext
{
public:
PDNSSEC_MF(E); PDNSSEC_MF(D); PDNSSEC_MF(P); PDNSSEC_MF(Q); PDNSSEC_MF(DP); PDNSSEC_MF(DQ); PDNSSEC_MF(QP); PDNSSEC_MF(RN); PDNSSEC_MF(RP); PDNSSEC_MF(RQ);
}
+ bool operator<(const RSAContext& rhs) const
+ {
+ return tie(d_context.N, d_context.E, d_context.D, d_context.P, d_context.Q, d_context.DP, d_context.DQ, d_context.QP)
+ < tie(rhs.d_context.N, rhs.d_context.E, rhs.d_context.D, rhs.d_context.P, rhs.d_context.Q, rhs.d_context.DP, rhs.d_context.DQ, rhs.d_context.QP);
+ }
+
RSAContext(const RSAContext& orig)
{
d_context.ver = orig.d_context.ver;
rsa_context d_context;
};
-// ?
+// see above
#undef PDNSSEC_MC
#undef PDNSSEC_MI
#undef PDNSSEC_MF
UeberBackend d_db;
public:
DNSSECKeeper() : d_db("key-only"){}
- bool haveActiveKSKFor(const std::string& zone, DNSSECPrivateKey* ksk=0);
+ bool haveActiveKSKFor(const std::string& zone);
keyset_t getKeys(const std::string& zone, boost::tribool allOrKeyOrZone = boost::indeterminate);
DNSSECPrivateKey getKeyById(const std::string& zone, unsigned int id);
bool haveOne=false;
DNSSECPrivateKey dpk;
- if(d_dk.haveActiveKSKFor(p->qdomain, &dpk)) {
- rr.qtype=QType::DNSKEY;
- rr.ttl=3600;
- rr.qname=p->qdomain;
- rr.content=dpk.getDNSKEY().getZoneRepresentation();
- rr.auth = true;
- r->addRecord(rr);
- haveOne=true;
- }
-
- DNSSECKeeper::keyset_t zskset = d_dk.getKeys(p->qdomain, false);
- BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, zskset) {
+ DNSSECKeeper::keyset_t keyset = d_dk.getKeys(p->qdomain);
+ BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
rr.qtype=QType::DNSKEY;
rr.ttl=3600;
rr.qname=p->qdomain;
rr.content=value.first.getDNSKEY().getZoneRepresentation();
-
+ rr.auth=true;
r->addRecord(rr);
haveOne=true;
}
void PacketHandler::synthesiseRRSIGs(DNSPacket* p, DNSPacket* r)
{
- cerr<<"Need to fake up the RRSIGs some way.."<<endl;
+ cerr<<"Need to fake up the RRSIGs if someone asked for them explicitly"<<endl;
B.lookup(QType(QType::ANY), p->qdomain, p);
DNSResourceRecord rr;
rr.qtype = QType::RRSIG;
BOOST_FOREACH(records_t::value_type& iter, records) {
- RRSIGRecordContent rrc;
- for(int ksk =0 ; ksk < 2; ++ksk) {
- getRRSIGForRRSET(d_dk, p->qdomain, iter.first, 3600, iter.second, rrc, ksk);
+ vector<RRSIGRecordContent> rrcs;
+
+ getRRSIGsForRRSET(d_dk, p->qdomain, iter.first, 3600, iter.second, rrcs, iter.first == QType::DNSKEY);
+ BOOST_FOREACH(RRSIGRecordContent& rrc, rrcs) {
rr.content=rrc.getZoneRepresentation();
r->addRecord(rr);
- if(iter.first != QType::DNSKEY)
- break;
}
}
}
cerr<<"Checked "<<numrecords<<" records, "<<numerrors<<" errors"<<endl;
}
+void showZone(DNSSECKeeper& dk, const std::string& zone)
+{
+ NSEC3PARAMRecordContent ns3pr;
+ bool narrow;
+ dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
+
+ if(ns3pr.d_salt.empty())
+ cerr<<"Zone has NSEC semantics"<<endl;
+ else
+ cerr<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
+
+ DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
+
+ if(keyset.empty()) {
+ cerr << "No keys for zone '"<<zone<<"'."<<endl;
+ }
+ else {
+ cout << "keys: "<<endl;
+ BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
+ cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
+ cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.d_key.getConstContext().len*8<<"\tActive: "<<value.second.active<< endl; // humanTime(value.second.beginValidity)<<" - "<<humanTime(value.second.endValidity)<<endl;
+ if(value.second.keyOrZone) {
+ cout<<"KSK DNSKEY = "<<zone<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << endl;
+ cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << endl;
+ cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << endl << endl;
+ }
+ }
+ }
+}
int main(int argc, char** argv)
try
return 0;
}
const string& zone=cmds[1];
-
- NSEC3PARAMRecordContent ns3pr;
- bool narrow;
- dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
-
- if(ns3pr.d_salt.empty())
- cerr<<"Zone has NSEC semantics"<<endl;
- else
- cerr<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
-
- DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
-
- if(keyset.empty()) {
- cerr << "No keys for zone '"<<zone<<"'."<<endl;
- }
- else {
- cout << "keys: "<<endl;
- BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
- cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
- cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.d_key.getConstContext().len*8<<"\tActive: "<<value.second.active<< endl; // humanTime(value.second.beginValidity)<<" - "<<humanTime(value.second.endValidity)<<endl;
- if(value.second.keyOrZone) {
- cout<<"KSK DNSKEY = "<<zone<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << endl;
- cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << endl;
- cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << endl << endl;
- }
- }
- }
+ showZone(dk, zone);
}
else if(cmds[0] == "activate-zone-key") {
const string& zone=cmds[1];
const string& zone=cmds[1];
DNSSECPrivateKey dpk;
- if(dk.haveActiveKSKFor(zone, &dpk) && !g_vm.count("force")) {
- cerr << "There is a key already for zone '"<<zone<<"', use --force to overwrite"<<endl;
+ if(dk.haveActiveKSKFor(zone)) {
+ cerr << "There is a KSK already for zone '"<<zone<<"', remove with pdnssec remove-zone-key if needed"<<endl;
return 0;
}
- dk.secureZone(zone, 5);
+ dk.secureZone(zone, 8);
- if(!dk.haveActiveKSKFor(zone, &dpk)) {
+ if(!dk.haveActiveKSKFor(zone)) {
cerr << "This should not happen, still no key!" << endl;
return 0;
}
- cout<<"Created KSK with tag "<<dpk.getDNSKEY().getTag()<<endl;
DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
- if(!zskset.empty() && !g_vm.count("force")) {
- cerr<<"There were ZSKs already for zone '"<<zone<<"'"<<endl;
+ if(!zskset.empty()) {
+ cerr<<"There were ZSKs already for zone '"<<zone<<"', no need to add more"<<endl;
return 0;
}
- dk.addKey(zone, false, 5);
- dk.addKey(zone, false, 5, 0, false); // not active
-
- zskset = dk.getKeys(zone, false);
- if(zskset.empty()) {
- cerr<<"This should not happen, still no ZSK!"<<endl;
- }
-
- cout<<"There are now "<<zskset.size()<<" ZSKs"<<endl;
- BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, zskset) {
- cout<<"id = "<<value.second.id<<", tag = "<<value.first.getDNSKEY().getTag()<<", algo = "<<(int)value.first.d_algorithm<<
- ", bits = "<<value.first.d_key.getContext().len*8;
- cout<<"\tActive: "<<value.second.active<<endl;
- }
+ dk.addKey(zone, false, 8);
+ dk.addKey(zone, false, 8, 0, false); // not active
+ rectifyZone(dk, zone);
+ showZone(dk, zone);
}
else if(cmds[0]=="set-nsec3") {
string nsec3params = cmds.size() > 2 ? cmds[2] : "1 0 1 ab";