From: Christos Tsantilas Date: Thu, 13 Dec 2012 22:31:55 +0000 (+0200) Subject: cert validation cache X-Git-Tag: SQUID_3_4_0_1~446 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=14798e73bd28260d30e4512e2376c3a7dbf51164;p=thirdparty%2Fsquid.git cert validation cache This patch add cache to cert validation helper. The following new options added to "sslcrtvalidator_program" configuration parameter to control cache behaviour: ttl=n TTL in seconds for cached results.The default is 60 secs cache=n limit the result cache size. The default value is 2048 To implement the cert validation cache a new template class investigated, the LruMap which implements a simple lru cache. The LruMap templete class also used to replace the old Ssl::LocalContextStorage class which implements a SSL contexts cache. This is a Measurement Factory project --- diff --git a/src/base/LruMap.h b/src/base/LruMap.h new file mode 100644 index 0000000000..0875989a24 --- /dev/null +++ b/src/base/LruMap.h @@ -0,0 +1,211 @@ + +/* + */ + +#ifndef SQUID_LRUMAP_H +#define SQUID_LRUMAP_H + +#include "SquidTime.h" +#if HAVE_LIST +#include +#endif +#if HAVE_MAP +#include +#endif + +template class LruMap +{ +public: + class Entry + { + public: + Entry(const char *aKey, EntryValue *t): key(aKey), value(t), date(squid_curtime) {} + ~Entry() {delete value;} + private: + Entry(LruMap::Entry &); + LruMap::Entry & operator = (LruMap::Entry &); + public: + std::string key; ///< the key of entry + EntryValue *value; ///< A pointer to the stored value + time_t date; ///< The date the entry created + }; + typedef std::list Queue; + typedef typename std::list::iterator QueueIterator; + + /// key:queue_item mapping for fast lookups by key + typedef std::map Map; + typedef typename Map::iterator MapIterator; + typedef std::pair MapPair; + + + LruMap(int ttl, size_t size); + ~LruMap(); + /// Search for an entry, and return a pointer + EntryValue *get(const char *key); + /// Add an entry to the map + bool add(const char *key, EntryValue *t); + /// Delete an entry from the map + bool del(const char *key); + /// (Re-)set the maximum size for this map + void setMemLimit(size_t aSize); + /// The available size for the map + size_t memLimit() const {return memLimit_;} + /// The free space of the map + size_t freeMem() const { return (memLimit() - size());} + /// The current size of the map + size_t size() const {return (entries_ * EntryCost);} + /// The number of stored entries + int entries() const {return entries_;} +private: + LruMap(LruMap const &); + LruMap & operator = (LruMap const &); + + bool expired(Entry &e); + void trim(); + void touch(const MapIterator &i); + bool del(const MapIterator &i); + void findEntry(const char *key, LruMap::MapIterator &i); + + Map storage; ///< The Key/value * pairs + Queue index; ///< LRU cache index + int ttl;///< >0 ttl for caching, == 0 cache is disabled, < 0 store for ever + size_t memLimit_; ///< The maximum memory to use + int entries_; ///< The stored entries +}; + +template +LruMap::LruMap(int aTtl, size_t aSize): entries_(0) +{ + ttl = aTtl; + + setMemLimit(aSize); +} + +template +LruMap::~LruMap() +{ + for (QueueIterator i = index.begin(); i != index.end(); ++i) { + delete *i; + } +} + +template +void +LruMap::setMemLimit(size_t aSize) +{ + if (aSize > 0) + memLimit_ = aSize; + else + memLimit_ = 0; +} + +template +void +LruMap::findEntry(const char *key, LruMap::MapIterator &i) +{ + i = storage.find(key); + if (i == storage.end()) { + return; + } + index.push_front(*(i->second)); + index.erase(i->second); + i->second = index.begin(); + + LruMap::Entry *e = *i->second; + + if (e && expired(*e)) { + del(i); + e = NULL; + } +} + +template +EntryValue * +LruMap::get(const char *key) +{ + LruMap::MapIterator i; + findEntry(key, i); + LruMap::Entry *e = *i->second; + if (i != storage.end()) { + touch(i); + return e->value; + } + return NULL; +} + +template +bool +LruMap::add(const char *key, EntryValue *t) +{ + if (ttl == 0) + return false; + + del(key); + trim(); + index.push_front(new Entry(key, t)); + storage.insert(MapPair(key, index.begin())); + + ++entries_; + return true; +} + +template +bool +LruMap::expired(LruMap::Entry &entry) +{ + if (ttl < 0) + return false; + + return (entry.date + ttl < squid_curtime); +} + +template +bool +LruMap::del(LruMap::MapIterator const &i) +{ + if (i != storage.end()) { + delete *(i->second); + index.erase(i->second); + storage.erase(i); + --entries_; + return true; + } + return false; +} + +template +bool +LruMap::del(const char *key) +{ + LruMap::MapIterator i; + findEntry(key, i); + return del(i); +} + +template +void +LruMap::trim() +{ + while(memLimit() > 0 && size() >= memLimit()) { + LruMap::QueueIterator i = index.end(); + --i; + if (i != index.end()) { + del((*i)->key.c_str()); + } + } +} + +template +void +LruMap::touch(LruMap::MapIterator const &i) +{ + // this must not be done when nothing is being cached. + if (ttl == 0) + return; + + index.push_front(*(i->second)); + index.erase(i->second); + i->second = index.begin(); +} + +#endif diff --git a/src/base/Makefile.am b/src/base/Makefile.am index f774d22f30..23ca7e0e2c 100644 --- a/src/base/Makefile.am +++ b/src/base/Makefile.am @@ -16,6 +16,7 @@ libbase_la_SOURCES = \ CbcPointer.h \ InstanceId.h \ Lock.h \ + LruMap.h \ RunnersRegistry.cc \ RunnersRegistry.h \ Subscription.h \ diff --git a/src/cf.data.pre b/src/cf.data.pre index 75d2ef536a..d04b07a618 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -2440,7 +2440,12 @@ DEFAULT: none LOC: Ssl::TheConfig.ssl_crt_validator DOC_START Specify the location and options of the executable for ssl_crt_validator - process. + process. Usage: + sslcrtvalidator_program [ttl=n] [cache=n] path ... + + Options: + ttl=n TTL in seconds for cached results.The default is 60 secs + cache=n limit the result cache size. The default value is 2048 DOC_END NAME: sslcrtvalidator_children diff --git a/src/client_side.cc b/src/client_side.cc index 6f84459fc4..3b9531f521 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -3823,8 +3823,9 @@ ConnStateData::getSslContextStart() debugs(33, 5, HERE << "Finding SSL certificate for " << sslBumpCertKey << " in cache"); Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s)); - SSL_CTX * dynCtx = ssl_ctx_cache.find(sslBumpCertKey.termedBuf()); - if (dynCtx) { + SSL_CTX * dynCtx = NULL; + Ssl::SSL_CTX_Pointer *cachedCtx = ssl_ctx_cache.get(sslBumpCertKey.termedBuf()); + if (cachedCtx && (dynCtx = cachedCtx->get())) { debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " have found in cache"); if (Ssl::verifySslCertificate(dynCtx, certProperties)) { debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is valid"); @@ -3832,7 +3833,7 @@ ConnStateData::getSslContextStart() return; } else { debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is out of date. Delete this certificate from cache"); - ssl_ctx_cache.remove(sslBumpCertKey.termedBuf()); + ssl_ctx_cache.del(sslBumpCertKey.termedBuf()); } } else { debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " haven't found in cache"); @@ -3877,7 +3878,7 @@ ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew) Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s)); assert(sslBumpCertKey.defined() && sslBumpCertKey[0] != '\0'); if (sslContext) { - if (!ssl_ctx_cache.add(sslBumpCertKey.termedBuf(), sslContext)) { + if (!ssl_ctx_cache.add(sslBumpCertKey.termedBuf(), new Ssl::SSL_CTX_Pointer(sslContext))) { // If it is not in storage delete after using. Else storage deleted it. fd_table[clientConnection->fd].dynamicSslContext = sslContext; } diff --git a/src/forward.cc b/src/forward.cc index a6ab3daae8..4ed6f43ae0 100644 --- a/src/forward.cc +++ b/src/forward.cc @@ -744,15 +744,11 @@ FwdState::negotiateSSL(int fd) if (Ssl::TheConfig.ssl_crt_validator) { Ssl::CertValidationRequest validationRequest; - // WARNING: The STACK_OF(*) OpenSSL objects does not support locking. - // If we need to support locking we need to sk_X509_dup the STACK_OF(X509) - // list and lock all of the X509 members of the list. - // Currently we do not use any locking for any of the members of the - // Ssl::CertValidationRequest class. If the ssl object gone, the value returned - // from SSL_get_peer_cert_chain may not exist any more. In this code the + // 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.peerCerts = SSL_get_peer_cert_chain(ssl); + validationRequest.ssl = ssl; validationRequest.domainName = request->GetHost(); if (Ssl::Errors *errs = static_cast(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors))) // validationRequest disappears on return so no need to cbdataReference @@ -761,11 +757,7 @@ FwdState::negotiateSSL(int fd) validationRequest.errors = NULL; try { 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, "SSL crtvd request: " << requestMsg.compose().c_str()); - Ssl::CertValidationHelper::GetInstance()->sslSubmit(requestMsg, sslCrtvdHandleReplyWrapper, this); + Ssl::CertValidationHelper::GetInstance()->sslSubmit(validationRequest, sslCrtvdHandleReplyWrapper, this); return; } catch (const std::exception &e) { debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " << @@ -788,14 +780,14 @@ FwdState::negotiateSSL(int fd) } void -FwdState::sslCrtvdHandleReplyWrapper(void *data, const HelperReply &reply) +FwdState::sslCrtvdHandleReplyWrapper(void *data, Ssl::CertValidationResponse const &validationResponse) { FwdState * fwd = (FwdState *)(data); - fwd->sslCrtvdHandleReply(reply); + fwd->sslCrtvdHandleReply(validationResponse); } void -FwdState::sslCrtvdHandleReply(const HelperReply &reply) +FwdState::sslCrtvdHandleReply(Ssl::CertValidationResponse const &validationResponse) { Ssl::Errors *errs = NULL; Ssl::ErrorDetail *errDetails = NULL; @@ -803,39 +795,17 @@ FwdState::sslCrtvdHandleReply(const HelperReply &reply) if (!Comm::IsConnOpen(serverConnection())) { return; } - SSL *ssl = fd_table[serverConnection()->fd].ssl; - if (!reply.other().hasContent()) { - debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper return reply"); - validatorFailed = true; - } else if (reply.result == HelperReply::BrokenHelper) { - debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper error response: " << reply.other().content()); + 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; - } 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, "Reply from ssl_crtvd for " << request->GetHost() << " is incorrect"); - validatorFailed = true; - } else { - if (reply.result == HelperReply::Okay) { - debugs(83, 5, "Certificate for " << request->GetHost() << " was successfully validated from ssl_crtvd"); - } else if (reply.result == HelperReply::Error) { - debugs(83, 5, "Certificate for " << request->GetHost() << " found buggy by ssl_crtvd"); - errs = sslCrtvdCheckForErrors(validationResponse, errDetails); - } else { - debugs(83, 5, "Certificate for " << request->GetHost() << " cannot be validated. ssl_crtvd response: " << replyMsg.getBody()); - validatorFailed = true; - } - if (!errDetails && !validatorFailed) { - dispatch(); - return; - } - } + if (!errDetails && !validatorFailed) { + dispatch(); + return; } ErrorState *anErr = NULL; @@ -874,7 +844,7 @@ FwdState::sslCrtvdHandleReply(const HelperReply &reply) /// 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 * -FwdState::sslCrtvdCheckForErrors(Ssl::CertValidationResponse &resp, Ssl::ErrorDetail *& errDetails) +FwdState::sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &resp, Ssl::ErrorDetail *& errDetails) { Ssl::Errors *errs = NULL; diff --git a/src/forward.h b/src/forward.h index e269703141..73e359b210 100644 --- a/src/forward.h +++ b/src/forward.h @@ -86,11 +86,11 @@ public: #if USE_SSL /// Callback function called when squid receive message from cert validator helper - static void sslCrtvdHandleReplyWrapper(void *data, const HelperReply &reply); + static void sslCrtvdHandleReplyWrapper(void *data, Ssl::CertValidationResponse const &); /// Process response from cert validator helper - void sslCrtvdHandleReply(const HelperReply &reply); + void sslCrtvdHandleReply(Ssl::CertValidationResponse const &); /// Check SSL errors returned from cert validator against sslproxy_cert_error access list - Ssl::Errors *sslCrtvdCheckForErrors(Ssl::CertValidationResponse &, Ssl::ErrorDetail *&); + Ssl::Errors *sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &, Ssl::ErrorDetail *&); #endif private: // hidden for safer management of self; use static fwdStart diff --git a/src/ssl/cert_validate_message.cc b/src/ssl/cert_validate_message.cc index 1110982355..c6c310341f 100644 --- a/src/ssl/cert_validate_message.cc +++ b/src/ssl/cert_validate_message.cc @@ -20,11 +20,12 @@ Ssl::CertValidationMsg::composeRequest(CertValidationRequest const &vcert) } } - if (vcert.peerCerts) { + 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(vcert.peerCerts); ++i) { - X509 *cert = sk_X509_value(vcert.peerCerts, i); + 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) + "="; char *ptr; diff --git a/src/ssl/cert_validate_message.h b/src/ssl/cert_validate_message.h index b5d4f19154..c5a465ff15 100644 --- a/src/ssl/cert_validate_message.h +++ b/src/ssl/cert_validate_message.h @@ -18,10 +18,10 @@ namespace Ssl class CertValidationRequest { public: - STACK_OF(X509) *peerCerts; ///< The list of sent by SSL server + SSL *ssl; Errors *errors; ///< The list of errors detected std::string domainName; ///< The server name - CertValidationRequest() : peerCerts(NULL), errors(NULL) {} + CertValidationRequest() : ssl(NULL), errors(NULL) {} }; /** @@ -54,6 +54,7 @@ public: /// If none found a new RecvdError item added with the given id; RecvdError &getError(int errorId); RecvdErrors errors; ///< The list of parsed errors + HelperReply::Result_ resultCode; ///< The helper result code }; /** diff --git a/src/ssl/context_storage.cc b/src/ssl/context_storage.cc index 5eb74bd08f..a5944f8507 100644 --- a/src/ssl/context_storage.cc +++ b/src/ssl/context_storage.cc @@ -34,93 +34,16 @@ void Ssl::CertificateStorageAction::dump (StoreEntry *sentry) for (std::map::iterator i = TheGlobalContextStorage.storage.begin(); i != TheGlobalContextStorage.storage.end(); ++i) { stream << i->first << delimiter; LocalContextStorage & ssl_store_policy(*(i->second)); - stream << ssl_store_policy.max_memory / 1024 << delimiter; - stream << ssl_store_policy.memory_used / SSL_CTX_SIZE << delimiter; + stream << ssl_store_policy.memLimit() / 1024 << delimiter; + stream << ssl_store_policy.entries() << delimiter; stream << SSL_CTX_SIZE / 1024 << delimiter; - stream << ssl_store_policy.memory_used / 1024 << delimiter; - stream << (ssl_store_policy.max_memory - ssl_store_policy.memory_used) / 1024 << endString; + stream << ssl_store_policy.size() / 1024 << delimiter; + stream << ssl_store_policy.freeMem() / 1024 << endString; } stream << endString; stream.flush(); } -Ssl::LocalContextStorage::LocalContextStorage(size_t aMax_memory) - : max_memory(aMax_memory), memory_used(0) -{} - -Ssl::LocalContextStorage::~LocalContextStorage() -{ - for (QueueIterator i = lru_queue.begin(); i != lru_queue.end(); ++i) { - delete *i; - } -} - -SSL_CTX * Ssl::LocalContextStorage::add(const char * host_name, SSL_CTX * ssl_ctx) -{ - if (max_memory < SSL_CTX_SIZE) { - return NULL; - } - remove(host_name); - while (SSL_CTX_SIZE + memory_used > max_memory) { - purgeOne(); - } - lru_queue.push_front(new Item(ssl_ctx, host_name)); - storage.insert(MapPair(host_name, lru_queue.begin())); - memory_used += SSL_CTX_SIZE; - return ssl_ctx; -} - -SSL_CTX * Ssl::LocalContextStorage::find(char const * host_name) -{ - MapIterator i = storage.find(host_name); - if (i == storage.end()) { - return NULL; - } - lru_queue.push_front(*(i->second)); - lru_queue.erase(i->second); - i->second = lru_queue.begin(); - return (*lru_queue.begin())->ssl_ctx; -} - -void Ssl::LocalContextStorage::remove(char const * host_name) -{ - deleteAt(storage.find(host_name)); -} - -void Ssl::LocalContextStorage::purgeOne() -{ - QueueIterator i = lru_queue.end(); - --i; - if (i != lru_queue.end()) { - remove((*i)->host_name.c_str()); - } -} - -void Ssl::LocalContextStorage::deleteAt(LocalContextStorage::MapIterator i) -{ - if (i != storage.end()) { - - delete *(i->second); - lru_queue.erase(i->second); - storage.erase(i); - memory_used -= SSL_CTX_SIZE; - } -} - -void Ssl::LocalContextStorage::SetSize(size_t aMax_memory) -{ - max_memory = aMax_memory; -} - -Ssl::LocalContextStorage::Item::Item(SSL_CTX * aSsl_ctx, std::string const & aName) - : ssl_ctx(aSsl_ctx), host_name(aName) -{} - -Ssl::LocalContextStorage::Item::~Item() -{ - SSL_CTX_free(ssl_ctx); -} - /////////////////////////////////////////////////////// Ssl::GlobalContextStorage::GlobalContextStorage() @@ -166,14 +89,14 @@ void Ssl::GlobalContextStorage::reconfigureFinish() if (conf_i == configureStorage.end()) { storage.erase(i); } else { - i->second->SetSize(conf_i->second); + i->second->setMemLimit(conf_i->second); } } // add new local storages. for (std::map::iterator conf_i = configureStorage.begin(); conf_i != configureStorage.end(); ++conf_i ) { if (storage.find(conf_i->first) == storage.end()) { - storage.insert(std::pair(conf_i->first, new LocalContextStorage(conf_i->second))); + storage.insert(std::pair(conf_i->first, new LocalContextStorage(-1, conf_i->second))); } } } diff --git a/src/ssl/context_storage.h b/src/ssl/context_storage.h index a1df921b65..59dd08c7d5 100644 --- a/src/ssl/context_storage.h +++ b/src/ssl/context_storage.h @@ -3,11 +3,13 @@ #if USE_SSL +#include "base/LruMap.h" #include "SquidTime.h" #include "CacheManager.h" #include "ip/Address.h" #include "mgr/Action.h" #include "mgr/Command.h" +#include "ssl/gadgets.h" #if HAVE_MAP #include #endif @@ -38,53 +40,7 @@ public: virtual bool aggregatable() const { return false; } }; -/** - * Memory cache for store generated SSL context. Enforces total size limits - * using an LRU algorithm. - */ -class LocalContextStorage -{ - friend class CertificateStorageAction; -public: - /// Cache item is an (SSL_CTX, host name) tuple. - class Item - { - public: - Item(SSL_CTX * aSsl_ctx, std::string const & aName); - ~Item(); - public: - SSL_CTX * ssl_ctx; ///< The SSL context. - std::string host_name; ///< The host name of the SSL context. - }; - - typedef std::list Queue; - typedef Queue::iterator QueueIterator; - - /// host_name:queue_item mapping for fast lookups by host name - typedef std::map Map; - typedef Map::iterator MapIterator; - typedef std::pair MapPair; - - LocalContextStorage(size_t aMax_memory); - ~LocalContextStorage(); - /// Set maximum memory size for this storage. - void SetSize(size_t aMax_memory); - /// Return a pointer to the added ssl_ctx or NULL if fails (eg. max cache size equal 0). - SSL_CTX * add(char const * host_name, SSL_CTX * ssl_ctx); - /// Find SSL_CTX in storage by host name. Lru queue will be updated. - SSL_CTX * find(char const * host_name); - void remove(char const * host_name); ///< Delete the SSL context by hostname - -private: - void purgeOne(); ///< Delete oldest object. - /// Delete object by iterator. It is used in deletePurge() and remove(...) methods. - void deleteAt(MapIterator i); - - size_t max_memory; ///< Max cache size. - size_t memory_used; ///< Used cache size. - Map storage; ///< The hostnames/SSL_CTX * pairs - Queue lru_queue; ///< LRU cache index -}; +typedef LruMap LocalContextStorage; /// Class for storing/manipulating LocalContextStorage per local listening address/port. class GlobalContextStorage diff --git a/src/ssl/helper.cc b/src/ssl/helper.cc index a025d05c21..13e7f0293b 100644 --- a/src/ssl/helper.cc +++ b/src/ssl/helper.cc @@ -5,9 +5,12 @@ #include "SquidString.h" #include "SquidTime.h" #include "SwapDir.h" +#include "ssl/cert_validate_message.h" #include "wordlist.h" #include "SquidConfig.h" +LruMap *Ssl::CertValidationHelper::HelperCache = NULL; + #if USE_SSL_CRTD Ssl::Helper * Ssl::Helper::GetInstance() { @@ -145,16 +148,34 @@ void Ssl::CertValidationHelper::Init() // going to use the '\1' char as the end-of-message mark. ssl_crt_validator->eom = '\1'; assert(ssl_crt_validator->cmdline == NULL); + + int ttl = 60; + size_t cache = 2048; { 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); + continue; + } else if (strncmp(token, "cache=", 6) == 0) { + cache = atoi(token + 6); + continue; + } else + parseParams = false; + } wordlistAdd(&ssl_crt_validator->cmdline, token); } xfree(tmp_begin); } helperOpenServers(ssl_crt_validator); + + //WARNING: initializing static member in an object initialization method + assert(HelperCache == NULL); + HelperCache = new LruMap(ttl, cache); } void Ssl::CertValidationHelper::Shutdown() @@ -165,9 +186,58 @@ void Ssl::CertValidationHelper::Shutdown() wordlistDestroy(&ssl_crt_validator->cmdline); delete ssl_crt_validator; ssl_crt_validator = NULL; + + // CertValidationHelper::HelperCache is a static member, it is not good policy to + // reset it here. Will work because the current Ssl::CertValidationHelper is + // always the same static object. + delete HelperCache; + HelperCache = NULL; +} + +struct submitData { + std::string query; + Ssl::CertValidationHelper::CVHCB *callback; + void *data; + SSL *ssl; + CBDATA_CLASS2(submitData); +}; +CBDATA_CLASS_INIT(submitData); + +static void +sslCrtvdHandleReplyWrapper(void *data, const HelperReply &reply) +{ + Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY); + Ssl::CertValidationResponse *validationResponse = new Ssl::CertValidationResponse; + std::string error; + + submitData *crtdvdData = static_cast(data); + STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(crtdvdData->ssl); + if (reply.result == HelperReply::BrokenHelper) { + debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper error response: " << reply.other().content()); + validationResponse->resultCode = HelperReply::BrokenHelper; + } else if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK || + !replyMsg.parseResponse(*validationResponse, peerCerts, 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 = HelperReply::BrokenHelper; + } + else + validationResponse->resultCode = reply.result; + + crtdvdData->callback(crtdvdData->data, *validationResponse); + + if (Ssl::CertValidationHelper::HelperCache && + (validationResponse->resultCode == HelperReply::Okay || validationResponse->resultCode == HelperReply::Error)) { + Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query.c_str(), validationResponse); + } else + delete validationResponse; + + cbdataReferenceDone(crtdvdData->data); + SSL_free(crtdvdData->ssl); + delete crtdvdData; } -void Ssl::CertValidationHelper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void * data) +void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &request, Ssl::CertValidationHelper::CVHCB * callback, void * data) { static time_t first_warn = 0; assert(ssl_crt_validator); @@ -178,15 +248,34 @@ void Ssl::CertValidationHelper::sslSubmit(CrtdMessage const & message, HLPCB * c if (squid_curtime - first_warn > 3 * 60) fatal("ssl_crtvd queue being overloaded for long time"); debugs(83, DBG_IMPORTANT, "WARNING: ssl_crtvd queue overload, rejecting"); - HelperReply failReply; - failReply.result = HelperReply::BrokenHelper; - failReply.notes.add("message", "error 45 Temporary network problem, please retry later"); - callback(data, failReply); + Ssl::CertValidationResponse resp; + resp.resultCode = HelperReply::BrokenHelper; + callback(data, resp); return; } - first_warn = 0; - std::string msg = message.compose(); - msg += '\n'; - helperSubmit(ssl_crt_validator, msg.c_str(), callback, data); + + 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->callback = callback; + crtdvdData->data = cbdataReference(data); + crtdvdData->ssl = request.ssl; + CRYPTO_add(&crtdvdData->ssl->references,1,CRYPTO_LOCK_SSL); + Ssl::CertValidationResponse const*validationResponse; + + if (CertValidationHelper::HelperCache && + (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query.c_str()))) { + callback(data, *validationResponse); + cbdataReferenceDone(crtdvdData->data); + SSL_free(crtdvdData->ssl); + delete crtdvdData; + return; + } + helperSubmit(ssl_crt_validator, crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData); } diff --git a/src/ssl/helper.h b/src/ssl/helper.h index cc51ec8208..0044191a02 100644 --- a/src/ssl/helper.h +++ b/src/ssl/helper.h @@ -1,7 +1,9 @@ #ifndef SQUID_SSL_HELPER_H #define SQUID_SSL_HELPER_H +#include "base/LruMap.h" #include "../helper.h" +#include "ssl/cert_validate_message.h" #include "ssl/crtd_message.h" namespace Ssl @@ -28,19 +30,24 @@ private: }; #endif +class CertValidationRequest; +class CertValidationResponse; class CertValidationHelper { public: + typedef void CVHCB(void *, Ssl::CertValidationResponse const &); static CertValidationHelper * GetInstance(); ///< Instance class. void Init(); ///< Init helper structure. void Shutdown(); ///< Shutdown helper structure. - /// Submit crtd message to external crtd server. - void sslSubmit(CrtdMessage const & message, HLPCB * callback, void *data); + /// Submit crtd request message to external crtd server. + void sslSubmit(Ssl::CertValidationRequest const & request, CVHCB * callback, void *data); private: CertValidationHelper(); ~CertValidationHelper(); helper * ssl_crt_validator; ///< helper for management of ssl_crtd. +public: + static LruMap *HelperCache; ///< cache for cert validation helper }; } //namespace Ssl diff --git a/src/tests/stub_libsslsquid.cc b/src/tests/stub_libsslsquid.cc index e2ccc07e2f..08f3c5f26d 100644 --- a/src/tests/stub_libsslsquid.cc +++ b/src/tests/stub_libsslsquid.cc @@ -18,19 +18,9 @@ Ssl::Config Ssl::TheConfig; //Ssl::CertificateStorageAction::CertificateStorageAction(const Mgr::Command::Pointer &cmd) STUB Ssl::CertificateStorageAction::Pointer Ssl::CertificateStorageAction::Create(const Mgr::Command::Pointer &cmd) STUB_RETSTATREF(Ssl::CertificateStorageAction::Pointer) void Ssl::CertificateStorageAction::dump(StoreEntry *sentry) STUB -Ssl::LocalContextStorage::Item::Item(SSL_CTX * aSsl_ctx, std::string const & aName) STUB -Ssl::LocalContextStorage::Item::~Item() STUB -Ssl::LocalContextStorage::LocalContextStorage(size_t aMax_memory) STUB -Ssl::LocalContextStorage::~LocalContextStorage() STUB -void Ssl::LocalContextStorage::SetSize(size_t aMax_memory) STUB -SSL_CTX * Ssl::LocalContextStorage::add(char const * host_name, SSL_CTX * ssl_ctx) STUB_RETVAL(NULL) -SSL_CTX * Ssl::LocalContextStorage::find(char const * host_name) STUB_RETVAL(NULL) -void Ssl::LocalContextStorage::remove(char const * host_name) STUB -//Ssl::GlobalContextStorage::GlobalContextStorage() STUB -//Ssl::GlobalContextStorage::~GlobalContextStorage() STUB void Ssl::GlobalContextStorage::addLocalStorage(Ip::Address const & address, size_t size_of_store) STUB Ssl::LocalContextStorage & Ssl::GlobalContextStorage::getLocalStorage(Ip::Address const & address) -{ fatal(STUB_API " required"); static Ssl::LocalContextStorage v(0); return v; } +{ fatal(STUB_API " required"); static Ssl::LocalContextStorage v(0,0); return v; } void Ssl::GlobalContextStorage::reconfigureStart() STUB //Ssl::GlobalContextStorage Ssl::TheGlobalContextStorage;