#!/usr/bin/perl -w use Digest::MD5; use MIME::Base64; use URI::Escape; use strict; my $cache_dir = "/var/cache/nsupdate"; my $hosts_file = "/etc/nsupdate/hosts"; my $nsupdate = "/usr/sbin/nsupdate -k /etc/bind/keys:dyn.doit.org."; print "Content-type: text/plain\n\n"; my %opt; foreach (split('&', $ENV{'QUERY_STRING'})) { my ($name, $value) = split('='); $opt{$name} = uri_unescape($value); } if (!defined($opt{host})) { exit 1; } my %hosts; open(F, "$hosts_file") or die "$hosts_file $!"; while () { chomp; my ($host, $pass) = split(':'); $hosts{$host} = $pass; } close F; # must have a valid host from the hosts file if (!defined($hosts{$opt{host}})) { exit 1; } if (!defined($opt{auth})) { my $seed; open(F, "/dev/random") or die "/dev/random $!"; read(F, $seed, 32); close F, # save the challenge in the cache so it can be used # later to authenticate open(F, ">$cache_dir/$opt{host}") or die "$!"; print F $seed; close F, print encode_base64($seed); } else { # load up the saved challenge for this host my $file = "$cache_dir/$opt{host}"; open(F, $file) or die "$file: $!"; my $challenge; $challenge = ; close F, unlink($file); # compute MD5(challenge+ip+password) my $md = Digest::MD5->new; $md->add($challenge); # include the ip if specified $md->add($opt{ip}) if defined($opt{ip}); $md->add($hosts{$opt{host}}); my $digest = $md->digest; my $response = decode_base64($opt{auth}); $opt{ip} = $ENV{'REMOTE_ADDR'} unless defined($opt{ip}); if ($digest eq $response && $opt{ip} =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { print "$opt{host} $opt{ip}\n"; open(F, "|$nsupdate") or die "nsupdate $!"; print F "update delete $opt{host} A\n"; print F "update add $opt{host} 300 A $opt{ip}\n"; print F "\n"; close F; } else { #print "got encode_base64($response), "exp ", encode_base64($digest); print "error"; } }