• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/pdns/dnssecsigner.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
#ifdef HAVE_CONFIG_H
23
#include "config.h"
24
#endif
25
#include "dnssecinfra.hh"
26
#include "namespaces.hh"
27

28
#include "digests.hh"
29
#include "dnsseckeeper.hh"
30
#include "dns_random.hh"
31
#include "lock.hh"
32
#include "arguments.hh"
33
#include "statbag.hh"
34
#include "sha.hh"
35

36
extern StatBag S;
37

38
using signaturecache_t = map<pair<string, string>, string>;
39
static SharedLockGuarded<signaturecache_t> g_signatures;
40
static int g_cacheweekno;
41

42
const static std::set<uint16_t> g_KSKSignedQTypes {QType::DNSKEY, QType::CDS, QType::CDNSKEY};
43
AtomicCounter* g_signatureCount;
44

45
static std::string getLookupKeyFromMessage(const std::string& msg)
46
{
×
47
  try {
×
48
    return pdns::md5(msg);
×
49
  }
×
50
  catch(const std::runtime_error& e) {
×
51
    return pdns::sha1(msg);
×
52
  }
×
53
}
×
54

55
static std::string getLookupKeyFromPublicKey(const std::string& pubKey)
56
{
×
57
  /* arbitrarily cut off at 64 bytes, the main idea is to save space
58
     for very large keys like RSA ones (1024+ bits so 128+ bytes) by storing a 20 bytes hash
59
     instead */
60
  if (pubKey.size() <= 64) {
×
61
    return pubKey;
×
62
  }
×
63
  return pdns::sha1sum(pubKey);
×
64
}
×
65

66
static void fillOutRRSIG(DNSSECPrivateKey& dpk, const DNSName& signQName, RRSIGRecordContent& rrc, const sortedRecords_t& toSign)
67
{
×
68
  if(!g_signatureCount)
×
69
    g_signatureCount = S.getPointer("signatures");
×
70

71
  DNSKEYRecordContent drc = dpk.getDNSKEY();
×
72
  const std::shared_ptr<DNSCryptoKeyEngine>& rc = dpk.getKey();
×
73
  rrc.d_tag = drc.getTag();
×
74
  rrc.d_algorithm = drc.d_algorithm;
×
75

76
  string msg = getMessageForRRSET(signQName, rrc, toSign); // this is what we will hash & sign
×
77
  pair<string, string> lookup(getLookupKeyFromPublicKey(drc.d_key), getLookupKeyFromMessage(msg));  // this hash is a memory saving exercise
×
78

79
  bool doCache = true;
×
80
  if (doCache) {
×
81
    auto signatures = g_signatures.read_lock();
×
82
    signaturecache_t::const_iterator iter = signatures->find(lookup);
×
83
    if (iter != signatures->end()) {
×
84
      rrc.d_signature=iter->second;
×
85
      return;
×
86
    }
×
87
    // else cerr<<"Miss!"<<endl;
88
  }
×
89

90
  rrc.d_signature = rc->sign(msg);
×
91
  (*g_signatureCount)++;
×
92
  if(doCache) {
×
93
    /* we add some jitter here so not all your secondaries start pruning their caches at the very same millisecond */
94
    int weekno = (time(nullptr) - dns_random(3600)) / (86400*7);  // we just spent milliseconds doing a signature, microsecond more won't kill us
×
95
    const static int maxcachesize=::arg().asNum("max-signature-cache-entries", INT_MAX);
×
96

97
    auto signatures = g_signatures.write_lock();
×
98
    if (g_cacheweekno < weekno || signatures->size() >= (uint) maxcachesize) {  // blunt but effective (C) Habbie, mind04
×
99
      g_log<<Logger::Warning<<"Cleared signature cache."<<endl;
×
100
      signatures->clear();
×
101
      g_cacheweekno = weekno;
×
102
    }
×
103
    (*signatures)[lookup] = rrc.d_signature;
×
104
  }
×
105
}
×
106

