]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
cert validation cache
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Thu, 13 Dec 2012 22:31:55 +0000 (00:31 +0200)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Thu, 13 Dec 2012 22:31:55 +0000 (00:31 +0200)
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

13 files changed:
src/base/LruMap.h [new file with mode: 0644]
src/base/Makefile.am
src/cf.data.pre
src/client_side.cc
src/forward.cc
src/forward.h
src/ssl/cert_validate_message.cc
src/ssl/cert_validate_message.h
src/ssl/context_storage.cc
src/ssl/context_storage.h
src/ssl/helper.cc
src/ssl/helper.h
src/tests/stub_libsslsquid.cc

diff --git a/src/base/LruMap.h b/src/base/LruMap.h
new file mode 100644 (file)
index 0000000..0875989
--- /dev/null
@@ -0,0 +1,211 @@
+
+/*
+ */
+
+#ifndef SQUID_LRUMAP_H
+#define SQUID_LRUMAP_H
+
+#include "SquidTime.h"
+#if HAVE_LIST
+#include <list>
+#endif
+#if HAVE_MAP
+#include <map>
+#endif
+
+template <class EntryValue, size_t EntryCost = sizeof(EntryValue)> 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<EntryValue, EntryCost>::Entry &);
+        LruMap<EntryValue, EntryCost>::Entry & operator = (LruMap<EntryValue, EntryCost>::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<Entry *> Queue;
+    typedef typename std::list<Entry *>::iterator QueueIterator;
+    
+    /// key:queue_item mapping for fast lookups by key
+    typedef std::map<std::string, QueueIterator> Map;
+    typedef typename Map::iterator MapIterator;
+    typedef std::pair<std::string, QueueIterator> 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<EntryValue, EntryCost> const &);
+    LruMap<EntryValue, EntryCost> & operator = (LruMap<EntryValue, EntryCost> 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 <class EntryValue, size_t EntryCost>
+LruMap<EntryValue, EntryCost>::LruMap(int aTtl, size_t aSize): entries_(0)
+{
+    ttl = aTtl;
+
+    setMemLimit(aSize);
+}
+
+template <class EntryValue, size_t EntryCost>
+LruMap<EntryValue, EntryCost>::~LruMap()
+{
+    for (QueueIterator i = index.begin(); i != index.end(); ++i) {
+        delete *i;
+    }
+}
+
+template <class EntryValue, size_t EntryCost>
+void
+LruMap<EntryValue, EntryCost>::setMemLimit(size_t aSize)
+{
+    if (aSize > 0)
+        memLimit_ = aSize;
+    else
+        memLimit_ = 0;
+}
+
+template <class EntryValue, size_t EntryCost>
+void
+LruMap<EntryValue, EntryCost>::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 <class EntryValue, size_t EntryCost>
+EntryValue *
+LruMap<EntryValue, EntryCost>::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 <class EntryValue, size_t EntryCost>
+bool
+LruMap<EntryValue, EntryCost>::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 <class EntryValue, size_t EntryCost>
+bool
+LruMap<EntryValue, EntryCost>::expired(LruMap::Entry &entry)
+{
+    if (ttl < 0)
+        return false;
+
+    return (entry.date + ttl < squid_curtime);
+}
+
+template <class EntryValue, size_t EntryCost>
+bool
+LruMap<EntryValue, EntryCost>::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 <class EntryValue, size_t EntryCost>
+bool
+LruMap<EntryValue, EntryCost>::del(const char *key)
+{
+    LruMap::MapIterator i;
+    findEntry(key, i);
+    return del(i);
+}
+
+template <class EntryValue, size_t EntryCost>
+void
+LruMap<EntryValue, EntryCost>::trim()
+{
+    while(memLimit() > 0 && size() >= memLimit()) {
+        LruMap::QueueIterator i = index.end();
+        --i;
+        if (i != index.end()) {
+            del((*i)->key.c_str());
+        }
+    }
+}
+
+template <class EntryValue, size_t EntryCost>
+void
+LruMap<EntryValue, EntryCost>::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
index f774d22f305f6bc88c3f8d0f6b097466c715741e..23ca7e0e2cfa472774a1e323e44705888b0fe3aa 100644 (file)
@@ -16,6 +16,7 @@ libbase_la_SOURCES = \
        CbcPointer.h \
        InstanceId.h \
        Lock.h \
+       LruMap.h \
        RunnersRegistry.cc \
        RunnersRegistry.h \
        Subscription.h \
index 75d2ef536ac4b885548c7a40209a3ca9a831451d..d04b07a618a7c46b38dc6f377dafb32a97347a1f 100644 (file)
@@ -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
index 6f84459fc46e5a183a8a60cffb4c6b95f82b29a9..3b9531f5213648485afb531b36790016a64d76be 100644 (file)
@@ -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;
             }
