# File lib/Dnsruby/resource/TSIG.rb, line 273 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