]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Added filed forgotten during initial import.
authorAlex Rousskov <rousskov@measurement-factory.com>
Fri, 14 Sep 2012 22:17:01 +0000 (16:17 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Fri, 14 Sep 2012 22:17:01 +0000 (16:17 -0600)
helpers/ssl/Makefile.am [new file with mode: 0644]
helpers/ssl/cert_valid.pl [new file with mode: 0644]
src/ssl/cert_validate_message.cc [new file with mode: 0644]
src/ssl/cert_validate_message.h [new file with mode: 0644]

diff --git a/helpers/ssl/Makefile.am b/helpers/ssl/Makefile.am
new file mode 100644 (file)
index 0000000..eb2aa45
--- /dev/null
@@ -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 (file)
index 0000000..00ba684
--- /dev/null
@@ -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 (file)
index 0000000..b33645c
--- /dev/null
@@ -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<Ssl::ValidateCertificateResponse::CertItem> 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<Ssl::ValidateCertificateResponse::CertItem>::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 (file)
index 0000000..1b0af4e
--- /dev/null
@@ -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 <vector>
+
+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<ErrorItem> 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