Commit f663788138ee017d728988252a0ace41cbdf5813

Authored by Frederik Lindenaar
1 parent e568ed64

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
Showing 1 changed file with 53 additions and 43 deletions
dyndns.pl
... ... @@ -117,19 +117,37 @@ sub DNS_get($;$) {
117 117 }
118 118 }
119 119  
  120 +##############################
  121 +# Initialize CGI, determine whether debugging is on and declaire fail method
  122 +my $cgi = CGI->new;
  123 +my $debug = ($AllowDebugKey cmp 'off') && (($AllowDebugKey eq '') || ($AllowDebugKey eq $cgi->param('debug')) );
  124 +my $SE = 'System Error';
  125 +my $CE = 'Configuration Error';
  126 +my $PE = 'Required parameter missing';
  127 +sub fail($$;$) {
  128 + my ($errormsg, $debugmsg, $exitcode) = @_;
  129 + print $debug . "\n";
  130 + print $cgi->header(-status=>$exitcode || 503, -type=>'text/plain'),
  131 + "ERROR - $errormsg" . ($debug ? ": $debugmsg\n" : "\n");
  132 + exit 0;
  133 +}
  134 +
  135 +
120 136 # Check whether the hostname provided is actually a CNAME, returns the host
121 137 # it points to in case of a CNAME or the original hostname if it is not.
122   -sub expand_CNAME($;$) {
123   - my ($host, @found) = @_;
124   - if(my $cname=DNS_get($host, 'CNAME')) {
  138 +sub expand_CNAME($) {
  139 + my ($host) = @_;
  140 + my @found;
  141 + while(my $cname = DNS_get($host, 'CNAME')) {
125 142 push(@found, $host);
126   - die $found[0]."has > $ExpandCNAMEs level deep CNAME chain, aborting!\n"
  143 + $host = $cname;
  144 + fail("DNS CNAME Not supported", "$found[0] points to a CNAME but this is not supported", 400)
  145 + unless($ExpandCNAMEs);
  146 + fail("DNS CNAME Chain Error", "$found[0] has > $ExpandCNAMEs level deep CNAME chain", 400)
127 147 unless($#found < $ExpandCNAMEs);
128   - foreach my $r (@found) {
129   - die "found CNAME loop for $host, aborting!\n" if($cname eq $r);
  148 + foreach my $r (@found) {
  149 + fail("DNS CNAME Loop", "found CNAME loop for $found[0]", 400) if($cname eq $r);
130 150 }
131   -
132   - return &expand_CNAME($cname, @found);
133 151 }
134 152 return $host;
135 153 }
... ... @@ -145,11 +163,8 @@ sub get_authinfo($$) {
145 163 || (($AuthMode eq 'both') ? $StaticSigner : undef));
146 164  
147 165 # Ensure we have a value for signer and key, otherwise abort the processing
148   - if($signer eq '' or $key eq '') {
149   - print $cgi->header(-status=>400, -type=>'text/plain'),
150   - "ERROR - No/incomplete authentication information provided\n";
151   - exit
152   - }
  166 + fail($PE, "No/incomplete authentication information provided", 400)
  167 + if($signer eq '' or $key eq '');
153 168  
154 169 # and return the values
155 170 return($signer, $key);
... ... @@ -195,7 +210,7 @@ sub DNS_Update($$$$$$$) {
195 210 $response->header->rcode . $debugmessage);
196 211 } else {
197 212 # REFUSED, FORMERR
198   - return (400, "ERROR - DNS update for $dnshost failed: " .
  213 + return (403, "ERROR - DNS update for $dnshost failed: " .
199 214 $response->header->rcode . $debugmessage);
200 215 }
201 216 } else {
... ... @@ -207,8 +222,8 @@ sub DNS_Update($$$$$$$) {
207 222  
208 223 ##############################
209 224 # Handlers for the different requests
210   -sub handle_update($$$$$$) {
211   - my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_;
  225 +sub handle_update($$$$$) {
  226 + my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_;
212 227 my ($signer, $key) = get_authinfo($cgi, $host);
213 228  
214 229 # perform the action
... ... @@ -236,8 +251,8 @@ sub handle_update($$$$$$) {
236 251  
237 252 }
238 253  
239   -sub handle_expire($$$$$$) {
240   - my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_;
  254 +sub handle_expire($$$$$) {
  255 + my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_;
241 256 my ($signer, $key) = get_authinfo($cgi, $host);
242 257  
243 258 my $debugmsg = ($debug) ? "\n" : '';
... ... @@ -271,8 +286,8 @@ sub handle_expire($$$$$$) {
271 286 }
272 287 }
273 288  
274   -sub handle_view($$$$$$) {
275   - my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_;
  289 +sub handle_view($$$$$) {
  290 + my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_;
276 291 my $title = "DynDNS Updater - $host";
277 292 print $cgi->header(-status=>200),
278 293 $cgi->start_html(-title => $title),
... ... @@ -295,8 +310,8 @@ sub handle_view($$$$$$) {
295 310 print $cgi->end_html();
296 311 }
297 312  
298   -sub handle_list($$$$$$) {
299   - my ($cgi, $mode, $host, $dnshost, $dnsdomain, $debug) = @_;
  313 +sub handle_list($$$$$) {
  314 + my ($mode, $host, $dnshost, $dnsdomain, $debug) = @_;
300 315 my $title = "DynDNS Updater - $dnsdomain";
301 316  
302 317 print $cgi->header(-status=>200),
... ... @@ -322,7 +337,6 @@ sub handle_list($$$$$$) {
322 337 print $cgi->end_html();
323 338 }
324 339  
325   -my $cgi = CGI->new;
326 340  
327 341 ##############################
328 342 # Load configuration, if desired
... ... @@ -346,36 +360,36 @@ if ($ConfigFile cmp &#39;ignore&#39;) {
346 360 my $dst = $CONFIG{$key}; # get destination for value
347 361 if (ref $dst eq 'SCALAR') { $$dst = $value; } # store scalar value
348 362 elsif (ref $dst eq 'CODE') { &$dst($value); } # call setter function
349   - elsif (ref $dst cmp 'ARRAY') { die "Invalid $key in $CFGFile\n"; }
350   - else { @$dst = split(/\s*,\s*/, $value); } # split and store array
  363 + elsif (ref $dst cmp 'ARRAY') { fail($SE, "Invalid config: $key"); }
  364 + else { @$dst = split(/\s*,\s*/, $value); } # split and store array
351 365 }
352 366 close CONFIG;
353 367 } elsif ($ConfigFile eq 'required') {
354   - die "unable to load configuration from $CFGFile: $!, aborting\n"
  368 + fail($SE, "unable to load configuration from $CFGFile: $!");
355 369 }
356 370 }
357 371  
  372 +
358 373 ##############################
359 374 # Validate Configuration
360   -my $CE = 'Configuration Error:';
361   -die "$CE ConfigFile must be optional, required or ignore, not '$ConfigFile'\n"
  375 +fail($CE, "ConfigFile must be optional, required or ignore, not '$ConfigFile'")
362 376 unless $ConfigFile=~/optional|required|ignore/;
363   -die "$CE AuthMode '$AuthMode' is unsupported must be remote, static or both\n"
  377 +fail($CE, "AuthMode '$AuthMode' is unsupported must be remote, static or both")
364 378 unless $AuthMode=~/remote|static|both/;
365   -die "$CE StaticSigner must be set for \$AuthMode '$AuthMode'\n"
  379 +fail($CE, "StaticSigner must be set for \$AuthMode '$AuthMode'")
366 380 unless ($StaticSigner or $AuthMode eq 'remote');
367   -die "$CE StaticKey must be set for \$AuthMode '$AuthMode'\n"
  381 +fail($CE, "StaticKey must be set for \$AuthMode '$AuthMode'")
368 382 unless ($StaticKey or $AuthMode eq 'remote');
369   -die "$CE RequireRR is set to unsupported type '$RequireRR'\n"
  383 +fail($CE, "RequireRR is set to unsupported type '$RequireRR'")
370 384 if ($RequireRR and not $DNS_label{$RequireRR});
371   -die "$CE RecordTTL '$RecordTTL' is not supported\n"
  385 +fail($CE, "RecordTTL '$RecordTTL' is not supported")
372 386 unless ($RecordTTL=~/^\d+[smhw]?$/);
373   -die "$CE ExpireAfter '$ExpireAfter' is not supported\n"
  387 +fail($CE, "ExpireAfter '$ExpireAfter' is not supported")
374 388 unless ($ExpireAfter=~/^\d+[smhw]?$/);
375   -die "$CE UpdateTXT must be set when \$ExpireAfter is set\n"
  389 +fail($CE, "UpdateTXT must be set when \$ExpireAfter is set")
376 390 if($ExpireAfter and not $UpdateTXT);
377 391 foreach my $rrtype (@ReplaceRR) {
378   - die "$CE ReplaceRR contains unsupported type '$rrtype'\n"
  392 + fail($CE, "ReplaceRR contains unsupported type '$rrtype'")
379 393 unless exists $DNS_label{$rrtype};
380 394 }
381 395  
... ... @@ -385,7 +399,6 @@ foreach my $rrtype (@ReplaceRR) {
385 399 my $mode = $cgi->path_info || $cgi->param('mode') || 'view';
386 400 $mode=~s/^\/([^\/]+)(\/(.*))?/$1/;
387 401 my $host = $cgi->param('host') || $3;
388   -my $debug = ($AllowDebugKey eq 'off') ? 0 : ($AllowDebugKey eq ($cgi->param('debug') || ''));
389 402  
390 403  
391 404 ##############################
... ... @@ -398,16 +411,13 @@ my %handlers = (
398 411 expire => \&handle_expire,
399 412 );
400 413 if($host eq '' and $mode cmp 'list' and $mode cmp 'expire') {
401   - print $cgi->header(-status=>400, -type=>'text/plain'),
402   - "ERROR - No host name to act on specified\n";
  414 + fail($PE, "No host name to act on specified", 400);
403 415 } elsif(my $handler = $handlers{$mode}) {
404 416 # Replace provided host with that of a CNAME it points to and determine domain
405 417 my $dnshost = ($host) ? expand_CNAME($host) : undef;
406 418 my $dnsdomain = $cgi->param('domain') || ($dnshost=~/\.(.*)$/)[0];
407   - $handler->($cgi, $mode, $host, $dnshost, $dnsdomain, $debug);
  419 + $handler->($mode, $host, $dnshost, $dnsdomain, $debug);
408 420 } else {
409   - print $cgi->header(-status=>($cgi->path_info) ? 404 : 400,
410   - -type=>'text/plain'),
411   - "ERROR - File Not Found / Invalid Mode '$mode' specified\n";
  421 + fail("File Not Found", "Invalid Mode '$mode' specified", 404);
412 422 }
413 423  
... ...