Tying validation errors to certificates

When Squid sends errors to the certificate validation daemon, the daemon cannot
tell which certificate caused which error. This is especially bad because the
validator has to return that same information in the response (the response
format requires the validator to match the error to the certificate).
This patch adjust the validation request format to provide that information
using a set of the following key=value pairs:

    error_name_N=the name of the certificate error number N
    error_cert_N=the ID of the certificate which caused error_name_N


where N is non-negative integer. N values start from zero and increase
sequentially. 

This is a Measurement Factory project

=== modified file 'helpers/ssl/cert_valid.pl'
--- helpers/ssl/cert_valid.pl	2012-12-05 01:13:21 +0000
+++ helpers/ssl/cert_valid.pl	2013-05-29 08:14:55 +0000
@@ -63,76 +63,76 @@
     my $first_line = $_;
     my @line_args = split;
 
     if ($first_line =~ /^\s*$/) {
         next;
     }
 
     my $response;
     my $haserror = 0;
     my $channelId = $line_args[0];
     my $code = $line_args[1];
     my $bodylen = $line_args[2];
     my $body = $line_args[3] . "\n";
     if ($channelId !~ /\d+/) {
         $response = $channelId." BH message=\"This helper is  concurrent and requires the concurrency option to be specified.\"\1";
     } elsif ($bodylen !~ /\d+/) {
         $response = $channelId." BH message=\"cert validator request syntax error \" \1";
     } else {
         my $readlen = length($body);
         my %certs = ();
-        my @errors = ();
+        my %errors = ();
         my @responseErrors = ();
 
         while($readlen < $bodylen) {
 	    my $t = <>;
             if (defined $t) {
                 $body  = $body . $t;
                 $readlen = length($body);
             }
         }
 
         print(STDERR logPrefix()."GOT ". "Code=".$code." $bodylen \n") if ($debug); #.$body;
         my $hostname;
-        parseRequest($body, \$hostname, \@errors, \%certs);
+        parseRequest($body, \$hostname, \%errors, \%certs);
         print(STDERR logPrefix()."Parse result: \n") if ($debug);
         print(STDERR logPrefix()."\tFOUND host:".$hostname."\n") if ($debug);
         print(STDERR logPrefix()."\tFOUND ERRORS:") if ($debug);
-        foreach my $err (@errors) {
-            print(STDERR logPrefix()."$err ,")  if ($debug);
+        foreach my $err (keys %errors) {
+            print(STDERR logPrefix().$errors{$err}{"name"}."/".$errors{$err}{"cert"}." ,")  if ($debug);
         }
         print(STDERR "\n") if ($debug);
         foreach my $key (keys %certs) {
             ## Use "perldoc Crypt::OpenSSL::X509" for X509 available methods.
             print(STDERR logPrefix()."\tFOUND cert ".$key.": ".$certs{$key}->subject() . "\n") if ($debug);
         }
 
         #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 my $err (@errors) {
+        foreach my $err (keys %errors) {
             $haserror = 1;
             appendError (\@responseErrors, 
-                         $err, #The error name
+                         $errors{$err}{"name"}, #The error name
                          "Checked by Cert Validator", # An error reason
-                         $peerCertId # The cert ID. We are always filling with the peer certificate.
+                         $errors{$err}{"cert"} # The cert ID. We are always filling with the peer certificate.
                 );
         }
 
         $response = createResponse(\@responseErrors);
         my $len = length($response);
         if ($haserror) {
             $response = $channelId." ERR ".$len." ".$response."\1";
         } else {
             $response = $channelId." OK ".$len." ".$response."\1";
         }
     }
 
     print $response;
     print(STDERR logPrefix().">> ".$response."\n") if ($debug);
 }
 
 sub trim
 {
     my $s = shift;
     $s =~ s/^\s+//;
@@ -158,47 +158,53 @@
         $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//;
+            $request =~ s/^host=.*$//m;
         }
-        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+)=/) {
+        if ($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);
         }
+        elsif ($request =~ /^error_name_(\d+)=(.*)$/m) {
+            my $errorId = $1;
+            my $errorName = $2;
+            $request =~ s/^error_name_\d+=.*$//m;
+            $errors->{$errorId}{"name"} = $errorName;
+        }
+        elsif ($request =~ /^error_cert_(\d+)=(.*)$/m) {
+            my $errorId = $1;
+            my $certId = $2;
+            $request =~ s/^error_cert_\d+=.*$//m;
+            $errors->{$errorId}{"cert"} = $certId;
+        }
         else {
             print(STDERR logPrefix()."ParseError on \"".$request."\"\n") if ($debug);
             $request = "";# finish processing....
         }
     }
 }
 
 
 sub logPrefix
 {
   return strftime("%Y/%m/%d %H:%M:%S.0", localtime)." ".$0." ".$$." | " ;
 }

