]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Fix SSL certificate cache refresh and collision handling (#40)
authorChristos Tsantilas <christos@chtsanti.net>
Wed, 23 Aug 2017 21:13:52 +0000 (00:13 +0300)
committerAlex Rousskov <rousskov@measurement-factory.com>
Wed, 23 Aug 2017 21:13:52 +0000 (15:13 -0600)
SslBump was ignoring some origin server certificate changes or differences,
incorrectly using the previously cached fake certificate (mimicking
now-stale properties or properties of a slightly different certificate).
Also, Squid was not detecting key collisions inside certificate caches.

On-disk certificate cache fixes:

    Use the original certificate signature instead of the certificate
    subject as part of the key. Using signatures reduces certificate key
    collisions to deliberate attacks and woefully misconfigured origins,
    and makes any mishandled attacks a lot less dangerous because the
    attacking origin server certificate cannot by trusted by a properly
    configured Squid and cannot be used for encryption by an attacker.

    We have considered using certificate digests instead of signatures.
    Digests would further reduce the attack surface to copies of public
    certificates (as if the origin server was woefully misconfigured).
    However, unlike the origin-supplied signatures, digests require
    (expensive) computation in Squid, and implemented collision handling
    should make any signature-based attacks unappealing. Signatures won
    on performance grounds.

    Other key components remain the same: NotValidAfter, NotValidBefore,
    forced common name, non-default signing algorithm, and signing hash.

    Store the original server certificate in the cache (together with
    the generated certificate) for reliable key collision detection.

    Upon detecting key collisions, ignore and replace the existing cache
    entry with a freshly computed one. This change is required to
    prevent an attacker from tricking Squid into hitting a cached
    impersonating certificate when talking to a legitimate origin.

In-memory SSL context cache fixes:

    Use the original server certificate (in ASN.1 form) as a part of the
    cache key, to completely eliminate cache key collisions.

Other related improvements:

    Make the LruMap keys template parameters.
    Polish Ssl::CertificateDb class member names to match Squid coding
    style. Rename some functions parameters to better match their
    meaning.
    Replace Ssl::CertificateProperties::dbKey() with:
        Ssl::OnDiskCertificateDbKey() in ssl/gadgets.cc for
        on-disk key generation by the ssl_crtd helper;
        Ssl::InRamCertificateDbKey() in ssl/support.cc for
        in-memory binary keys generation by the SSL context memory cache.
    Optimization: Added Ssl::BIO_new_SBuf(SBuf*) for OpenSSL to write
    directly into SBuf objects.

This is a Measurement Factory project.

17 files changed:
acinclude/lib-checks.m4
src/base/LruMap.h
src/client_side.cc
src/client_side.h
src/security/cert_generators/file/certificate_db.cc
src/security/cert_generators/file/certificate_db.h
src/security/cert_generators/file/security_file_certgen.cc
src/ssl/ServerBump.h
src/ssl/context_storage.h
src/ssl/gadgets.cc
src/ssl/gadgets.h
src/ssl/helper.cc
src/ssl/helper.h
src/ssl/support.cc
src/ssl/support.h
src/tests/stub_client_side.cc
src/tests/stub_libsslsquid.cc

index 0863b88caa8af20964a8394761356ef794cb3f85..68bf33a1830742e78049f2c7dbd94874562afe74 100644 (file)
@@ -72,6 +72,7 @@ AC_DEFUN([SQUID_CHECK_LIBCRYPTO_API],[
   AH_TEMPLATE(HAVE_LIBCRYPTO_X509_UP_REF, "Define to 1 if the X509_up_ref() OpenSSL API function exists")
   AH_TEMPLATE(HAVE_LIBCRYPTO_X509_CRL_UP_REF, "Define to 1 if the X509_CRL_up_ref() OpenSSL API function exists")
   AH_TEMPLATE(HAVE_LIBCRYPTO_DH_UP_REF, "Define to 1 if the DH_up_ref() OpenSSL API function exists")
+  AH_TEMPLATE(HAVE_LIBCRYPTO_X509_GET0_SIGNATURE, "Define to 1 if the X509_get0_signature() OpenSSL API function exists")
   SQUID_STATE_SAVE(check_openssl_libcrypto_api)
   LIBS="$LIBS $SSLLIB"
   AC_CHECK_LIB(crypto, EVP_PKEY_get0_RSA, AC_DEFINE(HAVE_LIBCRYPTO_EVP_PKEY_GET0_RSA, 1))
@@ -85,6 +86,7 @@ AC_DEFUN([SQUID_CHECK_LIBCRYPTO_API],[
   AC_CHECK_LIB(crypto, X509_up_ref, AC_DEFINE(HAVE_LIBCRYPTO_X509_UP_REF, 1))
   AC_CHECK_LIB(crypto, X509_CRL_up_ref, AC_DEFINE(HAVE_LIBCRYPTO_X509_CRL_UP_REF, 1))
   AC_CHECK_LIB(crypto, DH_up_ref, AC_DEFINE(HAVE_LIBCRYPTO_DH_UP_REF, 1))
+  AC_CHECK_LIB(crypto, X509_get0_signature, AC_DEFINE(HAVE_LIBCRYPTO_X509_GET0_SIGNATURE, 1))
   SQUID_STATE_ROLLBACK(check_openssl_libcrypto_api)
 ])
 
index 4936498251b7f6de3369d89f2f77de9f4c7b887f..74cd356e767a6a5c59a3ee293c9bacbb4f0451c5 100644 (file)
 #include <list>
 #include <map>
 
-template <class EntryValue, size_t EntryCost = sizeof(EntryValue)> class LruMap
+template <class Key, 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(const Key &aKey, EntryValue *t): key(aKey), value(t), date(squid_curtime) {}
         ~Entry() {delete value;}
     private:
         Entry(Entry &);
         Entry & operator = (Entry &);
     public:
-        std::string key; ///< the key of entry
+        Key key; ///< the key of entry
         EntryValue *value; ///< A pointer to the stored value
         time_t date; ///< The date the entry created
     };
@@ -34,18 +34,18 @@ public:
     typedef typename std::list<Entry *>::iterator QueueIterator;
 
     /// key:queue_item mapping for fast lookups by key
-    typedef std::map<std::string, QueueIterator> Map;
+    typedef std::map<Key, QueueIterator> Map;
     typedef typename Map::iterator MapIterator;
-    typedef std::pair<std::string, QueueIterator> MapPair;
+    typedef std::pair<Key, QueueIterator> MapPair;
 
     LruMap(int ttl, size_t size);
     ~LruMap();
     /// Search for an entry, and return a pointer
-    EntryValue *get(const char *key);
+    EntryValue *get(const Key &key);
     /// Add an entry to the map
-    bool add(const char *key, EntryValue *t);
+    bool add(const Key &key, EntryValue *t);
     /// Delete an entry from the map
-    bool del(const char *key);
+    bool del(const Key &key);
     /// (Re-)set the maximum size for this map
     void setMemLimit(size_t aSize);
     /// The available size for the map
@@ -64,7 +64,7 @@ private:
     void trim();
     void touch(const MapIterator &i);
     bool del(const MapIterator &i);
-    void findEntry(const char *key, LruMap::MapIterator &i);
+    void findEntry(const Key &key, LruMap::MapIterator &i);
 
     Map storage; ///< The Key/value * pairs
     Queue index; ///< LRU cache index
@@ -73,25 +73,25 @@ private:
     int entries_; ///< The stored entries
 };
 
-template <class EntryValue, size_t EntryCost>
-LruMap<EntryValue, EntryCost>::LruMap(int aTtl, size_t aSize): entries_(0)
+template <class Key, class EntryValue, size_t EntryCost>
+    LruMap<Key, EntryValue, EntryCost>::LruMap(int aTtl, size_t aSize): entries_(0)
 {
     ttl = aTtl;
 
     setMemLimit(aSize);
 }
 
-template <class EntryValue, size_t EntryCost>
-LruMap<EntryValue, EntryCost>::~LruMap()
+template <class Key, class EntryValue, size_t EntryCost>
+LruMap<Key, EntryValue, EntryCost>::~LruMap()
 {
     for (QueueIterator i = index.begin(); i != index.end(); ++i) {
         delete *i;
     }
 }
 
-template <class EntryValue, size_t EntryCost>
+template <class Key, class EntryValue, size_t EntryCost>
 void
-LruMap<EntryValue, EntryCost>::setMemLimit(size_t aSize)
+LruMap<Key, EntryValue, EntryCost>::setMemLimit(size_t aSize)
 {
     if (aSize > 0)
         memLimit_ = aSize;
@@ -99,9 +99,9 @@ LruMap<EntryValue, EntryCost>::setMemLimit(size_t aSize)
         memLimit_ = 0;
 }
 
-template <class EntryValue, size_t EntryCost>
+template <class Key, class EntryValue, size_t EntryCost>
 void
-LruMap<EntryValue, EntryCost>::findEntry(const char *key, LruMap::MapIterator &i)
+LruMap<Key, EntryValue, EntryCost>::findEntry(const Key &key, LruMap::MapIterator &i)
 {
     i = storage.find(key);
     if (i == storage.end()) {
@@ -121,9 +121,9 @@ LruMap<EntryValue, EntryCost>::findEntry(const char *key, LruMap::MapIterator &i
     i = storage.end();
 }
 
-template <class EntryValue, size_t EntryCost>
+template <class Key, class EntryValue, size_t EntryCost>
 EntryValue *
-LruMap<EntryValue, EntryCost>::get(const char *key)
+LruMap<Key, EntryValue, EntryCost>::get(const Key &key)
 {
     MapIterator i;
     findEntry(key, i);
@@ -135,9 +135,9 @@ LruMap<EntryValue, EntryCost>::get(const char *key)
     return NULL;
 }
 
-template <class EntryValue, size_t EntryCost>
+template <class Key, class EntryValue, size_t EntryCost>
 bool
-LruMap<EntryValue, EntryCost>::add(const char *key, EntryValue *t)
+LruMap<Key, EntryValue, EntryCost>::add(const Key &key, EntryValue *t)
 {
     if (ttl == 0)
         return false;
@@ -155,9 +155,9 @@ LruMap<EntryValue, EntryCost>::add(const char *key, EntryValue *t)
     return true;
 }
 
-template <class EntryValue, size_t EntryCost>
+template <class Key, class EntryValue, size_t EntryCost>
 bool
-LruMap<EntryValue, EntryCost>::expired(const LruMap::Entry &entry) const
+LruMap<Key, EntryValue, EntryCost>::expired(const LruMap::Entry &entry) const
 {
     if (ttl < 0)
         return false;
@@ -165,9 +165,9 @@ LruMap<EntryValue, EntryCost>::expired(const LruMap::Entry &entry) const
     return (entry.date + ttl < squid_curtime);
 }
 
-template <class EntryValue, size_t EntryCost>
+template <class Key, class EntryValue, size_t EntryCost>
 bool
-LruMap<EntryValue, EntryCost>::del(LruMap::MapIterator const &i)
+LruMap<Key, EntryValue, EntryCost>::del(LruMap::MapIterator const &i)
 {
     if (i != storage.end()) {
         Entry *e = *i->second;
@@ -180,31 +180,31 @@ LruMap<EntryValue, EntryCost>::del(LruMap::MapIterator const &i)
     return false;
 }
 
-template <class EntryValue, size_t EntryCost>
+template <class Key, class EntryValue, size_t EntryCost>
 bool
-LruMap<EntryValue, EntryCost>::del(const char *key)
+LruMap<Key, EntryValue, EntryCost>::del(const Key &key)
 {
     MapIterator i;
     findEntry(key, i);
     return del(i);
 }
 
-template <class EntryValue, size_t EntryCost>
+template <class Key, class EntryValue, size_t EntryCost>
 void
-LruMap<EntryValue, EntryCost>::trim()
+LruMap<Key, EntryValue, EntryCost>::trim()
 {
     while (size() >= memLimit()) {
         QueueIterator i = index.end();
         --i;
         if (i != index.end()) {
-            del((*i)->key.c_str());
+            del((*i)->key);
         }
     }
 }
 
-template <class EntryValue, size_t EntryCost>
+template <class Key, class EntryValue, size_t EntryCost>
 void
-LruMap<EntryValue, EntryCost>::touch(LruMap::MapIterator const &i)
+LruMap<Key, EntryValue, EntryCost>::touch(LruMap::MapIterator const &i)
 {
     // this must not be done when nothing is being cached.
     if (ttl == 0)
index d75119260a8cbe4f66a483c56555c40fd1086e57..74dd408db3dc96d0121f5c450dd201282ee68f55 100644 (file)
@@ -2838,8 +2838,10 @@ ConnStateData::sslCrtdHandleReply(const Helper::Reply &reply)
                     Security::ContextPointer ctx(Security::GetFrom(fd_table[clientConnection->fd].ssl));
                     Ssl::configureUnconfiguredSslContext(ctx, signAlgorithm, *port);
                 } else {
-                    Security::ContextPointer ctx(Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port));
-                    getSslContextDone(ctx, true);
+                    Security::ContextPointer ctx(Ssl::GenerateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port, (signAlgorithm == Ssl::algSignTrusted)));
+                    if (ctx && !sslBumpCertKey.isEmpty())
+                        storeTlsContextToCache(sslBumpCertKey, ctx);
+                    getSslContextDone(ctx);
                 }
                 return;
             }
@@ -2853,9 +2855,8 @@ void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &cer
 {
     certProperties.commonName =  sslCommonName_.isEmpty() ? sslConnectHostOrIp.termedBuf() : sslCommonName_.c_str();
 
-    const bool triedToConnect = sslServerBump && sslServerBump->entry;
-    const bool connectedOK = triedToConnect && sslServerBump->entry->isEmpty();
-    if (connectedOK) {
+    const bool connectedOk = sslServerBump && sslServerBump->connectedOk();
+    if (connectedOk) {
         if (X509 *mimicCert = sslServerBump->serverCert.get())
             certProperties.mimicCert.resetAndLock(mimicCert);
 
@@ -2926,6 +2927,34 @@ void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &cer
     certProperties.signHash = Ssl::DefaultSignHash;
 }
 
+Security::ContextPointer
+ConnStateData::getTlsContextFromCache(const SBuf &cacheKey, const Ssl::CertificateProperties &certProperties)
+{
+    debugs(33, 5, "Finding SSL certificate for " << cacheKey << " in cache");
+    Ssl::LocalContextStorage * ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s);
+    if (Security::ContextPointer *ctx = ssl_ctx_cache ? ssl_ctx_cache->get(cacheKey) : nullptr) {
+        if (Ssl::verifySslCertificate(*ctx, certProperties)) {
+            debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is valid");
+            return *ctx;
+        } else {
+            debugs(33, 5, "Cached SSL certificate for " << certProperties.commonName << " is out of date. Delete this certificate from cache");
+            if (ssl_ctx_cache)
+                ssl_ctx_cache->del(cacheKey);
+        }
+    }
+    return Security::ContextPointer(nullptr);
+}
+
+void
+ConnStateData::storeTlsContextToCache(const SBuf &cacheKey, Security::ContextPointer &ctx)
+{
+    Ssl::LocalContextStorage *ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s);
+    if (!ssl_ctx_cache || !ssl_ctx_cache->add(cacheKey, new Security::ContextPointer(ctx))) {
+        // If it is not in storage delete after using. Else storage deleted it.
+        fd_table[clientConnection->fd].dynamicTlsContext = ctx;
+    }
+}
+
 void
 ConnStateData::getSslContextStart()
 {
@@ -2941,27 +2970,17 @@ ConnStateData::getSslContextStart()
     if (port->generateHostCertificates) {
         Ssl::CertificateProperties certProperties;
         buildSslCertGenerationParams(certProperties);
-        sslBumpCertKey = certProperties.dbKey().c_str();
-        assert(sslBumpCertKey.size() > 0 && sslBumpCertKey[0] != '\0');
 
         // Disable caching for bumpPeekAndSplice mode
         if (!(sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare))) {
-            debugs(33, 5, "Finding SSL certificate for " << sslBumpCertKey << " in cache");
-            Ssl::LocalContextStorage * ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s);
-            Security::ContextPointer *cachedCtx = ssl_ctx_cache ? ssl_ctx_cache->get(sslBumpCertKey.termedBuf()) : nullptr;
-            if (cachedCtx) {
-                debugs(33, 5, "SSL certificate for " << sslBumpCertKey << " found in cache");
-                if (Ssl::verifySslCertificate(*cachedCtx, certProperties)) {
-                    debugs(33, 5, "Cached SSL certificate for " << sslBumpCertKey << " is valid");
-                    getSslContextDone(*cachedCtx);
-                    return;
-                } else {
-                    debugs(33, 5, "Cached SSL certificate for " << sslBumpCertKey << " is out of date. Delete this certificate from cache");
-                    if (ssl_ctx_cache)
-                        ssl_ctx_cache->del(sslBumpCertKey.termedBuf());
-                }
-            } else {
-                debugs(33, 5, "SSL certificate for " << sslBumpCertKey << " haven't found in cache");
+            sslBumpCertKey.clear();
+            Ssl::InRamCertificateDbKey(certProperties, sslBumpCertKey);
+            assert(!sslBumpCertKey.isEmpty());
+
+            Security::ContextPointer ctx(getTlsContextFromCache(sslBumpCertKey, certProperties));
+            if (ctx) {
+                getSslContextDone(ctx);
+                return;
             }
         }
 
@@ -2993,8 +3012,10 @@ ConnStateData::getSslContextStart()
             Security::ContextPointer ctx(Security::GetFrom(fd_table[clientConnection->fd].ssl));
             Ssl::configureUnconfiguredSslContext(ctx, certProperties.signAlgorithm, *port);
         } else {
-            Security::ContextPointer dynCtx(Ssl::generateSslContext(certProperties, *port));
-            getSslContextDone(dynCtx, true);
+            Security::ContextPointer dynCtx(Ssl::GenerateSslContext(certProperties, *port, (signAlgorithm == Ssl::algSignTrusted)));
+            if (dynCtx && !sslBumpCertKey.isEmpty())
+                storeTlsContextToCache(sslBumpCertKey, dynCtx);
+            getSslContextDone(dynCtx);
         }
         return;
     }
@@ -3004,28 +3025,10 @@ ConnStateData::getSslContextStart()
 }
 
 void
-ConnStateData::getSslContextDone(Security::ContextPointer &ctx, bool isNew)
+ConnStateData::getSslContextDone(Security::ContextPointer &ctx)
 {
-    // Try to add generated ssl context to storage.
-    if (port->generateHostCertificates && isNew) {
-
-        if (ctx && (signAlgorithm == Ssl::algSignTrusted)) {
-            Ssl::chainCertificatesToSSLContext(ctx, *port);
-        } else if (signAlgorithm == Ssl::algSignTrusted) {
-            debugs(33, DBG_IMPORTANT, "WARNING: can not add signing certificate to SSL context chain because SSL context chain is invalid!");
-        }
-        //else it is self-signed or untrusted do not attrach any certificate
-
-        Ssl::LocalContextStorage *ssl_ctx_cache = Ssl::TheGlobalContextStorage.getLocalStorage(port->s);
-        assert(sslBumpCertKey.size() > 0 && sslBumpCertKey[0] != '\0');
-        if (ctx) {
-            if (!ssl_ctx_cache || !ssl_ctx_cache->add(sslBumpCertKey.termedBuf(), new Security::ContextPointer(ctx))) {
-                // If it is not in storage delete after using. Else storage deleted it.
-                fd_table[clientConnection->fd].dynamicTlsContext = ctx;
-            }
-        } else {
-            debugs(33, 2, HERE << "Failed to generate SSL cert for " << sslConnectHostOrIp);
-        }
+    if (port->generateHostCertificates && !ctx) {
+        debugs(33, 2, "Failed to generate TLS cotnext for " << sslConnectHostOrIp);
     }
 
     // If generated ssl context = NULL, try to use static ssl context.
index ea5d0c0c9ae0de2455325e783f8390c80731bfa0..bdf135b632a10e744c97db312789eb20cfd8af3b 100644 (file)
@@ -238,12 +238,10 @@ public:
 
     /// Start to create dynamic Security::ContextPointer for host or uses static port SSL context.
     void getSslContextStart();
-    /**
-     * Done create dynamic ssl certificate.
-     *
-     * \param[in] isNew if generated certificate is new, so we need to add this certificate to storage.
-     */
-    void getSslContextDone(Security::ContextPointer &, bool isNew = false);
+
+    /// finish configuring the newly created SSL context"
+    void getSslContextDone(Security::ContextPointer &);
+
     /// Callback function. It is called when squid receive message from ssl_crtd.
     static void sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply);
     /// Proccess response from ssl_crtd.
@@ -373,6 +371,15 @@ private:
     bool parseProxy2p0();
     bool proxyProtocolError(const char *reason);
 
+#if USE_OPENSSL
+    /// \returns a pointer to the matching cached TLS context or nil
+    Security::ContextPointer getTlsContextFromCache(const SBuf &cacheKey, const Ssl::CertificateProperties &certProperties);
+
+    /// Attempts to add a given TLS context to the cache, replacing the old
+    /// same-key context, if any
+    void storeTlsContextToCache(const SBuf &cacheKey, Security::ContextPointer &ctx);
+#endif
+
     /// whether PROXY protocol header is still expected
     bool needProxyProtocolHeader_;
 
@@ -394,7 +401,7 @@ private:
 
     /// TLS client delivered SNI value. Empty string if none has been received.
     SBuf tlsClientSni_;
-    String sslBumpCertKey; ///< Key to use to store/retrieve generated certificate
+    SBuf sslBumpCertKey; ///< Key to use to store/retrieve generated certificate
 
     /// HTTPS server cert. fetching state for bump-ssl-server-first
     Ssl::ServerBump *sslServerBump;
index 370020d967ebc702c4079103ab3bdf1ac5aa3b3d..09304d04df3df33f1acd50dcdced02036cba4374 100644 (file)
@@ -208,7 +208,7 @@ void Ssl::CertificateDb::sq_TXT_DB_delete_row(TXT_DB *db, int idx) {
 
     Row row(rrow, cnlNumber); // row wrapper used to free the rrow
 
-    const Columns db_indexes[]= {cnlSerial, cnlName};
+    const Columns db_indexes[]= {cnlSerial, cnlKey};
     for (unsigned int i = 0; i < countof(db_indexes); ++i) {
         void *data = NULL;
 #if SQUID_SSLTXTDB_PSTRINGDATA
@@ -238,11 +238,11 @@ int Ssl::CertificateDb::index_serial_cmp(const char **a, const char **b) {
 }
 
 unsigned long Ssl::CertificateDb::index_name_hash(const char **a) {
-    return(lh_strhash(a[Ssl::CertificateDb::cnlName]));
+    return(lh_strhash(a[Ssl::CertificateDb::cnlKey]));
 }
 
 int Ssl::CertificateDb::index_name_cmp(const char **a, const char **b) {
-    return(strcmp(a[Ssl::CertificateDb::cnlName], b[CertificateDb::cnlName]));
+    return(strcmp(a[Ssl::CertificateDb::cnlKey], b[CertificateDb::cnlKey]));
 }
 
 const std::string Ssl::CertificateDb::db_file("index.txt");
@@ -264,10 +264,12 @@ Ssl::CertificateDb::CertificateDb(std::string const & aDb_path, size_t aMax_db_s
         throw std::runtime_error("security_file_certgen is missing the required parameter. There should be -s and -M parameters together.");
 }
 
-bool Ssl::CertificateDb::find(std::string const & host_name, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey) {
+bool
+Ssl::CertificateDb::find(std::string const &key,  const Security::CertPointer &expectedOrig, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey)
+{
     const Locker locker(dbLock, Here);
     load();
-    return pure_find(host_name, cert, pkey);
+    return pure_find(key, expectedOrig, cert, pkey);
 }
 
 bool Ssl::CertificateDb::purgeCert(std::string const & key) {
@@ -276,19 +278,23 @@ bool Ssl::CertificateDb::purgeCert(std::string const & key) {
     if (!db)
         return false;
 
-    if (!deleteByHostname(key))
+    if (!deleteByKey(key))
         return false;
 
     save();
     return true;
 }
 
-bool Ssl::CertificateDb::addCertAndPrivateKey(Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey, std::string const & useName) {
+bool
+Ssl::CertificateDb::addCertAndPrivateKey(std::string const & useKey, const Security::CertPointer & cert, const Ssl::EVP_PKEY_Pointer & pkey, const Security::CertPointer &orig) {
     const Locker locker(dbLock, Here);
     load();
     if (!db || !cert || !pkey)
         return false;
 
+    if(useKey.empty())
+        return false;
+
     // Functor to wrap xfree() for std::unique_ptr
     typedef HardFun<void, const void*, &xfree> CharDeleter;
 
@@ -309,20 +315,8 @@ bool Ssl::CertificateDb::addCertAndPrivateKey(Security::CertPointer & cert, Ssl:
         return true;
     }
 
-    {
-        std::unique_ptr<char, CharDeleter> subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), nullptr, 0));
-        Security::CertPointer findCert;
-        Ssl::EVP_PKEY_Pointer findPkey;
-        if (pure_find(useName.empty() ? subject.get() : useName, findCert, findPkey)) {
-            // Replace with database certificate
-            cert = std::move(findCert);
-            pkey = std::move(findPkey);
-            return true;
-        }
-        // pure_find may fail because the entry is expired, or because the
-        // certs file is corrupted. Remove any entry with given hostname
-        deleteByHostname(useName.empty() ? subject.get() : useName);
-    }
+    // Remove any entry with given key
+    deleteByKey(useKey);
 
     // check db size while trying to minimize calls to size()
     size_t dbSize = size();
@@ -346,16 +340,11 @@ bool Ssl::CertificateDb::addCertAndPrivateKey(Security::CertPointer & cert, Ssl:
         dbSize = size(); // get the current database size
     }
 
-    row.setValue(cnlType, "V");
     ASN1_UTCTIME * tm = X509_get_notAfter(cert.get());
     row.setValue(cnlExp_date, std::string(reinterpret_cast<char *>(tm->data), tm->length).c_str());
-    row.setValue(cnlFile, "unknown");
-    if (!useName.empty())
-        row.setValue(cnlName, useName.c_str());
-    else {
-        std::unique_ptr<char, CharDeleter> subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), nullptr, 0));
-        row.setValue(cnlName, subject.get());
-    }
+    std::unique_ptr<char, CharDeleter> subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), nullptr, 0));
+    row.setValue(cnlName, subject.get());
+    row.setValue(cnlKey, useKey.c_str());
 
     if (!TXT_DB_insert(db.get(), row.getRow())) {
         // failed to add index (???) but we may have already modified
@@ -367,7 +356,7 @@ bool Ssl::CertificateDb::addCertAndPrivateKey(Security::CertPointer & cert, Ssl:
     row.reset();
 
     std::string filename(cert_full + "/" + serial_string + ".pem");
-    if (!writeCertAndPrivateKeyToFile(cert, pkey, filename.c_str())) {
+    if (!WriteEntry(filename.c_str(), cert, pkey, orig)) {
         //remove row from txt_db and save
         sq_TXT_DB_delete(db.get(), (const char **)rrow);
         save();
@@ -379,7 +368,8 @@ bool Ssl::CertificateDb::addCertAndPrivateKey(Security::CertPointer & cert, Ssl:
     return true;
 }
 
-void Ssl::CertificateDb::create(std::string const & db_path) {
+void
+Ssl::CertificateDb::Create(std::string const & db_path) {
     if (db_path == "")
         throw std::runtime_error("Path to db is empty");
     std::string db_full(db_path + "/" + db_file);
@@ -402,7 +392,8 @@ void Ssl::CertificateDb::create(std::string const & db_path) {
         throw std::runtime_error("Cannot open " + db_full + " to open");
 }
 
-void Ssl::CertificateDb::check(std::string const & db_path, size_t max_db_size, size_t fs_block_size) {
+void
+Ssl::CertificateDb::Check(std::string const & db_path, size_t max_db_size, size_t fs_block_size) {
     CertificateDb db(db_path, max_db_size, fs_block_size);
     db.load();
 
@@ -432,26 +423,32 @@ size_t Ssl::CertificateDb::rebuildSize()
     return dbSize;
 }
 
-bool Ssl::CertificateDb::pure_find(std::string const & host_name, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey) {
+bool
+Ssl::CertificateDb::pure_find(std::string const &key, const Security::CertPointer &expectedOrig, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey)
+{
     if (!db)
         return false;
 
     Row row;
-    row.setValue(cnlName, host_name.c_str());
+    row.setValue(cnlKey, key.c_str());
 
-    char **rrow = TXT_DB_get_by_index(db.get(), cnlName, row.getRow());
+    char **rrow = TXT_DB_get_by_index(db.get(), cnlKey, row.getRow());
     if (rrow == NULL)
         return false;
 
     if (!sslDateIsInTheFuture(rrow[cnlExp_date]))
         return false;
 
+    Security::CertPointer storedOrig;
     // read cert and pkey from file.
     std::string filename(cert_full + "/" + rrow[cnlSerial] + ".pem");
-    readCertAndPrivateKeyFromFiles(cert, pkey, filename.c_str(), NULL);
-    if (!cert || !pkey)
+    if (!ReadEntry(filename.c_str(), cert, pkey, storedOrig))
         return false;
-    return true;
+
+    if (!storedOrig && !expectedOrig)
+        return true;
+    else
+        return Ssl::CertificatesCmp(expectedOrig, storedOrig);
 }
 
 size_t Ssl::CertificateDb::size() {
@@ -514,7 +511,7 @@ void Ssl::CertificateDb::load() {
     if (!corrupt && !TXT_DB_create_index(temp_db.get(), cnlSerial, NULL, LHASH_HASH_FN(index_serial_hash), LHASH_COMP_FN(index_serial_cmp)))
         corrupt = true;
 
-    if (!corrupt && !TXT_DB_create_index(temp_db.get(), cnlName, NULL, LHASH_HASH_FN(index_name_hash), LHASH_COMP_FN(index_name_cmp)))
+    if (!corrupt && !TXT_DB_create_index(temp_db.get(), cnlKey, NULL, LHASH_HASH_FN(index_name_hash), LHASH_COMP_FN(index_name_cmp)))
         corrupt = true;
 
     if (corrupt)
@@ -596,7 +593,8 @@ bool Ssl::CertificateDb::deleteOldestCertificate()
     return true;
 }
 
-bool Ssl::CertificateDb::deleteByHostname(std::string const & host) {
+bool
+Ssl::CertificateDb::deleteByKey(std::string const & key) {
     if (!db)
         return false;
 
@@ -611,7 +609,7 @@ bool Ssl::CertificateDb::deleteByHostname(std::string const & host) {
     for (int i = 0; i < sk_num(db.get()->data); ++i) {
         const char ** current_row = ((const char **)sk_value(db.get()->data, i));
 #endif
-        if (host == current_row[cnlName]) {
+        if (key == current_row[cnlKey]) {
             deleteRow(current_row, i);
             return true;
         }
@@ -637,3 +635,32 @@ bool Ssl::CertificateDb::IsEnabledDiskStore() const {
     return enabled_disk_store;
 }
 
+bool
+Ssl::CertificateDb::WriteEntry(const std::string &filename, const Security::CertPointer & cert, const Ssl::EVP_PKEY_Pointer & pkey, const Security::CertPointer &orig)
+{
+    Ssl::BIO_Pointer bio;
+    if (!Ssl::OpenCertsFileForWriting(bio, filename.c_str()))
+        return false;
+    if (!Ssl::WriteX509Certificate(bio, cert))
+        return false;
+    if (!Ssl::WritePrivateKey(bio, pkey))
+        return false;
+    if (orig && !Ssl::WriteX509Certificate(bio, orig))
+        return false;
+    return true;
+}
+
+bool
+Ssl::CertificateDb::ReadEntry(std::string filename, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey, Security::CertPointer &orig)
+{
+    Ssl::BIO_Pointer bio;
+    if (!Ssl::OpenCertsFileForReading(bio, filename.c_str()))
+        return false;
+    if (!Ssl::ReadX509Certificate(bio, cert))
+        return false;
+    if (!Ssl::ReadPrivateKey(bio, pkey, NULL))
+        return false;
+    // The orig certificate is not mandatory
+    (void)Ssl::ReadX509Certificate(bio, orig);
+    return true;
+}
index 0325b00436ce5250b6f4add2b2181ea8e75349e5..8c843690c649b98366c54c0f106cf31ba5102431 100644 (file)
@@ -69,11 +69,10 @@ class CertificateDb
 public:
     /// Names of db columns.
     enum Columns {
-        cnlType = 0,
+        cnlKey = 0, //< The key to use for storing/retrieving entries from DB.
         cnlExp_date,
         cnlRev_date,
         cnlSerial,
-        cnlFile,
         cnlName,
         cnlNumber
     };
@@ -97,17 +96,19 @@ public:
     };
 
     CertificateDb(std::string const & db_path, size_t aMax_db_size, size_t aFs_block_size);
-    /// Find certificate and private key for host name
-    bool find(std::string const & host_name, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey);
+    /// finds matching generated certificate and its private key
+    bool find(std::string const & key,  const Security::CertPointer &expectedOrig, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey);
     /// Delete a certificate from database
     bool purgeCert(std::string const & key);
     /// Save certificate to disk.
-    bool addCertAndPrivateKey(Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey, std::string const & useName);
+    bool addCertAndPrivateKey(std::string const & useKey, const Security::CertPointer & cert, const Ssl::EVP_PKEY_Pointer & pkey, const Security::CertPointer &orig);
+
+    bool IsEnabledDiskStore() const; ///< Check enabled of dist store.
+
     /// Create and initialize a database  under the  db_path
-    static void create(std::string const & db_path);
+    static void Create(std::string const & db_path);
     /// Check the database stored under the db_path.
-    static void check(std::string const & db_path, size_t max_db_size, size_t fs_block_size);
-    bool IsEnabledDiskStore() const; ///< Check enabled of dist store.
+    static void Check(std::string const & db_path, size_t max_db_size, size_t fs_block_size);
 private:
     void load(); ///< Load db from disk.
     void save(); ///< Save db to disk.
@@ -121,14 +122,20 @@ private:
     size_t getFileSize(std::string const & filename); ///< get file size on disk.
     size_t rebuildSize(); ///< Rebuild size_file
     /// Only find certificate in current db and return it.
-    bool pure_find(std::string const & host_name, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey);
+    bool pure_find(std::string const & key, const Security::CertPointer & expectedOrig, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey);
 
     void deleteRow(const char **row, int rowIndex); ///< Delete a row from TXT_DB
     bool deleteInvalidCertificate(); ///< Delete invalid certificate.
     bool deleteOldestCertificate(); ///< Delete oldest certificate.
-    bool deleteByHostname(std::string const & host); ///< Delete using host name.
+    bool deleteByKey(std::string const & key); ///< Delete using key.
     bool hasRows() const; ///< Whether the TXT_DB has stored items.
 
+    /// stores the db entry into a file
+    static bool WriteEntry(const std::string &filename, const Security::CertPointer & cert, const Ssl::EVP_PKEY_Pointer & pkey, const Security::CertPointer &orig);
+
+    /// loads a db entry from the file
+    static bool ReadEntry(std::string filename, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey, Security::CertPointer &orig);
+
     /// Removes the first matching row from TXT_DB. Ignores failures.
     static void sq_TXT_DB_delete(TXT_DB *db, const char **row);
     /// Remove the row on position idx from TXT_DB. Ignores failures.
index 47358fb244f6f9b057e6bad3e23f3f979d9731ac..bbbb73a168fc9e60e0d7450dfe7c9f958dcce325 100644 (file)
@@ -190,33 +190,24 @@ static bool processNewRequest(Ssl::CrtdMessage & request_message, std::string co
 
     Security::CertPointer cert;
     Ssl::EVP_PKEY_Pointer pkey;
-    std::string &cert_subject = certProperties.dbKey();
+    Security::CertPointer orig;
+    std::string &certKey = Ssl::OnDiskCertificateDbKey(certProperties);
 
     bool dbFailed = false;
     try {
-        db.find(cert_subject, cert, pkey);
+        db.find(certKey, certProperties.mimicCert, cert, pkey);
     } catch (std::runtime_error &err) {
         dbFailed = true;
         error = err.what();
     }
 
-    if (cert) {
-        if (!Ssl::certificateMatchesProperties(cert.get(), certProperties)) {
-            // The certificate changed (renewed or other reason).
-            // Generete a new one with the updated fields.
-            cert.reset();
-            pkey.reset();
-            db.purgeCert(cert_subject);
-        }
-    }
-
     if (!cert || !pkey) {
         if (!Ssl::generateSslCertificate(cert, pkey, certProperties))
             throw std::runtime_error("Cannot create ssl certificate or private key.");
 
         if (!dbFailed && db.IsEnabledDiskStore()) {
             try {
-                if (!db.addCertAndPrivateKey(cert, pkey, cert_subject)) {
+                if (!db.addCertAndPrivateKey(certKey, cert, pkey, certProperties.mimicCert)) {
                     dbFailed = true;
                     error = "Cannot add certificate to db.";
                 }
@@ -289,7 +280,7 @@ int main(int argc, char *argv[])
 
         if (create_new_db) {
             std::cout << "Initialization SSL db..." << std::endl;
-            Ssl::CertificateDb::create(db_path);
+            Ssl::CertificateDb::Create(db_path);
             std::cout << "Done" << std::endl;
             exit(EXIT_SUCCESS);
         }
@@ -308,7 +299,7 @@ int main(int argc, char *argv[])
         }
 
         {
-            Ssl::CertificateDb::check(db_path, max_db_size, fs_block_size);
+            Ssl::CertificateDb::Check(db_path, max_db_size, fs_block_size);
         }
         // Initialize SSL subsystem
         SSL_load_error_strings();
index 2bb866f302b94ef5699407b85eed153b616c0d4e..deb8456b71b67f6745a65560d7c2b0beb31128f6 100644 (file)
@@ -15,6 +15,7 @@
 #include "HttpRequest.h"
 #include "ip/Address.h"
 #include "security/forward.h"
+#include "Store.h"
 
 class ConnStateData;
 class store_client;
@@ -35,6 +36,9 @@ public:
     void attachServerSession(const Security::SessionPointer &); ///< Sets the server TLS session object
     const Security::CertErrors *sslErrors() const; ///< SSL [certificate validation] errors
 
+    /// whether there was a successful connection to (and peeking at) the origin server
+    bool connectedOk() const {return entry && entry->isEmpty();}
+
     /// faked, minimal request; required by Client API
     HttpRequest::Pointer request;
     StoreEntry *entry; ///< for receiving Squid-generated error messages
index d71589339f7551d9656c191236ab5aa6351a9b71..40bf1ddd8c63a387fca4d2541d9149f5579a25f6 100644 (file)
@@ -48,7 +48,7 @@ public:
     virtual bool aggregatable() const { return false; }
 };
 
-typedef LruMap<Security::ContextPointer, SSL_CTX_SIZE> LocalContextStorage;
+typedef LruMap<SBuf, Security::ContextPointer, SSL_CTX_SIZE> LocalContextStorage;
 
 /// Class for storing/manipulating LocalContextStorage per local listening address/port.
 class GlobalContextStorage
index b516e4d424d3a64152234ff1d90485941280ad5f..bc2e60a3eff9936234ac9c96f3a21f2c35872188 100644 (file)
@@ -9,6 +9,7 @@
 #include "squid.h"
 #include "ssl/gadgets.h"
 
+#include <openssl/asn1.h>
 #if HAVE_OPENSSL_X509V3_H
 #include <openssl/x509v3.h>
 #endif
@@ -117,26 +118,6 @@ bool Ssl::appendCertToMemory(Security::CertPointer const & cert, std::string & b
     return true;
 }
 
-bool Ssl::writeCertAndPrivateKeyToFile(Security::CertPointer const & cert, Ssl::EVP_PKEY_Pointer const & pkey, char const * filename)
-{
-    if (!pkey || !cert)
-        return false;
-
-    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
-    if (!bio)
-        return false;
-    if (!BIO_write_filename(bio.get(), const_cast<char *>(filename)))
-        return false;
-
-    if (!PEM_write_bio_X509(bio.get(), cert.get()))
-        return false;
-
-    if (!PEM_write_bio_PrivateKey(bio.get(), pkey.get(), NULL, NULL, 0, NULL, NULL))
-        return false;
-
-    return true;
-}
-
 bool Ssl::readCertAndPrivateKeyFromMemory(Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey, char const * bufferToRead)
 {
     Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
@@ -238,40 +219,60 @@ Ssl::CertificateProperties::CertificateProperties():
     signHash(NULL)
 {}
 
-std::string & Ssl::CertificateProperties::dbKey() const
+static void
+printX509Signature(const Security::CertPointer &cert, std::string &out)
+{
+    ASN1_BIT_STRING *sig = nullptr;
+#if HAVE_LIBCRYPTO_X509_GET0_SIGNATURE
+    X509_ALGOR *sig_alg;
+    X509_get0_signature(&sig, &sig_alg, cert.get());
+#else
+    sig = cert->signature;
+#endif
+
+    if (sig && sig->data) {
+        const unsigned char *s = sig->data;
+        for (int i = 0; i < sig->length; ++i) {
+            char hex[3];
+            snprintf(hex, sizeof(hex), "%02x", s[i]);
+            out.append(hex);
+        }
+    }
+}
+
+std::string &
+Ssl::OnDiskCertificateDbKey(const Ssl::CertificateProperties &properties)
 {
     static std::string certKey;
     certKey.clear();
     certKey.reserve(4096);
-    if (mimicCert.get()) {
-        char buf[1024];
-        certKey.append(X509_NAME_oneline(X509_get_subject_name(mimicCert.get()), buf, sizeof(buf)));
-    }
+    if (properties.mimicCert.get())
+        printX509Signature(properties.mimicCert, certKey);
 
     if (certKey.empty()) {
         certKey.append("/CN=", 4);
-        certKey.append(commonName);
+        certKey.append(properties.commonName);
     }
 
-    if (setValidAfter)
+    if (properties.setValidAfter)
         certKey.append("+SetValidAfter=on", 17);
 
-    if (setValidBefore)
+    if (properties.setValidBefore)
         certKey.append("+SetValidBefore=on", 18);
 
-    if (setCommonName) {
+    if (properties.setCommonName) {
         certKey.append("+SetCommonName=", 15);
-        certKey.append(commonName);
+        certKey.append(properties.commonName);
     }
 
-    if (signAlgorithm != Ssl::algSignEnd) {
+    if (properties.signAlgorithm != Ssl::algSignEnd) {
         certKey.append("+Sign=", 6);
-        certKey.append(certSignAlgorithm(signAlgorithm));
+        certKey.append(certSignAlgorithm(properties.signAlgorithm));
     }
 
-    if (signHash != NULL) {
+    if (properties.signHash != NULL) {
         certKey.append("+SignHash=", 10);
-        certKey.append(EVP_MD_name(signHash));
+        certKey.append(EVP_MD_name(properties.signHash));
     }
 
     return certKey;
@@ -698,46 +699,79 @@ bool Ssl::generateSslCertificate(Security::CertPointer & certToStore, Ssl::EVP_P
     return  generateFakeSslCertificate(certToStore, pkeyToStore, properties, serial);
 }
 
-/**
- \ingroup ServerProtocolSSLInternal
- * Read certificate from file.
- */
-static X509 * readSslX509Certificate(char const * certFilename)
+bool
+Ssl::OpenCertsFileForReading(Ssl::BIO_Pointer &bio, const char *filename)
 {
-    if (!certFilename)
-        return NULL;
-    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
+    bio.reset(BIO_new(BIO_s_file()));
     if (!bio)
-        return NULL;
-    if (!BIO_read_filename(bio.get(), certFilename))
-        return NULL;
-    X509 *certificate = PEM_read_bio_X509(bio.get(), NULL, NULL, NULL);
-    return certificate;
+        return false;
+    if (!BIO_read_filename(bio.get(), filename))
+        return false;
+    return true;
 }
 
-EVP_PKEY * Ssl::readSslPrivateKey(char const * keyFilename, pem_password_cb *passwd_callback)
+bool
+Ssl::ReadX509Certificate(Ssl::BIO_Pointer &bio, Security::CertPointer & cert)
+{
+    assert(bio);
+    if (X509 *certificate = PEM_read_bio_X509(bio.get(), NULL, NULL, NULL)) {
+        cert.resetWithoutLocking(certificate);
+        return true;
+    }
+    return false;
+}
+
+bool
+Ssl::ReadPrivateKey(Ssl::BIO_Pointer &bio, Ssl::EVP_PKEY_Pointer &pkey, pem_password_cb *passwd_callback)
+{
+    assert(bio);
+    if (EVP_PKEY *akey = PEM_read_bio_PrivateKey(bio.get(), NULL, passwd_callback, NULL)) {
+        pkey.resetWithoutLocking(akey);
+        return true;
+    }
+    return false;
+}
+
+void
+Ssl::ReadPrivateKeyFromFile(char const * keyFilename, Ssl::EVP_PKEY_Pointer &pkey, pem_password_cb *passwd_callback)
 {
     if (!keyFilename)
-        return NULL;
-    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file()));
+        return;
+    Ssl::BIO_Pointer bio;
+    if (!OpenCertsFileForReading(bio, keyFilename))
+        return;
+    ReadPrivateKey(bio, pkey, passwd_callback);
+}
+
+bool
+Ssl::OpenCertsFileForWriting(Ssl::BIO_Pointer &bio, const char *filename)
+{
+    bio.reset(BIO_new(BIO_s_file()));
     if (!bio)
-        return NULL;
-    if (!BIO_read_filename(bio.get(), keyFilename))
-        return NULL;
-    EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio.get(), NULL, passwd_callback, NULL);
-    return pkey;
+        return false;
+    if (!BIO_write_filename(bio.get(), const_cast<char *>(filename)))
+        return false;
+    return true;
 }
 
-void Ssl::readCertAndPrivateKeyFromFiles(Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey, char const * certFilename, char const * keyFilename)
+bool
+Ssl::WriteX509Certificate(Ssl::BIO_Pointer &bio, const Security::CertPointer & cert)
 {
-    if (keyFilename == NULL)
-        keyFilename = certFilename;
-    pkey.resetWithoutLocking(readSslPrivateKey(keyFilename));
-    cert.resetWithoutLocking(readSslX509Certificate(certFilename));
-    if (!pkey || !cert || !X509_check_private_key(cert.get(), pkey.get())) {
-        pkey.reset();
-        cert.reset();
-    }
+    if (!cert || !bio)
+        return false;
+    if (!PEM_write_bio_X509(bio.get(), cert.get()))
+        return false;
+    return true;
+}
+
+bool
+Ssl::WritePrivateKey(Ssl::BIO_Pointer &bio, const Ssl::EVP_PKEY_Pointer &pkey)
+{
+    if (!pkey || !bio)
+        return false;
+    if (!PEM_write_bio_PrivateKey(bio.get(), pkey.get(), NULL, NULL, 0, NULL, NULL))
+        return false;
+    return true;
 }
 
 bool Ssl::sslDateIsInTheFuture(char const * date)
@@ -893,3 +927,28 @@ const char *Ssl::getOrganization(X509 *x509)
     return getSubjectEntry(x509, NID_organizationName);
 }
 
+
+bool
+Ssl::CertificatesCmp(const Security::CertPointer &cert1, const Security::CertPointer &cert2)
+{
+    if (!cert1 || ! cert2)
+        return false;
+
+    int cert1Len;
+    unsigned char *cert1Asn = NULL;
+    cert1Len = ASN1_item_i2d((ASN1_VALUE *)cert1.get(), &cert1Asn, ASN1_ITEM_rptr(X509));
+
+    int cert2Len;
+    unsigned char *cert2Asn = NULL;
+    cert2Len = ASN1_item_i2d((ASN1_VALUE *)cert2.get(), &cert2Asn, ASN1_ITEM_rptr(X509));
+
+    if (cert1Len != cert2Len)
+        return false;
+
+    bool ret = (memcmp(cert1Asn, cert2Asn, cert1Len) == 0);
+
+    OPENSSL_free(cert1Asn);
+    OPENSSL_free(cert2Asn);
+
+    return ret;
+}
index 93fc8123dd847258d86090e2327ce048fa103a4a..b0e7f9004ee1100ac68790ddbfb25bed1bbacbf0 100644 (file)
@@ -94,12 +94,6 @@ bool writeCertAndPrivateKeyToMemory(Security::CertPointer const & cert, EVP_PKEY
  */
 bool appendCertToMemory(Security::CertPointer const & cert, std::string & bufferToWrite);
 
-/**
- \ingroup SslCrtdSslAPI
- * Write private key and SSL certificate to file.
- */
-bool writeCertAndPrivateKeyToFile(Security::CertPointer const & cert, EVP_PKEY_Pointer const & pkey, char const * filename);
-
 /**
  \ingroup SslCrtdSslAPI
  * Write private key and SSL certificate to memory.
@@ -112,6 +106,49 @@ bool readCertAndPrivateKeyFromMemory(Security::CertPointer & cert, EVP_PKEY_Poin
  */
 bool readCertFromMemory(Security::CertPointer & cert, char const * bufferToRead);
 
+/**
+ \ingroup SslCrtdSslAPI
+ * Read private key from file.
+ */
+void ReadPrivateKeyFromFile(char const * keyFilename, EVP_PKEY_Pointer &pkey, pem_password_cb *passwd_callback);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Initialize the bio with the file 'filename' openned for reading
+ */
+bool OpenCertsFileForReading(BIO_Pointer &bio, const char *filename);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Read a certificate from bio
+ */
+bool ReadX509Certificate(BIO_Pointer &bio, Security::CertPointer & cert);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Read a private key from bio
+ */
+bool ReadPrivateKey(BIO_Pointer &bio, EVP_PKEY_Pointer &pkey, pem_password_cb *passwd_callback);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Initialize the bio with the file 'filename' openned for writting
+ */
+
+bool OpenCertsFileForWriting(BIO_Pointer &bio, const char *filename);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Write certificate to BIO.
+ */
+bool WriteX509Certificate(BIO_Pointer &bio, const Security::CertPointer & cert);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Write private key to BIO.
+ */
+bool WritePrivateKey(BIO_Pointer &bio, const EVP_PKEY_Pointer &pkey);
+
 /**
   \ingroup SslCrtdSslAPI
  * Supported certificate signing algorithms
@@ -191,14 +228,15 @@ public:
     std::string commonName; ///< A CN to use for the generated certificate
     CertSignAlgorithm signAlgorithm; ///< The signing algorithm to use
     const EVP_MD *signHash; ///< The signing hash to use
-    /// Returns certificate database primary key. New fake certificates
-    /// purge old fake certificates with the same key.
-    std::string & dbKey() const;
 private:
     CertificateProperties(CertificateProperties &);
     CertificateProperties &operator =(CertificateProperties const &);
 };
 
+/// \ingroup SslCrtdSslAPI
+/// \returns certificate database key
+std::string & OnDiskCertificateDbKey(const CertificateProperties &);
+
 /**
  \ingroup SslCrtdSslAPI
  * Decide on the kind of certificate and generate a CA- or self-signed one.
@@ -208,20 +246,6 @@ private:
  */
 bool generateSslCertificate(Security::CertPointer & cert, EVP_PKEY_Pointer & pkey, CertificateProperties const &properties);
 
-/**
- \ingroup SslCrtdSslAPI
- * Read private key from file. Make sure that this is not encrypted file.
- */
-EVP_PKEY * readSslPrivateKey(char const * keyFilename, pem_password_cb *passwd_callback = NULL);
-
-/**
- \ingroup SslCrtdSslAPI
- *  Read certificate and private key from files.
- * \param certFilename name of file with certificate.
- * \param keyFilename name of file with private key.
- */
-void readCertAndPrivateKeyFromFiles(Security::CertPointer & cert, EVP_PKEY_Pointer & pkey, char const * certFilename, char const * keyFilename);
-
 /**
  \ingroup SslCrtdSslAPI
  * Verify date. Date format it ASN1_UTCTIME. if there is out of date error,
@@ -251,6 +275,9 @@ const char *CommonHostName(X509 *x509);
 */
 const char *getOrganization(X509 *x509);
 
+/// \ingroup ServerProtocolSSLAPI
+/// \return whether both certificates exist and are the same (e.g., have identical ASN.1 images)
+bool CertificatesCmp(const Security::CertPointer &cert1, const Security::CertPointer &cert2);
 } // namespace Ssl
 #endif // SQUID_SSL_GADGETS_H
 
index 2a36266a128393be30e08a69a5faad04435eff96..9d9723b4ab18b874e703feb573495dfd049bad13 100644 (file)
@@ -254,7 +254,7 @@ class submitData
     CBDATA_CLASS(submitData);
 
 public:
-    std::string query;
+    SBuf query;
     AsyncCall::Pointer callback;
     Security::SessionPointer ssl;
 };
@@ -291,7 +291,7 @@ sslCrtvdHandleReplyWrapper(void *data, const ::Helper::Reply &reply)
     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))
+        if (!Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query, item))
             delete item;
     }
 
@@ -308,14 +308,14 @@ void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &requ
     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;
     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);
index 88668d4f46a51c95f6d335982a0b5f24a42b45d0..fd06aba7532d70106019f4931d769c5ac9dcf236 100644 (file)
@@ -61,7 +61,7 @@ private:
 
     helper * ssl_crt_validator; ///< helper for management of ssl_crtd.
 public:
-    typedef LruMap<Ssl::CertValidationResponse::Pointer, sizeof(Ssl::CertValidationResponse::Pointer) + sizeof(Ssl::CertValidationResponse)> LruCache;
+    typedef LruMap<SBuf, Ssl::CertValidationResponse::Pointer, sizeof(Ssl::CertValidationResponse::Pointer) + sizeof(Ssl::CertValidationResponse)> LruCache;
     static LruCache *HelperCache; ///< cache for cert validation helper
 };
 
index d6c616c61c956642de43afc80d5278b01af09250..e80ea8936299cb111c76aafef5dee58ce0f9c1aa 100644 (file)
@@ -876,25 +876,31 @@ Ssl::createSSLContext(Security::CertPointer & x509, Ssl::EVP_PKEY_Pointer & pkey
 }
 
 Security::ContextPointer
-Ssl::generateSslContextUsingPkeyAndCertFromMemory(const char * data, AnyP::PortCfg &port)
+Ssl::GenerateSslContextUsingPkeyAndCertFromMemory(const char * data, AnyP::PortCfg &port, bool trusted)
 {
     Security::CertPointer cert;
     Ssl::EVP_PKEY_Pointer pkey;
     if (!readCertAndPrivateKeyFromMemory(cert, pkey, data) || !cert || !pkey)
         return Security::ContextPointer();
 
-    return createSSLContext(cert, pkey, port);
+    Security::ContextPointer ctx(createSSLContext(cert, pkey, port));
+    if (ctx && trusted)
+        Ssl::chainCertificatesToSSLContext(ctx, port);
+    return ctx;
 }
 
 Security::ContextPointer
-Ssl::generateSslContext(CertificateProperties const &properties, AnyP::PortCfg &port)
+Ssl::GenerateSslContext(CertificateProperties const &properties, AnyP::PortCfg &port, bool trusted)
 {
     Security::CertPointer cert;
     Ssl::EVP_PKEY_Pointer pkey;
     if (!generateSslCertificate(cert, pkey, properties) || !cert || !pkey)
         return Security::ContextPointer();
 
-    return createSSLContext(cert, pkey, port);
+    Security::ContextPointer ctx(createSSLContext(cert, pkey, port));
+    if (ctx && trusted)
+        Ssl::chainCertificatesToSSLContext(ctx, port);
+    return ctx;
 }
 
 void
@@ -985,11 +991,7 @@ Ssl::verifySslCertificate(Security::ContextPointer &ctx, CertificateProperties c
         return false;
     ASN1_TIME * time_notBefore = X509_get_notBefore(cert);
     ASN1_TIME * time_notAfter = X509_get_notAfter(cert);
-    bool ret = (X509_cmp_current_time(time_notBefore) < 0 && X509_cmp_current_time(time_notAfter) > 0);
-    if (!ret)
-        return false;
-
-    return certificateMatchesProperties(cert, properties);
+    return (X509_cmp_current_time(time_notBefore) < 0 && X509_cmp_current_time(time_notAfter) > 0);
 }
 
 bool
@@ -1323,7 +1325,7 @@ void Ssl::readCertChainAndPrivateKeyFromFiles(Security::CertPointer & cert, EVP_
     // XXX: ssl_ask_password_cb needs SSL_CTX_set_default_passwd_cb_userdata()
     // so this may not fully work iff Config.Program.ssl_password is set.
     pem_password_cb *cb = ::Config.Program.ssl_password ? &ssl_ask_password_cb : NULL;
-    pkey.resetWithoutLocking(readSslPrivateKey(keyFilename, cb));
+    Ssl::ReadPrivateKeyFromFile(keyFilename, pkey, cb);
     cert.resetWithoutLocking(readSslX509CertificatesChain(certFilename, chain.get()));
     if (!cert) {
         debugs(83, DBG_IMPORTANT, "WARNING: missing cert in '" << certFilename << "'");
@@ -1361,5 +1363,127 @@ bool Ssl::generateUntrustedCert(Security::CertPointer &untrustedCert, EVP_PKEY_P
     return Ssl::generateSslCertificate(untrustedCert, untrustedPkey, certProperties);
 }
 
+void Ssl::InRamCertificateDbKey(const Ssl::CertificateProperties &certProperties, SBuf &key)
+{
+    bool origSignatureAsKey = false;
+    if (certProperties.mimicCert.get()) {
+        ASN1_BIT_STRING *sig = nullptr;
+#if HAVE_LIBCRYPTO_X509_GET0_SIGNATURE
+        X509_ALGOR *sig_alg;
+        X509_get0_signature(&sig, &sig_alg, certProperties.mimicCert.get());
+#else
+        sig = certProperties.mimicCert->signature;
+#endif
+        if (sig) {
+            origSignatureAsKey = true;
+            key.append((const char *)sig->data, sig->length);
+        }
+    }
+
+    if (!origSignatureAsKey || certProperties.setCommonName) {
+        // Use common name instead
+        key.append(certProperties.commonName.c_str());
+    }
+    key.append(certProperties.setCommonName ? '1' : '0');
+    key.append(certProperties.setValidAfter ? '1' : '0');
+    key.append(certProperties.setValidBefore ? '1' : '0');
+    key.append(certProperties.signAlgorithm != Ssl:: algSignEnd ? certSignAlgorithm(certProperties.signAlgorithm) : "-");
+    key.append(certProperties.signHash ? EVP_MD_name(certProperties.signHash) : "-");
+
+    if (certProperties.mimicCert) {
+        BIO *bio = BIO_new_SBuf(&key);
+        ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509), bio, (ASN1_VALUE *)certProperties.mimicCert.get());
+    }
+}
+
+static int
+bio_sbuf_create(BIO* bio)
+{
+    BIO_set_init(bio, 0);
+    BIO_set_data(bio, NULL);
+    return 1;
+}
+
+static int
+bio_sbuf_destroy(BIO* bio)
+{
+    if (!bio)
+        return 0;
+    return 1;
+}
+
+int
+bio_sbuf_write(BIO* bio, const char* data, int len)
+{
+    SBuf *buf = static_cast<SBuf *>(BIO_get_data(bio));
+    buf->append(data, len);
+    return len;
+}
+
+int
+bio_sbuf_puts(BIO* bio, const char* data)
+{
+    SBuf *buf = static_cast<SBuf *>(BIO_get_data(bio));
+    size_t oldLen = buf->length();
+    buf->append(data);
+    return buf->length() - oldLen;
+}
+
+long
+bio_sbuf_ctrl(BIO* bio, int cmd, long num, void* ptr) {
+    SBuf *buf = static_cast<SBuf *>(BIO_get_data(bio));
+    switch (cmd) {
+    case BIO_CTRL_RESET:
+        buf->clear();
+        return 1;
+    case BIO_CTRL_FLUSH:
+        return 1;
+    default:
+        return 0;
+    }
+}
+
+
+#if HAVE_LIBCRYPTO_BIO_METH_NEW
+static BIO_METHOD *BioSBufMethods = nullptr;
+#else
+static BIO_METHOD BioSBufMethods = {
+    BIO_TYPE_MEM,
+    "Squid SBuf",
+    bio_sbuf_write,
+    nullptr,
+    bio_sbuf_puts,
+    nullptr,
+    bio_sbuf_ctrl,
+    bio_sbuf_create,
+    bio_sbuf_destroy,
+    NULL,
+
+};
+#endif
+
+BIO *Ssl::BIO_new_SBuf(SBuf *buf)
+{
+#if HAVE_LIBCRYPTO_BIO_METH_NEW
+    if (!BioSBufMethods) {
+        BioSBufMethods = BIO_meth_new(BIO_TYPE_MEM, "Squid-SBuf");
+        BIO_meth_set_write(BioSBufMethods, bio_sbuf_write);
+        BIO_meth_set_read(BioSBufMethods, nullptr);
+        BIO_meth_set_puts(BioSBufMethods, bio_sbuf_puts);
+        BIO_meth_set_gets(BioSBufMethods, nullptr);
+        BIO_meth_set_ctrl(BioSBufMethods, bio_sbuf_ctrl);
+        BIO_meth_set_create(BioSBufMethods, bio_sbuf_create);
+        BIO_meth_set_destroy(BioSBufMethods, bio_sbuf_destroy);
+    }
+#else
+    BIO *bio = BIO_new(&BioSBufMethods);
+#endif
+    if (!bio)
+        return nullptr;
+    BIO_set_data(bio, buf);
+    BIO_set_init(bio, 1);
+    return bio;
+}
+
 #endif /* USE_OPENSSL */
 
index 67d8c2b64b2e02358c0f7c3d4c00c501a142a248..b28322daf169e4149e1f0d6153c5c92280367946 100644 (file)
@@ -211,7 +211,7 @@ void unloadSquidUntrusted();
   \ingroup ServerProtocolSSLAPI
   * Decide on the kind of certificate and generate a CA- or self-signed one
 */
-Security::ContextPointer generateSslContext(CertificateProperties const &properties, AnyP::PortCfg &port);
+Security::ContextPointer GenerateSslContext(CertificateProperties const &properties, AnyP::PortCfg &port, bool trusted);
 
 /**
   \ingroup ServerProtocolSSLAPI
@@ -227,7 +227,7 @@ bool verifySslCertificate(Security::ContextPointer &, CertificateProperties cons
   * Read private key and certificate from memory and generate SSL context
   * using their.
  */
-Security::ContextPointer generateSslContextUsingPkeyAndCertFromMemory(const char * data, AnyP::PortCfg &port);
+Security::ContextPointer GenerateSslContextUsingPkeyAndCertFromMemory(const char * data, AnyP::PortCfg &port, bool trusted);
 
 /**
   \ingroup ServerProtocolSSLAPI
@@ -321,6 +321,18 @@ int asn1timeToString(ASN1_TIME *tm, char *buf, int len);
 */
 bool setClientSNI(SSL *ssl, const char *fqdn);
 
+
+/**
+  \ingroup ServerProtocolSSLAPI
+  * Generates a unique key based on CertificateProperties object and store it to key
+ */
+void InRamCertificateDbKey(const Ssl::CertificateProperties &certProperties, SBuf &key);
+
+/**
+  \ingroup ServerProtocolSSLAPI
+  Generates an OpenSSL BIO for writting to an SBuf object
+ */
+BIO *BIO_new_SBuf(SBuf *buf);
 } //namespace Ssl
 
 #if _SQUID_WINDOWS_
index 8a989b30dd280873bc2a07c127e0b6811b7ea4a9..d321452f8eaec963f6db533ab0cd0dd7bf870033 100644 (file)
@@ -44,7 +44,7 @@ NotePairs::Pointer ConnStateData::notes() STUB_RETVAL(NotePairs::Pointer())
 #if USE_OPENSSL
 void ConnStateData::httpsPeeked(PinnedIdleContext) STUB
 void ConnStateData::getSslContextStart() STUB
-void ConnStateData::getSslContextDone(Security::ContextPointer &, bool) STUB
+void ConnStateData::getSslContextDone(Security::ContextPointer &) STUB
 void ConnStateData::sslCrtdHandleReplyWrapper(void *, const Helper::Reply &) STUB
 void ConnStateData::sslCrtdHandleReply(const Helper::Reply &) STUB
 void ConnStateData::switchToHttps(HttpRequest *, Ssl::BumpMode) STUB
index 4481cef3bfe047353b55417933b9f2b7535b28d1..e0182ce12e52c413f1bff5cc3bd4af03cb902d82 100644 (file)
@@ -65,9 +65,9 @@ namespace Ssl
 //GETX509ATTRIBUTE GetX509Fingerprint;
 std::vector<const char *> BumpModeStr = {""};
 bool generateUntrustedCert(Security::CertPointer & untrustedCert, EVP_PKEY_Pointer & untrustedPkey, Security::CertPointer const & cert, EVP_PKEY_Pointer const & pkey) STUB_RETVAL(false)
-Security::ContextPointer generateSslContext(CertificateProperties const &, AnyP::PortCfg &) STUB_RETVAL(Security::ContextPointer())
+Security::ContextPointer GenerateSslContext(CertificateProperties const &, AnyP::PortCfg &, bool) STUB_RETVAL(Security::ContextPointer())
 bool verifySslCertificate(Security::ContextPointer &, CertificateProperties const &) STUB_RETVAL(false)
-Security::ContextPointer generateSslContextUsingPkeyAndCertFromMemory(const char *, AnyP::PortCfg &) STUB_RETVAL(Security::ContextPointer())
+Security::ContextPointer GenerateSslContextUsingPkeyAndCertFromMemory(const char *, AnyP::PortCfg &, bool) STUB_RETVAL(Security::ContextPointer())
 void addChainToSslContext(Security::ContextPointer &, STACK_OF(X509) *) STUB
 void readCertChainAndPrivateKeyFromFiles(Security::CertPointer & cert, EVP_PKEY_Pointer & pkey, X509_STACK_Pointer & chain, char const * certFilename, char const * keyFilename) STUB
 int matchX509CommonNames(X509 *peer_cert, void *check_data, int (*check_func)(void *check_data,  ASN1_STRING *cn_data)) STUB_RETVAL(0)