Class: Dnsruby::Dnssec

Inherits:
Object
  • Object
show all
Defined in:
lib/dnsruby/dnssec.rb

Overview

RFC4033, section 7

  "There is one more step that a security-aware stub resolver can take
  if, for whatever reason, it is not able to establish a useful trust
  relationship with the recursive name servers that it uses: it can
  perform its own signature validation by setting the Checking Disabled
  (CD) bit in its query messages.  A validating stub resolver is thus
  able to treat the DNSSEC signatures as trust relationships between
  the zone administrators and the stub resolver itself. "

Dnsruby is configured to validate responses by default. However, it is not
configured with any trusted keys by default. Applications may use the
verify() method to perform verification with of RRSets of Messages with
given keys. Alternatively, trusted keys may be added to this class (either
directly, or by loading the IANA TAR or the DLV ISC ZSK). Validation will then
be performed from these keys (or the DLV registry, if configured). Negative
and positive responses are validation.

Messages are tagged with the current security_level (Message::SecurityLevel).
UNCHECKED means Dnsruby has not attempted to validate the response.
BOGUS means the response has been checked, and is bogus.
INSECURE means the response has been validated to be insecure (e.g. in an unsigned zone)
SECURE means that the response has been verfied to be correct.

Several validators are provided, with each maintaining its own cache of trusted keys.
If validators are added or removed, the caches of the other validators are not affected.

Defined Under Namespace

Classes: ValidationPolicy

Constant Summary collapse

@@validation_policy =
ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT
@@root_verifier =
SingleVerifier.new(SingleVerifier::VerifierType::ROOT)
@@root_key =

#NOTE# You may wish to import these via a secure channel yourself, if

using Dnsruby for validation.
RR.create(". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5")
@@root_key_new =
RR.create(". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D")
@@dlv_verifier =
SingleVerifier.new(SingleVerifier::VerifierType::DLV)
@@anchor_verifier =

@TODO@ Could add a new one of these for each anchor.

SingleVerifier.new(SingleVerifier::VerifierType::ANCHOR)
@@do_validation_with_recursor =

Many nameservers don’t handle DNSSEC correctly yet

true
@@default_resolver =
Resolver.new

Class Method Summary collapse

Class Method Details

.add_dlv_key(dlv_key) ⇒ Object

Add a trusted Key Signing Key for the ISC DLV registry.



94
95
96
# File 'lib/dnsruby/dnssec.rb', line 94

def Dnssec.add_dlv_key(dlv_key)
  @@dlv_verifier.add_dlv_key(dlv_key)
end

.add_trust_anchor(t) ⇒ Object

Add a new trust anchor



98
99
100
101
# File 'lib/dnsruby/dnssec.rb', line 98

def Dnssec.add_trust_anchor(t)
  #  @TODO@ Create a new verifier?
  @@anchor_verifier.add_trust_anchor(t)
end

.add_trust_anchor_with_expiration(k, expiration) ⇒ Object

Add the trusted key with the given expiration time



103
104
105
106
# File 'lib/dnsruby/dnssec.rb', line 103

def self.add_trust_anchor_with_expiration(k, expiration)
  #  Create a new verifier?
  @@anchor_verifier.add_trust_anchor_with_expiration(k, expiration)
end

.anchor_verifierObject



301
302
303
# File 'lib/dnsruby/dnssec.rb', line 301

def self.anchor_verifier
  return @@anchor_verifier
end

.clear_trust_anchorsObject

Wipes the cache of trusted keys



112
113
114
# File 'lib/dnsruby/dnssec.rb', line 112

def self.clear_trust_anchors
  @@anchor_verifier.clear_trust_anchors
end

.clear_trusted_keysObject



120
121
122
123
124
# File 'lib/dnsruby/dnssec.rb', line 120

def self.clear_trusted_keys
  [@@anchor_verifier, @@root_verifier, @@dlv_verifier].each {|v|
    v.clear_trusted_keys
  }
end

.default_resolverObject



175
176
177
# File 'lib/dnsruby/dnssec.rb', line 175

def self.default_resolver
  return @@default_resolver
end

.default_resolver=(res) ⇒ Object

This method overrides the system default resolver configuration for validation