=== modified file 'src/AclRegs.cc'
--- src/AclRegs.cc	2013-01-27 17:35:07 +0000
+++ src/AclRegs.cc	2013-05-27 14:50:21 +0000
@@ -129,41 +129,41 @@
 ACL::Prototype ACLSourceDomain::LiteralRegistryProtoype(&ACLSourceDomain::LiteralRegistryEntry_, "srcdomain");
 ACLStrategised<char const *> ACLSourceDomain::LiteralRegistryEntry_(new ACLDomainData, ACLSourceDomainStrategy::Instance(), "srcdomain");
 ACL::Prototype ACLSourceDomain::RegexRegistryProtoype(&ACLSourceDomain::RegexRegistryEntry_, "srcdom_regex");
 ACLStrategised<char const *> ACLSourceDomain::RegexRegistryEntry_(new ACLRegexData,ACLSourceDomainStrategy::Instance() ,"srcdom_regex");
 ACL::Prototype ACLSourceIP::RegistryProtoype(&ACLSourceIP::RegistryEntry_, "src");
 ACLSourceIP ACLSourceIP::RegistryEntry_;
 ACL::Prototype ACLTime::RegistryProtoype(&ACLTime::RegistryEntry_, "time");
 ACLStrategised<time_t> ACLTime::RegistryEntry_(new ACLTimeData, ACLTimeStrategy::Instance(), "time");
 ACL::Prototype ACLUrl::RegistryProtoype(&ACLUrl::RegistryEntry_, "url_regex");
 ACLStrategised<char const *> ACLUrl::RegistryEntry_(new ACLRegexData, ACLUrlStrategy::Instance(), "url_regex");
 ACL::Prototype ACLUrlLogin::RegistryProtoype(&ACLUrlLogin::RegistryEntry_, "urllogin");
 ACLStrategised<char const *> ACLUrlLogin::RegistryEntry_(new ACLRegexData, ACLUrlLoginStrategy::Instance(), "urllogin");
 ACL::Prototype ACLUrlPath::LegacyRegistryProtoype(&ACLUrlPath::RegistryEntry_, "pattern");
 ACL::Prototype ACLUrlPath::RegistryProtoype(&ACLUrlPath::RegistryEntry_, "urlpath_regex");
 ACLStrategised<char const *> ACLUrlPath::RegistryEntry_(new ACLRegexData, ACLUrlPathStrategy::Instance(), "urlpath_regex");
 ACL::Prototype ACLUrlPort::RegistryProtoype(&ACLUrlPort::RegistryEntry_, "port");
 ACLStrategised<int> ACLUrlPort::RegistryEntry_(new ACLIntRange, ACLUrlPortStrategy::Instance(), "port");
 
 #if USE_SSL
 ACL::Prototype ACLSslError::RegistryProtoype(&ACLSslError::RegistryEntry_, "ssl_error");
-ACLStrategised<const Ssl::Errors *> ACLSslError::RegistryEntry_(new ACLSslErrorData, ACLSslErrorStrategy::Instance(), "ssl_error");
+ACLStrategised<const Ssl::CertErrors *> ACLSslError::RegistryEntry_(new ACLSslErrorData, ACLSslErrorStrategy::Instance(), "ssl_error");
 ACL::Prototype ACLCertificate::UserRegistryProtoype(&ACLCertificate::UserRegistryEntry_, "user_cert");
 ACLStrategised<X509 *> ACLCertificate::UserRegistryEntry_(new ACLCertificateData (Ssl::GetX509UserAttribute, "*"), ACLCertificateStrategy::Instance(), "user_cert");
 ACL::Prototype ACLCertificate::CARegistryProtoype(&ACLCertificate::CARegistryEntry_, "ca_cert");
 ACLStrategised<X509 *> ACLCertificate::CARegistryEntry_(new ACLCertificateData (Ssl::GetX509CAAttribute, "*"), ACLCertificateStrategy::Instance(), "ca_cert");
 ACL::Prototype ACLServerCertificate::X509FingerprintRegistryProtoype(&ACLServerCertificate::X509FingerprintRegistryEntry_, "server_cert_fingerprint");
 ACLStrategised<X509 *> ACLServerCertificate::X509FingerprintRegistryEntry_(new ACLCertificateData(Ssl::GetX509Fingerprint, "-sha1", true), ACLServerCertificateStrategy::Instance(), "server_cert_fingerprint");
 #endif
 
 #if USE_SQUID_EUI
 ACL::Prototype ACLARP::RegistryProtoype(&ACLARP::RegistryEntry_, "arp");
 ACLARP ACLARP::RegistryEntry_("arp");
 ACL::Prototype ACLEui64::RegistryProtoype(&ACLEui64::RegistryEntry_, "eui64");
 ACLEui64 ACLEui64::RegistryEntry_("eui64");
 #endif
 
 #if USE_IDENT
 ACL::Prototype ACLIdent::UserRegistryProtoype(&ACLIdent::UserRegistryEntry_, "ident");
 ACLIdent ACLIdent::UserRegistryEntry_(new ACLUserData, "ident");
 ACL::Prototype ACLIdent::RegexRegistryProtoype(&ACLIdent::RegexRegistryEntry_, "ident_regex" );
 ACLIdent ACLIdent::RegexRegistryEntry_(new ACLRegexData, "ident_regex");

=== modified file 'src/acl/FilledChecklist.h'
--- src/acl/FilledChecklist.h	2013-05-17 11:07:15 +0000
+++ src/acl/FilledChecklist.h	2013-05-27 14:43:45 +0000
@@ -52,41 +52,41 @@
 public:
     Ip::Address src_addr;
     Ip::Address dst_addr;
     Ip::Address my_addr;
     CachePeer *dst_peer;
     char *dst_rdns;
 
     HttpRequest *request;
     HttpReply *reply;
 
     char rfc931[USER_IDENT_SZ];
 #if USE_AUTH
     Auth::UserRequest::Pointer auth_user_request;
 #endif
 #if SQUID_SNMP
     char *snmp_community;
 #endif
 
 #if USE_SSL
     /// SSL [certificate validation] errors, in undefined order
-    Ssl::Errors *sslErrors;
+    Ssl::CertErrors *sslErrors;
     /// The peer certificate
     Ssl::X509_Pointer serverCert;
 #endif
 
     ExternalACLEntry *extacl_entry;
 
 private:
     ConnStateData * conn_;          /**< hack for ident and NTLM */
     int fd_;                        /**< may be available when conn_ is not */
     bool destinationDomainChecked_;
     bool sourceDomainChecked_;
     /// not implemented; will cause link failures if used
     ACLFilledChecklist(const ACLFilledChecklist &);
     /// not implemented; will cause link failures if used
     ACLFilledChecklist &operator=(const ACLFilledChecklist &);
 
     CBDATA_CLASS2(ACLFilledChecklist);
 };
 
 /// convenience and safety wrapper for dynamic_cast<ACLFilledChecklist*>

