From 7307fb20e430ef896131f0fd6bfe2ae2371e1008 Mon Sep 17 00:00:00 2001 From: joe Date: Wed, 14 May 2014 23:04:50 -0400 Subject: validatecert.hs demonstrating squid's SslServerCertValidator feature. --- cert_valid.pl | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100755 cert_valid.pl (limited to 'cert_valid.pl') diff --git a/cert_valid.pl b/cert_valid.pl new file mode 100755 index 0000000..41a1984 --- /dev/null +++ b/cert_valid.pl @@ -0,0 +1,210 @@ +#!/usr/bin/perl -w +# +# A dummy SSL certificate validator helper that +# echos back all the SSL errors sent by Squid. +# + +use warnings; +use strict; +use Getopt::Long; +use Pod::Usage; +use Crypt::OpenSSL::X509; +use FileHandle; +use POSIX qw(strftime); + +my $debug = 0; +my $help = 0; + +=pod + +=head1 NAME + +cert_valid.pl - A fake cert validation helper for Squid + +=head1 SYNOPSIS + +cert_valid.pl [-d | --debug] [-h | --help] + +=over 8 + +=item B<-h | --help> + +brief help message + +=item B<-d | --debug> + +enable debug messages to stderr + +=back + +=head1 DESCRIPTION + +Retrieves the SSL certificate error list from squid and echo back without any change. + +=head1 COPYRIGHT + +(C) 2012 The Measurement Factory, Author: Tsantilas Christos + +This program is free software. You may redistribute copies of it under the +terms of the GNU General Public License version 2, or (at your opinion) any +later version. + +=cut + +GetOptions( + 'help' => \$help, + 'debug' => \$debug, + ) or pod2usage(1); + +pod2usage(1) if ($help); + +$|=1; +while (<>) { + my $first_line = $_; + my @line_args = split; + + if ($first_line =~ /^\s*$/) { + next; + } + + my $response; + my $haserror = 0; + my $channelId = $line_args[0]; + my $code = $line_args[1]; + my $bodylen = $line_args[2]; + my $body = $line_args[3] . "\n"; + if ($channelId !~ /\d+/) { + $response = $channelId." BH message=\"This helper is concurrent and requires the concurrency option to be specified.\"\1"; + } elsif ($bodylen !~ /\d+/) { + $response = $channelId." BH message=\"cert validator request syntax error \" \1"; + } else { + my $readlen = length($body); + my %certs = (); + my %errors = (); + my @responseErrors = (); + + while($readlen < $bodylen) { + my $t = <>; + if (defined $t) { + $body = $body . $t; + $readlen = length($body); + } + } + + print(STDERR logPrefix()."GOT ". "Code=".$code." $bodylen \n") if ($debug); #.$body; + my $hostname; + parseRequest($body, \$hostname, \%errors, \%certs); + print(STDERR logPrefix()."Parse result: \n") if ($debug); + print(STDERR logPrefix()."\tFOUND host:".$hostname."\n") if ($debug); + print(STDERR logPrefix()."\tFOUND ERRORS:") if ($debug); + foreach my $err (keys %errors) { + print(STDERR logPrefix().$errors{$err}{"name"}."/".$errors{$err}{"cert"}." ,") if ($debug); + } + print(STDERR "\n") if ($debug); + foreach my $key (keys %certs) { + ## Use "perldoc Crypt::OpenSSL::X509" for X509 available methods. + print(STDERR logPrefix()."\tFOUND cert ".$key.": ".$certs{$key}->subject() . "\n") if ($debug); + } + + #got the peer certificate ID. Assume that the peer certificate is the first one. + my $peerCertId = (keys %certs)[0]; + + # Echo back the errors: fill the responseErrors array with the errors we read. + foreach my $err (keys %errors) { + $haserror = 1; + appendError (\@responseErrors, + $errors{$err}{"name"}, #The error name + "Checked by Cert Validator", # An error reason + $errors{$err}{"cert"} # The cert ID. We are always filling with the peer certificate. + ); + } + + $response = createResponse(\@responseErrors); + my $len = length($response); + if ($haserror) { + $response = $channelId." ERR ".$len." ".$response."\1"; + } else { + $response = $channelId." OK ".$len." ".$response."\1"; + } + } + + print $response; + print(STDERR logPrefix().">> ".$response."\n") if ($debug); +} + +sub trim +{ + my $s = shift; + $s =~ s/^\s+//; + $s =~ s/\s+$//; + return $s; +} + +sub appendError +{ + my ($errorArrays) = shift; + my($errorName) = shift; + my($errorReason) = shift; + my($errorCert) = shift; + push @$errorArrays, { "error_name" => $errorName, "error_reason" => $errorReason, "error_cert" => $errorCert}; +} + +sub createResponse +{ + my ($responseErrors) = shift; + my $response=""; + my $i = 0; + foreach my $err (@$responseErrors) { + $response=$response."error_name_".$i."=".$err->{"error_name"}."\n". + "error_reason_".$i."=".$err->{"error_reason"}."\n". + "error_cert_".$i."=".$err->{"error_cert"}."\n"; + $i++; + } + return $response; +} + +sub parseRequest +{ + my($request)=shift; + my $hostname = shift; + my $errors = shift; + my $certs = shift; + while ($request !~ /^\s*$/) { + $request = trim($request); + if ($request =~ /^host=/) { + my($vallen) = index($request, "\n"); + my $host = substr($request, 5, $vallen - 5); + $$hostname = $host; + $request =~ s/^host=.*$//m; + } + if ($request =~ /^cert_(\d+)=/) { + my $certId = "cert_".$1; + my($vallen) = index($request, "-----END CERTIFICATE-----") + length("-----END CERTIFICATE-----"); + my $x509 = Crypt::OpenSSL::X509->new_from_string(substr($request, index($request, "-----BEGIN"))); + $certs->{$certId} = $x509; + $request = substr($request, $vallen); + } + elsif ($request =~ /^error_name_(\d+)=(.*)$/m) { + my $errorId = $1; + my $errorName = $2; + $request =~ s/^error_name_\d+=.*$//m; + $errors->{$errorId}{"name"} = $errorName; + } + elsif ($request =~ /^error_cert_(\d+)=(.*)$/m) { + my $errorId = $1; + my $certId = $2; + $request =~ s/^error_cert_\d+=.*$//m; + $errors->{$errorId}{"cert"} = $certId; + } + else { + print(STDERR logPrefix()."ParseError on \"".$request."\"\n") if ($debug); + $request = "";# finish processing.... + } + } +} + + +sub logPrefix +{ + return strftime("%Y/%m/%d %H:%M:%S.0", localtime)." ".$0." ".$$." | " ; +} -- cgit v1.2.3