index a6ab3daae828d19114b6163476f9279a17eb9341..4ed6f43ae0deb1e71bc4b1968ce40064e7e58158 100644 (file)
@@ -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::Errors *>(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 <NULL> 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;
 
index e269703141b4db15c1a4f5c58095790a818b2cf2..73e359b210524982d219fd2aa5f4ad0658e6c561 100644 (file)
@@ -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
index 1110982355f7c746f49f305505a900c88e784a09..c6c310341f20b13c38f4ae904984aaf515ed27a4 100644 (file)
@@ -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;
index b5d4f191545293f0d93e87597f9ae6fec0fdf4bc..c5a465ff15dd8452f97696b9c2a25a303995ba5a 100644 (file)
@@ -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
 };
 
 /**
index 5eb74bd08fcd52f5383c6e1650d118b548714298..a5944f85071d549817e5b10202028c470f2e3c0e 100644 (file)
@@ -34,93 +34,16 @@ void Ssl::CertificateStorageAction::dump (StoreEntry *sentry)
     for (std::map<Ip::Address, LocalContextStorage *>::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<Ip::Address, size_t>::iterator conf_i = configureStorage.begin(); conf_i != configureStorage.end(); ++conf_i ) {
             if (storage.find(conf_i->first) == storage.end()) {
-                storage.insert(std::pair<Ip::Address, LocalContextStorage *>(conf_i->first, new LocalContextStorage(conf_i->second)));
+                storage.insert(std::pair<Ip::Address, LocalContextStorage *>(conf_i->first, new LocalContextStorage(-1, conf_i->second)));
             }
         }
     }
index a1df921b65e066ee3d1f12baa22a114458167d0e..59dd08c7d58e242417c2026798707e2599b2d491 100644 (file)
@@ -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 <map>
 #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<Item *> Queue;
-    typedef Queue::iterator QueueIterator;
-
-    /// host_name:queue_item mapping for fast lookups by host name
-    typedef std::map<std::string, QueueIterator> Map;
-    typedef Map::iterator MapIterator;
-    typedef std::pair<std::string, QueueIterator> 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<SSL_CTX_Pointer, SSL_CTX_SIZE> LocalContextStorage;
 
 /// Class for storing/manipulating LocalContextStorage per local listening address/port.
 class GlobalContextStorage
index a025d05c212450ecb3b8c479777bdabe4fa133f7..13e7f0293b3cdc94d71f94a78bb176a606a074a4 100644 (file)
@@ -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::CertValidationResponse> *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<Ssl::CertValidationResponse>(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<submitData *>(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);
 }
index cc51ec8208cf8f50a3b302736bc6485c76fb2c85..0044191a02126c9a5ade114473ffc5f642f3c161 100644 (file)
@@ -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<Ssl::CertValidationResponse> *HelperCache; ///< cache for cert validation helper
 };
 
 } //namespace Ssl
index e2ccc07e2fd94cad5aa64c088acf88e079e2dc6e..08f3c5f26dc191377b816b7765a60bfb576fd60c 100644 (file)
@@ -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;