=== modified file 'src/acl/SslError.h'
--- src/acl/SslError.h	2013-01-27 17:35:07 +0000
+++ src/acl/SslError.h	2013-05-27 14:49:33 +0000
@@ -1,33 +1,33 @@
 #ifndef SQUID_ACLSSL_ERROR_H
 #define SQUID_ACLSSL_ERROR_H
 #include "acl/Strategy.h"
 #include "acl/Strategised.h"
 #include "ssl/support.h"
 
-class ACLSslErrorStrategy : public ACLStrategy<const Ssl::Errors *>
+class ACLSslErrorStrategy : public ACLStrategy<const Ssl::CertErrors *>
 {
 
 public:
     virtual int match (ACLData<MatchType> * &, ACLFilledChecklist *, ACLFlags &);
     static ACLSslErrorStrategy *Instance();
     /* Not implemented to prevent copies of the instance. */
     /* Not private to prevent brain dead g+++ warnings about
      * private constructors with no friends */
     ACLSslErrorStrategy(ACLSslErrorStrategy const &);
 
 private:
     static ACLSslErrorStrategy Instance_;
     ACLSslErrorStrategy() {}
 
     ACLSslErrorStrategy&operator=(ACLSslErrorStrategy const &);
 };
 
 class ACLSslError
 {
 
 private:
     static ACL::Prototype RegistryProtoype;
-    static ACLStrategised<const Ssl::Errors *> RegistryEntry_;
+    static ACLStrategised<const Ssl::CertErrors *> RegistryEntry_;
 };
 
 #endif /* SQUID_ACLSSL_ERROR_H */

=== modified file 'src/acl/SslErrorData.cc'
--- src/acl/SslErrorData.cc	2012-08-31 16:57:39 +0000
+++ src/acl/SslErrorData.cc	2013-05-27 14:45:37 +0000
@@ -35,44 +35,44 @@
 #include "acl/SslErrorData.h"
 #include "acl/Checklist.h"
 #include "cache_cf.h"
 #include "wordlist.h"
 
 ACLSslErrorData::ACLSslErrorData() : values (NULL)
 {}
 
 ACLSslErrorData::ACLSslErrorData(ACLSslErrorData const &old) : values (NULL)
 {
     assert (!old.values);
 }
 
 ACLSslErrorData::~ACLSslErrorData()
 {
     if (values)
         delete values;
 }
 
 bool
-ACLSslErrorData::match(const Ssl::Errors *toFind)
+ACLSslErrorData::match(const Ssl::CertErrors *toFind)
 {
-    for (const Ssl::Errors *err = toFind; err; err = err->next ) {
-        if (values->findAndTune(err->element))
+    for (const Ssl::CertErrors *err = toFind; err; err = err->next ) {
+        if (values->findAndTune(err->element.code))
             return true;
     }
     return false;
 }
 
 /* explicit instantiation required for some systems */
 /** \cond AUTODOCS-IGNORE */
 // AYJ: 2009-05-20 : Removing. clashes with template <int> instantiation for other ACLs.
 // template cbdata_type Ssl::Errors::CBDATA_CbDataList;
 /** \endcond */
 
 wordlist *
 ACLSslErrorData::dump()
 {
     wordlist *W = NULL;
     Ssl::Errors *data = values;
 
     while (data != NULL) {
         wordlistAdd(&W, Ssl::GetErrorName(data->element));
         data = data->next;

=== modified file 'src/acl/SslErrorData.h'
--- src/acl/SslErrorData.h	2012-10-04 11:10:17 +0000
+++ src/acl/SslErrorData.h	2013-05-27 14:48:58 +0000
@@ -1,31 +1,31 @@
 #ifndef SQUID_ACLSSL_ERRORDATA_H
 #define SQUID_ACLSSL_ERRORDATA_H
 #include "acl/Acl.h"
 #include "acl/Data.h"
 #include "CbDataList.h"
 #include "ssl/support.h"
 #include "ssl/ErrorDetail.h"
 #include <vector>
 
-class ACLSslErrorData : public ACLData<const Ssl::Errors *>
+class ACLSslErrorData : public ACLData<const Ssl::CertErrors *>
 {
 
 public:
     MEMPROXY_CLASS(ACLSslErrorData);
 
     ACLSslErrorData();
     ACLSslErrorData(ACLSslErrorData const &);
     ACLSslErrorData &operator= (ACLSslErrorData const &);
     virtual ~ACLSslErrorData();
-    bool match(const Ssl::Errors *);
+    bool match(const Ssl::CertErrors *);
     wordlist *dump();
     void parse();
     bool empty() const;
     virtual  ACLSslErrorData *clone() const;
 
     Ssl::Errors *values;
 };
 
 MEMPROXY_CLASS_INLINE(ACLSslErrorData);
 
 #endif /* SQUID_ACLSSL_ERRORDATA_H */

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2013-05-23 08:18:09 +0000
+++ src/client_side.cc	2013-05-29 08:18:36 +0000
@@ -2589,72 +2589,72 @@
         clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
         assert(repContext);
         debugs(33, 5, "Responding with delated error for " << http->uri);
         repContext->setReplyToStoreEntry(sslServerBump->entry);
 
         // save the original request for logging purposes
         if (!context->http->al->request) {
             context->http->al->request = http->request;
             HTTPMSGLOCK(context->http->al->request);
         }
 
         // Get error details from the fake certificate-peeking request.
         http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
         context->pullData();
         return true;
     }
 
     // In bump-server-first mode, we have not necessarily seen the intended
     // server name at certificate-peeking time. Check for domain mismatch now,
     // when we can extract the intended name from the bumped HTTP request.
-    if (sslServerBump->serverCert.get()) {
+    if (X509 *srvCert = sslServerBump->serverCert.get()) {
         HttpRequest *request = http->request;
-        if (!Ssl::checkX509ServerValidity(sslServerBump->serverCert.get(), request->GetHost())) {
+        if (!Ssl::checkX509ServerValidity(srvCert, request->GetHost())) {
             debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " <<
                    "does not match domainname " << request->GetHost());
 
             bool allowDomainMismatch = false;
             if (Config.ssl_client.cert_error) {
                 ACLFilledChecklist check(Config.ssl_client.cert_error, request, dash_str);
-                check.sslErrors = new Ssl::Errors(SQUID_X509_V_ERR_DOMAIN_MISMATCH);
+                check.sslErrors = new Ssl::CertErrors(Ssl::CertError(SQUID_X509_V_ERR_DOMAIN_MISMATCH, srvCert));
                 allowDomainMismatch = (check.fastCheck() == ACCESS_ALLOWED);
                 delete check.sslErrors;
                 check.sslErrors = NULL;
             }
 
             if (!allowDomainMismatch) {
                 quitAfterError(request);
 
                 clientStreamNode *node = context->getClientReplyContext();
                 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
                 assert (repContext);
 
                 // Fill the server IP and hostname for error page generation.
                 HttpRequest::Pointer const & peekerRequest = sslServerBump->request;
                 request->hier.note(peekerRequest->hier.tcpServer, request->GetHost());
 
                 // Create an error object and fill it
                 ErrorState *err = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, request);
                 err->src_addr = clientConnection->remote;
                 Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(
                     SQUID_X509_V_ERR_DOMAIN_MISMATCH,
-                    sslServerBump->serverCert.get(), NULL);
+                    srvCert, NULL);
                 err->detail = errDetail;
                 // Save the original request for logging purposes.
                 if (!context->http->al->request) {
                     context->http->al->request = request;
                     HTTPMSGLOCK(context->http->al->request);
                 }
                 repContext->setReplyToError(request->method, err);
                 assert(context->http->out.offset == 0);
                 context->pullData();
                 return true;
             }
         }
     }
 
     return false;
 }
 #endif // USE_SSL
 
 static void
 clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, Http::ProtocolVersion http_ver)

