Sanitized HTTP Error codes

Implemented consistent output (normally provide an HTTP response when 
usual things go wrong)
Hid detailed error information in debug info so that end users cannot 
see this
This commit is contained in:
2019-08-05 15:49:55 +02:00
parent e568ed64cd
commit f663788138

View File

@@ -117,19 +117,37 @@ sub DNS_get($;$) {
}
}
##############################
# Initialize CGI, determine whether debugging is on and declaire fail method
my $cgi = CGI->new;
my $debug = ($AllowDebugKey cmp 'off') && (($AllowDebugKey eq '') || ($AllowDebugKey eq $cgi->param('debug')) );
my $SE = 'System Error';
my $CE = 'Configuration Error';
my $PE = 'Required parameter missing';
sub fail($$;$) {
my ($errormsg, $debugmsg, $exitcode) = @_;
print $debug . "\n";
print $cgi->header(-status=>$exitcode || 503, -type=>'text/plain'),
"ERROR - $errormsg" . ($debug ? ": $debugmsg\n" : "\n");
exit 0;
}
# Check whether the hostname provided is actually a CNAME, returns the host
# it points to in case of a CNAME or the original hostname if it is not.
sub expand_CNAME($;$) {
my ($host, @found) = @_;
if(my $cname=DNS_get($host, 'CNAME')) {
sub expand_CNAME($) {
my ($host) = @_;
my @found;
while(my $cname = DNS_get($host, 'CNAME')) {
push(@found, $host);
die $found[0]."has > $ExpandCNAMEs level deep CNAME chain, aborting!\n"
$host = $cname;
fail("DNS CNAME Not supported", "$found[0] points to a CNAME but this is not supported", 400)
unless($ExpandCNAMEs);
fail("DNS CNAME Chain Error", "$found[0] has > $ExpandCNAMEs level deep CNAME chain", 400)
unless($#found < $ExpandCNAMEs);
foreach my $r (@found) {
die "found CNAME loop for $host, aborting!\n" if($cname eq $r);
foreach my $r (@found) {
fail("DNS CNAME Loop", "found CNAME loop for $found[0]", 400) if($cname eq $r);
}
return &expand_CNAME($cname, @found);
}
return $host;
}
@@ -145,11 +163,8 @@ sub get_authinfo($$) {
|| (($AuthMode eq 'both') ? $StaticSigner : undef));
# Ensure we have a value for signer and key, otherwise abort the processing
if($signer eq '' or $key eq '') {
print $cgi->header(-status=>400, -type=>'text/plain'),
"ERROR - No/incomplete authentication information provided\n";
exit
}
fail($PE, "No/incomplete authentication information provided", 400)
if($signer eq '' or $key eq '');
# and return the values
return($signer, $key);
@@ -195,7 +210,7 @@ sub DNS_Update($$$$$$$) {
$response->header->rcode . $debugmessage);
} else {
# REFUSED, FORMERR
return (400, "ERROR - DNS update for $dnshost failed: " .
return (403, "ERROR - DNS update for $dnshost failed: " .
$response->header->rcode . $debugmessage);
}
} else {
@@ -207,8 +222,8 @@ sub DNS_Update($$$$$$$) {
##############################
# Handlers for the different requests
sub handle_update($$$$$$) {
my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_;
sub handle_update($$$$$) {
my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_;
my ($signer, $key) = get_authinfo($cgi, $host);
# perform the action
@@ -236,8 +251,8 @@ sub handle_update($$$$$$) {
}
sub handle_expire($$$$$$) {
my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_;
sub handle_expire($$$$$) {
my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_;
my ($signer, $key) = get_authinfo($cgi, $host);
my $debugmsg = ($debug) ? "\n" : '';
@@ -271,8 +286,8 @@ sub handle_expire($$$$$$) {
}
}
sub handle_view($$$$$$) {
my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_;
sub handle_view($$$$$) {
my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_;
my $title = "DynDNS Updater - $host";
print $cgi->header(-status=>200),
$cgi->start_html(-title => $title),
@@ -295,8 +310,8 @@ sub handle_view($$$$$$) {
print $cgi->end_html();
}
sub handle_list($$$$$$) {
my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_;
sub handle_list($$$$$) {
my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_;
my $title = "DynDNS Updater - $dnsdomain";
print $cgi->header(-status=>200),
@@ -322,7 +337,6 @@ sub handle_list($$$$$$) {
print $cgi->end_html();
}
my $cgi = CGI->new;
##############################
# Load configuration, if desired
@@ -346,36 +360,36 @@ if ($ConfigFile cmp 'ignore') {
my $dst = $CONFIG{$key}; # get destination for value
if (ref $dst eq 'SCALAR') { $$dst = $value; } # store scalar value
elsif (ref $dst eq 'CODE') { &$dst($value); } # call setter function
elsif (ref $dst cmp 'ARRAY') { die "Invalid $key in $CFGFile\n"; }
else { @$dst = split(/\s*,\s*/, $value); } # split and store array
elsif (ref $dst cmp 'ARRAY') { fail($SE, "Invalid config: $key"); }
else { @$dst = split(/\s*,\s*/, $value); } # split and store array
}
close CONFIG;
} elsif ($ConfigFile eq 'required') {
die "unable to load configuration from $CFGFile: $!, aborting\n"
fail($SE, "unable to load configuration from $CFGFile: $!");
}
}
##############################
# Validate Configuration
my $CE = 'Configuration Error:';
die "$CE ConfigFile must be optional, required or ignore, not '$ConfigFile'\n"
fail($CE, "ConfigFile must be optional, required or ignore, not '$ConfigFile'")
unless $ConfigFile=~/optional|required|ignore/;
die "$CE AuthMode '$AuthMode' is unsupported must be remote, static or both\n"
fail($CE, "AuthMode '$AuthMode' is unsupported must be remote, static or both")
unless $AuthMode=~/remote|static|both/;
die "$CE StaticSigner must be set for \$AuthMode '$AuthMode'\n"
fail($CE, "StaticSigner must be set for \$AuthMode '$AuthMode'")
unless ($StaticSigner or $AuthMode eq 'remote');
die "$CE StaticKey must be set for \$AuthMode '$AuthMode'\n"
fail($CE, "StaticKey must be set for \$AuthMode '$AuthMode'")
unless ($StaticKey or $AuthMode eq 'remote');
die "$CE RequireRR is set to unsupported type '$RequireRR'\n"
fail($CE, "RequireRR is set to unsupported type '$RequireRR'")
if ($RequireRR and not $DNS_label{$RequireRR});
die "$CE RecordTTL '$RecordTTL' is not supported\n"
fail($CE, "RecordTTL '$RecordTTL' is not supported")
unless ($RecordTTL=~/^\d+[smhw]?$/);
die "$CE ExpireAfter '$ExpireAfter' is not supported\n"
fail($CE, "ExpireAfter '$ExpireAfter' is not supported")
unless ($ExpireAfter=~/^\d+[smhw]?$/);
die "$CE UpdateTXT must be set when \$ExpireAfter is set\n"
fail($CE, "UpdateTXT must be set when \$ExpireAfter is set")
if($ExpireAfter and not $UpdateTXT);
foreach my $rrtype (@ReplaceRR) {
die "$CE ReplaceRR contains unsupported type '$rrtype'\n"
fail($CE, "ReplaceRR contains unsupported type '$rrtype'")
unless exists $DNS_label{$rrtype};
}
@@ -385,7 +399,6 @@ foreach my $rrtype (@ReplaceRR) {
my $mode = $cgi->path_info || $cgi->param('mode') || 'view';
$mode=~s/^\/([^\/]+)(\/(.*))?/$1/;
my $host = $cgi->param('host') || $3;
my $debug = ($AllowDebugKey eq 'off') ? 0 : ($AllowDebugKey eq ($cgi->param('debug') || ''));
##############################
@@ -398,16 +411,13 @@ my %handlers = (
expire => \&handle_expire,
);
if($host eq '' and $mode cmp 'list' and $mode cmp 'expire') {
print $cgi->header(-status=>400, -type=>'text/plain'),
"ERROR - No host name to act on specified\n";
fail($PE, "No host name to act on specified", 400);
} elsif(my $handler = $handlers{$mode}) {
# Replace provided host with that of a CNAME it points to and determine domain
my $dnshost = ($host) ? expand_CNAME($host) : undef;
my $dnsdomain = $cgi->param('domain') || ($dnshost=~/\.(.*)$/)[0];
$handler->($cgi, $mode, $host, $dnshost, $dnsdomain, $debug);
$handler->($mode, $host, $dnshost, $dnsdomain, $debug);
} else {
print $cgi->header(-status=>($cgi->path_info) ? 404 : 400,
-type=>'text/plain'),
"ERROR - File Not Found / Invalid Mode '$mode' specified\n";
fail("File Not Found", "Invalid Mode '$mode' specified", 404);
}