/*
- * Copyright (C) 1996-2016 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/helper.h"
#include "wordlist.h"
-Ssl::CertValidationHelper::LruCache *Ssl::CertValidationHelper::HelperCache = nullptr;
+#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::~Helper()
+/// Ssl::Helper query:GeneratorRequest map
+typedef std::unordered_map<SBuf, GeneratorRequest*> GeneratorRequests;
+
+static void HandleGeneratorReply(void *data, const ::Helper::Reply &reply);
+
+} // 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;
+ found = s->flags.tunnelSslBumping && s->secure.generateHostCertificates;
if (!found)
return;
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(::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
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 Ssl::CertValidationHelper::LruCache(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;
+ SBuf query;
AsyncCall::Pointer callback;
- SSL *ssl;
+ Security::SessionPointer ssl;
};
CBDATA_CLASS_INIT(submitData);
sslCrtvdHandleReplyWrapper(void *data, const ::Helper::Reply &reply)
{
Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY);
- Ssl::CertValidationResponse::Pointer 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;
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;
if (Ssl::CertValidationHelper::HelperCache &&
(validationResponse->resultCode == ::Helper::Okay || validationResponse->resultCode == ::Helper::Error)) {
- Ssl::CertValidationResponse::Pointer *item = new Ssl::CertValidationResponse::Pointer(validationResponse);
- if (!Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query.c_str(), item))
- delete item;
+ (void)Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query, validationResponse);
}
- SSL_free(crtdvdData->ssl);
delete crtdvdData;
}
-void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &request, AsyncCall::Pointer &callback)
+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->ssl = request.ssl;
- CRYPTO_add(&crtdvdData->ssl->references,1,CRYPTO_LOCK_SSL);
Ssl::CertValidationResponse::Pointer const*validationResponse;
if (CertValidationHelper::HelperCache &&
- (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query.c_str()))) {
+ (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query))) {
CertValidationHelper::CbDialer *dialer = dynamic_cast<CertValidationHelper::CbDialer*>(callback->getDialer());
Must(dialer);
dialer->arg1 = *validationResponse;
ScheduleCallHere(callback);
- SSL_free(crtdvdData->ssl);
delete crtdvdData;
return;
}
- if (!ssl_crt_validator->trySubmit(crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData)) {
- Ssl::CertValidationResponse::Pointer resp = new Ssl::CertValidationResponse;;
- resp->resultCode = ::Helper::BrokenHelper;
- Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(callback->getDialer());
- Must(dialer);
- dialer->arg1 = resp;
- ScheduleCallHere(callback);
-
- 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;
}