=== modified file 'src/forward.cc'
--- src/forward.cc	2013-05-17 08:36:45 +0000
+++ src/forward.cc	2013-05-28 19:29:30 +0000
@@ -693,138 +693,138 @@
                 // The errFromFailure is attached to the ssl object
                 // and will be released when ssl object destroyed.
                 // Copy errFromFailure to a new Ssl::ErrorDetail object.
                 errDetails = new Ssl::ErrorDetail(*errFromFailure);
             } else {
                 // server_cert can be NULL here
                 X509 *server_cert = SSL_get_peer_certificate(ssl);
                 errDetails = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, server_cert, NULL);
                 X509_free(server_cert);
             }
 
             if (ssl_lib_error != SSL_ERROR_NONE)
                 errDetails->setLibError(ssl_lib_error);
 
             if (request->clientConnectionManager.valid()) {
                 // remember the server certificate from the ErrorDetail object
                 if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
                     serverBump->serverCert.resetAndLock(errDetails->peerCert());
 
                     // remember validation errors, if any
-                    if (Ssl::Errors *errs = static_cast<Ssl::Errors*>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
+                    if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors*>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
                         serverBump->sslErrors = cbdataReference(errs);
                 }
             }
 
             // For intercepted connections, set the host name to the server
             // certificate CN. Otherwise, we just hope that CONNECT is using
             // a user-entered address (a host name or a user-entered IP).
             const bool isConnectRequest = !request->clientConnectionManager->port->flags.isIntercepted();
             if (request->flags.sslPeek && !isConnectRequest) {
                 if (X509 *srvX509 = errDetails->peerCert()) {
                     if (const char *name = Ssl::CommonHostName(srvX509)) {
                         request->SetHost(name);
                         debugs(83, 3, HERE << "reset request host: " << name);
                     }
                 }
             }
 
             ErrorState *const anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL);
             anErr->xerrno = sysErrNo;
             anErr->detail = errDetails;
             fail(anErr);
 
             if (serverConnection()->getPeer()) {
                 peerConnectFailed(serverConnection()->getPeer());
             }
 
             serverConn->close();
             return;
         }
     }
 
     if (request->clientConnectionManager.valid()) {
         // remember the server certificate from the ErrorDetail object
         if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
             serverBump->serverCert.reset(SSL_get_peer_certificate(ssl));
 
             // remember validation errors, if any
-            if (Ssl::Errors *errs = static_cast<Ssl::Errors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
+            if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
                 serverBump->sslErrors = cbdataReference(errs);
         }
     }
 
     if (serverConnection()->getPeer() && !SSL_session_reused(ssl)) {
         if (serverConnection()->getPeer()->sslSession)
             SSL_SESSION_free(serverConnection()->getPeer()->sslSession);
 
         serverConnection()->getPeer()->sslSession = SSL_get1_session(ssl);
     }
 
     if (Ssl::TheConfig.ssl_crt_validator) {
         Ssl::CertValidationRequest validationRequest;
         // WARNING: Currently we do not use any locking for any of the
         // members of the Ssl::CertValidationRequest class. In this code the
         // Ssl::CertValidationRequest object used only to pass data to
         // Ssl::CertValidationHelper::submit method.
         validationRequest.ssl = ssl;
         validationRequest.domainName = request->GetHost();
-        if (Ssl::Errors *errs = static_cast<Ssl::Errors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
+        if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
             // validationRequest disappears on return so no need to cbdataReference
             validationRequest.errors = errs;
         else
             validationRequest.errors = NULL;
         try {
             debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
             Ssl::CertValidationHelper::GetInstance()->sslSubmit(validationRequest, sslCrtvdHandleReplyWrapper, this);
             return;
         } catch (const std::exception &e) {
             debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " <<
                    "request for " << validationRequest.domainName <<
                    " certificate: " << e.what() << "; will now block to " <<
                    "validate that certificate.");
             // fall through to do blocking in-process generation.
             ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request);
             fail(anErr);
             if (serverConnection()->getPeer()) {
                 peerConnectFailed(serverConnection()->getPeer());
             }
             serverConn->close();
             self = NULL;
             return;
         }
     }
 
     dispatch();
 }
 
 void
 FwdState::sslCrtvdHandleReplyWrapper(void *data, Ssl::CertValidationResponse const &validationResponse)
 {
     FwdState * fwd = (FwdState *)(data);
     fwd->sslCrtvdHandleReply(validationResponse);
 }
 
 void
 FwdState::sslCrtvdHandleReply(Ssl::CertValidationResponse const &validationResponse)
 {
-    Ssl::Errors *errs = NULL;
+    Ssl::CertErrors *errs = NULL;
     Ssl::ErrorDetail *errDetails = NULL;
     bool validatorFailed = false;
     if (!Comm::IsConnOpen(serverConnection())) {
         return;
     }
 
     debugs(83,5, request->GetHost() << " cert validation result: " << validationResponse.resultCode);
 
     if (validationResponse.resultCode == HelperReply::Error)
         errs = sslCrtvdCheckForErrors(validationResponse, errDetails);
     else if (validationResponse.resultCode != HelperReply::Okay)
         validatorFailed = true;
 
     if (!errDetails && !validatorFailed) {
         dispatch();
         return;
     }
 
     ErrorState *anErr = NULL;
     if (validatorFailed) {
@@ -843,86 +843,86 @@
                 }
             }
         }
 
         anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL);
         anErr->detail = errDetails;
         /*anErr->xerrno= Should preserved*/
     }
 
     fail(anErr);
     if (serverConnection()->getPeer()) {
         peerConnectFailed(serverConnection()->getPeer());
     }
     serverConn->close();
     self = NULL;
     return;
 }
 
 /// Checks errors in the cert. validator response against sslproxy_cert_error.
 /// The first honored error, if any, is returned via errDetails parameter.
