From: Alex Rousskov Date: Fri, 14 Sep 2012 22:17:01 +0000 (-0600) Subject: Added filed forgotten during initial import. X-Git-Tag: SQUID_3_4_0_1~458^2~10^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a1f04d6450514588554daa3cbca74c90710220e6;p=thirdparty%2Fsquid.git Added filed forgotten during initial import. --- diff --git a/helpers/ssl/Makefile.am b/helpers/ssl/Makefile.am new file mode 100644 index 0000000000..eb2aa45d94 --- /dev/null +++ b/helpers/ssl/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/src/Common.am + +libexec_SCRIPTS = cert_valid.pl +EXTRA_DIST= \ + cert_valid.pl diff --git a/helpers/ssl/cert_valid.pl b/helpers/ssl/cert_valid.pl new file mode 100644 index 0000000000..00ba684e92 --- /dev/null +++ b/helpers/ssl/cert_valid.pl @@ -0,0 +1,147 @@ +#!/usr/bin/perl -w +# +# A dummy SSL certificate validator helper that +# echos back all the SSL errors sent by Squid. +# + +use warnings; +# TODO: +# use strict; + +use Crypt::OpenSSL::X509; +use FileHandle; + +my $LOGFILE = "/tmp/ssl_cert_valid.log"; + +open(LOG, ">>$LOGFILE") or die("Cannot open logfile $LOGFILE, stopped"); +LOG->autoflush(1); + +$|=1; +while (<>) { + my $first_line = $_; + my @line_args = split; + + if ($first_line =~ /^\s*$/) { + next; + } + + my $response; + my $code = $line_args[0]; + my $bodylen = $line_args[1]; + my $body = $line_args[2] . "\n"; + if ($bodylen =~ /\d+/) { + 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 LOG "GOT ". "Code=".$code." $bodylen \n"; #.$body; + parseRequest($body, \$hostname, \@errors, \%certs); + print LOG " Parse result: \n"; + print LOG "\tFOUND host:".$hostname."\n"; + print LOG "\tFOUND ERRORS:"; + foreach $err(@errors) { + print LOG "$err ,"; + } + print LOG "\n"; + foreach $key (keys %certs) { + ## Use "perldoc Crypt::OpenSSL::X509" for X509 available methods. + print LOG "\tFOUND cert ".$key.": ".$certs{$key}->subject() . "\n"; + } + + #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 $err (@errors) { + appendError (\@responseErrors, + $err, #The error name + "Checked by Cert Validator", # An error reason + $peerCertId # The cert ID. We are always filling with the peer certificate. + ); + } + + $response = createResponse(\@responseErrors); + my $len = length($response); + $response = "OK ".$len." ".$response."\1"; + } else { + $response = "ERROR 0 \1"; + } + + print $response; + print LOG ">> ".$response; +} +close(LOG); + +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 $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=.*\n//; + } + if ($request =~ /^errors=/) { + my($vallen) = index($request, "\n"); + my $listerrors = substr($request, 7, $vallen - 7); + @$errors = split /,/, $listerrors; + $request =~ s/^errors=.*\n//; + } + elsif ($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); + } + else { + print LOG "ParseError on \"".$request."\"\n"; + $request = "";# finish processing.... + } + } +} diff --git a/src/ssl/cert_validate_message.cc b/src/ssl/cert_validate_message.cc new file mode 100644 index 0000000000..b33645ce6d --- /dev/null +++ b/src/ssl/cert_validate_message.cc @@ -0,0 +1,216 @@ +#include "squid.h" +#include "ssl/support.h" +#include "ssl/cert_validate_message.h" +#include "ssl/ErrorDetail.h" + + +void Ssl::CertValidateMessage::composeRequest(ValidateCertificate const &vcert) +{ + body.clear(); + body += Ssl::CertValidateMessage::param_host + "=" + vcert.domainName; + if (vcert.errors) { + body += "\n" + Ssl::CertValidateMessage::param_error + "="; + bool comma = false; + for (const Ssl::Errors *err = vcert.errors; err; err = err->next ) { + if (comma) + body += ","; + body += GetErrorName(err->element); + comma = true; + } + } + + if (vcert.peerCerts) { + body +="\n"; + Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem())); + for (int i = 0; i < sk_X509_num(vcert.peerCerts); ++i) { + X509 *cert = sk_X509_value(vcert.peerCerts, i); + PEM_write_bio_X509(bio.get(), cert); + body = body + "cert_" + xitoa(i) + "="; + char *ptr; + long len = BIO_get_mem_data(bio.get(), &ptr); + body.append(ptr, len); + // Normally openssl toolkit terminates Certificate with a '\n'. + if (ptr[len-1] != '\n') + body +="\n"; + if (!BIO_reset(bio.get())) { + // print an error? + } + } + } +} + +static int get_error_id(const char *label, size_t len) +{ + const char *e = label + len -1; + while (e != label && xisdigit(*e)) --e; + if (e != label) ++e; + return strtol(e, 0 , 10); +} + +bool Ssl::CertValidateMessage::parseResponse(ValidateCertificateResponse &resp, std::string &error) +{ + int current_errorId = -1; + std::vector certs; + + Ssl::ValidateCertificateResponse::ErrorItem currentItem; + + const char *param = body.c_str(); + while(*param) { + while(xisspace(*param)) param++; + if (! *param) + break; + + size_t param_len = strcspn(param, "=\r\n"); + if (param[param_len] != '=') { + debugs(83, 2, "Error parsing: " << param); + return false; + } + const char *value=param+param_len+1; + + if (param_len > param_cert.length() && + strncmp(param, param_cert.c_str(), param_cert.length()) == 0) { + Ssl::ValidateCertificateResponse::CertItem ci; + ci.name.assign(param, param_len); + X509_Pointer x509; + readCertFromMemory(x509, value); + ci.setCert(x509.get()); + certs.push_back(ci); + + const char *b = strstr(value, "-----END CERTIFICATE-----"); + if (b == NULL) { + debugs(83, 2, "Parse error: Failed to find certificate boundary " << value); + return false; + } + b += strlen("-----END CERTIFICATE-----"); + param = b + 1; + continue; + } + + size_t value_len = strcspn(value, "\r\n"); + std::string v(value, value_len); + + debugs(83, 5, "Returned value: " << std::string(param, param_len).c_str() << ": " << + v.c_str()); + + int errorId = get_error_id(param, param_len); + + if (current_errorId >=0 && errorId != current_errorId) { + resp.errors.push_back(currentItem); + /*Reset current item:*/ + currentItem.clear(); + } + current_errorId = errorId; + + if (param_len > param_error_name.length() && + strncmp(param, param_error_name.c_str(), param_error_name.length()) == 0){ + currentItem.error_no = Ssl::GetErrorCode(v.c_str()); + } else if (param_len > param_error_reason.length() && + strncmp(param, param_error_reason.c_str(), param_error_reason.length()) == 0) { + currentItem.error_reason = v; + } else if (param_len > param_error_cert.length() && + strncmp(param, param_error_cert.c_str(), param_error_cert.length()) == 0) { + + for (std::vector::const_iterator ci = certs.begin(); ci != certs.end(); ++ci) { + if (ci->name.compare(v) == 0) { + currentItem.setCert(ci->cert); + debugs(83, 6, HERE << "The custom cert \"" << ci->name << "\" used."); + break; + } + } + if (!currentItem.cert) { + currentItem.certId = get_error_id(v.c_str(), v.length()); + debugs(83, 6, HERE << "Cert ID read:" << currentItem.certId); + } + } + + param = value + value_len +1; + } + + if (currentItem.error_no != SSL_ERROR_NONE) + resp.errors.push_back(currentItem); + + return true; +} + +Ssl::ValidateCertificateResponse::ErrorItem::ErrorItem(const ErrorItem &old) { + error_no = old.error_no; + error_reason = old.error_reason; + certId = old.certId; + cert = NULL; + setCert(old.cert); +} + +Ssl::ValidateCertificateResponse::ErrorItem::~ErrorItem() { + if (cert) + X509_free(cert); +} + +Ssl::ValidateCertificateResponse::ErrorItem & Ssl::ValidateCertificateResponse::ErrorItem::operator = (const ErrorItem &old) { + error_no = old.error_no; + error_reason = old.error_reason; + certId = old.certId; + setCert(old.cert); + return *this; +} + +void +Ssl::ValidateCertificateResponse::ErrorItem::setCert(X509 *aCert) { + if (cert) + X509_free(cert); + if (aCert) { + cert = aCert; + CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); + } else + cert = NULL; +} + +void +Ssl::ValidateCertificateResponse::ErrorItem::clear() { + error_no = SSL_ERROR_NONE; + error_reason = ""; + certId = 0; + if (cert) + X509_free(cert); + cert = NULL; +} + +Ssl::ValidateCertificateResponse::CertItem::CertItem(const CertItem &old) +{ + name = old.name; + cert = NULL; + setCert(old.cert); +} + +Ssl::ValidateCertificateResponse::CertItem & Ssl::ValidateCertificateResponse::CertItem::operator = (const CertItem &old) +{ + name = old.name; + setCert(old.cert); + return *this; +} + +Ssl::ValidateCertificateResponse::CertItem::~CertItem() +{ + if (cert) + X509_free(cert); +} + +void +Ssl::ValidateCertificateResponse::CertItem::setCert(X509 *aCert) +{ + if (cert) + X509_free(cert); + if (aCert) { + cert = aCert; + CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); + } else + cert = NULL; +} + +const std::string Ssl::CertValidateMessage::code_cert_validate("cert_validate"); +const std::string Ssl::CertValidateMessage::param_domain("domain"); +const std::string Ssl::CertValidateMessage::param_error("errors"); +const std::string Ssl::CertValidateMessage::param_cert("cert_"); +const std::string Ssl::CertValidateMessage::param_error_name("error_name_"); +const std::string Ssl::CertValidateMessage::param_error_reason("error_reason_"); +const std::string Ssl::CertValidateMessage::param_error_cert("error_cert_"); + diff --git a/src/ssl/cert_validate_message.h b/src/ssl/cert_validate_message.h new file mode 100644 index 0000000000..1b0af4e1dd --- /dev/null +++ b/src/ssl/cert_validate_message.h @@ -0,0 +1,79 @@ +/* + * $Id$ + */ + +#ifndef SQUID_SSL_CERT_VALIDATE_MESSAGE_H +#define SQUID_SSL_CERT_VALIDATE_MESSAGE_H + +#include "ssl/support.h" +#include "ssl/crtd_message.h" +#include + +namespace Ssl +{ + + +class ValidateCertificate { +public: + STACK_OF(X509) *peerCerts; + Errors *errors; + std::string domainName; + ValidateCertificate() : peerCerts(NULL), errors(NULL) {} +}; + +class ValidateCertificateResponse { +public: + class ErrorItem{ + public: + ErrorItem(): error_no(SSL_ERROR_NONE), certId(0), cert(NULL) {} + ErrorItem(const ErrorItem &); + ~ErrorItem(); + ErrorItem & operator = (const ErrorItem &); + void setCert(X509 *); + void clear(); + ssl_error_t error_no; + std::string error_reason; + int certId; + X509 *cert; + }; + + class CertItem { + public: + std::string name; + X509 *cert; + CertItem(): cert(NULL) {} + CertItem(const CertItem &); + CertItem & operator = (const CertItem &); + ~CertItem(); + void setCert(X509 *); + }; + + std::vector errors; + ValidateCertificateResponse() {} + ~ValidateCertificateResponse() {/*Maybe needs to release Errors*/}; +}; + +class CertValidateMessage: public CrtdMessage { +public: + CertValidateMessage(): CrtdMessage() {} + void composeRequest(ValidateCertificate const &vcert); + bool parseResponse(ValidateCertificateResponse &resp, std::string &error); + + /// String code for "cert_validate" messages + static const std::string code_cert_validate; + /// Parameter name for passing intended domain name + static const std::string param_domain; + /// Parameter name for passing SSL errors + static const std::string param_error; + /// Parameter name for passing SSL certificates + static const std::string param_cert; + /// Parameter name for passing the major SSL error + static const std::string param_error_name; + /// Parameter name for passing the error reason + static const std::string param_error_reason; + /// Parameter name for passing the error cert ID + static const std::string param_error_cert; +}; + +}//namespace Ssl +#endif // SQUID_SSL_CERT_VALIDATE_MESSAGE_H