# File lib/dnsruby/resource/TSIG.rb, line 282 def verify_envelope(response, response_bytes) # RFC2845 Section 4.4 # ----- # A DNS TCP session can include multiple DNS envelopes. This is, for # example, commonly used by zone transfer. Using TSIG on such a # connection can protect the connection from hijacking and provide data # integrity. The TSIG MUST be included on the first and last DNS # envelopes. It can be optionally placed on any intermediary # envelopes. It is expensive to include it on every envelopes, but it # MUST be placed on at least every 100'th envelope. The first envelope # is processed as a standard answer, and subsequent messages have the # following digest components: # # * Prior Digest (running) # * DNS Messages (any unsigned messages since the last TSIG) # * TSIG Timers (current message) # # This allows the client to rapidly detect when the session has been # altered; at which point it can close the connection and retry. If a # client TSIG verification fails, the client MUST close the connection. # If the client does not receive TSIG records frequently enough (as # specified above) it SHOULD assume the connection has been hijacked # and it SHOULD close the connection. The client SHOULD treat this the # same way as they would any other interrupted transfer (although the # exact behavior is not specified). # ----- # # Each time a new envelope comes in, this method is called on the QUERY TSIG RR. # It will set the response tsigstate to :Verified :Intermediate or :Failed # as appropriate. # Keep digest going of messages as they come in (and mark them intermediate) # When TSIG comes in, work out what key should be and check. If OK, mark # verified. Can reset digest then. if (!@buf) @num_envelopes = 0 @last_signed = 0 end @num_envelopes += 1 if (!response.tsig) if ((@num_envelopes > 1) && (@num_envelopes - @last_signed < 100)) Dnsruby.log.debug("Receiving intermediate envelope in TSIG TCP session") response.tsigstate = :Intermediate response.tsigerror = RCode.NOERROR @buf = @buf + response_bytes return else response.tsigstate = :Failed Dnsruby.log.error("Expecting signed packet") return false end end @last_signed = @num_envelopes # We have a TSIG - process it! tsig = response.tsig if (@num_envelopes == 1) Dnsruby.log.debug("First response in TSIG TCP session - verifying normally") # Process it as a standard answer ok = verify(@query, response, response_bytes) if (ok) mac_bytes = MessageEncoder.new {|m| m.put_pack('n', tsig.mac_size) m.put_bytes(tsig.mac) }.to_s @buf = mac_bytes end return ok end Dnsruby.log.debug("Processing TSIG on TSIG TCP session") if (!verify_common(response)) return false end # Now add the current message data - remember to frig the arcount response_bytes = Header.decrement_arcount_encoded(response_bytes) @buf += response_bytes[0, response.tsigstart] # Let's add the timers timers_data = MessageEncoder.new { |msg| time_high = (tsig.time_signed >> 32) time_low = (tsig.time_signed & 0xFFFFFFFF) msg.put_pack('nN', time_high, time_low) msg.put_pack('n', tsig.fudge) }.to_s @buf += timers_data mac = calculate_mac(tsig.algorithm, @buf) if (mac != tsig.mac) Dnsruby.log.error("TSIG Verify error on TSIG TCP session") response.tsigstate = :Failed return false end mac_bytes = MessageEncoder.new {|m| m.put_pack('n', mac.length) m.put_bytes(mac) }.to_s @buf=mac_bytes response.tsigstate = :Verified response.tsigerror = RCode.NOERROR return true end