-/// The method returns all seen errors except SSL_ERROR_NONE as Ssl::Errors.
-Ssl::Errors *
+/// The method returns all seen errors except SSL_ERROR_NONE as Ssl::CertErrors.
+Ssl::CertErrors *
 FwdState::sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &resp, Ssl::ErrorDetail *& errDetails)
 {
-    Ssl::Errors *errs = NULL;
+    Ssl::CertErrors *errs = NULL;
 
     ACLFilledChecklist *check = NULL;
     if (acl_access *acl = Config.ssl_client.cert_error)
         check = new ACLFilledChecklist(acl, request, dash_str);
 
     SSL *ssl = fd_table[serverConnection()->fd].ssl;
     typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
     for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
         debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason);
 
         assert(i->error_no != SSL_ERROR_NONE);
 
         if (!errDetails) {
             bool allowed = false;
             if (check) {
-                check->sslErrors = new Ssl::Errors(i->error_no);
+                check->sslErrors = new Ssl::CertErrors(Ssl::CertError(i->error_no, i->cert.get()));
                 if (check->fastCheck() == ACCESS_ALLOWED)
                     allowed = true;
             }
             // else the Config.ssl_client.cert_error access list is not defined
             // and the first error will cause the error page
 
             if (allowed) {
                 debugs(83, 3, "bypassing SSL error " << i->error_no << " in " << "buffer");
             } else {
                 debugs(83, 5, "confirming SSL error " << i->error_no);
                 X509 *brokenCert = i->cert.get();
                 Ssl::X509_Pointer peerCert(SSL_get_peer_certificate(ssl));
                 const char *aReason = i->error_reason.empty() ? NULL : i->error_reason.c_str();
                 errDetails = new Ssl::ErrorDetail(i->error_no, peerCert.get(), brokenCert, aReason);
             }
             if (check) {
                 delete check->sslErrors;
                 check->sslErrors = NULL;
             }
         }
 
         if (!errs)
-            errs = new Ssl::Errors(i->error_no);
+            errs = new Ssl::CertErrors(Ssl::CertError(i->error_no, i->cert.get()));
         else
