#
use warnings;
-# TODO:
-# use strict;
-
+use strict;
+use Getopt::Long;
+use Pod::Usage;
use Crypt::OpenSSL::X509;
use FileHandle;
+use POSIX qw(strftime);
+
+my $debug = 0;
+my $help = 0;
+
+=pod
+
+=head1 NAME
+
+cert_valid.pl - A fake cert validation helper for Squid
+
+=head1 SYNOPSIS
+
+cert_valid.pl [-d | --debug] [-h | --help]
+
+=over 8
+
+=item B<-h | --help>
+
+brief help message
+
+=item B<-d | --debug>
+
+enable debug messages to stderr
+
+=back
-my $LOGFILE = "/tmp/ssl_cert_valid.log";
+=head1 DESCRIPTION
-open(LOG, ">>$LOGFILE") or die("Cannot open logfile $LOGFILE, stopped");
-LOG->autoflush(1);
+Retrieves the SSL certificate error list from squid and echo back without any change.
+
+=head1 COPYRIGHT
+
+(C) 2012 The Measurement Factory, Author: Tsantilas Christos
+
+This program is free software. You may redistribute copies of it under the
+terms of the GNU General Public License version 2, or (at your opinion) any
+later version.
+
+=cut
+
+GetOptions(
+ 'help' => \$help,
+ 'debug' => \$debug,
+ ) or pod2usage(1);
+
+pod2usage(1) if ($help);
$|=1;
while (<>) {
my $response;
my $haserror = 0;
- my $code = $line_args[0];
- my $bodylen = $line_args[1];
- my $body = $line_args[2] . "\n";
- if ($bodylen =~ /\d+/) {
+ 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 = ();
}
}
- print LOG "GOT ". "Code=".$code." $bodylen \n"; #.$body;
+ print(STDERR logPrefix()."GOT ". "Code=".$code." $bodylen \n") if ($debug); #.$body;
+ my $hostname;
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(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);
}
- print LOG "\n";
- foreach $key (keys %certs) {
+ print(STDERR "\n") if ($debug);
+ foreach my $key (keys %certs) {
## Use "perldoc Crypt::OpenSSL::X509" for X509 available methods.
- print LOG "\tFOUND cert ".$key.": ".$certs{$key}->subject() . "\n";
+ 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 $err (@errors) {
+ foreach my $err (@errors) {
$haserror = 1;
appendError (\@responseErrors,
$err, #The error name
$response = createResponse(\@responseErrors);
my $len = length($response);
if ($haserror) {
- $response = "ERR ".$len." ".$response."\1";
+ $response = $channelId." ERR ".$len." ".$response."\1";
} else {
- $response = "OK ".$len." ".$response."\1";
+ $response = $channelId." OK ".$len." ".$response."\1";
}
- } else {
- $response = "BH 0 \1";
}
print $response;
- print LOG ">> ".$response;
+ print(STDERR logPrefix().">> ".$response."\n") if ($debug);
}
-close(LOG);
sub trim
{
my ($responseErrors) = shift;
my $response="";
my $i = 0;
- foreach $err (@$responseErrors) {
+ foreach my $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";
$request = substr($request, $vallen);
}
else {
- print LOG "ParseError on \"".$request."\"\n";
+ 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." ".$$." | " ;
+}
NAME: sslcrtvalidator_children
TYPE: HelperChildConfig
IFDEF: USE_SSL
-DEFAULT: 32 startup=5 idle=1
+DEFAULT: 32 startup=5 idle=1 concurrency=0
LOC: Ssl::TheConfig.ssl_crt_validator_Children
DOC_START
The maximum number of processes spawn to service ssl server.
at all times. When traffic begins to rise above what the existing
processes can handle this many more will be spawned up to the maximum
configured. A minimum setting of 1 is required.
+
+ concurrency=
+
+ The number of requests each certificate validator helper can handle in
+ parallel. Defaults to 0 which indicates the certficate validator
+ is a old-style single threaded redirector.
+
+ When this directive is set to a value >= 1 then the protocol
+ used to communicate with the helper is modified to include
+ a request ID in front of the request/response. The request
+ ID from the request must be echoed back with the response
+ to that request.
You must have at least one ssl_crt_validator process.
DOC_END
#include "urn.h"
#include "whois.h"
#if USE_SSL
+#if 1 // USE_SSL_CERT_VALIDATOR
#include "ssl/cert_validate_message.h"
#include "ssl/Config.h"
#include "ssl/helper.h"
+#endif
#include "ssl/support.h"
#include "ssl/ErrorDetail.h"
#include "ssl/ServerBump.h"
else
validationRequest.errors = NULL;
try {
- debugs(83, 5, HERE << "Sending SSL certificate for validation to ssl_crtvd.");
+ debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
Ssl::CertValidationMsg requestMsg(Ssl::CrtdMessage::REQUEST);
requestMsg.setCode(Ssl::CertValidationMsg::code_cert_validate);
requestMsg.composeRequest(validationRequest);
- debugs(83, 5, HERE << "SSL crtvd request: " << requestMsg.compose().c_str());
+ debugs(83, 5, "SSL crtvd request: " << requestMsg.compose().c_str());
Ssl::CertValidationHelper::GetInstance()->sslSubmit(requestMsg, sslCrtvdHandleReplyWrapper, this);
return;
} catch (const std::exception &e) {
- debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
+ debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " <<
"request for " << validationRequest.domainName <<
" certificate: " << e.what() << "; will now block to " <<
"validate that certificate.");
SSL *ssl = fd_table[serverConnection()->fd].ssl;
if (!reply.other().hasContent()) {
- debugs(83, 1, HERE << "\"ssl_crtvd\" helper return <NULL> reply");
+ debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper return <NULL> reply");
validatorFailed = true;
- } else {
+ } else if (reply.result == HelperReply::BrokenHelper) {
+ debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper error response: " << reply.other().content());
+ validatorFailed = true;
+ } else {
Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY);
Ssl::CertValidationResponse validationResponse;
std::string error;
STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(ssl);
if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK ||
- !replyMsg.parseResponse(validationResponse, peerCerts, error) ) {
- debugs(83, 5, HERE << "Reply from ssl_crtvd for " << request->GetHost() << " is incorrect");
+ !replyMsg.parseResponse(validationResponse, peerCerts, error) ) {
+ debugs(83, 5, "Reply from ssl_crtvd for " << request->GetHost() << " is incorrect");
validatorFailed = true;
} else {
if (reply.result == HelperReply::Okay) {
- debugs(83, 5, HERE << "Certificate for " << request->GetHost() << " was successfully validated from ssl_crtvd");
+ debugs(83, 5, "Certificate for " << request->GetHost() << " was successfully validated from ssl_crtvd");
} else if (reply.result == HelperReply::Error) {
- debugs(83, 5, HERE << "Certificate for " << request->GetHost() << " found buggy by ssl_crtvd");
+ debugs(83, 5, "Certificate for " << request->GetHost() << " found buggy by ssl_crtvd");
errs = sslCrtvdCheckForErrors(validationResponse, errDetails);
} else {
- debugs(83, 5, HERE << "Certificate for " << request->GetHost() << " cannot be validated. ssl_crtvd response: " << replyMsg.getBody());
+ debugs(83, 5, "Certificate for " << request->GetHost() << " cannot be validated. ssl_crtvd response: " << replyMsg.getBody());
validatorFailed = true;
}
for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason);
- if (i->error_no == SSL_ERROR_NONE)
- continue; //ignore????
+ assert(i->error_no != SSL_ERROR_NONE);
if (!errDetails) {
bool allowed = false;
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;
- X509 *peerCert = SSL_get_peer_certificate(ssl);
+ 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, brokenCert, aReason);
- X509_free(peerCert);
+ errDetails = new Ssl::ErrorDetail(i->error_no, peerCert.get(), brokenCert, aReason);
}
delete check->sslErrors;
check->sslErrors = NULL;
#include "comm/Connection.h"
#include "err_type.h"
#include "fde.h"
-#include "HelperReply.h"
#include "HttpStatusCode.h"
#include "ip/Address.h"
-#if USE_SSL
+#if USE_SSL //&& USE_SSL_CERT_VALIDATOR
#include "ssl/support.h"
#endif
class ErrorState;
class HttpRequest;
-#if USE_SSL
+#if USE_SSL //&& USE_SSL_CERT_VALIDATOR
namespace Ssl
{
class ErrorDetail;
*/
nfmark_t GetNfmarkToServer(HttpRequest * request);
+class HelperReply;
+
class FwdState : public RefCountable
{
public:
#include "LoadableModules.h"
#endif
#if USE_SSL_CRTD
-#include "ssl/helper.h"
#include "ssl/certificate_db.h"
#endif
#if USE_SSL
+#include "ssl/helper.h"
#include "ssl/context_storage.h"
#endif
#if ICAP_CLIENT
Ssl::Helper::GetInstance()->Shutdown();
#endif
#if USE_SSL
+#if 1 // USE_SSL_CERT_VALIDATOR
if (Ssl::CertValidationHelper::GetInstance())
Ssl::CertValidationHelper::GetInstance()->Shutdown();
+#endif
Ssl::TheGlobalContextStorage.reconfigureStart();
#endif
redirectShutdown();
#if USE_SSL_CRTD
Ssl::Helper::GetInstance()->Init();
#endif
-#if USE_SSL
+#if USE_SSL // && USE_SSL_CERT_VALIDATOR
if (Ssl::CertValidationHelper::GetInstance())
Ssl::CertValidationHelper::GetInstance()->Init();
#endif
Ssl::Helper::GetInstance()->Init();
#endif
-#if USE_SSL
+#if USE_SSL // && USE_SSL_CERT_VALIDATOR
if (Ssl::CertValidationHelper::GetInstance())
Ssl::CertValidationHelper::GetInstance()->Init();
#endif
#if USE_SSL_CRTD
Ssl::Helper::GetInstance()->Shutdown();
#endif
-#if USE_SSL
+#if USE_SSL //&& USE_SSL_CERT_VALIDATOR
if (Ssl::CertValidationHelper::GetInstance())
Ssl::CertValidationHelper::GetInstance()->Shutdown();
#endif
Ssl::Config Ssl::TheConfig;
-Ssl::Config::Config()
+Ssl::Config::Config():
#if USE_SSL_CRTD
- :
- ssl_crtd(NULL)
+ ssl_crtd(NULL),
#endif
+ ssl_crt_validator(NULL)
{
}
#if USE_SSL_CRTD
xfree(ssl_crtd);
#endif
+ xfree(ssl_crt_validator);
}
char *ssl_crtd; ///< Name of external ssl_crtd application.
/// The number of processes spawn for ssl_crtd.
HelperChildConfig ssl_crtdChildren;
+#endif
+#if 1 // USE_SSL_CERT_VALIDATOR
char *ssl_crt_validator;
HelperChildConfig ssl_crt_validator_Children;
#endif
if USE_SSL_CRTD
SSL_CRTD = ssl_crtd
-SSL_CRTD_SOURCE = \
- helper.cc \
- helper.h
else
SSL_CRTD =
-SSL_CRTD_SOURCE =
endif
## SSL stuff used by main Squid but not by ssl_crtd
ServerBump.h \
support.cc \
support.h \
- \
- $(SSL_CRTD_SOURCE)
+ helper.cc \
+ helper.h
## SSL stuff used by main Squid and ssl_crtd
libsslutil_la_SOURCES = \
size_t param_len = strcspn(param, "=\r\n");
if (param[param_len] != '=') {
- debugs(83, 2, "Cert validator response parse error: " << param);
+ debugs(83, DBG_IMPORTANT, "WARNING: cert validator response parse error: " << param);
return false;
}
const char *value=param+param_len+1;
const char *b = strstr(value, "-----END CERTIFICATE-----");
if (b == NULL) {
- debugs(83, 2, "Cert Vailidator response parse error: Failed to find certificate boundary " << value);
+ debugs(83, DBG_IMPORTANT, "WARNING: cert Validator response parse error: Failed to find certificate boundary " << value);
return false;
}
b += strlen("-----END CERTIFICATE-----");
size_t value_len = strcspn(value, "\r\n");
std::string v(value, value_len);
- debugs(83, 5, HERE << "Returned value: " << std::string(param, param_len).c_str() << ": " <<
+ debugs(83, 5, "Returned value: " << std::string(param, param_len).c_str() << ": " <<
v.c_str());
int errorId = get_error_id(param, param_len);
strncmp(param, param_error_name.c_str(), param_error_name.length()) == 0) {
currentItem.error_no = Ssl::GetErrorCode(v.c_str());
if (currentItem.error_no == SSL_ERROR_NONE) {
- debugs(83, 2, "Cert validator response parse error: Unknown SSL Error: " << v);
+ debugs(83, DBG_IMPORTANT, "WARNING: cert validator response parse error: Unknown SSL Error: " << v);
return false;
}
} else if (param_len > param_error_reason.length() &&
strncmp(param, param_error_cert.c_str(), param_error_cert.length()) == 0) {
if (X509 *cert = getCertByName(certs, v)) {
- debugs(83, 6, HERE << "The certificate with id \"" << v << "\" found.");
+ debugs(83, 6, "The certificate with id \"" << v << "\" found.");
currentItem.setCert(cert);
} else {
//In this case we assume that the certID is one of the certificates sent
// form "cert_xx" where the "xx" is an integer represents the position of
// the certificate inside peer certificates list.
const int certId = get_error_id(v.c_str(), v.length());
- debugs(83, 6, HERE << "Cert index in peer certificates list:" << certId);
+ debugs(83, 6, "Cert index in peer certificates list:" << certId);
//if certId is not correct sk_X509_value returns NULL
currentItem.setCert(sk_X509_value(peerCerts, certId));
}
} else {
- debugs(83, 2, "Cert validator response parse error: Unknown parameter name " << std::string(param, param_len).c_str());
+ debugs(83, DBG_IMPORTANT, "WARNING: cert validator response parse error: Unknown parameter name " << std::string(param, param_len).c_str());
return false;
}
typedef std::vector<CertItem>::const_iterator SVCI;
for (SVCI ci = certs.begin(); ci != certs.end(); ++ci) {
if (ci->name.compare(name) == 0)
- return ci->cert;
+ return ci->cert.get();
}
return NULL;
}
{
error_no = old.error_no;
error_reason = old.error_reason;
- cert = NULL;
- setCert(old.cert);
+ setCert(old.cert.get());
}
Ssl::CertValidationResponse::RecvdError::~RecvdError()
{
- if (cert)
- X509_free(cert);
}
Ssl::CertValidationResponse::RecvdError & Ssl::CertValidationResponse::RecvdError::operator = (const RecvdError &old)
{
error_no = old.error_no;
error_reason = old.error_reason;
- setCert(old.cert);
+ setCert(old.cert.get());
return *this;
}
void
Ssl::CertValidationResponse::RecvdError::setCert(X509 *aCert)
{
- if (cert)
- X509_free(cert);
- if (aCert) {
- cert = aCert;
- CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509);
- } else
- cert = NULL;
+ cert.resetAndLock(aCert);
}
Ssl::CertValidationMsg::CertItem::CertItem(const CertItem &old)
{
name = old.name;
- cert = NULL;
- setCert(old.cert);
+ setCert(old.cert.get());
}
Ssl::CertValidationMsg::CertItem & Ssl::CertValidationMsg::CertItem::operator = (const CertItem &old)
{
name = old.name;
- setCert(old.cert);
+ setCert(old.cert.get());
return *this;
}
Ssl::CertValidationMsg::CertItem::~CertItem()
{
- if (cert)
- X509_free(cert);
}
void
Ssl::CertValidationMsg::CertItem::setCert(X509 *aCert)
{
- if (cert)
- X509_free(cert);
- if (aCert) {
- cert = aCert;
- CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509);
- } else
- cert = NULL;
+ cert.resetAndLock(aCert);
}
const std::string Ssl::CertValidationMsg::code_cert_validate("cert_validate");
/*
- * $Id$
*/
#ifndef SQUID_SSL_CERT_VALIDATE_MESSAGE_H
int id; ///< The id of the error
ssl_error_t error_no; ///< The OpenSSL error code
std::string error_reason; ///< A string describing the error
- X509 *cert; ///< The broken certificate
+ X509_Pointer cert; ///< The broken certificate
};
typedef std::vector<RecvdError> RecvdErrors;
{
public:
std::string name; ///< The certificate Id to use
- X509 *cert; ///< A pointer to certificate
+ X509_Pointer cert; ///< A pointer to certificate
CertItem(): cert(NULL) {}
CertItem(const CertItem &);
CertItem & operator = (const CertItem &);
#include "wordlist.h"
#include "SquidConfig.h"
+#if USE_SSL_CRTD
Ssl::Helper * Ssl::Helper::GetInstance()
{
static Ssl::Helper sslHelper;
msg += '\n';
helperSubmit(ssl_crtd, msg.c_str(), callback, data);
}
+#endif //USE_SSL_CRTD
+#if 1 // USE_SSL_CERT_VALIDATOR
/*ssl_crtd_validator*/
Ssl::CertValidationHelper * Ssl::CertValidationHelper::GetInstance()
while ((token = strwordtok(NULL, &tmp))) {
wordlistAdd(&ssl_crt_validator->cmdline, token);
}
- safe_free(tmp_begin);
+ xfree(tmp_begin);
}
helperOpenServers(ssl_crt_validator);
}
if (first_warn == 0)
first_warn = squid_curtime;
if (squid_curtime - first_warn > 3 * 60)
- fatal("SSL servers not responding for 3 minutes");
- debugs(83, 1, HERE << "Queue overload, rejecting");
+ fatal("ssl_crtvd queue being overloaded for long time");
+ debugs(83, DBG_IMPORTANT, "WARNING: ssl_crtvd queue overload, rejecting");
const char *errMsg = "BH error 45 Temporary network problem, please retry later";
HelperReply failReply(errMsg,strlen(errMsg));
callback(data, failReply);
msg += '\n';
helperSubmit(ssl_crt_validator, msg.c_str(), callback, data);
}
+#endif // USE_SSL_CERT_VALIDATOR
* over GetIntance() static method. This class use helper structure
* for threads management.
*/
+#if USE_SSL_CRTD
class Helper
{
public:
helper * ssl_crtd; ///< helper for management of ssl_crtd.
};
+#endif
+#if 1 // USE_SSL_CERT_VALIDATOR
class CertValidationHelper
{
public:
helper * ssl_crt_validator; ///< helper for management of ssl_crtd.
};
+#endif // USE_SSL_CERT_VALIDATOR
} //namespace Ssl
#endif // SQUID_SSL_HELPER_H
#include "anyp/PortCfg.h"
#include "fde.h"
#include "globals.h"
-#include "protos.h"
#include "SquidConfig.h"
#include "ssl/Config.h"
#include "ssl/ErrorDetail.h"