If default_resolver is set, then it will be used to follow the chain of trust.
If it is not, then the default system resolver will be used (unless do_validation_with_recursor
is set.


172
173
174
# File 'lib/dnsruby/dnssec.rb', line 172

def self.default_resolver=(res)
  @@default_resolver = res
end

.dlv_verifierObject



304
305
306
# File 'lib/dnsruby/dnssec.rb', line 304

def self.dlv_verifier
  return @@dlv_verifier
end

.do_validation_with_recursor(on) ⇒ Object

This method defines the choice of Resolver or Recursor, when the validator

is checking responses.
If set to true, then a Recursor will be used to query for the DNSSEC records.
Otherwise, the default system resolver will be used.


162
163
164
# File 'lib/dnsruby/dnssec.rb', line 162

def self.do_validation_with_recursor(on)
  @@do_validation_with_recursor = on
end

.do_validation_with_recursor?Boolean

Returns:

  • (Boolean)


165
166
167
# File 'lib/dnsruby/dnssec.rb', line 165

def self.do_validation_with_recursor?
  return @@do_validation_with_recursor
end

.no_keys?Boolean

Returns:

  • (Boolean)


145
146
147
148
149
150
151
152
153
154
# File 'lib/dnsruby/dnssec.rb', line 145

def self.no_keys?
  no_keys = true
  [@@anchor_verifier, @@root_verifier, @@dlv_verifier].each {|v|
    if (v.trusted_keys.length() > 0 ||
          v.trust_anchors.length() > 0)
      no_keys = false
    end
  }
  return no_keys
end

.remove_trust_anchor(t) ⇒ Object

Remove the trusted key



108
109
110
# File 'lib/dnsruby/dnssec.rb', line 108

def Dnssec.remove_trust_anchor(t)
  @@anchor_verifier.remove_trust_anchor(t)
end

.resetObject



126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/dnsruby/dnssec.rb', line 126

def self.reset
  @@validation_policy = ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT
  @@root_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ROOT)
  @@root_verifier.add_root_ds(@@root_key)
  @@root_verifier.add_root_ds(@@root_key_new)

  @@dlv_verifier = SingleVerifier.new(SingleVerifier::VerifierType::DLV)

  #  @TODO@ Could add a new one of these for each anchor.
  @@anchor_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ANCHOR)
  @@do_validation_with_recursor = true # Many nameservers don't handle DNSSEC correctly yet
  @@default_resolver = Resolver.new
end

.root_verifierObject



307
308
309
# File 'lib/dnsruby/dnssec.rb', line 307

def self.root_verifier
  return @@root_verifier
end

.set_hints(hints) ⇒ Object



140
141
142
143
# File 'lib/dnsruby/dnssec.rb', line 140

def self.set_hints(hints)
  @@root_verifier.set_hints(hints)
  @@anchor_verifier.set_hints(hints)
end

.trust_anchorsObject



116
117
118
# File 'lib/dnsruby/dnssec.rb', line 116

def self.trust_anchors
  return @@anchor_verifier.trust_anchors
end

.try_validation(last_level, last_error, last_error_level, proc, msg, query) ⇒ Object

:nodoc:



264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/dnsruby/dnssec.rb', line 264

def self.try_validation(last_level, last_error, last_error_level, proc, msg, query)   # :nodoc:
  begin
    proc.call(msg, query)
    last_level = Message::SecurityLevel.new([msg.security_level.code, last_level.code].max)
  rescue VerifyError => e
    if (last_error_level < last_level)
      last_error = e.to_s
      last_error_level = last_level
    end
  end
  return last_level, last_error, last_error_level
end

.validate(msg) ⇒ Object

Returns true for secure/insecure, false otherwise

This method will set the security_level on msg to the appropriate value.
Could be : secure, insecure, bogus or indeterminate
If an error is encountered during verification, then the thrown exception
will define the error.


184
185
186
187
188
# File 'lib/dnsruby/dnssec.rb', line 184

def self.validate(msg)
  query = Message.new()
  query.header.cd=true
  return self.validate_with_query(query, msg)
end

.validate_with_anchors(msg, query) ⇒ Object



277
278
279
# File 'lib/dnsruby/dnssec.rb', line 277

def self.validate_with_anchors(msg, query)
  return @@anchor_verifier.validate(msg, query)
end

.validate_with_dlv(msg, query) ⇒ Object



285
286
287
# File 'lib/dnsruby/dnssec.rb', line 285

def self.validate_with_dlv(msg, query)
  return @@dlv_verifier.validate(msg, query)
end

.validate_with_query(query, msg) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/dnsruby/dnssec.rb', line 190