-            errs->push_back_unique(i->error_no);
+            errs->push_back_unique(Ssl::CertError(i->error_no, i->cert.get()));
     }
     if (check)
         delete check;
 
     return errs;
 }
 
 void
 FwdState::initiateSSL()
 {
     SSL *ssl;
     SSL_CTX *sslContext = NULL;
     const CachePeer *peer = serverConnection()->getPeer();
     int fd = serverConnection()->fd;
 
     if (peer) {
         assert(peer->use_ssl);
         sslContext = peer->sslContext;
     } else {
         sslContext = Config.ssl_client.sslContext;

=== modified file 'src/forward.h'
--- src/forward.h	2013-05-13 16:21:23 +0000
+++ src/forward.h	2013-05-27 15:47:49 +0000
@@ -73,41 +73,41 @@
     void initiateSSL();
     void negotiateSSL(int fd);
     bool checkRetry();
     bool checkRetriable();
     void dispatch();
     void pconnPush(Comm::ConnectionPointer & conn, const char *domain);
 
     bool dontRetry() { return flags.dont_retry; }
 
     void dontRetry(bool val) { flags.dont_retry = val; }
 
     /** return a ConnectionPointer to the current server connection (may or may not be open) */
     Comm::ConnectionPointer const & serverConnection() const { return serverConn; };
 
 #if USE_SSL
     /// Callback function called when squid receive message from cert validator helper
     static void sslCrtvdHandleReplyWrapper(void *data, Ssl::CertValidationResponse const &);
     /// Process response from cert validator helper
     void sslCrtvdHandleReply(Ssl::CertValidationResponse const &);
     /// Check SSL errors returned from cert validator against sslproxy_cert_error access list
-    Ssl::Errors *sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &, Ssl::ErrorDetail *&);
+    Ssl::CertErrors *sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &, Ssl::ErrorDetail *&);
 #endif
 private:
     // hidden for safer management of self; use static fwdStart
     FwdState(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp);
     void start(Pointer aSelf);
 
 #if STRICT_ORIGINAL_DST
     void selectPeerForIntercepted();
 #endif
     static void logReplyStatus(int tries, const Http::StatusCode status);
     void doneWithRetries();
     void completed();
     void retryOrBail();
     ErrorState *makeConnectingError(const err_type type) const;
     static void RegisterWithCacheManager(void);
 
 public:
     StoreEntry *entry;
     HttpRequest *request;
     AccessLogEntryPointer al; ///< info for the future access.log entry

=== modified file 'src/ssl/ServerBump.h'
--- src/ssl/ServerBump.h	2012-08-14 11:53:07 +0000
+++ src/ssl/ServerBump.h	2013-05-27 15:35:06 +0000
@@ -10,31 +10,31 @@
 class ConnStateData;
 class store_client;
 
 namespace Ssl
 {
 
 /**
   \ingroup ServerProtocolSSLAPI
  * Maintains bump-server-first related information.
  */
 class ServerBump
 {
 public:
     explicit ServerBump(HttpRequest *fakeRequest, StoreEntry *e = NULL);
     ~ServerBump();
 
     /// faked, minimal request; required by server-side API
     HttpRequest::Pointer request;
     StoreEntry *entry; ///< for receiving Squid-generated error messages
     Ssl::X509_Pointer serverCert; ///< HTTPS server certificate
-    Ssl::Errors *sslErrors; ///< SSL [certificate validation] errors
+    Ssl::CertErrors *sslErrors; ///< SSL [certificate validation] errors
 
 private:
     store_client *sc; ///< dummy client to prevent entry trimming
 
     CBDATA_CLASS2(ServerBump);
 };
 
 } // namespace Ssl
 
 #endif

=== modified file 'src/ssl/cert_validate_message.cc'
--- src/ssl/cert_validate_message.cc	2012-12-14 13:34:13 +0000
+++ src/ssl/cert_validate_message.cc	2013-05-29 08:47:52 +0000
@@ -1,62 +1,64 @@
 #include "squid.h"
 #include "acl/FilledChecklist.h"
 #include "helper.h"
 #include "ssl/support.h"
 #include "ssl/cert_validate_message.h"
 #include "ssl/ErrorDetail.h"
 
 void
 Ssl::CertValidationMsg::composeRequest(CertValidationRequest const &vcert)
 {
     body.clear();
     body += Ssl::CertValidationMsg::param_host + "=" + vcert.domainName;
-    if (vcert.errors) {
-        body += "\n" + Ssl::CertValidationMsg::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;
-        }
-    }
-
     STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(vcert.ssl);
     if (peerCerts) {
-        body +="\n";
         Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
         for (int i = 0; i < sk_X509_num(peerCerts); ++i) {
             X509 *cert = sk_X509_value(peerCerts, i);
             PEM_write_bio_X509(bio.get(), cert);
-            body = body + "cert_" + xitoa(i) + "=";
+            body = body + "\n" + param_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";
+            body.append(ptr, (ptr[len-1] == '\n' ? len - 1 : len));
             if (!BIO_reset(bio.get())) {
                 // print an error?
             }
         }
     }
