]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/ssl/cert_valid.pl
SourceFormat Enforcement
[thirdparty/squid.git] / helpers / ssl / cert_valid.pl
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-2015 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 my $sslVersion = "-";
103 my $sslCipher = "-";
104 parseRequest($body, \$hostname, \$sslVersion, \$sslCipher, \%errors, \%certs);
105 print(STDERR logPrefix()."Parse result: \n") if ($debug);
106 print(STDERR logPrefix()."\tFOUND host:".$hostname."\n") if ($debug);
107 print(STDERR logPrefix()."\tFOUND ssl version:".$sslVersion."\n") if ($debug);
108 print(STDERR logPrefix()."\tFOUND ssl cipher:".$sslCipher."\n") if ($debug);
109 print(STDERR logPrefix()."\tFOUND ERRORS:") if ($debug);
110 foreach my $err (keys %errors) {
111 print(STDERR logPrefix().$errors{$err}{"name"}."/".$errors{$err}{"cert"}." ,") if ($debug);
112 }
113 print(STDERR "\n") if ($debug);
114 foreach my $key (keys %certs) {
115 ## Use "perldoc Crypt::OpenSSL::X509" for X509 available methods.
116 print(STDERR logPrefix()."\tFOUND cert ".$key.": ".$certs{$key}->subject() . "\n") if ($debug);
117 }
118
119 #got the peer certificate ID. Assume that the peer certificate is the first one.
120 my $peerCertId = (keys %certs)[0];
121
122 # Echo back the errors: fill the responseErrors array with the errors we read.
123 foreach my $err (keys %errors) {
124 $haserror = 1;
125 appendError (\@responseErrors,
126 $errors{$err}{"name"}, #The error name
127 "Checked by Cert Validator", # An error reason
128 $errors{$err}{"cert"} # The cert ID. We are always filling with the peer certificate.
129 );
130 }
131
132 $response = createResponse(\@responseErrors);
133 my $len = length($response);
134 if ($haserror) {
135 $response = $channelId." ERR ".$len." ".$response."\1";
136 } else {
137 $response = $channelId." OK ".$len." ".$response."\1";
138 }
139 }
140
141 print $response;
142 print(STDERR logPrefix().">> ".$response."\n") if ($debug);
143 }
144
145 sub trim
146 {
147 my $s = shift;
148 $s =~ s/^\s+//;
149 $s =~ s/\s+$//;
150 return $s;
151 }
152
153 sub appendError
154 {
155 my ($errorArrays) = shift;
156 my($errorName) = shift;
157 my($errorReason) = shift;
158 my($errorCert) = shift;
159 push @$errorArrays, { "error_name" => $errorName, "error_reason" => $errorReason, "error_cert" => $errorCert};
160 }
161
162 sub createResponse
163 {
164 my ($responseErrors) = shift;
165 my $response="";
166 my $i = 0;
167 foreach my $err (@$responseErrors) {
168 $response=$response."error_name_".$i."=".$err->{"error_name"}."\n".
169 "error_reason_".$i."=".$err->{"error_reason"}."\n".
170 "error_cert_".$i."=".$err->{"error_cert"}."\n";
171 $i++;
172 }
173 return $response;
174 }
175
176 sub parseRequest
177 {
178 my($request)=shift;
179 my $hostname = shift;
180 my $sslVersion = shift;
181 my $sslCipher = shift;
182 my $errors = shift;
183 my $certs = shift;
184 while ($request !~ /^\s*$/) {
185 $request = trim($request);
186 if ($request =~ /^host=/) {
187 my($vallen) = index($request, "\n");
188 my $host = substr($request, 5, $vallen - 5);
189 $$hostname = $host;
190 $request =~ s/^host=.*$//m;
191 }
192 if ($request =~ s/^proto_version=(.*?)$//m) {
193 $$sslVersion = $1;
194 }
195 if ($request =~ s/^cipher=(.*?)$//m) {
196 $$sslCipher = $1;
197 }
198 if ($request =~ /^cert_(\d+)=/) {
199 my $certId = "cert_".$1;
200 my($vallen) = index($request, "-----END CERTIFICATE-----") + length("-----END CERTIFICATE-----");
201 my $x509 = Crypt::OpenSSL::X509->new_from_string(substr($request, index($request, "-----BEGIN")));
202 $certs->{$certId} = $x509;
203 $request = substr($request, $vallen);
204 }
205 elsif ($request =~ /^error_name_(\d+)=(.*)$/m) {
206 my $errorId = $1;
207 my $errorName = $2;
208 $request =~ s/^error_name_\d+=.*$//m;
209 $errors->{$errorId}{"name"} = $errorName;
210 }
211 elsif ($request =~ /^error_cert_(\d+)=(.*)$/m) {
212 my $errorId = $1;
213 my $certId = $2;
214 $request =~ s/^error_cert_\d+=.*$//m;
215 $errors->{$errorId}{"cert"} = $certId;
216 }
217 else {
218 print(STDERR logPrefix()."ParseError on \"".$request."\"\n") if ($debug);
219 $request = "";# finish processing....
220 }
221 }
222 }
223
224
225 sub logPrefix
226 {
227 return strftime("%Y/%m/%d %H:%M:%S.0", localtime)." ".$0." ".$$." | " ;
228 }