def self.validate_with_query(query, msg)
  if (!msg)
    return false
  end
  #  First, just check there is something to validate!
  found_sigs = false
  msg.each_resource {|rr|
    if (rr.type == Types::RRSIG)
      found_sigs = true
    end
  }
  if (found_sigs)
    begin
      if (verify(msg))
        msg.security_level = Message::SecurityLevel.SECURE
        return true
      end
    rescue VerifyError => e
      msg.security_error = e
      msg.security_level = Message::SecurityLevel.BOGUS
    end
  end

  #  SHOULD ALWAYS VERIFY DNSSEC-SIGNED RESPONSES?
  #  Yes - if a trust anchor is configured. Otherwise, act on CD bit (in query)
  TheLog.debug("Checking whether to validate, query.cd = #{query.header.cd}")
  if (((@@validation_policy > ValidationPolicy::ALWAYS_ROOT_ONLY) && (self.trust_anchors().length > 0)) ||
        #  Check query here, and validate if CD is true
      ((query.header.cd == true))) # && (query.do_validation)))
    TheLog.debug("Starting validation")

    #  Validate!
    #  Need to think about trapping/storing exceptions and security_levels here
    last_error = ""
    last_level = Message::SecurityLevel.BOGUS
    last_error_level = Message::SecurityLevel.BOGUS
    if (@@validation_policy == ValidationPolicy::ALWAYS_LOCAL_ANCHORS_ONLY)
      last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level,
        Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query)
    elsif (@@validation_policy == ValidationPolicy::ALWAYS_ROOT_ONLY)
      last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level,
        Proc.new{|m, q| validate_with_root(m, q)}, msg, query)
    elsif (@@validation_policy == ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT)
      last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level,
        Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query)
      if (last_level != Message::SecurityLevel.SECURE)
        last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level,
          Proc.new{|m, q| validate_with_root(m, q)}, msg, query)
      end
    elsif (@@validation_policy == ValidationPolicy::ROOT_THEN_LOCAL_ANCHORS)
      last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level,
        Proc.new{|m, q| validate_with_root(m, q)}, msg, query)
      if (last_level != Message::SecurityLevel.SECURE)
        last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level,
          Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query)
      end
    end
    if (last_level != Message::SecurityLevel.SECURE && last_level != Message::SecurityLevel.BOGUS)
      last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level,
        Proc.new{|m, q| validate_with_dlv(m, q)}, msg, query)
    end
    #  Set the message security level!
    msg.security_level = last_level
    msg.security_error = last_error
    if (last_error && last_error.index("ification error"))
      msg.security_level = Message::SecurityLevel.BOGUS
    end
    raise VerifyError.new(last_error) if (last_level < 0)
    return (msg.security_level.code > Message::SecurityLevel::UNCHECKED)
  end
  msg.security_level = Message::SecurityLevel.UNCHECKED
  return true
end

.validate_with_root(msg, query) ⇒ Object



281
282
283
# File 'lib/dnsruby/dnssec.rb', line 281

def self.validate_with_root(msg, query)
  return @@root_verifier.validate(msg, query)
end

.validation_policyObject



73
74
75
# File 'lib/dnsruby/dnssec.rb', line 73

def Dnssec.validation_policy
  @@validation_policy
end

.validation_policy=(p) ⇒ Object



67
68
69
70
71
72
# File 'lib/dnsruby/dnssec.rb', line 67

def Dnssec.validation_policy=(p)
  if ((p >= ValidationPolicy::ALWAYS_ROOT_ONLY) && (p <= ValidationPolicy::ALWAYS_LOCAL_ANCHORS_ONLY))
    @@validation_policy = p
    #  @TODO@ Should we be clearing the trusted keys now?
  end
end

.verify(msg, keys = nil) ⇒ Object



289
290
291
292
293
294
295
296
297
298
299
# File 'lib/dnsruby/dnssec.rb', line 289

def self.verify(msg, keys=nil)
  begin
    return true if @@anchor_verifier.verify(msg, keys)
  rescue VerifyError
    begin
      return true if @@root_verifier.verify(msg, keys)
    rescue VerifyError
      return true if @@dlv_verifier.verify(msg, keys) # Will carry error to client
    end
  end
end

.verify_rrset(rrset, keys = nil) ⇒ Object



314
315
316
317
318
# File 'lib/dnsruby/dnssec.rb', line 314

def self.verify_rrset(rrset, keys = nil)
  return ((@@anchor_verifier.verify_rrset(rrset, keys) ||
        @@root_verifier.verify_rrset(rrset, keys) ||
        @@dlv_verifier.verify_rrset(rrset, keys)))
end