+
+    if (vcert.errors) {
+        int i = 0;
+        for (const Ssl::CertErrors *err = vcert.errors; err; err = err->next, ++i) {
+            body +="\n";
+            body = body + param_error_name + xitoa(i) + "=" + GetErrorName(err->element.code) + "\n";
+            int errorCertPos = -1;
+            if (err->element.cert.get()) 
+                errorCertPos = sk_X509_find(peerCerts, err->element.cert.get());
+            if (errorCertPos < 0) {
+                // assert this error ?
+                debugs(83, 4, "WARNING: wrong cert in cert validator request");
+            }
+            body += param_error_cert + xitoa(i) + "=";
+            body += param_cert + xitoa((errorCertPos >= 0 ? errorCertPos : 0));
+        }
+    }
 }
 
 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::CertValidationMsg::parseResponse(CertValidationResponse &resp, STACK_OF(X509) *peerCerts, std::string &error)
 {
     std::vector<CertItem> certs;
 
     const char *param = body.c_str();
     while (*param) {
         while (xisspace(*param)) param++;
         if (! *param)
@@ -195,26 +197,25 @@
 {
     name = old.name;
     setCert(old.cert.get());
 }
 
 Ssl::CertValidationMsg::CertItem & Ssl::CertValidationMsg::CertItem::operator = (const CertItem &old)
 {
     name = old.name;
     setCert(old.cert.get());
     return *this;
 }
 
 void
 Ssl::CertValidationMsg::CertItem::setCert(X509 *aCert)
 {
     cert.resetAndLock(aCert);
 }
 
 const std::string Ssl::CertValidationMsg::code_cert_validate("cert_validate");
 const std::string Ssl::CertValidationMsg::param_domain("domain");
-const std::string Ssl::CertValidationMsg::param_error("errors");
 const std::string Ssl::CertValidationMsg::param_cert("cert_");
 const std::string Ssl::CertValidationMsg::param_error_name("error_name_");
 const std::string Ssl::CertValidationMsg::param_error_reason("error_reason_");
 const std::string Ssl::CertValidationMsg::param_error_cert("error_cert_");
 

=== modified file 'src/ssl/cert_validate_message.h'
--- src/ssl/cert_validate_message.h	2012-12-14 08:25:59 +0000
+++ src/ssl/cert_validate_message.h	2013-05-28 19:04:04 +0000
@@ -3,41 +3,41 @@
 
 #ifndef SQUID_SSL_CERT_VALIDATE_MESSAGE_H
 #define SQUID_SSL_CERT_VALIDATE_MESSAGE_H
 
 #include "HelperReply.h"
 #include "ssl/support.h"
 #include "ssl/crtd_message.h"
 #include <vector>
 
 namespace Ssl
 {
 
 /**
  * This class is used to hold the required informations to build
  * a request message for the certificate validator helper
  */
 class CertValidationRequest
 {
 public:
     SSL *ssl;
-    Errors *errors; ///< The list of errors detected
+    CertErrors *errors; ///< The list of errors detected
     std::string domainName; ///< The server name
     CertValidationRequest() : ssl(NULL), errors(NULL) {}
 };
 
 /**
  * This class is used to store informations found in certificate validation
  * response messages read from certificate validator helper
  */
 class CertValidationResponse
 {
 public:
     /**
      * This class used to hold error informations returned from
      * cert validator helper.
      */
     class  RecvdError
     {
     public:
         RecvdError(): id(0), error_no(SSL_ERROR_NONE), cert(NULL) {}
         RecvdError(const RecvdError &);
@@ -82,34 +82,32 @@
         void setCert(X509 *); ///< Sets cert to the given certificate
     };
 
 public:
     CertValidationMsg(MessageKind kind): CrtdMessage(kind) {}
 
     /// Build a request message for the cert validation helper
     /// using informations provided by vcert object
     void composeRequest(CertValidationRequest const &vcert);
 
     /// Parse a response message and fill the resp object with parsed informations
     bool parseResponse(CertValidationResponse &resp, STACK_OF(X509) *peerCerts, std::string &error);
 
     /// Search a CertItems list for the certificate with ID "name"
     X509 *getCertByName(std::vector<CertItem> const &, std::string const & name);
 
     /// 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

=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc	2013-05-24 15:14:50 +0000
+++ src/ssl/support.cc	2013-05-29 08:22:37 +0000
@@ -244,60 +244,64 @@
 
         if (server) {
             if (!Ssl::checkX509ServerValidity(peer_cert, server)) {
                 debugs(83, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " << buffer << " does not match domainname " << server);
                 ok = 0;
                 error_no = SQUID_X509_V_ERR_DOMAIN_MISMATCH;
             }
         }
     }
 
     if (ok && peeked_cert) {
         // Check whether the already peeked certificate matches the new one.
         if (X509_cmp(peer_cert, peeked_cert) != 0) {
             debugs(83, 2, "SQUID_X509_V_ERR_CERT_CHANGE: Certificate " << buffer << " does not match peeked certificate");
             ok = 0;
             error_no =  SQUID_X509_V_ERR_CERT_CHANGE;
         }
     }
 
     if (!ok) {
-        Ssl::Errors *errs = static_cast<Ssl::Errors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors));
+        X509 *broken_cert =  X509_STORE_CTX_get_current_cert(ctx);
+        if (!broken_cert)
+            broken_cert = peer_cert;
+
+        Ssl::CertErrors *errs = static_cast<Ssl::CertErrors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors));
         if (!errs) {
-            errs = new Ssl::Errors(error_no);
+            errs = new Ssl::CertErrors(Ssl::CertError(error_no, broken_cert));
             if (!SSL_set_ex_data(ssl, ssl_ex_index_ssl_errors,  (void *)errs)) {
                 debugs(83, 2, "Failed to set ssl error_no in ssl_verify_cb: Certificate " << buffer);
                 delete errs;
                 errs = NULL;
             }
         } else // remember another error number
-            errs->push_back_unique(error_no);
+            errs->push_back_unique(Ssl::CertError(error_no, broken_cert));
 
         if (const char *err_descr = Ssl::GetErrorDescr(error_no))
             debugs(83, 5, err_descr << ": " << buffer);
         else
             debugs(83, DBG_IMPORTANT, "SSL unknown certificate error " << error_no << " in " << buffer);
 
         if (check) {
             ACLFilledChecklist *filledCheck = Filled(check);
             assert(!filledCheck->sslErrors);
-            filledCheck->sslErrors = new Ssl::Errors(error_no);
+            filledCheck->sslErrors = new Ssl::CertErrors(Ssl::CertError(error_no, broken_cert));
             filledCheck->serverCert.resetAndLock(peer_cert);
             if (check->fastCheck() == ACCESS_ALLOWED) {
                 debugs(83, 3, "bypassing SSL error " << error_no << " in " << buffer);
                 ok = 1;
             } else {
                 debugs(83, 5, "confirming SSL error " << error_no);
             }
             delete filledCheck->sslErrors;
             filledCheck->sslErrors = NULL;
             filledCheck->serverCert.reset(NULL);
         }
         // If the certificate validator is used then we need to allow all errors and
         // pass them to certficate validator for more processing
         else if (Ssl::TheConfig.ssl_crt_validator)
             ok = 1;
     }
 
     if (!dont_verify_domain && server) {}
 
     if (!ok && !SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) ) {
@@ -618,41 +622,41 @@
 static void
 ssl_freeAclChecklist(void *, void *ptr, CRYPTO_EX_DATA *,
                      int, long, void *)
 {
     delete static_cast<ACLChecklist *>(ptr); // may be NULL
 }
 
 // "free" function for SSL_get_ex_new_index("ssl_error_detail")
 static void
 ssl_free_ErrorDetail(void *, void *ptr, CRYPTO_EX_DATA *,
                      int, long, void *)
 {
     Ssl::ErrorDetail  *errDetail = static_cast <Ssl::ErrorDetail *>(ptr);
     delete errDetail;
 }
 
 static void
 ssl_free_SslErrors(void *, void *ptr, CRYPTO_EX_DATA *,
                    int, long, void *)
 {
-    Ssl::Errors *errs = static_cast <Ssl::Errors*>(ptr);
+    Ssl::CertErrors *errs = static_cast <Ssl::CertErrors*>(ptr);
     delete errs;
 }
 
 // "free" function for X509 certificates
 static void
 ssl_free_X509(void *, void *ptr, CRYPTO_EX_DATA *,
               int, long, void *)
 {
     X509  *cert = static_cast <X509 *>(ptr);
     X509_free(cert);
 }
 
 /// \ingroup ServerProtocolSSLInternal
 static void
 ssl_initialize(void)
 {
     static int ssl_initialized = 0;
 
     if (!ssl_initialized) {
         ssl_initialized = 1;
@@ -1579,21 +1583,51 @@
     Ssl::CertificateProperties certProperties;
     if (const char *cn = CommonHostName(cert.get())) {
         certProperties.commonName = "Not trusted by \"";
         certProperties.commonName += cn;
         certProperties.commonName += "\"";
     } else if (const char *org = getOrganization(cert.get())) {
         certProperties.commonName =  "Not trusted by \"";
         certProperties.commonName += org;
         certProperties.commonName += "\"";
     } else
         certProperties.commonName =  "Not trusted";
     certProperties.setCommonName = true;
     // O, OU, and other CA subject fields will be mimicked
     // Expiration date and other common properties will be mimicked
     certProperties.signAlgorithm = Ssl::algSignSelf;
     certProperties.signWithPkey.resetAndLock(pkey.get());
     certProperties.mimicCert.resetAndLock(cert.get());
     return Ssl::generateSslCertificate(untrustedCert, untrustedPkey, certProperties);
 }
 
+Ssl::CertError::CertError(ssl_error_t anErr, X509 *aCert): code(anErr)
+{ 
+    cert.resetAndLock(aCert);
+}
+
+Ssl::CertError::CertError(CertError const &err): code(err.code)
+{
+    cert.resetAndLock(err.cert.get());
+}
+
+Ssl::CertError &
+Ssl::CertError::operator = (const CertError &old) 
+{
+    code = old.code; 
+    cert.resetAndLock(old.cert.get());
+    return *this;
+}
+
+bool
+Ssl::CertError::operator == (const CertError &ce) const
+{
+    return code == ce.code && cert.get() == ce.cert.get();
+}
+
+bool
+Ssl::CertError::operator != (const CertError &ce) const
+{
+    return code != ce.code || cert.get() != ce.cert.get();
+}
+
 #endif /* USE_SSL */

=== modified file 'src/ssl/support.h'
--- src/ssl/support.h	2012-11-13 18:19:17 +0000
+++ src/ssl/support.h	2013-05-29 08:23:56 +0000
@@ -57,40 +57,56 @@
 // Custom SSL errors; assumes all official errors are positive
 #define SQUID_X509_V_ERR_CERT_CHANGE -3
 #define SQUID_ERR_SSL_HANDSHAKE -2
 #define SQUID_X509_V_ERR_DOMAIN_MISMATCH -1
 // All SSL errors range: from smallest (negative) custom to largest SSL error
 #define SQUID_SSL_ERROR_MIN SQUID_X509_V_ERR_CERT_CHANGE
 #define SQUID_SSL_ERROR_MAX INT_MAX
 
 namespace AnyP
 {
 class PortCfg;
 };
 
 namespace Ssl
 {
 /// Squid defined error code (<0),  an error code returned by SSL X509 api, or SSL_ERROR_NONE
 typedef int ssl_error_t;
 
 typedef CbDataList<Ssl::ssl_error_t> Errors;
 
+/// An SSL certificate-related error.
+/// Pairs an error code with the certificate experiencing the error.
+class CertError {
+public:
+    ssl_error_t code; ///< certificate error code
+    X509_Pointer cert; ///< certificate with the above error code
+    CertError(ssl_error_t anErr, X509 *aCert);
+    CertError(CertError const &err);
+    CertError & operator = (const CertError &old);
+    bool operator == (const CertError &ce) const;
+    bool operator != (const CertError &ce) const;
+};
+
+/// Holds a list of certificate SSL errors
+typedef CbDataList<Ssl::CertError> CertErrors;
+
 } //namespace Ssl
 
 /// \ingroup ServerProtocolSSLAPI
 SSL_CTX *sslCreateServerContext(AnyP::PortCfg &port);
 
 /// \ingroup ServerProtocolSSLAPI
 SSL_CTX *sslCreateClientContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options, const char *flags, const char *CAfile, const char *CApath, const char *CRLfile);
 
 /// \ingroup ServerProtocolSSLAPI
 int ssl_read_method(int, char *, int);
 
 /// \ingroup ServerProtocolSSLAPI
 int ssl_write_method(int, const char *, int);
 
 /// \ingroup ServerProtocolSSLAPI
 void ssl_shutdown_method(SSL *ssl);
 
 /// \ingroup ServerProtocolSSLAPI
 const char *sslGetUserEmail(SSL *ssl);