File: validate-recursor.cc

package info (click to toggle)
pdns-recursor 4.0.4-1+deb9u3~bpo8+1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-backports
  • size: 5,484 kB
  • sloc: cpp: 36,380; sh: 11,771; makefile: 305; xml: 37
file content (154 lines) | stat: -rw-r--r-- 4,896 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include "validate.hh"
#include "validate-recursor.hh"
#include "syncres.hh"
#include "logger.hh"

DNSSECMode g_dnssecmode{DNSSECMode::ProcessNoValidate};
bool g_dnssecLogBogus;

#define LOG(x) if(g_dnssecLOG) { L <<Logger::Warning << x; }

class SRRecordOracle : public DNSRecordOracle
{
public:
  vector<DNSRecord> get(const DNSName& qname, uint16_t qtype) override
  {
    struct timeval tv;
    gettimeofday(&tv, 0);
    SyncRes sr(tv);

    vector<DNSRecord> ret;
    sr.d_doDNSSEC=true;
    if (qtype == QType::DS || qtype == QType::DNSKEY || qtype == QType::NS)
      sr.setSkipCNAMECheck(true);
    sr.beginResolve(qname, QType(qtype), 1, ret);
    d_queries += sr.d_outqueries;
    return ret;
  }
  int d_queries{0};
};

inline vState increaseDNSSECStateCounter(const vState& state)
{
  g_stats.dnssecResults[state]++;
  return state;
}

/*
 * This inline possibly sets currentState based on the new state. It will only
 * set it to Secure iff the newState is Secure and mayUpgradeToSecure == true.
 * This should be set by the calling function when checking more than one record
 * and this is not the first record, this way, we can never go *back* to Secure
 * from an Insecure vState
 */
inline void processNewState(vState& currentState, const vState& newState, bool& hadNTA, const bool& mayUpgradeToSecure)
{
  if (mayUpgradeToSecure && newState == Secure)
    currentState = Secure;

  if (newState == Insecure || newState == NTA) // We can never go back to Secure
    currentState = Insecure;

  if (newState == NTA)
    hadNTA = true;
}

vState validateRecords(const vector<DNSRecord>& recs)
{
  if(recs.empty())
    return Insecure; // can't secure nothing 

  g_stats.dnssecValidations++;

  cspmap_t cspmap=harvestCSPFromRecs(recs);
  LOG("Got "<<cspmap.size()<<" RRSETs: "<<endl);
  int numsigs=0;
  for(const auto& csp : cspmap) {
    LOG("Going to validate: "<<csp.first.first<<"/"<<DNSRecordContent::NumberToType(csp.first.second)<<": "<<csp.second.signatures.size()<<" sigs for "<<csp.second.records.size()<<" records"<<endl);
    numsigs+= csp.second.signatures.size();
  }
   
  set<DNSKEYRecordContent> keys;
  cspmap_t validrrsets;

  SRRecordOracle sro;

  vState state=Insecure;
  bool hadNTA = false;
  if(numsigs) {
    bool first = true;
    for(const auto& csp : cspmap) {
      for(const auto& sig : csp.second.signatures) {

        if (!csp.first.first.isPartOf(sig->d_signer)) {
          return increaseDNSSECStateCounter(Bogus);
        }

        vState newState = getKeysFor(sro, sig->d_signer, keys); // XXX check validity here

        if (newState == Bogus) // No hope
          return increaseDNSSECStateCounter(Bogus);

        processNewState(state, newState, hadNTA, first);

        first = false;

        LOG("! state = "<<vStates[state]<<", now have "<<keys.size()<<" keys"<<endl);
        for(const auto& k : keys) {
          LOG("Key: "<<k.getZoneRepresentation()<< " {tag="<<k.getTag()<<"}"<<endl);
        }
      }
    }
    validateWithKeySet(cspmap, validrrsets, keys);
  }
  else {
    LOG("! no sigs, hoping for Insecure status of "<<recs.begin()->d_name<<endl);

    bool first = true;
    for(const auto& rec : recs) {
      vState newState = getKeysFor(sro, rec.d_name, keys);

      if (newState == Bogus) // We're done
        return increaseDNSSECStateCounter(Bogus);

      processNewState(state, newState, hadNTA, first);
      first = false;

      LOG("! state = "<<vStates[state]<<", now have "<<keys.size()<<" keys "<<endl);
    }
    return increaseDNSSECStateCounter(state);
  }

  LOG("Took "<<sro.d_queries<<" queries"<<endl);
  if(validrrsets.size() == cspmap.size())// shortcut - everything was ok
    return increaseDNSSECStateCounter(Secure);

  if(state == Insecure || keys.empty()) {
    if (hadNTA) {
      increaseDNSSECStateCounter(NTA);
      return Insecure;
    }
    return increaseDNSSECStateCounter(Insecure);
  }

#if 0
  cerr<<"! validated "<<validrrsets.size()<<" RRsets out of "<<cspmap.size()<<endl;

  cerr<<"% validated RRs:"<<endl;
  for(auto i=validrrsets.begin(); i!=validrrsets.end(); i++) {
        cerr<<"% "<<i->first.first<<"/"<<DNSRecordContent::NumberToType(i->first.second)<<endl;
    for(auto j=i->second.records.begin(); j!=i->second.records.end(); j++) {
            cerr<<"\t% > "<<(*j)->getZoneRepresentation()<<endl;
    }
  }
#endif
  //  cerr<<"Input to validate: "<<endl;
  for(const auto& csp : cspmap) {
    LOG(csp.first.first<<"|"<<DNSRecordContent::NumberToType(csp.first.second)<<" with "<<csp.second.signatures.size()<<" signatures"<<endl);
    if(!csp.second.signatures.empty() && !validrrsets.count(csp.first)) {
      LOG("Lacks signature, must have one, signatures: "<<csp.second.signatures.size()<<", valid rrsets: "<<validrrsets.count(csp.first)<<endl);
      return increaseDNSSECStateCounter(Bogus);
    }
  }
  return increaseDNSSECStateCounter(Insecure);
}