]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/ssl/cert_valid.pl
Boilerplate: update copyright blurbs on Squid helpers
[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-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 }