Blame lib/Net/DNS/RR/TSIG.pm

Packit Service f6e53a
package Net::DNS::RR::TSIG;
Packit Service f6e53a
Packit Service f6e53a
#
Packit Service f6e53a
# $Id: TSIG.pm 1597 2017-09-22 08:04:02Z willem $
Packit Service f6e53a
#
Packit Service f6e53a
our $VERSION = (qw$LastChangedRevision: 1597 $)[1];
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
use strict;
Packit Service f6e53a
use warnings;
Packit Service f6e53a
use base qw(Net::DNS::RR);
Packit Service f6e53a
Packit Service f6e53a
=head1 NAME
Packit Service f6e53a
Packit Service f6e53a
Net::DNS::RR::TSIG - DNS TSIG resource record
Packit Service f6e53a
Packit Service f6e53a
=cut
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
use integer;
Packit Service f6e53a
Packit Service f6e53a
use Carp;
Packit Service f6e53a
Packit Service f6e53a
eval 'require Digest::HMAC';
Packit Service f6e53a
eval 'require Digest::MD5';
Packit Service f6e53a
eval 'require Digest::SHA';
Packit Service f6e53a
eval 'require MIME::Base64';
Packit Service f6e53a
Packit Service f6e53a
use Net::DNS::DomainName;
Packit Service f6e53a
use Net::DNS::Parameters;
Packit Service f6e53a
Packit Service f6e53a
use constant ANY  => classbyname qw(ANY);
Packit Service f6e53a
use constant TSIG => typebyname qw(TSIG);
Packit Service f6e53a
Packit Service f6e53a
{
Packit Service f6e53a
	# source: http://www.iana.org/assignments/tsig-algorithm-names
Packit Service f6e53a
	my @algbyname = (
Packit Service f6e53a
		'HMAC-MD5.SIG-ALG.REG.INT' => 157,
Packit Service f6e53a
		'HMAC-SHA1'		   => 161,
Packit Service f6e53a
		'HMAC-SHA224'		   => 162,
Packit Service f6e53a
		'HMAC-SHA256'		   => 163,
Packit Service f6e53a
		'HMAC-SHA384'		   => 164,
Packit Service f6e53a
		'HMAC-SHA512'		   => 165,
Packit Service f6e53a
		);
Packit Service f6e53a
Packit Service f6e53a
	my @algalias = (
Packit Service f6e53a
		'HMAC-MD5' => 157,
Packit Service f6e53a
		'HMAC-SHA' => 161,
Packit Service f6e53a
		);
Packit Service f6e53a
Packit Service f6e53a
	my %algbyval = reverse @algbyname;
Packit Service f6e53a
Packit Service f6e53a
	my @algrehash = map /^\d/ ? ($_) x 3 : do { s/[\W_]//g; uc($_) }, @algbyname, @algalias;
Packit Service f6e53a
	my %algbyname = @algrehash;				# work around broken cperl
Packit Service f6e53a
Packit Service f6e53a
	sub _algbyname {
Packit Service f6e53a
		my $key = uc shift;				# synthetic key
Packit Service f6e53a
		$key =~ s/[\W_]//g;				# strip non-alphanumerics
Packit Service f6e53a
		$algbyname{$key};
Packit Service f6e53a
	}
Packit Service f6e53a
Packit Service f6e53a
	sub _algbyval {
Packit Service f6e53a
		my $value = shift;
Packit Service f6e53a
		$algbyval{$value};
Packit Service f6e53a
	}
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub _decode_rdata {			## decode rdata from wire-format octet string
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
	my ( $data, $offset ) = @_;
Packit Service f6e53a
Packit Service f6e53a
	my $limit = $offset + $self->{rdlength};
Packit Service f6e53a
	( $self->{algorithm}, $offset ) = decode Net::DNS::DomainName(@_);
Packit Service f6e53a
Packit Service f6e53a
	# Design decision: Use 32 bits, which will work until the end of time()!
Packit Service f6e53a
	@{$self}{qw(time_signed fudge)} = unpack "\@$offset xxN n", $$data;
Packit Service f6e53a
	$offset += 8;
Packit Service f6e53a
Packit Service f6e53a
	my $mac_size = unpack "\@$offset n", $$data;
Packit Service f6e53a
	$self->{macbin} = unpack "\@$offset xx a$mac_size", $$data;
Packit Service f6e53a
	$offset += $mac_size + 2;
Packit Service f6e53a
Packit Service f6e53a
	@{$self}{qw(original_id error)} = unpack "\@$offset nn", $$data;
Packit Service f6e53a
	$offset += 4;
Packit Service f6e53a
Packit Service f6e53a
	my $other_size = unpack "\@$offset n", $$data;
Packit Service f6e53a
	$self->{other} = unpack "\@$offset xx a$other_size", $$data;
Packit Service f6e53a
	$offset += $other_size + 2;
Packit Service f6e53a
Packit Service f6e53a
	croak('misplaced or corrupt TSIG') unless $limit == length $$data;
Packit Service f6e53a
	my $raw = substr $$data, 0, $self->{offset};
Packit Service f6e53a
	$self->{rawref} = \$raw;
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub _encode_rdata {			## encode rdata as wire-format octet string
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	my $macbin = $self->macbin;
Packit Service f6e53a
	unless ($macbin) {
Packit Service f6e53a
		my ( $offset, undef, $packet ) = @_;
Packit Service f6e53a
Packit Service f6e53a
		my $sigdata = $self->sig_data($packet);		# form data to be signed
Packit Service f6e53a
		$macbin = $self->macbin( $self->_mac_function($sigdata) );
Packit Service f6e53a
		$self->original_id( $packet->header->id );
Packit Service f6e53a
	}
Packit Service f6e53a
Packit Service f6e53a
	my $rdata = $self->{algorithm}->canonical;
Packit Service f6e53a
Packit Service f6e53a
	# Design decision: Use 32 bits, which will work until the end of time()!
Packit Service f6e53a
	$rdata .= pack 'xxN n', $self->time_signed, $self->fudge;
Packit Service f6e53a
Packit Service f6e53a
	$rdata .= pack 'na*', length($macbin), $macbin;
Packit Service f6e53a
Packit Service f6e53a
	$rdata .= pack 'nn', $self->original_id, $self->{error};
Packit Service f6e53a
Packit Service f6e53a
	my $other = $self->other;
Packit Service f6e53a
	$rdata .= pack 'na*', length($other), $other;
Packit Service f6e53a
Packit Service f6e53a
	return $rdata;
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub _defaults {				## specify RR attribute default values
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	$self->algorithm(157);
Packit Service f6e53a
	$self->class('ANY');
Packit Service f6e53a
	$self->error(0);
Packit Service f6e53a
	$self->fudge(300);
Packit Service f6e53a
	$self->other('');
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub _size {				## estimate encoded size
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
	my $clone = bless {%$self}, ref($self);			   # shallow clone
Packit Service f6e53a
	length $clone->encode( 0, undef, new Net::DNS::Packet() );
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub encode {				## overide RR method
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	my $kname = $self->{owner}->encode();			# uncompressed key name
Packit Service f6e53a
	my $rdata = eval { $self->_encode_rdata(@_) } || '';
Packit Service f6e53a
	pack 'a* n2 N n a*', $kname, TSIG, ANY, 0, length $rdata, $rdata;
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub string {				## overide RR method
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	my $owner	= $self->{owner}->string;
Packit Service f6e53a
	my $type	= $self->type;
Packit Service f6e53a
	my $algorithm	= $self->algorithm;
Packit Service f6e53a
	my $time_signed = $self->time_signed;
Packit Service f6e53a
	my $fudge	= $self->fudge;
Packit Service f6e53a
	my $signature	= $self->mac;
Packit Service f6e53a
	my $original_id = $self->original_id;
Packit Service f6e53a
	my $error	= $self->error;
Packit Service f6e53a
	my $other	= $self->other;
Packit Service f6e53a
Packit Service f6e53a
	return <<"QQ";
Packit Service f6e53a
; $owner	$type	
Packit Service f6e53a
;	algorithm:	$algorithm
Packit Service f6e53a
;	time signed:	$time_signed	fudge:	$fudge
Packit Service f6e53a
;	signature:	$signature
Packit Service f6e53a
;	original id:	$original_id
Packit Service f6e53a
;			$error	$other
Packit Service f6e53a
QQ
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub algorithm { &_algorithm; }
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub key {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	$self->keybin( MIME::Base64::decode( join "", @_ ) ) if scalar @_;
Packit Service f6e53a
	MIME::Base64::encode( $self->keybin(), "" ) if defined wantarray;
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub keybin { &_keybin; }
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub time_signed {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	$self->{time_signed} = 0 + shift if scalar @_;
Packit Service f6e53a
	$self->{time_signed} = time() unless $self->{time_signed};
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub fudge {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	$self->{fudge} = 0 + shift if scalar @_;
Packit Service f6e53a
	$self->{fudge} || 0;
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub mac {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	$self->macbin( pack "H*", map { die "!hex!" if m/[^0-9A-Fa-f]/; $_ } join "", @_ ) if scalar @_;
Packit Service f6e53a
	unpack "H*", $self->macbin() if defined wantarray;
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub macbin {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	$self->{macbin} = shift if scalar @_;
Packit Service f6e53a
	$self->{macbin} || "";
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub prior_mac {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
	return unpack "H*", $self->prior_macbin() unless scalar @_;
Packit Service f6e53a
	$self->prior_macbin( pack "H*", map /[^\dA-F]/i ? croak "corrupt hex" : $_, join "", @_ );
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub prior_macbin {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	$self->{prior_macbin} = shift if scalar @_;
Packit Service f6e53a
	$self->{prior_macbin} || "";
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub request_mac {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
	return unpack "H*", $self->request_macbin() unless scalar @_;
Packit Service f6e53a
	$self->request_macbin( pack "H*", map /[^\dA-F]/i ? croak "corrupt hex" : $_, join "", @_ );
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub request_macbin {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	$self->{request_macbin} = shift if scalar @_;
Packit Service f6e53a
	$self->{request_macbin} || "";
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub original_id {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	$self->{original_id} = 0 + shift if scalar @_;
Packit Service f6e53a
	$self->{original_id} || 0;
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub error {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
	$self->{error} = rcodebyname(shift) if scalar @_;
Packit Service f6e53a
	rcodebyval( $self->{error} );
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub other {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
	$self->{other} = shift if scalar @_;
Packit Service f6e53a
	my $time = $self->{error} == 18 ? pack 'xxN', time() : '';
Packit Service f6e53a
	$self->{other} = $time unless $self->{other};
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub other_data { &other; }					# uncoverable pod
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub sig_function {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
	return $self->{sig_function} unless scalar @_;
Packit Service f6e53a
	$self->{sig_function} = shift;
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
sub sign_func { &sig_function; }				# uncoverable pod
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub sig_data {
Packit Service f6e53a
	my ( $self, $message ) = @_;
Packit Service f6e53a
Packit Service f6e53a
	if ( ref($message) ) {
Packit Service f6e53a
		die 'missing packet reference' unless $message->isa('Net::DNS::Packet');
Packit Service f6e53a
		my @unsigned = grep ref($_) ne ref($self), @{$message->{additional}};
Packit Service f6e53a
		local $message->{additional} = \@unsigned;	# remake header image
Packit Service f6e53a
		my @part = qw(question answer authority additional);
Packit Service f6e53a
		my @size = map scalar( @{$message->{$_}} ), @part;
Packit Service f6e53a
		if ( my $rawref = $self->{rawref} ) {
Packit Service f6e53a
			delete $self->{rawref};
Packit Service f6e53a
			my $hbin = pack 'n6', $self->original_id, $message->{status}, @size;
Packit Service f6e53a
			$message = join '', $hbin, substr $$rawref, length $hbin;
Packit Service f6e53a
		} else {
Packit Service f6e53a
			my $data = $message->data;
Packit Service f6e53a
			my $hbin = pack 'n6', $message->{id}, $message->{status}, @size;
Packit Service f6e53a
			$message = join '', $hbin, substr $data, length $hbin;
Packit Service f6e53a
		}
Packit Service f6e53a
	}
Packit Service f6e53a
Packit Service f6e53a
	# Design decision: Use 32 bits, which will work until the end of time()!
Packit Service f6e53a
	my $time = pack 'xxN n', $self->time_signed, $self->fudge;
Packit Service f6e53a
Packit Service f6e53a
	# Insert the prior MAC if present (multi-packet message).
Packit Service f6e53a
	$self->prior_macbin( $self->{link}->macbin ) if $self->{link};
Packit Service f6e53a
	my $prior_macbin = $self->prior_macbin;
Packit Service f6e53a
	return pack 'na* a* a*', length($prior_macbin), $prior_macbin, $message, $time if $prior_macbin;
Packit Service f6e53a
Packit Service f6e53a
	# Insert the request MAC if present (used to validate responses).
Packit Service f6e53a
	my $req_mac = $self->request_macbin;
Packit Service f6e53a
	my $sigdata = $req_mac ? pack( 'na*', length($req_mac), $req_mac ) : '';
Packit Service f6e53a
Packit Service f6e53a
	$sigdata .= $message || '';
Packit Service f6e53a
Packit Service f6e53a
	my $kname = $self->{owner}->canonical;			# canonical key name
Packit Service f6e53a
	$sigdata .= pack 'a* n N', $kname, ANY, 0;
Packit Service f6e53a
Packit Service f6e53a
	$sigdata .= $self->{algorithm}->canonical;		# canonical algorithm name
Packit Service f6e53a
Packit Service f6e53a
	$sigdata .= $time;
Packit Service f6e53a
Packit Service f6e53a
	$sigdata .= pack 'n', $self->{error};
Packit Service f6e53a
Packit Service f6e53a
	my $other = $self->other;
Packit Service f6e53a
	$sigdata .= pack 'na*', length($other), $other;
Packit Service f6e53a
Packit Service f6e53a
	return $sigdata;
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub create {
Packit Service f6e53a
	my $class = shift;
Packit Service f6e53a
	my $karg  = shift;
Packit Service f6e53a
	croak 'argument undefined' unless defined $karg;
Packit Service f6e53a
Packit Service f6e53a
	if ( ref($karg) ) {
Packit Service f6e53a
		if ( $karg->isa('Net::DNS::Packet') ) {
Packit Service f6e53a
			my $sigrr = $karg->sigrr;
Packit Service f6e53a
			croak 'no TSIG in request packet' unless defined $sigrr;
Packit Service f6e53a
			return new Net::DNS::RR(		# ( request, options )
Packit Service f6e53a
				name	       => $sigrr->name,
Packit Service f6e53a
				type	       => 'TSIG',
Packit Service f6e53a
				algorithm      => $sigrr->algorithm,
Packit Service f6e53a
				request_macbin => $sigrr->macbin,
Packit Service f6e53a
				@_
Packit Service f6e53a
				);
Packit Service f6e53a
Packit Service f6e53a
		} elsif ( ref($karg) eq __PACKAGE__ ) {
Packit Service f6e53a
			my $tsig = $karg->_chain;
Packit Service f6e53a
			$tsig->{macbin} = undef;
Packit Service f6e53a
			return $tsig;
Packit Service f6e53a
Packit Service f6e53a
		} elsif ( ref($karg) eq 'Net::DNS::RR::KEY' ) {
Packit Service f6e53a
			return new Net::DNS::RR(
Packit Service f6e53a
				name	  => $karg->name,
Packit Service f6e53a
				type	  => 'TSIG',
Packit Service f6e53a
				algorithm => $karg->algorithm,
Packit Service f6e53a
				key	  => $karg->key,
Packit Service f6e53a
				@_
Packit Service f6e53a
				);
Packit Service f6e53a
		}
Packit Service f6e53a
Packit Service f6e53a
		croak "Usage:	create $class(keyfile)\n\tcreate $class(keyname, key)"
Packit Service f6e53a
Packit Service f6e53a
	} elsif ( scalar(@_) == 1 ) {
Packit Service f6e53a
		my $key = shift;				# ( keyname, key )
Packit Service f6e53a
		return new Net::DNS::RR(
Packit Service f6e53a
			name => $karg,
Packit Service f6e53a
			type => 'TSIG',
Packit Service f6e53a
			key  => $key
Packit Service f6e53a
			);
Packit Service f6e53a
Packit Service f6e53a
	} elsif ( $karg =~ /private$/ ) {			# ( keyfile, options )
Packit Service f6e53a
		require File::Spec;
Packit Service f6e53a
		require Net::DNS::ZoneFile;
Packit Service f6e53a
		my $keyfile = new Net::DNS::ZoneFile($karg);
Packit Service f6e53a
		my ( $alg, $key, $junk );
Packit Service f6e53a
		while ( $keyfile->_getline ) {
Packit Service f6e53a
			( $junk, $alg ) = split if /Algorithm:/;
Packit Service f6e53a
			( $junk, $key ) = split if /Key:/;
Packit Service f6e53a
		}
Packit Service f6e53a
Packit Service f6e53a
		my ( $vol, $dir, $file ) = File::Spec->splitpath( $keyfile->name );
Packit Service f6e53a
		croak "misnamed private key" unless $file =~ /^K([^+]+)+.+private$/;
Packit Service f6e53a
		my $kname = $1;
Packit Service f6e53a
		return new Net::DNS::RR(
Packit Service f6e53a
			name	  => $kname,
Packit Service f6e53a
			type	  => 'TSIG',
Packit Service f6e53a
			algorithm => $alg,
Packit Service f6e53a
			key	  => $key,
Packit Service f6e53a
			@_
Packit Service f6e53a
			);
Packit Service f6e53a
Packit Service f6e53a
	} else {						# ( keyfile, options )
Packit Service f6e53a
		require Net::DNS::ZoneFile;
Packit Service f6e53a
		my $keyrr = new Net::DNS::ZoneFile($karg)->read;
Packit Service f6e53a
		croak 'key file incompatible with TSIG' unless $keyrr->type eq 'KEY';
Packit Service f6e53a
		return new Net::DNS::RR(
Packit Service f6e53a
			name	  => $keyrr->name,
Packit Service f6e53a
			type	  => 'TSIG',
Packit Service f6e53a
			algorithm => $keyrr->algorithm,
Packit Service f6e53a
			key	  => $keyrr->key,
Packit Service f6e53a
			@_
Packit Service f6e53a
			);
Packit Service f6e53a
	}
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
sub verify {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
	my $data = shift;
Packit Service f6e53a
Packit Service f6e53a
	unless ( abs( time() - $self->time_signed ) < $self->fudge ) {
Packit Service f6e53a
		$self->error(18);				# bad time
Packit Service f6e53a
		return;
Packit Service f6e53a
	}
Packit Service f6e53a
Packit Service f6e53a
	if ( scalar @_ ) {
Packit Service f6e53a
		my $arg = shift;
Packit Service f6e53a
Packit Service f6e53a
		unless ( ref($arg) ) {
Packit Service f6e53a
			$self->error(16);			# bad sig (multi-packet)
Packit Service f6e53a
			return;
Packit Service f6e53a
		}
Packit Service f6e53a
Packit Service f6e53a
		my $signerkey = lc( join '+', $self->name, $self->algorithm );
Packit Service f6e53a
		if ( $arg->isa('Net::DNS::Packet') ) {
Packit Service f6e53a
			my $request = $arg->sigrr;		# request TSIG
Packit Service f6e53a
			my $rqstkey = lc( join '+', $request->name, $request->algorithm );
Packit Service f6e53a
			$self->error(17) unless $signerkey eq $rqstkey;
Packit Service f6e53a
			$self->request_macbin( $request->macbin );
Packit Service f6e53a
Packit Service f6e53a
		} elsif ( $arg->isa(__PACKAGE__) ) {
Packit Service f6e53a
			my $priorkey = lc( join '+', $arg->name, $arg->algorithm );
Packit Service f6e53a
			$self->error(17) unless $signerkey eq $priorkey;
Packit Service f6e53a
			$self->prior_macbin( $arg->macbin );
Packit Service f6e53a
Packit Service f6e53a
		} else {
Packit Service f6e53a
			croak 'Usage: $tsig->verify( $reply, $query )';
Packit Service f6e53a
		}
Packit Service f6e53a
	}
Packit Service f6e53a
	return if $self->{error};
Packit Service f6e53a
Packit Service f6e53a
	my $sigdata = $self->sig_data($data);			# form data to be verified
Packit Service f6e53a
	my $tsigmac = $self->_mac_function($sigdata);
Packit Service f6e53a
	my $tsig    = $self->_chain;
Packit Service f6e53a
Packit Service f6e53a
	my $macbin = $self->macbin;
Packit Service f6e53a
	my $maclen = length $macbin;
Packit Service f6e53a
	my $minlen = length($tsigmac) >> 1;			# per RFC4635, 3.1
Packit Service f6e53a
	$self->error(16) unless $macbin eq substr $tsigmac, 0, $maclen;
Packit Service f6e53a
	$self->error(1) if $maclen < $minlen or $maclen < 10 or $maclen > length $tsigmac;
Packit Service f6e53a
Packit Service f6e53a
	return $self->{error} ? undef : $tsig;
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
sub vrfyerrstr {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
	return $self->error;
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
########################################
Packit Service f6e53a
Packit Service f6e53a
{
Packit Service f6e53a
	my %digest = (
Packit Service f6e53a
		'157' => ['Digest::MD5'],
Packit Service f6e53a
		'161' => ['Digest::SHA'],
Packit Service f6e53a
		'162' => ['Digest::SHA', 224, 64],
Packit Service f6e53a
		'163' => ['Digest::SHA', 256, 64],
Packit Service f6e53a
		'164' => ['Digest::SHA', 384, 128],
Packit Service f6e53a
		'165' => ['Digest::SHA', 512, 128],
Packit Service f6e53a
		);
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
	my %keytable;
Packit Service f6e53a
Packit Service f6e53a
	sub _algorithm {		## install sig function in key table
Packit Service f6e53a
		my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
		if ( my $algname = shift ) {
Packit Service f6e53a
Packit Service f6e53a
			unless ( my $digtype = _algbyname($algname) ) {
Packit Service f6e53a
				$self->{algorithm} = new Net::DNS::DomainName($algname);
Packit Service f6e53a
Packit Service f6e53a
			} else {
Packit Service f6e53a
				$algname = _algbyval($digtype);
Packit Service f6e53a
				$self->{algorithm} = new Net::DNS::DomainName($algname);
Packit Service f6e53a
Packit Service f6e53a
				my ( $hash, @param ) = @{$digest{$digtype}};
Packit Service f6e53a
				my ( undef, @block ) = @param;
Packit Service f6e53a
				my $digest   = new $hash(@param);
Packit Service f6e53a
				my $function = sub {
Packit Service f6e53a
					my $hmac = new Digest::HMAC( shift, $digest, @block );
Packit Service f6e53a
					$hmac->add(shift);
Packit Service f6e53a
					return $hmac->digest;
Packit Service f6e53a
				};
Packit Service f6e53a
Packit Service f6e53a
				$self->sig_function($function);
Packit Service f6e53a
Packit Service f6e53a
				my $keyname = ( $self->{owner} || return )->canonical;
Packit Service f6e53a
				$keytable{$keyname}{digest} = $function;
Packit Service f6e53a
			}
Packit Service f6e53a
		}
Packit Service f6e53a
Packit Service f6e53a
		return $self->{algorithm}->name if defined wantarray;
Packit Service f6e53a
	}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
	sub _keybin {			## install key in key table
Packit Service f6e53a
		my $self = shift;
Packit Service f6e53a
		croak 'Unauthorised access to TSIG key material denied' unless scalar @_;
Packit Service f6e53a
		my $keyref = $keytable{$self->{owner}->canonical} ||= {};
Packit Service f6e53a
		my $private = shift;	# closure keeps private key private
Packit Service f6e53a
		$keyref->{key} = sub {
Packit Service f6e53a
			my $function = $keyref->{digest};
Packit Service f6e53a
			return &$function( $private, @_ );
Packit Service f6e53a
		};
Packit Service f6e53a
		return undef;
Packit Service f6e53a
	}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
	sub _mac_function {		## apply keyed hash function to argument
Packit Service f6e53a
		my $self = shift;
Packit Service f6e53a
Packit Service f6e53a
		my $owner = $self->{owner}->canonical;
Packit Service f6e53a
		$self->algorithm( $self->algorithm ) unless $keytable{$owner}{digest};
Packit Service f6e53a
		my $keyref = $keytable{$owner};
Packit Service f6e53a
		$keyref->{digest} = $self->sig_function unless $keyref->{digest};
Packit Service f6e53a
		my $function = $keyref->{key};
Packit Service f6e53a
		&$function(@_);
Packit Service f6e53a
	}
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
# _chain() creates a new TSIG object linked to the original
Packit Service f6e53a
# RR, for the purpose of signing multi-message transfers.
Packit Service f6e53a
Packit Service f6e53a
sub _chain {
Packit Service f6e53a
	my $self = shift;
Packit Service f6e53a
	$self->{link} = undef;
Packit Service f6e53a
	bless {%$self, link => $self}, ref($self);
Packit Service f6e53a
}
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
1;
Packit Service f6e53a
__END__
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head1 SYNOPSIS
Packit Service f6e53a
Packit Service f6e53a
    use Net::DNS;
Packit Service f6e53a
    $tsig = create Net::DNS::RR::TSIG( $keyfile );
Packit Service f6e53a
Packit Service f6e53a
    $tsig = create Net::DNS::RR::TSIG( $keyfile,
Packit Service f6e53a
					fudge => 300
Packit Service f6e53a
					);
Packit Service f6e53a
Packit Service f6e53a
=head1 DESCRIPTION
Packit Service f6e53a
Packit Service f6e53a
Class for DNS Transaction Signature (TSIG) resource records.
Packit Service f6e53a
Packit Service f6e53a
=head1 METHODS
Packit Service f6e53a
Packit Service f6e53a
The available methods are those inherited from the base class augmented
Packit Service f6e53a
by the type-specific methods defined in this package.
Packit Service f6e53a
Packit Service f6e53a
Use of undocumented package features or direct access to internal data
Packit Service f6e53a
structures is discouraged and could result in program termination or
Packit Service f6e53a
other unpredictable behaviour.
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head2 algorithm
Packit Service f6e53a
Packit Service f6e53a
    $algorithm = $rr->algorithm;
Packit Service f6e53a
    $rr->algorithm( $algorithm );
Packit Service f6e53a
Packit Service f6e53a
A domain name which specifies the name of the algorithm.
Packit Service f6e53a
Packit Service f6e53a
=head2 key
Packit Service f6e53a
Packit Service f6e53a
    $rr->key( $key );
Packit Service f6e53a
Packit Service f6e53a
Base64 representation of the key material.
Packit Service f6e53a
Packit Service f6e53a
=head2 keybin
Packit Service f6e53a
Packit Service f6e53a
    $rr->keybin( $keybin );
Packit Service f6e53a
Packit Service f6e53a
Binary representation of the key material.
Packit Service f6e53a
Packit Service f6e53a
=head2 time_signed
Packit Service f6e53a
Packit Service f6e53a
    $time_signed = $rr->time_signed;
Packit Service f6e53a
    $rr->time_signed( $time_signed );
Packit Service f6e53a
Packit Service f6e53a
Signing time as the number of seconds since 1 Jan 1970 00:00:00 UTC.
Packit Service f6e53a
The default signing time is the current time.
Packit Service f6e53a
Packit Service f6e53a
=head2 fudge
Packit Service f6e53a
Packit Service f6e53a
    $fudge = $rr->fudge;
Packit Service f6e53a
    $rr->fudge( $fudge );
Packit Service f6e53a
Packit Service f6e53a
"fudge" represents the permitted error in the signing time.
Packit Service f6e53a
The default fudge is 300 seconds.
Packit Service f6e53a
Packit Service f6e53a
=head2 mac
Packit Service f6e53a
Packit Service f6e53a
    $mac = $rr->mac;
Packit Service f6e53a
Packit Service f6e53a
Returns the message authentication code (MAC) as a string of hex
Packit Service f6e53a
characters.  The programmer must call the Net::DNS::Packet data()
Packit Service f6e53a
object method before this will return anything meaningful.
Packit Service f6e53a
Packit Service f6e53a
=cut
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head2 macbin
Packit Service f6e53a
Packit Service f6e53a
    $macbin = $rr->macbin;
Packit Service f6e53a
    $rr->macbin( $macbin );
Packit Service f6e53a
Packit Service f6e53a
Binary message authentication code (MAC).
Packit Service f6e53a
Packit Service f6e53a
=head2 prior_mac
Packit Service f6e53a
Packit Service f6e53a
    $prior_mac = $rr->prior_mac;
Packit Service f6e53a
    $rr->prior_mac( $prior_mac );
Packit Service f6e53a
Packit Service f6e53a
Prior message authentication code (MAC).
Packit Service f6e53a
Packit Service f6e53a
=head2 prior_macbin
Packit Service f6e53a
Packit Service f6e53a
    $prior_macbin = $rr->prior_macbin;
Packit Service f6e53a
    $rr->prior_macbin( $prior_macbin );
Packit Service f6e53a
Packit Service f6e53a
Binary prior message authentication code.
Packit Service f6e53a
Packit Service f6e53a
=head2 request_mac
Packit Service f6e53a
Packit Service f6e53a
    $request_mac = $rr->request_mac;
Packit Service f6e53a
    $rr->request_mac( $request_mac );
Packit Service f6e53a
Packit Service f6e53a
Request message authentication code (MAC).
Packit Service f6e53a
Packit Service f6e53a
=head2 request_macbin
Packit Service f6e53a
Packit Service f6e53a
    $request_macbin = $rr->request_macbin;
Packit Service f6e53a
    $rr->request_macbin( $request_macbin );
Packit Service f6e53a
Packit Service f6e53a
Binary request message authentication code.
Packit Service f6e53a
Packit Service f6e53a
=head2 original_id
Packit Service f6e53a
Packit Service f6e53a
    $original_id = $rr->original_id;
Packit Service f6e53a
    $rr->original_id( $original_id );
Packit Service f6e53a
Packit Service f6e53a
The message ID from the header of the original packet.
Packit Service f6e53a
Packit Service f6e53a
=head2 error
Packit Service f6e53a
Packit Service f6e53a
=head2 vrfyerrstr
Packit Service f6e53a
Packit Service f6e53a
     $rcode = $tsig->error;
Packit Service f6e53a
Packit Service f6e53a
Returns the RCODE covering TSIG processing.  Common values are
Packit Service f6e53a
NOERROR, BADSIG, BADKEY, and BADTIME.  See RFC 2845 for details.
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head2 other
Packit Service f6e53a
Packit Service f6e53a
     $other = $tsig->other;
Packit Service f6e53a
Packit Service f6e53a
This field should be empty unless the error is BADTIME, in which
Packit Service f6e53a
case it will contain the server time as the number of seconds since
Packit Service f6e53a
1 Jan 1970 00:00:00 UTC.
Packit Service f6e53a
Packit Service f6e53a
=head2 sig_function
Packit Service f6e53a
Packit Service f6e53a
    sub signing_function {
Packit Service f6e53a
	my ( $keybin, $data ) = @_;
Packit Service f6e53a
Packit Service f6e53a
	my $hmac = new Digest::HMAC( $keybin, 'Digest::MD5' );
Packit Service f6e53a
	$hmac->add( $data );
Packit Service f6e53a
	return $hmac->digest;
Packit Service f6e53a
    }
Packit Service f6e53a
Packit Service f6e53a
    $tsig->sig_function( \&signing_function );
Packit Service f6e53a
Packit Service f6e53a
This sets the signing function to be used for this TSIG record.
Packit Service f6e53a
The default signing function is HMAC-MD5.
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head2 sig_data
Packit Service f6e53a
Packit Service f6e53a
     $sigdata = $tsig->sig_data($packet);
Packit Service f6e53a
Packit Service f6e53a
Returns the packet packed according to RFC2845 in a form for signing. This
Packit Service f6e53a
is only needed if you want to supply an external signing function, such as is
Packit Service f6e53a
needed for TSIG-GSS.
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head2 create
Packit Service f6e53a
Packit Service f6e53a
    $tsig = create Net::DNS::RR::TSIG( $keyfile );
Packit Service f6e53a
Packit Service f6e53a
    $tsig = create Net::DNS::RR::TSIG( $keyfile,
Packit Service f6e53a
					fudge => 300
Packit Service f6e53a
					);
Packit Service f6e53a
Packit Service f6e53a
Returns a TSIG RR constructed using the parameters in the specified
Packit Service f6e53a
key file, which is assumed to have been generated by dnssec-keygen.
Packit Service f6e53a
Packit Service f6e53a
    $tsig = create Net::DNS::RR::TSIG( $keyname, $key );
Packit Service f6e53a
Packit Service f6e53a
The two argument form is supported for backward compatibility.
Packit Service f6e53a
Packit Service f6e53a
=head2 verify
Packit Service f6e53a
Packit Service f6e53a
    $verify = $tsig->verify( $data );
Packit Service f6e53a
    $verify = $tsig->verify( $packet );
Packit Service f6e53a
Packit Service f6e53a
    $verify = $tsig->verify( $reply,  $query );
Packit Service f6e53a
Packit Service f6e53a
    $verify = $tsig->verify( $packet, $prior );
Packit Service f6e53a
Packit Service f6e53a
The boolean verify method will return true if the hash over the
Packit Service f6e53a
packet data conforms to the data in the TSIG itself
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head1 TSIG Keys
Packit Service f6e53a
Packit Service f6e53a
TSIG keys are symmetric keys generated using dnssec-keygen:
Packit Service f6e53a
Packit Service f6e53a
	$ dnssec-keygen -a HMAC-SHA1 -b 160 -n HOST <keyname>
Packit Service f6e53a
Packit Service f6e53a
	The key will be stored as a private and public keyfile pair
Packit Service f6e53a
	K<keyname>+161+<keyid>.private and K<keyname>+161+<keyid>.key
Packit Service f6e53a
Packit Service f6e53a
    where
Packit Service f6e53a
	<keyname> is the DNS name of the key.
Packit Service f6e53a
Packit Service f6e53a
	<keyid> is the (generated) numerical identifier used to
Packit Service f6e53a
	distinguish this key.
Packit Service f6e53a
Packit Service f6e53a
Other algorithms may be substituted for HMAC-SHA1 in the above example.
Packit Service f6e53a
Packit Service f6e53a
It is recommended that the keyname be globally unique and incorporate
Packit Service f6e53a
the fully qualified domain names of the resolver and nameserver in
Packit Service f6e53a
that order. It should be possible for more than one key to be in use
Packit Service f6e53a
simultaneously between any such pair of hosts.
Packit Service f6e53a
Packit Service f6e53a
Although the formats differ, the private and public keys are identical
Packit Service f6e53a
and both should be stored and handled as secret data.
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head1 Configuring BIND Nameserver
Packit Service f6e53a
Packit Service f6e53a
The following lines must be added to the /etc/named.conf file:
Packit Service f6e53a
Packit Service f6e53a
    key <keyname> {
Packit Service f6e53a
	algorithm HMAC-SHA1;
Packit Service f6e53a
	secret "<keydata>";
Packit Service f6e53a
    };
Packit Service f6e53a
Packit Service f6e53a
<keyname> is the name of the key chosen when the key was generated.
Packit Service f6e53a
Packit Service f6e53a
<keydata> is the key string extracted from the generated key file.
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head1 ACKNOWLEDGMENT
Packit Service f6e53a
Packit Service f6e53a
Most of the code in the Net::DNS::RR::TSIG module was contributed
Packit Service f6e53a
by Chris Turbeville. 
Packit Service f6e53a
Packit Service f6e53a
Support for external signing functions was added by Andrew Tridgell.
Packit Service f6e53a
Packit Service f6e53a
TSIG verification, BIND keyfile handling and support for HMAC-SHA1,
Packit Service f6e53a
HMAC-SHA224, HMAC-SHA256, HMAC-SHA384 and HMAC-SHA512 functions was
Packit Service f6e53a
added by Dick Franks.
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head1 BUGS
Packit Service f6e53a
Packit Service f6e53a
A 32-bit representation of time is used, contrary to RFC2845 which
Packit Service f6e53a
demands 48 bits.  This design decision will need to be reviewed
Packit Service f6e53a
before the code stops working on 7 February 2106.
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head1 COPYRIGHT
Packit Service f6e53a
Packit Service f6e53a
Copyright (c)2000,2001 Michael Fuhr. 
Packit Service f6e53a
Packit Service f6e53a
Portions Copyright (c)2002,2003 Chris Reinhardt.
Packit Service f6e53a
Packit Service f6e53a
Portions Copyright (c)2013 Dick Franks.
Packit Service f6e53a
Packit Service f6e53a
All rights reserved.
Packit Service f6e53a
Packit Service f6e53a
Package template (c)2009,2012 O.M.Kolkman and R.W.Franks.
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head1 LICENSE
Packit Service f6e53a
Packit Service f6e53a
Permission to use, copy, modify, and distribute this software and its
Packit Service f6e53a
documentation for any purpose and without fee is hereby granted, provided
Packit Service f6e53a
that the above copyright notice appear in all copies and that both that
Packit Service f6e53a
copyright notice and this permission notice appear in supporting
Packit Service f6e53a
documentation, and that the name of the author not be used in advertising
Packit Service f6e53a
or publicity pertaining to distribution of the software without specific
Packit Service f6e53a
prior written permission.
Packit Service f6e53a
Packit Service f6e53a
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Packit Service f6e53a
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Packit Service f6e53a
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
Packit Service f6e53a
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Packit Service f6e53a
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
Packit Service f6e53a
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
Packit Service f6e53a
DEALINGS IN THE SOFTWARE.
Packit Service f6e53a
Packit Service f6e53a
Packit Service f6e53a
=head1 SEE ALSO
Packit Service f6e53a
Packit Service f6e53a
L<perl>, L<Net::DNS>, L<Net::DNS::RR>, RFC2845, RFC4635
Packit Service f6e53a
Packit Service f6e53a
L<TSIG Algorithm Names|http://www.iana.org/assignments/tsig-algorithm-names>
Packit Service f6e53a
Packit Service f6e53a
=cut