Class | Dnsruby::Dnssec |
In: |
lib/Dnsruby/dnssec.rb
|
Parent: | Object |
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.
Add a trusted Key Signing Key for the ISC DLV registry.
# File lib/Dnsruby/dnssec.rb, line 88 88: def Dnssec.add_dlv_key(dlv_key) 89: @@dlv_verifier.add_dlv_key(dlv_key) 90: end
Add a new trust anchor
# File lib/Dnsruby/dnssec.rb, line 92 92: def Dnssec.add_trust_anchor(t) 93: # @TODO@ Create a new verifier? 94: @@anchor_verifier.add_trust_anchor(t) 95: end
Add the trusted key with the given expiration time
# File lib/Dnsruby/dnssec.rb, line 97 97: def self.add_trust_anchor_with_expiration(k, expiration) 98: # Create a new verifier? 99: @@anchor_verifier.add_trust_anchor_with_expiration(k, expiration) 100: end
# File lib/Dnsruby/dnssec.rb, line 315 315: def self.anchor_verifier 316: return @@anchor_verifier 317: end
Wipes the cache of trusted keys
# File lib/Dnsruby/dnssec.rb, line 106 106: def self.clear_trust_anchors 107: @@anchor_verifier.clear_trust_anchors 108: end
# File lib/Dnsruby/dnssec.rb, line 114 114: def self.clear_trusted_keys 115: [@@anchor_verifier, @@root_verifier, @@dlv_verifier].each {|v| 116: v.clear_trusted_keys 117: } 118: end
# File lib/Dnsruby/dnssec.rb, line 193 193: def self.default_resolver 194: return @@default_resolver 195: 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 190 190: def self.default_resolver=(res) 191: @@default_resolver = res 192: end
# File lib/Dnsruby/dnssec.rb, line 318 318: def self.dlv_verifier 319: return @@dlv_verifier 320: 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 180 180: def self.do_validation_with_recursor(on) 181: @@do_validation_with_recursor = on 182: end
# File lib/Dnsruby/dnssec.rb, line 183 183: def self.do_validation_with_recursor? 184: return @@do_validation_with_recursor 185: end
Load the IANA TAR. THIS METHOD IS NOT SECURE!!!
# File lib/Dnsruby/dnssec.rb, line 144 144: def self.load_itar 145: # @TODO@ THIS IS VERY INSECURE!! WRITE THIS PROPERLY!! 146: # Should really check the signatures here to make sure the keys are good! 147: Net::FTP::open("ftp.iana.org") { |ftp| 148: ftp.login("anonymous") 149: ftp.passive = true 150: ftp.chdir("/itar") 151: lastname=nil 152: ftp.gettextfile("anchors.mf") {|line| 153: next if (line.strip.length == 0) 154: first = line[0] 155: if (first.class == String) 156: first = first.getbyte(0) # Ruby 1.9 157: end 158: # print "Reading ITAR : #{line}, first : #{first}\n" 159: next if (first==59) # ";") 160: if (line.strip=~(/^DS /) || line.strip=~(/^DNSKEY /)) 161: line = lastname.to_s + ((lastname.absolute?)?".":"") + " " + line 162: end 163: ds = RR.create(line) 164: if ((ds.type == Types::DS) || (ds.type == Types::DNSKEY)) 165: # assert(ds.name.absolute?) 166: Dnssec.add_trust_anchor(ds) 167: end 168: lastname = ds.name 169: } 170: } 171: end
# File lib/Dnsruby/dnssec.rb, line 132 132: def self.no_keys? 133: no_keys = true 134: [@@anchor_verifier, @@root_verifier, @@dlv_verifier].each {|v| 135: if (v.trusted_keys.length() > 0 || 136: v.trust_anchors.length() > 0) 137: no_keys = false 138: end 139: } 140: return no_keys 141: end
Remove the trusted key
# File lib/Dnsruby/dnssec.rb, line 102 102: def Dnssec.remove_trust_anchor(t) 103: @@anchor_verifier.remove_trust_anchor(t) 104: end
# File lib/Dnsruby/dnssec.rb, line 120 120: def self.reset 121: @@validation_policy = ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT 122: @@root_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ROOT) 123: 124: @@dlv_verifier = SingleVerifier.new(SingleVerifier::VerifierType::DLV) 125: 126: # @TODO@ Could add a new one of these for each anchor. 127: @@anchor_verifier = SingleVerifier.new(SingleVerifier::VerifierType::ANCHOR) 128: @@do_validation_with_recursor = true # Many nameservers don't handle DNSSEC correctly yet 129: @@default_resolver = Resolver.new 130: end
# File lib/Dnsruby/dnssec.rb, line 321 321: def self.root_verifier 322: return @@root_verifier 323: end
# File lib/Dnsruby/dnssec.rb, line 110 110: def self.trust_anchors 111: return @@anchor_verifier.trust_anchors 112: 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 202 202: def self.validate(msg) 203: query = Message.new() 204: query.header.cd=true 205: return self.validate_with_query(query, msg) 206: end
# File lib/Dnsruby/dnssec.rb, line 291 291: def self.validate_with_anchors(msg, query) 292: return @@anchor_verifier.validate(msg, query) 293: end
# File lib/Dnsruby/dnssec.rb, line 299 299: def self.validate_with_dlv(msg, query) 300: return @@dlv_verifier.validate(msg, query) 301: end
# File lib/Dnsruby/dnssec.rb, line 208 208: def self.validate_with_query(query, msg) 209: if (!msg) 210: return false 211: end 212: # First, just check there is something to validate! 213: found_sigs = false 214: msg.each_resource {|rr| 215: if (rr.type == Types::RRSIG) 216: found_sigs = true 217: end 218: } 219: if (found_sigs) 220: begin 221: if (verify(msg)) 222: msg.security_level = Message::SecurityLevel.SECURE 223: return true 224: end 225: rescue VerifyError => e 226: msg.security_error = e 227: end 228: end 229: 230: # SHOULD ALWAYS VERIFY DNSSEC-SIGNED RESPONSES? 231: # Yes - if a trust anchor is configured. Otherwise, act on CD bit (in query) 232: TheLog.debug("Checking whether to validate, query.cd = #{query.header.cd}") 233: if (((@@validation_policy > ValidationPolicy::ALWAYS_ROOT_ONLY) && (self.trust_anchors().length > 0)) || 234: # Check query here, and validate if CD is true 235: (query.header.cd == true)) 236: TheLog.debug("Starting validation") 237: 238: # Validate! 239: # Need to think about trapping/storing exceptions and security_levels here 240: last_error = "" 241: last_level = Message::SecurityLevel.BOGUS 242: last_error_level = Message::SecurityLevel.BOGUS 243: if (@@validation_policy == ValidationPolicy::ALWAYS_LOCAL_ANCHORS_ONLY) 244: last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, 245: Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query) 246: elsif (@@validation_policy == ValidationPolicy::ALWAYS_ROOT_ONLY) 247: last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, 248: Proc.new{|m, q| validate_with_root(m, q)}, msg, query) 249: elsif (@@validation_policy == ValidationPolicy::LOCAL_ANCHORS_THEN_ROOT) 250: last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, 251: Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query) 252: if (last_level != Message::SecurityLevel.SECURE) 253: last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, 254: Proc.new{|m, q| validate_with_root(m, q)}, msg, query) 255: end 256: elsif (@@validation_policy == ValidationPolicy::ROOT_THEN_LOCAL_ANCHORS) 257: last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, 258: Proc.new{|m, q| validate_with_root(m, q)}, msg, query) 259: if (last_level != Message::SecurityLevel.SECURE) 260: last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, 261: Proc.new{|m, q| validate_with_anchors(m, q)}, msg, query) 262: end 263: end 264: if (last_level != Message::SecurityLevel.SECURE) 265: last_level, last_error, last_error_level = try_validation(last_level, last_error, last_error_level, 266: Proc.new{|m, q| validate_with_dlv(m, q)}, msg, query) 267: end 268: # Set the message security level! 269: msg.security_level = last_level 270: msg.security_error = last_error 271: raise VerifyError.new(last_error) if (last_level < 0) 272: return (msg.security_level.code > Message::SecurityLevel::UNCHECKED) 273: end 274: msg.security_level = Message::SecurityLevel.UNCHECKED 275: return true 276: end
# File lib/Dnsruby/dnssec.rb, line 295 295: def self.validate_with_root(msg, query) 296: return @@root_verifier.validate(msg, query) 297: end
# File lib/Dnsruby/dnssec.rb, line 74 74: def Dnssec.validation_policy 75: @@validation_policy 76: end
# File lib/Dnsruby/dnssec.rb, line 68 68: def Dnssec.validation_policy=(p) 69: if ((p >= ALWAYS_ROOT_ONY) && (p <= ALWAYS_LOCAL_ANCHORS)) 70: @@validation_policy = p 71: # @TODO@ Should we be clearing the trusted keys now? 72: end 73: end
# File lib/Dnsruby/dnssec.rb, line 303 303: def self.verify(msg, keys=nil) 304: begin 305: return true if @@anchor_verifier.verify(msg, keys=nil) 306: rescue VerifyError 307: begin 308: return true if @@root_verifier.verify(msg, keys=nil) 309: rescue VerifyError 310: return true if @@dlv_verifier.verify(msg, keys=nil) # Will carry error to client 311: end 312: end 313: end