]>
Commit | Line | Data |
---|---|---|
a1f04d64 AR |
1 | #!/usr/bin/perl -w |
2 | # | |
3 | # A dummy SSL certificate validator helper that | |
4 | # echos back all the SSL errors sent by Squid. | |
5 | # | |
6 | ||
7 | use warnings; | |
4a77bb4e CT |
8 | use strict; |
9 | use Getopt::Long; | |
10 | use Pod::Usage; | |
a1f04d64 AR |
11 | use Crypt::OpenSSL::X509; |
12 | use FileHandle; | |
4a77bb4e CT |
13 | use POSIX qw(strftime); |
14 | ||
15 | my $debug = 0; | |
16 | my $help = 0; | |
17 | ||
18 | =pod | |
19 | ||
20 | =head1 NAME | |
21 | ||
22 | cert_valid.pl - A fake cert validation helper for Squid | |
23 | ||
24 | =head1 SYNOPSIS | |
25 | ||
26 | cert_valid.pl [-d | --debug] [-h | --help] | |
27 | ||
28 | =over 8 | |
29 | ||
30 | =item B<-h | --help> | |
31 | ||
32 | brief help message | |
33 | ||
34 | =item B<-d | --debug> | |
35 | ||
36 | enable debug messages to stderr | |
37 | ||
38 | =back | |
a1f04d64 | 39 | |
4a77bb4e | 40 | =head1 DESCRIPTION |
a1f04d64 | 41 | |
4a77bb4e CT |
42 | Retrieves the SSL certificate error list from squid and echo back without any change. |
43 | ||
44 | =head1 COPYRIGHT | |
45 | ||
ca02e0ec AJ |
46 | * Copyright (C) 1996-2014 The Squid Software Foundation and contributors |
47 | * | |
48 | * Squid software is distributed under GPLv2+ license and includes | |
49 | * contributions from numerous individuals and organizations. | |
50 | * Please see the COPYING and CONTRIBUTORS files for details. | |
51 | ||
4a77bb4e CT |
52 | (C) 2012 The Measurement Factory, Author: Tsantilas Christos |
53 | ||
54 | This program is free software. You may redistribute copies of it under the | |
55 | terms of the GNU General Public License version 2, or (at your opinion) any | |
56 | later version. | |
57 | ||
58 | =cut | |
59 | ||
60 | GetOptions( | |
61 | 'help' => \$help, | |
62 | 'debug' => \$debug, | |
63 | ) or pod2usage(1); | |
64 | ||
65 | pod2usage(1) if ($help); | |
a1f04d64 AR |
66 | |
67 | $|=1; | |
68 | while (<>) { | |
69 | my $first_line = $_; | |
70 | my @line_args = split; | |
71 | ||
72 | if ($first_line =~ /^\s*$/) { | |
73 | next; | |
74 | } | |
75 | ||
76 | my $response; | |
dd7167ea | 77 | my $haserror = 0; |
4a77bb4e CT |
78 | my $channelId = $line_args[0]; |
79 | my $code = $line_args[1]; | |
80 | my $bodylen = $line_args[2]; | |
81 | my $body = $line_args[3] . "\n"; | |
82 | if ($channelId !~ /\d+/) { | |
83 | $response = $channelId." BH message=\"This helper is concurrent and requires the concurrency option to be specified.\"\1"; | |
84 | } elsif ($bodylen !~ /\d+/) { | |
85 | $response = $channelId." BH message=\"cert validator request syntax error \" \1"; | |
86 | } else { | |
a1f04d64 AR |
87 | my $readlen = length($body); |
88 | my %certs = (); | |
62a7607e | 89 | my %errors = (); |
a1f04d64 AR |
90 | my @responseErrors = (); |
91 | ||
92 | while($readlen < $bodylen) { | |
93 | my $t = <>; | |
94 | if (defined $t) { | |
95 | $body = $body . $t; | |
96 | $readlen = length($body); | |
97 | } | |
98 | } | |
99 | ||
4a77bb4e CT |
100 | print(STDERR logPrefix()."GOT ". "Code=".$code." $bodylen \n") if ($debug); #.$body; |
101 | my $hostname; | |
62a7607e | 102 | parseRequest($body, \$hostname, \%errors, \%certs); |
4a77bb4e CT |
103 | print(STDERR logPrefix()."Parse result: \n") if ($debug); |
104 | print(STDERR logPrefix()."\tFOUND host:".$hostname."\n") if ($debug); | |
105 | print(STDERR logPrefix()."\tFOUND ERRORS:") if ($debug); | |
62a7607e CT |
106 | foreach my $err (keys %errors) { |
107 | print(STDERR logPrefix().$errors{$err}{"name"}."/".$errors{$err}{"cert"}." ,") if ($debug); | |
a1f04d64 | 108 | } |
4a77bb4e CT |
109 | print(STDERR "\n") if ($debug); |
110 | foreach my $key (keys %certs) { | |
a1f04d64 | 111 | ## Use "perldoc Crypt::OpenSSL::X509" for X509 available methods. |
4a77bb4e | 112 | print(STDERR logPrefix()."\tFOUND cert ".$key.": ".$certs{$key}->subject() . "\n") if ($debug); |
a1f04d64 AR |
113 | } |
114 | ||
115 | #got the peer certificate ID. Assume that the peer certificate is the first one. | |
116 | my $peerCertId = (keys %certs)[0]; | |
117 | ||
118 | # Echo back the errors: fill the responseErrors array with the errors we read. | |
62a7607e | 119 | foreach my $err (keys %errors) { |
dd7167ea | 120 | $haserror = 1; |
a1f04d64 | 121 | appendError (\@responseErrors, |
62a7607e | 122 | $errors{$err}{"name"}, #The error name |
a1f04d64 | 123 | "Checked by Cert Validator", # An error reason |
62a7607e | 124 | $errors{$err}{"cert"} # The cert ID. We are always filling with the peer certificate. |
a1f04d64 AR |
125 | ); |
126 | } | |
127 | ||
128 | $response = createResponse(\@responseErrors); | |
129 | my $len = length($response); | |
dd7167ea | 130 | if ($haserror) { |
4a77bb4e | 131 | $response = $channelId." ERR ".$len." ".$response."\1"; |
dd7167ea | 132 | } else { |
4a77bb4e | 133 | $response = $channelId." OK ".$len." ".$response."\1"; |
dd7167ea | 134 | } |
a1f04d64 AR |
135 | } |
136 | ||
137 | print $response; | |
4a77bb4e | 138 | print(STDERR logPrefix().">> ".$response."\n") if ($debug); |
a1f04d64 | 139 | } |
a1f04d64 AR |
140 | |
141 | sub trim | |
142 | { | |
143 | my $s = shift; | |
144 | $s =~ s/^\s+//; | |
145 | $s =~ s/\s+$//; | |
146 | return $s; | |
147 | } | |
148 | ||
149 | sub appendError | |
150 | { | |
151 | my ($errorArrays) = shift; | |
152 | my($errorName) = shift; | |
153 | my($errorReason) = shift; | |
154 | my($errorCert) = shift; | |
155 | push @$errorArrays, { "error_name" => $errorName, "error_reason" => $errorReason, "error_cert" => $errorCert}; | |
156 | } | |
157 | ||
158 | sub createResponse | |
159 | { | |
160 | my ($responseErrors) = shift; | |
161 | my $response=""; | |
162 | my $i = 0; | |
4a77bb4e | 163 | foreach my $err (@$responseErrors) { |
a1f04d64 AR |
164 | $response=$response."error_name_".$i."=".$err->{"error_name"}."\n". |
165 | "error_reason_".$i."=".$err->{"error_reason"}."\n". | |
166 | "error_cert_".$i."=".$err->{"error_cert"}."\n"; | |
167 | $i++; | |
168 | } | |
169 | return $response; | |
170 | } | |
171 | ||
172 | sub parseRequest | |
173 | { | |
174 | my($request)=shift; | |
175 | my $hostname = shift; | |
176 | my $errors = shift; | |
177 | my $certs = shift; | |
178 | while ($request !~ /^\s*$/) { | |
179 | $request = trim($request); | |
180 | if ($request =~ /^host=/) { | |
181 | my($vallen) = index($request, "\n"); | |
182 | my $host = substr($request, 5, $vallen - 5); | |
183 | $$hostname = $host; | |
721fabb6 | 184 | $request =~ s/^host=.*$//m; |
a1f04d64 | 185 | } |
62a7607e | 186 | if ($request =~ /^cert_(\d+)=/) { |
a1f04d64 AR |
187 | my $certId = "cert_".$1; |
188 | my($vallen) = index($request, "-----END CERTIFICATE-----") + length("-----END CERTIFICATE-----"); | |
189 | my $x509 = Crypt::OpenSSL::X509->new_from_string(substr($request, index($request, "-----BEGIN"))); | |
190 | $certs->{$certId} = $x509; | |
191 | $request = substr($request, $vallen); | |
192 | } | |
62a7607e CT |
193 | elsif ($request =~ /^error_name_(\d+)=(.*)$/m) { |
194 | my $errorId = $1; | |
195 | my $errorName = $2; | |
196 | $request =~ s/^error_name_\d+=.*$//m; | |
197 | $errors->{$errorId}{"name"} = $errorName; | |
198 | } | |
199 | elsif ($request =~ /^error_cert_(\d+)=(.*)$/m) { | |
200 | my $errorId = $1; | |
201 | my $certId = $2; | |
202 | $request =~ s/^error_cert_\d+=.*$//m; | |
203 | $errors->{$errorId}{"cert"} = $certId; | |
204 | } | |
a1f04d64 | 205 | else { |
4a77bb4e | 206 | print(STDERR logPrefix()."ParseError on \"".$request."\"\n") if ($debug); |
a1f04d64 AR |
207 | $request = "";# finish processing.... |
208 | } | |
209 | } | |
210 | } | |
4a77bb4e CT |
211 | |
212 | ||
213 | sub logPrefix | |
214 | { | |
215 | return strftime("%Y/%m/%d %H:%M:%S.0", localtime)." ".$0." ".$$." | " ; | |
216 | } |