107
/* this is where the RRSIGs begin, keys are retrieved,
108
   but the actual signing happens in fillOutRRSIG */
109
static int getRRSIGsForRRSET(DNSSECKeeper& dk, const DNSName& signer, const DNSName& signQName, uint16_t signQType, uint32_t signTTL,
110
                             const sortedRecords_t& toSign, vector<RRSIGRecordContent>& rrcs)
111
{
×
112
  if(toSign.empty())
×
113
    return -1;
×
114
  uint32_t startOfWeek = getStartOfWeek();
×
115
  RRSIGRecordContent rrc;
×
116
  rrc.d_type=signQType;
×
117

118
  rrc.d_labels=signQName.countLabels()-signQName.isWildcard();
×
119
  rrc.d_originalttl=signTTL;
×
120
  rrc.d_siginception=startOfWeek - 7*86400; // XXX should come from zone metadata
×
121
  rrc.d_sigexpire=startOfWeek + 14*86400;
×
122
  rrc.d_signer = signer;
×
123
  rrc.d_tag = 0;
×
124

125
  DNSSECKeeper::keyset_t keys = dk.getKeys(signer);
×
126

127
  for(DNSSECKeeper::keyset_t::value_type& keymeta : keys) {
×
128
    if(!keymeta.second.active)
×
129
      continue;
×
130

131
    bool signWithKSK = g_KSKSignedQTypes.count(signQType) != 0;
×
132
    // Do not sign DNSKEY RRsets with the ZSK
133
    if((signQType == QType::DNSKEY && keymeta.second.keyType == DNSSECKeeper::ZSK) ||
×
134
       // Do not sign any other RRset than DNSKEY, CDS and CDNSKEY with a KSK
135
       (!signWithKSK && keymeta.second.keyType == DNSSECKeeper::KSK)) {
×
136
      continue;
×
137
    }
×
138

139
    fillOutRRSIG(keymeta.first, signQName, rrc, toSign);
×
140
    rrcs.push_back(rrc);
×
141
  }
×
142
  return 0;
×
143
}
×
144

145
// this is the entrypoint from DNSPacket
146
static void addSignature(DNSSECKeeper& dk, UeberBackend& db, const DNSName& signer, const DNSName& signQName, const DNSName& wildcardname, uint16_t signQType,
147
                         uint32_t signTTL, DNSResourceRecord::Place signPlace,
148
                         sortedRecords_t& toSign, vector<DNSZoneRecord>& outsigned, uint32_t origTTL, DNSPacket* packet)
149
{
×
150
  //cerr<<"Asked to sign '"<<signQName<<"'|"<<DNSRecordContent::NumberToType(signQType)<<", "<<toSign.size()<<" records\n";
151
  if(toSign.empty())
×
152
    return;
×
153
  vector<RRSIGRecordContent> rrcs;
×
154
  if(dk.isPresigned(signer)) {
×
155
    //cerr<<"Doing presignatures"<<endl;
156
    dk.getPreRRSIGs(db, outsigned, origTTL, packet); // does it all
×
157
  }
×
158
  else {
×
159
    if(getRRSIGsForRRSET(dk, signer, wildcardname.countLabels() ? wildcardname : signQName, signQType, signTTL, toSign, rrcs) < 0)  {
×
160
      // cerr<<"Error signing a record!"<<endl;
161
      return;
×
162
    }
×
163

164
    DNSZoneRecord rr;
×
165
    rr.dr.d_name=signQName;
×
166
    rr.dr.d_type=QType::RRSIG;
×
167
    if(origTTL)
×
168
      rr.dr.d_ttl=origTTL;
×
169
    else
×
170
      rr.dr.d_ttl=signTTL;
×
171
    rr.auth=false;
×
172
    rr.dr.d_place = signPlace;
×
173
    for(RRSIGRecordContent& rrc :  rrcs) {
×
174
      rr.dr.setContent(std::make_shared<RRSIGRecordContent>(rrc));
×
175
      outsigned.push_back(rr);
×
176
    }
×
177
  }
×
178
  toSign.clear();
×
179
}
×
180

