/*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "squid.h"
#include "../helper.h"
#include "anyp/PortCfg.h"
+#include "cache_cf.h"
+#include "fs_io.h"
#include "helper/Reply.h"
+#include "Parsing.h"
+#include "sbuf/Stream.h"
#include "SquidConfig.h"
#include "SquidString.h"
#include "SquidTime.h"
#include "ssl/cert_validate_message.h"
#include "ssl/Config.h"
#include "ssl/helper.h"
-#include "SwapDir.h"
#include "wordlist.h"
-LruMap<Ssl::CertValidationResponse> *Ssl::CertValidationHelper::HelperCache = NULL;
+#include <limits>
+
+Ssl::CertValidationHelper::CacheType *Ssl::CertValidationHelper::HelperCache = nullptr;
#if USE_SSL_CRTD
-Ssl::Helper * Ssl::Helper::GetInstance()
-{
- static Ssl::Helper sslHelper;
- return &sslHelper;
-}
-Ssl::Helper::Helper() : ssl_crtd(NULL)
-{
-}
+namespace Ssl {
+
+/// Initiator of an Ssl::Helper query.
+class GeneratorRequestor {
+public:
+ GeneratorRequestor(HLPCB *aCallback, void *aData): callback(aCallback), data(aData) {}
+ HLPCB *callback;
+ CallbackData data;
+};
+
+/// A pending Ssl::Helper request, combining the original and collapsed queries.
+class GeneratorRequest {
+ CBDATA_CLASS(GeneratorRequest);
+
+public:
+ /// adds a GeneratorRequestor
+ void emplace(HLPCB *callback, void *data) { requestors.emplace_back(callback, data); }
+
+ SBuf query; ///< Ssl::Helper request message (GeneratorRequests key)
+
+ /// Ssl::Helper request initiators waiting for the same answer (FIFO).
+ typedef std::vector<GeneratorRequestor> GeneratorRequestors;
+ GeneratorRequestors requestors;
+};
+
+/// Ssl::Helper query:GeneratorRequest map
+typedef std::unordered_map<SBuf, GeneratorRequest*> GeneratorRequests;
+
+static void HandleGeneratorReply(void *data, const ::Helper::Reply &reply);
-Ssl::Helper::~Helper()
+} // namespace Ssl
+
+CBDATA_NAMESPACED_CLASS_INIT(Ssl, GeneratorRequest);
+
+/// prints Ssl::GeneratorRequest for debugging
+static std::ostream &
+operator <<(std::ostream &os, const Ssl::GeneratorRequest &gr)
{
- Shutdown();
+ return os << "crtGenRq" << gr.query.id.value << "/" << gr.requestors.size();
}
+/// pending Ssl::Helper requests (to all certificate generator helpers combined)
+static Ssl::GeneratorRequests TheGeneratorRequests;
+
+helper *Ssl::Helper::ssl_crtd = nullptr;
+
void Ssl::Helper::Init()
{
assert(ssl_crtd == NULL);
// TODO: generate host certificates for SNI enabled accel ports
bool found = false;
for (AnyP::PortCfgPointer s = HttpPortList; !found && s != NULL; s = s->next)
- found = s->flags.tunnelSslBumping && s->generateHostCertificates;
- for (AnyP::PortCfgPointer s = HttpsPortList; !found && s != NULL; s = s->next)
- found = s->flags.tunnelSslBumping && s->generateHostCertificates;
+ found = s->flags.tunnelSslBumping && s->secure.generateHostCertificates;
if (!found)
return;
- ssl_crtd = new helper("ssl_crtd");
+ ssl_crtd = new helper(Ssl::TheConfig.ssl_crtd);
ssl_crtd->childs.updateLimits(Ssl::TheConfig.ssl_crtdChildren);
ssl_crtd->ipc_type = IPC_STREAM;
// The crtd messages may contain the eol ('\n') character. We are
{
char *tmp = xstrdup(Ssl::TheConfig.ssl_crtd);
char *tmp_begin = tmp;
- char * token = NULL;
- bool db_path_was_found = false;
- bool block_size_was_found = false;
- char buffer[20] = "2048";
+ char *token = NULL;
while ((token = strwordtok(NULL, &tmp))) {
wordlistAdd(&ssl_crtd->cmdline, token);
- if (!strcmp(token, "-b"))
- block_size_was_found = true;
- if (!strcmp(token, "-s")) {
- db_path_was_found = true;
- } else if (db_path_was_found) {
- db_path_was_found = false;
- int fs_block_size = 0;
- storeDirGetBlkSize(token, &fs_block_size);
- snprintf(buffer, sizeof(buffer), "%i", fs_block_size);
- }
- }
- if (!block_size_was_found) {
- wordlistAdd(&ssl_crtd->cmdline, "-b");
- wordlistAdd(&ssl_crtd->cmdline, buffer);
}
safe_free(tmp_begin);
}
ssl_crtd = NULL;
}
-void Ssl::Helper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void * data)
+void
+Ssl::Helper::Reconfigure()
{
- assert(ssl_crtd);
-
- std::string msg = message.compose();
- msg += '\n';
- if (!ssl_crtd->trySubmit(msg.c_str(), callback, data)) {
- ::Helper::Reply failReply;
- failReply.result = ::Helper::BrokenHelper;
- failReply.notes.add("message", "error 45 Temporary network problem, please retry later");
- callback(data, failReply);
- return;
- }
+ Shutdown();
+ Init();
}
-#endif //USE_SSL_CRTD
-Ssl::CertValidationHelper * Ssl::CertValidationHelper::GetInstance()
+void Ssl::Helper::Submit(CrtdMessage const & message, HLPCB * callback, void * data)
{
- static Ssl::CertValidationHelper sslHelper;
- if (!Ssl::TheConfig.ssl_crt_validator)
- return NULL;
- return &sslHelper;
-}
+ SBuf rawMessage(message.compose().c_str()); // XXX: helpers cannot use SBuf
+ rawMessage.append("\n", 1);
-Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL)
-{
+ const auto pending = TheGeneratorRequests.find(rawMessage);
+ if (pending != TheGeneratorRequests.end()) {
+ pending->second->emplace(callback, data);
+ debugs(83, 5, "collapsed request from " << data << " onto " << *pending->second);
+ return;
+ }
+
+ GeneratorRequest *request = new GeneratorRequest;
+ request->query = rawMessage;
+ request->emplace(callback, data);
+ TheGeneratorRequests.emplace(request->query, request);
+ debugs(83, 5, "request from " << data << " as " << *request);
+ // ssl_crtd becomes nil if Squid is reconfigured without SslBump or
+ // certificate generation disabled in the new configuration
+ if (ssl_crtd && ssl_crtd->trySubmit(request->query.c_str(), HandleGeneratorReply, request))
+ return;
+
+ ::Helper::Reply failReply(::Helper::BrokenHelper);
+ failReply.notes.add("message", "error 45 Temporary network problem, please retry later");
+ HandleGeneratorReply(request, failReply);
}
-Ssl::CertValidationHelper::~CertValidationHelper()
+/// receives helper response
+static void
+Ssl::HandleGeneratorReply(void *data, const ::Helper::Reply &reply)
{
- Shutdown();
+ const std::unique_ptr<Ssl::GeneratorRequest> request(static_cast<Ssl::GeneratorRequest*>(data));
+ assert(request);
+ const auto erased = TheGeneratorRequests.erase(request->query);
+ assert(erased);
+
+ for (auto &requestor: request->requestors) {
+ if (void *cbdata = requestor.data.validDone()) {
+ debugs(83, 5, "to " << cbdata << " in " << *request);
+ requestor.callback(cbdata, reply);
+ }
+ }
}
+#endif //USE_SSL_CRTD
+
+helper *Ssl::CertValidationHelper::ssl_crt_validator = nullptr;
void Ssl::CertValidationHelper::Init()
{
+ if (!Ssl::TheConfig.ssl_crt_validator)
+ return;
+
assert(ssl_crt_validator == NULL);
// we need to start ssl_crtd only if some port(s) need to bump SSL
bool found = false;
for (AnyP::PortCfgPointer s = HttpPortList; !found && s != NULL; s = s->next)
found = s->flags.tunnelSslBumping;
- for (AnyP::PortCfgPointer s = HttpsPortList; !found && s != NULL; s = s->next)
- found = s->flags.tunnelSslBumping;
if (!found)
return;
ssl_crt_validator->eom = '\1';
assert(ssl_crt_validator->cmdline == NULL);
- int ttl = 60;
- size_t cache = 2048;
+ /* defaults */
+ int ttl = 3600; // 1 hour
+ size_t cache = 64*1024*1024; // 64 MB
{
+ // TODO: Do this during parseConfigFile() for proper parsing, error handling
char *tmp = xstrdup(Ssl::TheConfig.ssl_crt_validator);
char *tmp_begin = tmp;
char * token = NULL;
bool parseParams = true;
while ((token = strwordtok(NULL, &tmp))) {
if (parseParams) {
- if (strncmp(token, "ttl=", 4) == 0) {
- ttl = atoi(token + 4);
+ if (strcmp(token, "ttl=infinity") == 0) {
+ ttl = std::numeric_limits<CacheType::Ttl>::max();
+ continue;
+ } else if (strncmp(token, "ttl=", 4) == 0) {
+ ttl = xatoi(token + 4);
+ if (ttl < 0) {
+ throw TextException(ToSBuf("Negative TTL in sslcrtvalidator_program ", Ssl::TheConfig.ssl_crt_validator,
+ Debug::Extra, "For unlimited TTL, use ttl=infinity"),
+ Here());
+ }
continue;
} else if (strncmp(token, "cache=", 6) == 0) {
- cache = atoi(token + 6);
+ cache = xatoi(token + 6);
continue;
} else
parseParams = false;
//WARNING: initializing static member in an object initialization method
assert(HelperCache == NULL);
- HelperCache = new LruMap<Ssl::CertValidationResponse>(ttl, cache);
+ HelperCache = new CacheType(cache, ttl);
}
void Ssl::CertValidationHelper::Shutdown()
HelperCache = NULL;
}
+void
+Ssl::CertValidationHelper::Reconfigure()
+{
+ Shutdown();
+ Init();
+}
+
class submitData
{
CBDATA_CLASS(submitData);
public:
- std::string query;
- Ssl::CertValidationHelper::CVHCB *callback;
- void *data;
- SSL *ssl;
+ SBuf query;
+ AsyncCall::Pointer callback;
+ Security::SessionPointer ssl;
};
CBDATA_CLASS_INIT(submitData);
sslCrtvdHandleReplyWrapper(void *data, const ::Helper::Reply &reply)
{
Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY);
- Ssl::CertValidationResponse *validationResponse = new Ssl::CertValidationResponse;
std::string error;
submitData *crtdvdData = static_cast<submitData *>(data);
- STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(crtdvdData->ssl);
+ assert(crtdvdData->ssl.get());
+ Ssl::CertValidationResponse::Pointer validationResponse = new Ssl::CertValidationResponse(crtdvdData->ssl);
if (reply.result == ::Helper::BrokenHelper) {
debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper error response: " << reply.other().content());
validationResponse->resultCode = ::Helper::BrokenHelper;
+ } else if (!reply.other().hasContent()) {
+ debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper returned NULL response");
+ validationResponse->resultCode = ::Helper::BrokenHelper;
} else if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK ||
- !replyMsg.parseResponse(*validationResponse, peerCerts, error) ) {
+ !replyMsg.parseResponse(*validationResponse, error) ) {
debugs(83, DBG_IMPORTANT, "WARNING: Reply from ssl_crtvd for " << " is incorrect");
debugs(83, DBG_IMPORTANT, "Certificate cannot be validated. ssl_crtvd response: " << replyMsg.getBody());
validationResponse->resultCode = ::Helper::BrokenHelper;
} else
validationResponse->resultCode = reply.result;
- crtdvdData->callback(crtdvdData->data, *validationResponse);
+ Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(crtdvdData->callback->getDialer());
+ Must(dialer);
+ dialer->arg1 = validationResponse;
+ ScheduleCallHere(crtdvdData->callback);
if (Ssl::CertValidationHelper::HelperCache &&
(validationResponse->resultCode == ::Helper::Okay || validationResponse->resultCode == ::Helper::Error)) {
- Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query.c_str(), validationResponse);
- } else
- delete validationResponse;
+ (void)Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query, validationResponse);
+ }
- cbdataReferenceDone(crtdvdData->data);
- SSL_free(crtdvdData->ssl);
delete crtdvdData;
}
-void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &request, Ssl::CertValidationHelper::CVHCB * callback, void * data)
+void Ssl::CertValidationHelper::Submit(Ssl::CertValidationRequest const &request, AsyncCall::Pointer &callback)
{
- assert(ssl_crt_validator);
-
Ssl::CertValidationMsg message(Ssl::CrtdMessage::REQUEST);
message.setCode(Ssl::CertValidationMsg::code_cert_validate);
message.composeRequest(request);
debugs(83, 5, "SSL crtvd request: " << message.compose().c_str());
submitData *crtdvdData = new submitData;
- crtdvdData->query = message.compose();
- crtdvdData->query += '\n';
+ crtdvdData->query.assign(message.compose().c_str());
+ crtdvdData->query.append('\n');
crtdvdData->callback = callback;
- crtdvdData->data = cbdataReference(data);
crtdvdData->ssl = request.ssl;
- CRYPTO_add(&crtdvdData->ssl->references,1,CRYPTO_LOCK_SSL);
- Ssl::CertValidationResponse const*validationResponse;
+ Ssl::CertValidationResponse::Pointer const*validationResponse;
if (CertValidationHelper::HelperCache &&
- (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query.c_str()))) {
- callback(data, *validationResponse);
- cbdataReferenceDone(crtdvdData->data);
- SSL_free(crtdvdData->ssl);
+ (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query))) {
+
+ CertValidationHelper::CbDialer *dialer = dynamic_cast<CertValidationHelper::CbDialer*>(callback->getDialer());
+ Must(dialer);
+ dialer->arg1 = *validationResponse;
+ ScheduleCallHere(callback);
delete crtdvdData;
return;
}
- if (!ssl_crt_validator->trySubmit(crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData)) {
- Ssl::CertValidationResponse resp;
- resp.resultCode = ::Helper::BrokenHelper;
- callback(data, resp);
-
- cbdataReferenceDone(crtdvdData->data);
- SSL_free(crtdvdData->ssl);
- delete crtdvdData;
+ // ssl_crt_validator becomes nil if Squid is reconfigured with cert
+ // validator disabled in the new configuration
+ if (ssl_crt_validator && ssl_crt_validator->trySubmit(crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData))
return;
- }
+
+ Ssl::CertValidationResponse::Pointer resp = new Ssl::CertValidationResponse(crtdvdData->ssl);
+ resp->resultCode = ::Helper::BrokenHelper;
+ Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(callback->getDialer());
+ Must(dialer);
+ dialer->arg1 = resp;
+ ScheduleCallHere(callback);
+ delete crtdvdData;
+ return;
}