class Dnsruby::Dnssec
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.
Public Class Methods
Add a trusted Key Signing Key for the ISC DLV registry.
# File lib/dnsruby/dnssec.rb, line 94 def Dnssec.add_dlv_key(dlv_key) @@dlv_verifier.add_dlv_key(dlv_key) end
Add a new trust anchor
# 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 the trusted key with the given expiration time
# 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
# File lib/dnsruby/dnssec.rb, line 301 def self.anchor_verifier return @@anchor_verifier end
Wipes the cache of trusted keys
# File lib/dnsruby/dnssec.rb, line 112 def self.clear_trust_anchors @@anchor_verifier.clear_trust_anchors end
# 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
# File lib/dnsruby/dnssec.rb, line 175 def self.default_resolver return @@default_resolver end
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.
# File lib/dnsruby/dnssec.rb, line 172 def self.default_resolver=(res) @@default_resolver = res end
# File lib/dnsruby/dnssec.rb, line 304 def self.dlv_verifier return @@dlv_verifier end
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.
# File lib/dnsruby/dnssec.rb, line 162 def self.do_validation_with_recursor(on) @@do_validation_with_recursor = on end
# File lib/dnsruby/dnssec.rb, line 165 def self.do_validation_with_recursor? return @@do_validation_with_recursor end
# 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 the trusted key
# File lib/dnsruby/dnssec.rb, line 108 def Dnssec.remove_trust_anchor(t) @@anchor_verifier.remove_trust_anchor(t) end
# 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
# File lib/dnsruby/dnssec.rb, line 307 def self.root_verifier return @@root_verifier end
# File lib/dnsruby/dnssec.rb, line 140 def self.set_hints(hints) @@root_verifier.set_hints(hints) @@anchor_verifier.set_hints(hints) end
# File lib/dnsruby/dnssec.rb, line 116 def self.trust_anchors return @@anchor_verifier.trust_anchors end
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.
# 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
# File lib/dnsruby/dnssec.rb, line 277 def self.validate_with_anchors(msg, query) return @@anchor_verifier.validate(msg, query) end
# File lib/dnsruby/dnssec.rb, line 285 def self.validate_with_dlv(msg, query) return @@dlv_verifier.validate(msg, query) end
# 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
# File lib/dnsruby/dnssec.rb, line 281 def self.validate_with_root(msg, query) return @@root_verifier.validate(msg, query) end
# File lib/dnsruby/dnssec.rb, line 73 def Dnssec.validation_policy @@validation_policy end
# 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
# 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
# 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