181
uint64_t signatureCacheSize(const std::string& /* str */)
182
{
×
183
  return g_signatures.read_lock()->size();
×
184
}
×
185

186
static bool rrsigncomp(const DNSZoneRecord& a, const DNSZoneRecord& b)
187
{
×
188
  return std::tie(a.dr.d_place, a.dr.d_type) < std::tie(b.dr.d_place, b.dr.d_type);
×
189
}
×
190

191
static bool getBestAuthFromSet(const set<DNSName>& authSet, const DNSName& name, DNSName& auth)
192
{
×
193
  auth.trimToLabels(0);
×
194
  DNSName sname(name);
×
195
  do {
×
196
    if(authSet.find(sname) != authSet.end()) {
×
197
      auth = sname;
×
198
      return true;
×
199
    }
×
200
  }
×
201
  while(sname.chopOff());
×
202

203
  return false;
×
204
}
×
205

206
void addRRSigs(DNSSECKeeper& dk, UeberBackend& db, const set<DNSName>& authSet, vector<DNSZoneRecord>& rrs, DNSPacket* packet)
207
{
×
208
  stable_sort(rrs.begin(), rrs.end(), rrsigncomp);
×
209

210
  DNSName authQName, signQName, wildcardQName;
×
211
  uint16_t signQType=0;
×
212
  uint32_t signTTL=0;
×
213
  uint32_t origTTL=0;
×
214

215
  DNSResourceRecord::Place signPlace=DNSResourceRecord::ANSWER;
×
216
  sortedRecords_t toSign;
×
217

218
  vector<DNSZoneRecord> signedRecords;
×
219
  signedRecords.reserve(rrs.size()*1.5);
×
220
  //  cout<<rrs.size()<<", "<<sizeof(DNSZoneRecord)<<endl;
221
  DNSName signer;
×
222
  for(auto pos = rrs.cbegin(); pos != rrs.cend(); ++pos) {
×
223
    if(pos != rrs.cbegin() && (signQType != pos->dr.d_type  || signQName != pos->dr.d_name)) {
×
224
      if (getBestAuthFromSet(authSet, authQName, signer))
×
225
        addSignature(dk, db, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords, origTTL, packet);
×
226
    }
×
227
    signedRecords.push_back(*pos);
×
228
    signQName = pos->dr.d_name.makeLowerCase();
×
229
    if (pos->dr.d_type == QType::NSEC) {
×
230
      authQName = signQName.getCommonLabels(getRR<NSECRecordContent>(pos->dr)->d_next);
×
231
    }
×
232
    else {
×
233
      authQName = signQName;
×
234
    }
×
235
    if(!pos->wildcardname.empty())
×
236
      wildcardQName = pos->wildcardname.makeLowerCase();
×
237
    else
×
238
      wildcardQName.clear();
×
239
    signQType = pos->dr.d_type;
×
240
    if(pos->signttl)
×
241
      signTTL = pos->signttl;
×
242
    else
×
243
      signTTL = pos->dr.d_ttl;
×
244
    origTTL = pos->dr.d_ttl;
×
245
    signPlace = pos->dr.d_place;
×
246
    if(pos->auth || pos->dr.d_type == QType::DS) {
×
247
      toSign.insert(pos->dr.getContent()); // so ponder.. should this be a deep copy perhaps?
×
248
    }
×
249
  }
×
250
  if (getBestAuthFromSet(authSet, authQName, signer))
×
251
    addSignature(dk, db, signer, signQName, wildcardQName, signQType, signTTL, signPlace, toSign, signedRecords, origTTL, packet);
×
252
  rrs.swap(signedRecords);
×
253
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc