]>
Commit | Line | Data |
---|---|---|
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; | |
8 | use strict; | |
9 | use Getopt::Long; | |
10 | use Pod::Usage; | |
11 | use Crypt::OpenSSL::X509; | |
12 | use FileHandle; | |
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 | |
39 | ||
40 | =head1 DESCRIPTION | |
41 | ||
42 | Retrieves the SSL certificate error list from squid and echo back without any change. | |
43 | ||
44 | =head1 COPYRIGHT | |
45 | ||
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 | ||
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); | |
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; | |
77 | my $haserror = 0; | |
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 { | |
87 | my $readlen = length($body); | |
88 | my %certs = (); | |
89 | my %errors = (); | |
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 | ||
100 | print(STDERR logPrefix()."GOT ". "Code=".$code." $bodylen \n") if ($debug); #.$body; | |
101 | my $hostname; | |
102 | parseRequest($body, \$hostname, \%errors, \%certs); | |
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); | |
106 | foreach my $err (keys %errors) { | |
107 | print(STDERR logPrefix().$errors{$err}{"name"}."/".$errors{$err}{"cert"}." ,") if ($debug); | |
108 | } | |
109 | print(STDERR "\n") if ($debug); | |
110 | foreach my $key (keys %certs) { | |
111 | ## Use "perldoc Crypt::OpenSSL::X509" for X509 available methods. | |
112 | print(STDERR logPrefix()."\tFOUND cert ".$key.": ".$certs{$key}->subject() . "\n") if ($debug); | |
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. | |
119 | foreach my $err (keys %errors) { | |
120 | $haserror = 1; | |
121 | appendError (\@responseErrors, | |
122 | $errors{$err}{"name"}, #The error name | |
123 | "Checked by Cert Validator", # An error reason | |
124 | $errors{$err}{"cert"} # The cert ID. We are always filling with the peer certificate. | |
125 | ); | |
126 | } | |
127 | ||
128 | $response = createResponse(\@responseErrors); | |
129 | my $len = length($response); | |
130 | if ($haserror) { | |
131 | $response = $channelId." ERR ".$len." ".$response."\1"; | |
132 | } else { | |
133 | $response = $channelId." OK ".$len." ".$response."\1"; | |
134 | } | |
135 | } | |
136 | ||
137 | print $response; | |
138 | print(STDERR logPrefix().">> ".$response."\n") if ($debug); | |
139 | } | |
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; | |
163 | foreach my $err (@$responseErrors) { | |
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; | |
184 | $request =~ s/^host=.*$//m; | |
185 | } | |
186 | if ($request =~ /^cert_(\d+)=/) { | |
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 | } | |
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 | } | |
205 | else { | |
206 | print(STDERR logPrefix()."ParseError on \"".$request."\"\n") if ($debug); | |
207 | $request = "";# finish processing.... | |
208 | } | |
209 | } | |
210 | } | |
211 | ||
212 | ||
213 | sub logPrefix | |
214 | { | |
215 | return strftime("%Y/%m/%d %H:%M:%S.0", localtime)." ".$0." ".$$." | " ; | |
216 | } |