]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Author: Alex Rousskov, Andrew Balabohin, Christos Tsantilas
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Thu, 18 Nov 2010 08:01:53 +0000 (10:01 +0200)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Thu, 18 Nov 2010 08:01:53 +0000 (10:01 +0200)
Dynamic SSL certificate generartion

This patch implements dynamic SSL certificate generartion in Squid.When
used with SSL Bump, the feature allows Squid to dynamically
generate (using a configurable CA certificate) and cache SSL
certificates for the proxied hosts.

A description for this feature can be found at:
   http://wiki.squid-cache.org/Features/DynamicSslCert

A first version of the patch posted by Alex, some months before:
  http://www.squid-cache.org/mail-archive/squid-dev/201003/0201.html

Some words about the patch:

  * ssl related source files moved under the src/ssl directory

  * Introduce the TidyPointer class similar to std::auto_ptr, which implements
    a  pointer that deletes the object it points to when the pointer's owner
    or context is gone. It  is designed to avoid memory leaks  in the presence
    of exceptions and processing short cuts.

  * Implements ssl context cache to use with generated ssl contexts. The
    Ssl::LocalContextStorage class stores the hostname/ssl context pairs for
    a local listening address/port. The  Ssl::GlobalContextStorage class used
    to store Ssl::LocalContextStorages per local listening address and handles
    squid shutdown/configure/reconfigure

  * Ssl::Helper class implements the squid part of the ssl_crtd helpers.

  * The ssl_crtd helper implemented in ssl_crtd.cc and certificate_db.* files

  * The Ssl::CertificateDb class (certificate_db.* files)  implements  a
    database of certificates on disk files. It is used by ssl_crtd helper to
    manipulate generated certificates.

  * The ssl related files included in the libraries libsslutil.a which
    contains common classes and functions and the libsquidssl.a which has
    squid related ssl objects and functions

  * Use the Ssl namespace for new ssl code

This is a Measurement Factory Project.

36 files changed:
configure.in
src/Makefile.am
src/ProtoPort.cc
src/ProtoPort.h
src/acl/Certificate.h
src/acl/CertificateData.h
src/acl/SslErrorData.h
src/base/Makefile.am
src/base/TidyPointer.h [new file with mode: 0644]
src/cache_cf.cc
src/cf.data.pre
src/client_side.cc
src/client_side.h
src/client_side_request.cc
src/comm.cc
src/fde.h
src/helper.cc
src/helper.h
src/main.cc
src/squid.h
src/ssl/Config.cc [new file with mode: 0644]
src/ssl/Config.h [new file with mode: 0644]
src/ssl/Makefile.am [new file with mode: 0644]
src/ssl/certificate_db.cc [new file with mode: 0644]
src/ssl/certificate_db.h [new file with mode: 0644]
src/ssl/context_storage.cc [new file with mode: 0644]
src/ssl/context_storage.h [new file with mode: 0644]
src/ssl/crtd_message.cc [new file with mode: 0644]
src/ssl/crtd_message.h [new file with mode: 0644]
src/ssl/gadgets.cc [new file with mode: 0644]
src/ssl/gadgets.h [new file with mode: 0644]
src/ssl/helper.cc [new file with mode: 0644]
src/ssl/helper.h [new file with mode: 0644]
src/ssl/ssl_crtd.cc [new file with mode: 0644]
src/ssl/ssl_support.cc [moved from src/ssl_support.cc with 94% similarity]
src/ssl/ssl_support.h [moved from src/ssl_support.h with 84% similarity]

index 5398c29aa51137eaa6fc80a5fcd589d05a6e138e..2229784b52de0e23c64f76f830412976bc45b8bd 100644 (file)
@@ -1513,6 +1513,21 @@ SQUID_DEFINE_BOOL(USE_DNSSERVERS,$squid_opt_use_dnsserver,
 AM_CONDITIONAL([USE_DNSSERVER],[test "x$squid_opt_use_dnsserver" = "xyes" ])
 
 
+AM_CONDITIONAL(USE_SSL_CRTD, false)
+AC_ARG_ENABLE(ssl-crtd,
+  AC_HELP_STRING([--enable-ssl-crtd],
+                 [Prevent Squid from directly generation of SSL private key and 
+                  certificate request and instead enables the ssl_crtd processes.]), [
+  SQUID_YESNO([$enableval],
+  [unrecogized argument to --enable-ssl-crtd: $enableval])
+])
+
+if test "x$enable_ssl_crtd" = "xyes" -a "x$enable_ssl" = "xno" ; then
+   AC_MSG_ERROR([You need to enable ssl gatewaying support to use ssl_crtd feature. Try to use --enable-ssl. ])
+fi
+SQUID_DEFINE_BOOL(USE_SSL_CRTD, ${enable_ssl_crtd:=no},[Use ssl_crtd daemon])
+AM_CONDITIONAL(USE_SSL_CRTD, [test "x$enable_ssl_crtd" = "xyes"])
+
 dnl Select Default hosts file location
 AC_ARG_ENABLE(default-hostsfile,
   AS_HELP_STRING([--enable-default-hostsfile=path],
@@ -2187,6 +2202,7 @@ AC_CHECK_HEADERS( \
   crypt.h \
   cstdlib \
   cstring \
+  list \
   ctype.h \
   errno.h \
   execinfo.h \
@@ -2199,6 +2215,7 @@ AC_CHECK_HEADERS( \
   iosfwd \
   iomanip \
   iostream \
+  fstream \
   climits \
   ip_compat.h \
   ip_fil_compat.h \
@@ -2217,6 +2234,7 @@ AC_CHECK_HEADERS( \
   map \
   math.h \
   memory.h \
+  memory \
   mount.h \
   netdb.h \
   netinet/in.h \
@@ -2228,6 +2246,7 @@ AC_CHECK_HEADERS( \
   openssl/x509v3.h \
   netinet/tcp.h \
   openssl/engine.h \
+  openssl/txt_db.h \
   ostream \
   paths.h \
   poll.h \
@@ -3332,6 +3351,7 @@ AC_CONFIG_FILES([\
        src/ip/Makefile \
        src/log/Makefile \
        src/ipc/Makefile \
+       src/ssl/Makefile \
        src/mgr/Makefile \
        contrib/Makefile \
        snmplib/Makefile \
index 81a8172c59e99dfa5324213689956404b90bf142..f57d284e497c80c3baa9d5619f1facb95e63b063 100644 (file)
@@ -41,6 +41,15 @@ LOADABLE_MODULES_SOURCES = \
 
 SUBDIRS        = base comm eui acl fs repl auth ip icmp ident log ipc mgr
 
+if ENABLE_SSL
+SUBDIRS += ssl
+SSL_LIBS = \
+       ssl/libsslutil.la \
+       ssl/libsslsquid.la
+else
+SSL_LOCAL_LIBS =
+endif
+
 if USE_ADAPTATION
 SUBDIRS += adaptation
 endif
@@ -114,16 +123,6 @@ UNLINKDSOURCE =
 UNLINKD = 
 endif
 
-SSL_ALL_SOURCE = \
-       ssl_support.cc \
-       ssl_support.h
-
-if ENABLE_SSL
-SSL_SOURCE = $(SSL_ALL_SOURCE)
-else
-SSL_SOURCE = 
-endif
-
 WIN32_ALL_SOURCE = \
        win32.cc \
        WinSvc.cc
@@ -430,7 +429,6 @@ squid_SOURCES = \
        SquidMath.h \
        SquidMath.cc \
        SquidNew.cc \
-       $(SSL_SOURCE) \
        stat.cc \
        StatHist.cc \
        String.cc \
@@ -517,7 +515,6 @@ EXTRA_squid_SOURCES = \
        LeakFinder.h \
        $(SNMP_ALL_SOURCE) \
        $(UNLINKDSOURCE) \
-       $(SSL_ALL_SOURCE) \
        $(WIN32_ALL_SOURCE) \
        $(LOADABLE_MODULES_SOURCES) \
        DiskIO/DiskThreads/aiops.cc \
@@ -567,6 +564,7 @@ squid_LDADD = \
        $(SNMPLIB) \
        ${ADAPTATION_LIBS} \
        $(ESI_LIBS) \
+       $(SSL_LIBS) \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
@@ -583,6 +581,7 @@ squid_DEPENDENCIES = \
        $(SNMPLIB) \
        ${ADAPTATION_LIBS} \
        $(ESI_LOCAL_LIBS) \
+       $(SSL_LIBS) \
        $(COMMON_LIBS)
 
 if USE_LOADABLE_MODULES
@@ -761,12 +760,14 @@ DEFAULT_CONFIG_DIR        = $(sysconfdir)
 DEFAULT_CONFIG_FILE    = $(DEFAULT_CONFIG_DIR)/squid.conf
 DEFAULT_MIME_TABLE     = $(DEFAULT_CONFIG_DIR)/mime.conf
 DEFAULT_DNSSERVER      = $(libexecdir)/`echo dnsserver | sed '$(transform);s/$$/$(EXEEXT)/'`
+DEFAULT_SSL_CRTD       = $(libexecdir)/`echo ssl_crtd  | sed '$(transform);s/$$/$(EXEEXT)/'`
 DEFAULT_LOG_PREFIX     = $(DEFAULT_LOG_DIR)
 DEFAULT_CACHE_LOG      = $(DEFAULT_LOG_PREFIX)/cache.log
 DEFAULT_ACCESS_LOG     = $(DEFAULT_LOG_PREFIX)/access.log
 DEFAULT_STORE_LOG      = $(DEFAULT_LOG_PREFIX)/store.log
 DEFAULT_NETDB_FILE     = $(DEFAULT_LOG_PREFIX)/netdb.state
 DEFAULT_SWAP_DIR       = $(localstatedir)/cache
+DEFAULT_SSL_DB_DIR     = $(localstatedir)/lib/ssl_db
 DEFAULT_PINGER         = $(libexecdir)/`echo pinger | sed '$(transform);s/$$/$(EXEEXT)/'`
 DEFAULT_UNLINKD                = $(libexecdir)/`echo unlinkd | sed '$(transform);s/$$/$(EXEEXT)/'`
 DEFAULT_LOGFILED       = $(libexecdir)/`echo log_file_daemon | sed '$(transform);s/$$/$(EXEEXT)/'`
@@ -833,6 +834,7 @@ cf.data: cf.data.pre Makefile
        -e "s%[@]DEFAULT_CACHE_EFFECTIVE_USER[@]%${CACHE_EFFECTIVE_USER}%g" \
        -e "s%[@]DEFAULT_MIME_TABLE[@]%$(DEFAULT_MIME_TABLE)%g" \
        -e "s%[@]DEFAULT_DNSSERVER[@]%$(DEFAULT_DNSSERVER)%g" \
+       -e "s%[@]DEFAULT_SSL_CRTD[@]%$(DEFAULT_SSL_CRTD)%g" \
        -e "s%[@]DEFAULT_UNLINKD[@]%$(DEFAULT_UNLINKD)%g" \
        -e "s%[@]DEFAULT_PINGER[@]%$(DEFAULT_PINGER)%g" \
        -e "s%[@]DEFAULT_DISKD[@]%$(DEFAULT_DISKD)%g" \
@@ -843,6 +845,7 @@ cf.data: cf.data.pre Makefile
        -e "s%[@]DEFAULT_PID_FILE[@]%$(DEFAULT_PID_FILE)%g" \
        -e "s%[@]DEFAULT_NETDB_FILE[@]%$(DEFAULT_NETDB_FILE)%g" \
        -e "s%[@]DEFAULT_SWAP_DIR[@]%$(DEFAULT_SWAP_DIR)%g" \
+       -e "s%[@]DEFAULT_SSL_DB_DIR[@]%$(DEFAULT_SSL_DB_DIR)%g" \
        -e "s%[@]DEFAULT_ICON_DIR[@]%$(DEFAULT_ICON_DIR)%g" \
        -e "s%[@]DEFAULT_CONFIG_DIR[@]%$(DEFAULT_CONFIG_DIR)%g" \
        -e "s%[@]DEFAULT_PREFIX[@]%$(DEFAULT_PREFIX)%g" \
@@ -1234,7 +1237,6 @@ tests_testCacheManager_SOURCES = \
        RemovalPolicy.cc \
        Server.cc \
        $(SNMP_SOURCE) \
-       $(SSL_SOURCE) \
        SquidMath.h \
        SquidMath.cc \
        stat.cc \
@@ -1285,6 +1287,7 @@ tests_testCacheManager_LDADD = \
        $(REPL_OBJS) \
        ${ADAPTATION_LIBS} \
        $(ESI_LIBS) \
+       $(SSL_LIBS) \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
@@ -1438,7 +1441,6 @@ tests_testEvent_SOURCES = \
        refresh.cc \
        Server.cc \
        $(SNMP_SOURCE) \
-       $(SSL_SOURCE) \
        SquidMath.h \
        SquidMath.cc \
        stat.cc \
@@ -1488,6 +1490,7 @@ tests_testEvent_LDADD = \
        $(REPL_OBJS) \
        ${ADAPTATION_LIBS} \
        $(ESI_LIBS) \
+       $(SSL_LIBS) \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
@@ -1601,7 +1604,6 @@ tests_testEventLoop_SOURCES = \
        refresh.cc \
        Server.cc \
        $(SNMP_SOURCE) \
-       $(SSL_SOURCE) \
        SquidMath.h \
        SquidMath.cc \
        stat.cc \
@@ -1651,6 +1653,7 @@ tests_testEventLoop_LDADD = \
        $(REPL_OBJS) \
        ${ADAPTATION_LIBS} \
        $(ESI_LIBS) \
+       $(SSL_LIBS) \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
@@ -1755,7 +1758,6 @@ tests_test_http_range_SOURCES = \
        RemovalPolicy.cc \
        Server.cc \
        $(SNMP_SOURCE) \
-       $(SSL_SOURCE) \
        SquidMath.h \
        SquidMath.cc \
        stat.cc \
@@ -1809,6 +1811,7 @@ tests_test_http_range_LDADD = \
        $(REPL_OBJS) \
        ${ADAPTATION_LIBS} \
        $(ESI_LIBS) \
+       $(SSL_LIBS) \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
@@ -1922,7 +1925,6 @@ tests_testHttpRequest_SOURCES = \
        RemovalPolicy.cc \
        Server.cc \
        $(SNMP_SOURCE) \
-       $(SSL_SOURCE) \
        SquidMath.h \
        SquidMath.cc \
        stat.cc \
@@ -1972,6 +1974,7 @@ tests_testHttpRequest_LDADD = \
        $(REPL_OBJS) \
        ${ADAPTATION_LIBS} \
        $(ESI_LIBS) \
+       $(SSL_LIBS) \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
@@ -2339,7 +2342,6 @@ tests_testURL_SOURCES = \
        refresh.cc \
        Server.cc \
        $(SNMP_SOURCE) \
-       $(SSL_SOURCE) \
        SquidMath.h \
        SquidMath.cc \
        stat.cc \
@@ -2388,6 +2390,7 @@ tests_testURL_LDADD = \
        $(REPL_OBJS) \
        ${ADAPTATION_LIBS} \
        $(ESI_LIBS) \
+       $(SSL_LIBS) \
        $(SNMPLIB) \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
index c03f2efa79e2953eb88c0e11180bde498d214c74..98d4142a62992876fab889434fe8270f3c67ff77 100644 (file)
@@ -4,11 +4,14 @@
 
 #include "squid.h"
 #include "ProtoPort.h"
+#if HAVE_LIMITS
+#include <limits>
+#endif
 
 http_port_list::http_port_list(const char *aProtocol)
 #if USE_SSL
         :
-        http(*this)
+        http(*this), dynamicCertMemCacheSize(std::numeric_limits<size_t>::max())
 #endif
 {
     protocol = xstrdup(aProtocol);
@@ -31,6 +34,7 @@ http_port_list::~http_port_list()
     safe_free(capath);
     safe_free(dhfile);
     safe_free(sslflags);
+    safe_free(sslContextSessionId);
 #endif
 }
 
index 2afd5592ba20a59102ca2993f9a4c20c590b6ccc..6bee84f57862711765bd6f1524e163600461efd3 100644 (file)
@@ -8,6 +8,10 @@
 #include "cbdata.h"
 #include "comm/ListenStateData.h"
 
+#if USE_SSL
+#include "ssl/gadgets.h"
+#endif
+
 struct http_port_list {
     http_port_list(const char *aProtocol);
     ~http_port_list();
@@ -60,8 +64,13 @@ struct http_port_list {
     char *crlfile;
     char *dhfile;
     char *sslflags;
-    char *sslcontext;
-    SSL_CTX *sslContext;
+    char *sslContextSessionId; ///< "session id context" for staticSslContext
+    bool generateHostCertificates; ///< dynamically make host cert for sslBump
+    size_t dynamicCertMemCacheSize; ///< max size of generated certificates memory cache
+
+    Ssl::SSL_CTX_Pointer staticSslContext; ///< for HTTPS accelerator or static sslBump
+    Ssl::X509_Pointer signingCert; ///< x509 certificate for signing generated certificates
+    Ssl::EVP_PKEY_Pointer signPkey; ///< private key for sighing generated certificates
 #endif
 
     CBDATA_CLASS2(http_port_list);
index 4cea946fb895a51a6757140477a7cc9c82dd5e8c..a2ccbbc3a576e93c6be361142aa323b5aa19a711 100644 (file)
@@ -38,7 +38,7 @@
 #include "acl/Acl.h"
 #include "acl/Data.h"
 #include "acl/Checklist.h"
-#include "ssl_support.h"
+#include "ssl/support.h"
 #include "acl/Strategised.h"
 
 /// \ingroup ACLAPI
index 08056aeb0075215c9e6f44d7f8a14eec096a8c18..3ddbc855c804ea94fcc6d13ef4fce0bec1267997 100644 (file)
@@ -38,7 +38,7 @@
 #include "splay.h"
 #include "acl/Acl.h"
 #include "acl/Data.h"
-#include "ssl_support.h"
+#include "ssl/support.h"
 #include "acl/StringData.h"
 
 /// \ingroup ACLAPI
index c26e17c383b258998ef31de83412bb5a91b04216..fc0afff5498900a9beaa8c700c131cd0c835ff14 100644 (file)
@@ -8,7 +8,7 @@
 #include "acl/Acl.h"
 #include "acl/Data.h"
 #include "CbDataList.h"
-#include "ssl_support.h"
+#include "ssl/support.h"
 
 class ACLSslErrorData : public ACLData<ssl_error_t>
 {
index b50866e0c9c301d53040b036e82afa659a80666d..408d20984bf2807d1057a67cec83990fcb9727dc 100644 (file)
@@ -12,6 +12,7 @@ libbase_la_SOURCES = \
        AsyncJobCalls.h \
        AsyncCallQueue.cc \
        AsyncCallQueue.h \
+       TidyPointer.h \
        CbcPointer.h \
        InstanceId.h \
        Subscription.h \
diff --git a/src/base/TidyPointer.h b/src/base/TidyPointer.h
new file mode 100644 (file)
index 0000000..5448874
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_BASE_TIDYPOINTER_H
+#define SQUID_BASE_TIDYPOINTER_H
+
+#include "config.h"
+
+/**
+ * A pointer that deletes the object it points to when the pointer's owner or
+ * context is gone. Similar to std::auto_ptr but without confusing assignment
+ * and with a customizable cleanup method. Prevents memory leaks in
+ * the presence of exceptions and processing short cuts.
+*/
+template <typename T, void (*DeAllocator)(T *t)> class TidyPointer
+{
+public:
+    /// Delete callback.
+    typedef void DCB (T *t);
+    TidyPointer(T *t = NULL)
+            :   raw(t), deAllocator(DeAllocator) {}
+public:
+    bool operator !() const { return !raw; }
+    /// Returns raw and possibly NULL pointer
+    T *get() const { return raw; }
+    /// Address of the raw pointer, for pointer-setting functions
+    T **addr() { return &raw; }
+    /// Reset raw pointer - delete last one and save new one.
+    void reset(T *t) {
+        deletePointer();
+        raw = t;
+    }
+
+    /// Forget the raw pointer without freeing it. Become a nil pointer.
+    T *release() {
+        T *ret = raw;
+        raw = NULL;
+        return ret;
+    }
+    /// Deallocate raw pointer.
+    ~TidyPointer() {
+        deletePointer();
+    }
+private:
+    /// Forbidden copy constructor.
+    TidyPointer(TidyPointer<T, DeAllocator> const &);
+    /// Forbidden assigment operator.
+    TidyPointer <T, DeAllocator> & operator = (TidyPointer<T, DeAllocator> const &);
+    /// Deallocate raw pointer. Become a nil pointer.
+    void deletePointer() {
+        if (raw) {
+            deAllocator(raw);
+        }
+        raw = NULL;
+    }
+    T *raw; ///< pointer to T object or NULL
+    DCB *deAllocator; ///< cleanup function
+};
+
+/// DeAllocator for pointers that need free(3) from the std C library
+template<typename T> void tidyFree(T *p)
+{
+    xfree(p);
+}
+
+#endif // SQUID_BASE_TIDYPOINTER_H
index d5db4bd869f2cea80a88ffa9a535825d94f5c814..71fde205ef55f4d4e468f71307d1cd133537d204 100644 (file)
@@ -46,6 +46,9 @@
 #if USE_ECAP
 #include "adaptation/ecap/Config.h"
 #endif
+#if USE_SSL
+#include "ssl/Config.h"
+#endif
 #include "auth/Config.h"
 #include "auth/Scheme.h"
 #include "ConfigParser.h"
 #include <limits>
 #endif
 
+#if USE_SSL
+#include "ssl/gadgets.h"
+#endif
+
 #if USE_ADAPTATION
 static void parse_adaptation_service_set_type();
 static void parse_adaptation_service_chain_type();
@@ -146,6 +153,9 @@ static void defaults_if_none(void);
 static int parse_line(char *);
 static void parse_obsolete(const char *);
 static void parseBytesLine(size_t * bptr, const char *units);
+#if USE_SSL
+static void parseBytesOptionValue(size_t * bptr, const char *units, char const * value);
+#endif
 #if !USE_DNSSERVERS
 static void parseBytesLineSigned(ssize_t * bptr, const char *units);
 #endif
@@ -875,7 +885,13 @@ configDoConfigure(void)
 
             debugs(3, 1, "Initializing http_port " << s->http.s << " SSL context");
 
-            s->sslContext = sslCreateServerContext(s->cert, s->key, s->version, s->cipher, s->options, s->sslflags, s->clientca, s->cafile, s->capath, s->crlfile, s->dhfile, s->sslcontext);
+            s->staticSslContext.reset(
+                sslCreateServerContext(s->cert, s->key,
+                                       s->version, s->cipher, s->options, s->sslflags, s->clientca,
+                                       s->cafile, s->capath, s->crlfile, s->dhfile,
+                                       s->sslContextSessionId));
+
+            Ssl::readCertAndPrivateKeyFromFiles(s->signingCert, s->signPkey, s->cert, s->key);
         }
     }
 
@@ -886,7 +902,11 @@ configDoConfigure(void)
         for (s = Config.Sockaddr.https; s != NULL; s = (https_port_list *) s->http.next) {
             debugs(3, 1, "Initializing https_port " << s->http.s << " SSL context");
 
-            s->sslContext = sslCreateServerContext(s->cert, s->key, s->version, s->cipher, s->options, s->sslflags, s->clientca, s->cafile, s->capath, s->crlfile, s->dhfile, s->sslcontext);
+            s->staticSslContext.reset(
+                sslCreateServerContext(s->cert, s->key,
+                                       s->version, s->cipher, s->options, s->sslflags, s->clientca,
+                                       s->cafile, s->capath, s->crlfile, s->dhfile,
+                                       s->sslContextSessionId));
         }
     }
 
@@ -1113,6 +1133,44 @@ parseBytesLineSigned(ssize_t * bptr, const char *units)
 }
 #endif
 
+#if USE_SSL
+/**
+ * Parse bytes from a string.
+ * Similar to the parseBytesLine function but parses the string value instead of
+ * the current token value.
+ */
+static void parseBytesOptionValue(size_t * bptr, const char *units, char const * value)
+{
+    int u;
+    if ((u = parseBytesUnits(units)) == 0) {
+        self_destruct();
+        return;
+    }
+
+    // Find number from string beginning.
+    char const * number_begin = value;
+    char const * number_end = value;
+
+    while ((*number_end >= '0' && *number_end <= '9')) {
+        number_end++;
+    }
+
+    String number;
+    number.limitInit(number_begin, number_end - number_begin);
+
+    int d = xatoi(number.termedBuf());
+    int m;
+    if ((m = parseBytesUnits(number_end)) == 0) {
+        self_destruct();
+        return;
+    }
+
+    *bptr = static_cast<size_t>(m * d / u);
+    if (static_cast<double>(*bptr) * 2 != m * d / u * 2)
+        self_destruct();
+}
+#endif
+
 static size_t
 parseBytesUnits(const char *unit)
 {
@@ -3571,8 +3629,16 @@ parse_http_port_option(http_port_list * s, char *token)
         safe_free(s->sslflags);
         s->sslflags = xstrdup(token + 9);
     } else if (strncmp(token, "sslcontext=", 11) == 0) {
-        safe_free(s->sslcontext);
-        s->sslcontext = xstrdup(token + 11);
+        safe_free(s->sslContextSessionId);
+        s->sslContextSessionId = xstrdup(token + 11);
+    } else if (strcmp(token, "generate-host-certificates") == 0) {
+        s->generateHostCertificates = true;
+    } else if (strcmp(token, "generate-host-certificates=on") == 0) {
+        s->generateHostCertificates = true;
+    } else if (strcmp(token, "generate-host-certificates=off") == 0) {
+        s->generateHostCertificates = false;
+    } else if (strncmp(token, "dynamic_cert_mem_cache_size=", 28) == 0) {
+        parseBytesOptionValue(&s->dynamicCertMemCacheSize, B_BYTES_STR, token + 28);
 #endif
     } else {
         self_destruct();
@@ -3638,7 +3704,7 @@ clone_http_port_list(http_port_list *a)
     char *crlfile;
     char *dhfile;
     char *sslflags;
-    char *sslcontext;
+    char *sslContextSessionId;
     SSL_CTX *sslContext;
 #endif
 
@@ -3762,8 +3828,14 @@ dump_generic_http_port(StoreEntry * e, const char *n, const http_port_list * s)
     if (s->sslflags)
         storeAppendPrintf(e, " sslflags=%s", s->sslflags);
 
-    if (s->sslcontext)
-        storeAppendPrintf(e, " sslcontext=%s", s->sslcontext);
+    if (s->sslContextSessionId)
+        storeAppendPrintf(e, " sslcontext=%s", s->sslContextSessionId);
+
+    if (s->generateHostCertificates)
+        storeAppendPrintf(e, " generate-host-certificates");
+
+    if (s->dynamicCertMemCacheSize != std::numeric_limits<size_t>::max())
+        storeAppendPrintf(e, "dynamic_cert_mem_cache_size=%lu%s\n", (unsigned long)s->dynamicCertMemCacheSize, B_BYTES_STR);
 #endif
 }
 
index 6a4a5d32636d79ebdec2cd3b920ea74dd99dd824..5ed6f4110eff6fd84eb46caecd976ea2c4964116 100644 (file)
@@ -1390,6 +1390,24 @@ DOC_START
 
           sslcontext=  SSL session ID context identifier.
 
+          generate-host-certificates[=<on|off>]
+                       Dynamically create SSL server certificates for the
+                       destination hosts of bumped CONNECT requests.When 
+                       enabled, the cert and key options are used to sign
+                       generated certificates. Otherwise generated
+                       certificate will be selfsigned.
+                       If there is CA certificate life time of generated 
+                       certificate equals lifetime of CA certificate. If
+                       generated certificate is selfsigned lifetime is three 
+                       years.
+                       This option is enabled by default when SslBump is used.
+                       See the sslBump option above for more information.
+                       
+          dynamic_cert_mem_cache_size=SIZE
+                       Approximate total RAM size spent on cached generated
+                       certificates. If set to zero, caching is disabled. The
+                       default value is 4MB. An average XXX-bit certificate
+                       consumes about XXX bytes of RAM.
 
        Other Options:
 
@@ -1967,6 +1985,54 @@ DOC_START
        keys.
 DOC_END
 
+COMMENT_START
+ OPTIONS RELATING TO EXTERNAL SSL_CRTD 
+ -----------------------------------------------------------------------------
+COMMENT_END
+
+NAME: sslcrtd_program
+TYPE: eol 
+IFDEF: USE_SSL_CRTD
+DEFAULT: @DEFAULT_SSL_CRTD@ -s @DEFAULT_SSL_DB_DIR@ -M 4MB
+LOC: Ssl::TheConfig.ssl_crtd
+DOC_START
+       Specify the location and options of the executable for ssl_crtd process.
+       @DEFAULT_SSL_CRTD@ program requires -s and -M parameters
+       For more information use:
+               @DEFAULT_SSL_CRTD@ -h
+DOC_END
+
+NAME: sslcrtd_children
+TYPE: HelperChildConfig
+IFDEF: USE_SSL_CRTD
+DEFAULT: 32 startup=5 idle=1
+LOC: Ssl::TheConfig.ssl_crtdChildren
+DOC_START
+       The maximum number of processes spawn to service ssl server.
+       The maximum this may be safely set to is 32.
+       
+       The startup= and idle= options allow some measure of skew in your
+       tuning.
+       
+               startup=N
+       
+       Sets the minimum number of processes to spawn when Squid
+       starts or reconfigures. When set to zero the first request will
+       cause spawning of the first child process to handle it.
+       
+       Starting too few children temporary slows Squid under load while it
+       tries to spawn enough additional processes to cope with traffic.
+       
+               idle=N
+       
+       Sets a minimum of how many processes Squid is to try and keep available
+       at all times. When traffic begins to rise above what the existing
+       processes can handle this many more will be spawned up to the maximum
+       configured. A minimum setting of 1 is required.
+       
+       You must have at least one ssl_crtd process.
+DOC_END
+
 COMMENT_START
  OPTIONS WHICH AFFECT THE NEIGHBOR SELECTION ALGORITHM
  -----------------------------------------------------------------------------
index 5c70177abe1b4d4f74a91fbbcf8c1e7d4ffb8b16..8f9de2bb529aeceb0385459e21fe491e31ee54ad 100644 (file)
 #include "ClientInfo.h"
 #endif
 
+#if USE_SSL
+#include "ssl/context_storage.h"
+#include "ssl/helper.h"
+#include "ssl/gadgets.h"
+#endif
+#if USE_SSL_CRTD
+#include "ssl/crtd_message.h"
+#include "ssl/certificate_db.h"
+#endif
+
+#if HAVE_LIMITS
+#include <limits>
+#endif
+
 #if LINGERING_CLOSE
 #define comm_close comm_lingering_close
 #endif
@@ -3346,7 +3360,7 @@ httpsAccept(int sock, int newfd, ConnectionDetail *details,
             comm_err_t flag, int xerrno, void *data)
 {
     https_port_list *s = (https_port_list *)data;
-    SSL_CTX *sslContext = s->sslContext;
+    SSL_CTX *sslContext = s->staticSslContext.get();
 
     if (flag != COMM_OK) {
         errno = xerrno;
@@ -3396,24 +3410,109 @@ httpsAccept(int sock, int newfd, ConnectionDetail *details,
     incoming_sockets_accepted++;
 }
 
-bool
-ConnStateData::switchToHttps()
+void
+ConnStateData::sslCrtdHandleReplyWrapper(void *data, char *reply)
 {
-    assert(!switchedToHttps_);
+    ConnStateData * state_data = (ConnStateData *)(data);
+    state_data->sslCrtdHandleReply(reply);
+}
 
-    //HTTPMSGLOCK(currentobject->http->request);
-    assert(areAllContextsForThisConnection());
-    freeAllContexts();
-    //currentobject->connIsFinished();
+void
+ConnStateData::sslCrtdHandleReply(const char * reply)
+{
+    if (!reply) {
+        debugs(1, 1, HERE << "\"ssl_crtd\" helper return <NULL> reply");
+    } else {
+        Ssl::CrtdMessage reply_message;
+        if (reply_message.parse(reply, strlen(reply)) != Ssl::CrtdMessage::OK) {
+            debugs(33, 5, HERE << "Reply from ssl_crtd for " << sslHostName << " is incorrect");
+        } else {
+            if (reply_message.getCode() != "ok") {
+                debugs(33, 5, HERE << "Certificate for " << sslHostName << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
+            } else {
+                debugs(33, 5, HERE << "Certificate for " << sslHostName << " was successfully recieved from ssl_crtd");
+                getSslContextDone(Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str()), true);
+                return;
+            }
+        }
+    }
+    getSslContextDone(NULL);
+}
 
-    debugs(33, 5, HERE << "converting FD " << fd << " to SSL");
+bool
+ConnStateData::getSslContextStart()
+{
+    char const * host = sslHostName.termedBuf();
+    if (port->generateHostCertificates && host && strcmp(host, "") != 0) {
+        debugs(33, 5, HERE << "Finding SSL certificate for " << host << " in cache");
+        Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
+        SSL_CTX * dynCtx = ssl_ctx_cache.find(host);
+        if (dynCtx) {
+            debugs(33, 5, HERE << "SSL certificate for " << host << " have found in cache");
+            if (Ssl::verifySslCertificateDate(dynCtx)) {
+                debugs(33, 5, HERE << "Cached SSL certificate for " << host << " is valid");
+                return getSslContextDone(dynCtx);
+            } else {
+                debugs(33, 5, HERE << "Cached SSL certificate for " << host << " is out of date. Delete this certificate from cache");
+                ssl_ctx_cache.remove(host);
+            }
+        } else {
+            debugs(33, 5, HERE << "SSL certificate for " << host << " haven't found in cache");
+        }
+
+#ifdef USE_SSL_CRTD
+        debugs(33, 5, HERE << "Generating SSL certificate for " << host << " using ssl_crtd.");
+        Ssl::CrtdMessage request_message;
+        request_message.setCode(Ssl::CrtdMessage::code_new_certificate);
+        Ssl::CrtdMessage::BodyParams map;
+        map.insert(std::make_pair(Ssl::CrtdMessage::param_host, host));
+        std::string bufferToWrite;
+        Ssl::writeCertAndPrivateKeyToMemory(port->signingCert, port->signPkey, bufferToWrite);
+        request_message.composeBody(map, bufferToWrite);
+        Ssl::Helper::GetInstance()->sslSubmit(request_message, sslCrtdHandleReplyWrapper, this);
+        return true;
+#else
+        debugs(33, 5, HERE << "Generating SSL certificate for " << host);
+        dynCtx = generateSslContext(host, port->signingCert, port->signPkey);
+        return getSslContextDone(dynCtx, true);
+#endif //USE_SSL_CRTD
+    }
+    return getSslContextDone(NULL);
+}
+
+bool
+ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew)
+{
+    // Try to add generated ssl context to storage.
+    if (port->generateHostCertificates && isNew) {
+        Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
+        if (sslContext && sslHostName != "") {
+            if (!ssl_ctx_cache.add(sslHostName.termedBuf(), sslContext)) {
+                // If it is not in storage delete after using. Else storage deleted it.
+                fd_table[fd].dynamicSslContext = sslContext;
+            }
+        } else {
+            debugs(33, 2, HERE << "Failed to generate SSL cert for " << sslHostName);
+        }
+    }
+
+    // If generated ssl context = NULL, try to use static ssl context.
+    if (!sslContext) {
+        if (!port->staticSslContext) {
+            debugs(83, 1, "Closing SSL FD " << fd << " as lacking SSL context");
+            comm_close(fd);
+            return false;
+        } else {
+            debugs(33, 5, HERE << "Using static ssl context.");
+            sslContext = port->staticSslContext.get();
+        }
+    }
 
     // fake a ConnectionDetail object; XXX: make ConnState a ConnectionDetail?
     ConnectionDetail detail;
     detail.me = me;
     detail.peer = peer;
 
-    SSL_CTX *sslContext = port->sslContext;
     SSL *ssl = NULL;
     if (!(ssl = httpsCreate(fd, &detail, sslContext)))
         return false;
@@ -3429,6 +3528,23 @@ ConnStateData::switchToHttps()
     return true;
 }
 
+bool
+ConnStateData::switchToHttps(const char *host)
+{
+    assert(!switchedToHttps_);
+
+    sslHostName = host;
+
+    //HTTPMSGLOCK(currentobject->http->request);
+    assert(areAllContextsForThisConnection());
+    freeAllContexts();
+    //currentobject->connIsFinished();
+
+    debugs(33, 5, HERE << "converting FD " << fd << " to SSL");
+
+    return getSslContextStart();
+}
+
 #endif /* USE_SSL */
 
 /// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
@@ -3476,14 +3592,21 @@ clientHttpConnectionsOpen(void)
         }
 
 #if USE_SSL
-        if (s->sslBump && s->sslContext == NULL) {
+        if (s->sslBump &&
+                !s->staticSslContext && !s->generateHostCertificates) {
             debugs(1, 1, "Will not bump SSL at http_port " <<
                    s->http.s << " due to SSL initialization failure.");
             s->sslBump = 0;
         }
-        if (s->sslBump)
+        if (s->sslBump) {
             ++bumpCount;
+            // Create ssl_ctx cache for this port.
+            Ssl::TheGlobalContextStorage.addLocalStorage(s->s, s->dynamicCertMemCacheSize == std::numeric_limits<size_t>::max() ? 4194304 : s->dynamicCertMemCacheSize);
+        }
 #endif
+#if USE_SSL_CRTD
+        Ssl::Helper::GetInstance();
+#endif //USE_SSL_CRTD
 
         /* AYJ: 2009-12-27: bit bumpy. new ListenStateData(...) should be doing all the Comm:: stuff ... */
 
@@ -3545,7 +3668,7 @@ clientHttpsConnectionsOpen(void)
             continue;
         }
 
-        if (s->sslContext == NULL) {
+        if (!s->staticSslContext) {
             debugs(1, 1, "Ignoring https_port " << s->http.s <<
                    " due to SSL initialization failure.");
             continue;
@@ -3713,7 +3836,7 @@ clientAclChecklistCreate(const acl_access * acl, ClientHttpRequest * http)
 
 CBDATA_CLASS_INIT(ConnStateData);
 
-ConnStateData::ConnStateData() :AsyncJob("ConnStateData"), transparent_ (false), closing_ (false)
+ConnStateData::ConnStateData() :AsyncJob("ConnStateData"), transparent_ (false), closing_ (false), switchedToHttps_(false)
 {
     pinning.fd = -1;
     pinning.pinned = false;
index 6f932f35dec99e87ae54700fd3fc7256ac280f78..707b748eac0ecdafe5a0c0f9a953c62a338087ba 100644 (file)
@@ -275,7 +275,20 @@ public:
     virtual void swanSong();
 
 #if USE_SSL
-    bool switchToHttps();
+    /// Start to create dynamic SSL_CTX for host or uses static port SSL context.
+    bool getSslContextStart();
+    /**
+     * Done create dynamic ssl certificate.
+     *
+     * \param[in] isNew if generated certificate is new, so we need to add this certificate to storage.
+     */
+    bool getSslContextDone(SSL_CTX * sslContext, bool isNew = false);
+    /// Callback function. It is called when squid receive message from ssl_crtd.
+    static void sslCrtdHandleReplyWrapper(void *data, char *reply);
+    /// Proccess response from ssl_crtd.
+    void sslCrtdHandleReply(const char * reply);
+
+    bool switchToHttps(const char *host);
     bool switchedToHttps() const { return switchedToHttps_; }
 #else
     bool switchedToHttps() const { return false; }
@@ -299,6 +312,7 @@ private:
     bool closing_;
 
     bool switchedToHttps_;
+    String sslHostName; ///< Host name for SSL certificate generation
     AsyncCall::Pointer reader; ///< set when we are reading
     BodyPipe::Pointer bodyPipe; // set when we are reading request body
 };
index 8278b01046e0320305b8b2ae0481424f1a8e9b2c..59148f53aad30e4653cdf399c71d5ba7543848a9 100644 (file)
@@ -1190,7 +1190,7 @@ ClientHttpRequest::sslBumpEstablish(comm_err_t errflag)
         return;
     }
 
-    getConn()->switchToHttps();
+    getConn()->switchToHttps(request->GetHost());
 }
 
 void
index 8edf0efa70652cadaf7f2265de71eeb4b1a461a2..b5e6a8190e06e0db10eb05b7d0317e487f120a76 100644 (file)
@@ -1540,6 +1540,10 @@ comm_close_complete(int fd, void *data)
         F->ssl = NULL;
     }
 
+    if (F->dynamicSslContext) {
+        SSL_CTX_free(F->dynamicSslContext);
+        F->dynamicSslContext = NULL;
+    }
 #endif
     fd_close(fd);              /* update fdstat */
 
index d73f387fb01b24cf1a50ea0eafbbe85a497724ae..05e2c899a0a84e6e7c6bd0b98d35a663f750b1fc 100644 (file)
--- a/src/fde.h
+++ b/src/fde.h
@@ -113,6 +113,7 @@ public:
     WRITE_HANDLER *write_method;
 #if USE_SSL
     SSL *ssl;
+    SSL_CTX *dynamicSslContext; ///< cached and then freed when fd is closed
 #endif
 #ifdef _SQUID_MSWIN_
     struct {
@@ -166,6 +167,7 @@ private:
         write_method = NULL;
 #if USE_SSL
         ssl = NULL;
+        dynamicSslContext = NULL;
 #endif
 #ifdef _SQUID_MSWIN_
         win32.handle = NULL;
index c159c35756b7fff4998f7fa20b2292854b96b61c..968bb8c6809feded648f8a91ebeed427d446a42d 100644 (file)
@@ -792,6 +792,55 @@ helperStatefulServerFree(int fd, void *data)
     cbdataFree(srv);
 }
 
+/// Calls back with a pointer to the buffer with the helper output
+static void helperReturnBuffer(int request_number, helper_server * srv, helper * hlp, char * msg, char * msg_end)
+{
+    helper_request *r = srv->requests[request_number];
+    if (r) {
+        HLPCB *callback = r->callback;
+
+        srv->requests[request_number] = NULL;
+
+        r->callback = NULL;
+
+        void *cbdata = NULL;
+        if (cbdataReferenceValidDone(r->data, &cbdata))
+            callback(cbdata, msg);
+
+        srv->stats.pending--;
+
+        hlp->stats.replies++;
+
+        srv->answer_time = current_time;
+
+        srv->dispatch_time = r->dispatch_time;
+
+        hlp->stats.avg_svc_time =
+            Math::intAverage(hlp->stats.avg_svc_time,
+                       tvSubMsec(r->dispatch_time, current_time),
+                       hlp->stats.replies, REDIRECT_AV_FACTOR);
+
+        helperRequestFree(r);
+    } else {
+        debugs(84, 1, "helperHandleRead: unexpected reply on channel " <<
+               request_number << " from " << hlp->id_name << " #" << srv->index + 1 <<
+               " '" << srv->rbuf << "'");
+    }
+    srv->roffset -= (msg_end - srv->rbuf);
+    memmove(srv->rbuf, msg_end, srv->roffset + 1);
+
+    if (!srv->flags.shutdown) {
+        helperKickQueue(hlp);
+    } else if (!srv->flags.closing && !srv->stats.pending) {
+        int wfd = srv->wfd;
+        srv->wfd = -1;
+        if (srv->rfd == wfd)
+            srv->rfd = -1;
+        srv->flags.closing=1;
+        comm_close(wfd);
+        return;
+    }
+}
 
 static void
 helperHandleRead(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
@@ -834,69 +883,29 @@ helperHandleRead(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, voi
         srv->rbuf[0] = '\0';
     }
 
-    while ((t = strchr(srv->rbuf, '\n'))) {
-        /* end of reply found */
-        helper_request *r;
-        char *msg = srv->rbuf;
-        int i = 0;
-        debugs(84, 3, "helperHandleRead: end of reply found");
-
-        if (t > srv->rbuf && t[-1] == '\r')
-            t[-1] = '\0';
-
-        *t++ = '\0';
-
-        if (hlp->childs.concurrency) {
-            i = strtol(msg, &msg, 10);
-
-            while (*msg && xisspace(*msg))
-                msg++;
-        }
-
-        r = srv->requests[i];
-
-        if (r) {
-            HLPCB *callback = r->callback;
-            void *cbdata;
-
-            srv->requests[i] = NULL;
-
-            r->callback = NULL;
-
-            if (cbdataReferenceValidDone(r->data, &cbdata))
-                callback(cbdata, msg);
-
-            srv->stats.pending--;
-
-            hlp->stats.replies++;
-
-            srv->answer_time = current_time;
+    if (hlp->return_full_reply) {
+        debugs(84, 3, HERE << "Return entire buffer");
+        helperReturnBuffer(0, srv, hlp, srv->rbuf, srv->rbuf + srv->roffset);
+    } else {
+        while ((t = strchr(srv->rbuf, '\n'))) {
+            /* end of reply found */
+            char *msg = srv->rbuf;
+            int i = 0;
+            debugs(84, 3, "helperHandleRead: end of reply found");
 
-            srv->dispatch_time = r->dispatch_time;
+            if (t > srv->rbuf && t[-1] == '\r')
+                t[-1] = '\0';
 
-            hlp->stats.avg_svc_time = Math::intAverage(hlp->stats.avg_svc_time, tvSubMsec(r->dispatch_time, current_time), hlp->stats.replies, REDIRECT_AV_FACTOR);
+            *t++ = '\0';
 
-            helperRequestFree(r);
-        } else {
-            debugs(84, 1, "helperHandleRead: unexpected reply on channel " <<
-                   i << " from " << hlp->id_name << " #" << srv->index + 1 <<
-                   " '" << srv->rbuf << "'");
+            if (hlp->childs.concurrency) {
+                i = strtol(msg, &msg, 10);
 
-        }
+                while (*msg && xisspace(*msg))
+                    msg++;
+            }
 
-        srv->roffset -= (t - srv->rbuf);
-        memmove(srv->rbuf, t, srv->roffset + 1);
-
-        if (!srv->flags.shutdown) {
-            helperKickQueue(hlp);
-        } else if (!srv->flags.closing && !srv->stats.pending) {
-            int wfd = srv->wfd;
-            srv->wfd = -1;
-            if (srv->rfd == wfd)
-                srv->rfd = -1;
-            srv->flags.closing=1;
-            comm_close(wfd);
-            return;
+            helperReturnBuffer(i, srv, hlp, msg, t);
         }
     }
 
index 6427d93fc37374746bb3770e83f0dd9459d39e26..b07d38d32fa2912e7480222d8cd4dd8a3f0ff19a 100644 (file)
@@ -69,6 +69,8 @@ public:
         int queue_size;
         int avg_svc_time;
     } stats;
+    /// True if callback expects the whole helper output, as a c-string.
+    bool return_full_reply;
 
 private:
     CBDATA_CLASS2(helper);
index 182474ed60ba5c2f1de5db2fc3e83d58b74571fe..742124523737539f376a1cdc3f8d043c404dc519 100644 (file)
 #include "LoadableModules.h"
 #endif
 
+#if USE_SSL_CRTD
+#include "ssl/helper.h"
+#include "ssl/certificate_db.h"
+#endif
+
+#if USE_SSL
+#include "ssl/context_storage.h"
+#endif
+
 #if ICAP_CLIENT
 #include "adaptation/icap/Config.h"
 #endif
@@ -733,7 +742,12 @@ mainReconfigureStart(void)
 
     idnsShutdown();
 #endif
-
+#if USE_SSL_CRTD
+    Ssl::Helper::GetInstance()->Shutdown();
+#endif
+#if USE_SSL
+    Ssl::TheGlobalContextStorage.reconfigureStart();
+#endif
     redirectShutdown();
     authenticateReset();
     externalAclShutdown();
@@ -819,6 +833,9 @@ mainReconfigureFinish(void *)
 
     idnsInit();
 #endif
+#if USE_SSL_CRTD
+    Ssl::Helper::GetInstance()->Init();
+#endif
 
     redirectInit();
     authenticateInit(&Auth::TheConfig);
@@ -1812,7 +1829,9 @@ SquidShutdown()
 
     idnsShutdown();
 #endif
-
+#if USE_SSL_CRTD
+    Ssl::Helper::GetInstance()->Shutdown();
+#endif
     redirectShutdown();
     externalAclShutdown();
     icpConnectionClose();
index 81cba215cefeb9dfa7d2f4052ca152323296874a..2c05e4b325966d6d950249abf888890d5e52369c 100644 (file)
@@ -155,7 +155,7 @@ using namespace Squid;
 
 #include "md5.h"
 #if USE_SSL
-#include "ssl_support.h"
+#include "ssl/support.h"
 #endif
 #if SQUID_SNMP
 #include "cache_snmp.h"
diff --git a/src/ssl/Config.cc b/src/ssl/Config.cc
new file mode 100644 (file)
index 0000000..72d1acc
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * $Id$
+ */
+
+#include "ssl/Config.h"
+
+Ssl::Config Ssl::TheConfig;
+
+Ssl::Config::Config():
+#if USE_SSL_CRTD
+ssl_crtd(NULL)
+#endif
+{
+}
+
+Ssl::Config::~Config()
+{
+#if USE_SSL_CRTD
+    xfree(ssl_crtd);
+#endif
+}
diff --git a/src/ssl/Config.h b/src/ssl/Config.h
new file mode 100644 (file)
index 0000000..4d5f7f3
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_SSL_CONFIG_H
+#define SQUID_SSL_CONFIG_H
+
+#include "config.h"
+#include "HelperChildConfig.h"
+
+namespace Ssl
+{
+
+class Config
+{
+public:
+#if USE_SSL_CRTD
+    char *ssl_crtd; ///< Name of external ssl_crtd application.
+    /// The number of processes spawn for ssl_crtd.
+    HelperChildConfig ssl_crtdChildren;
+#endif
+    Config();
+    ~Config();
+private:
+    Config(const Config &); // not implemented
+    Config &operator =(const Config &); // not implemented
+};
+
+extern Config TheConfig;
+
+} // namespace Ssl
+#endif
diff --git a/src/ssl/Makefile.am b/src/ssl/Makefile.am
new file mode 100644 (file)
index 0000000..39778b5
--- /dev/null
@@ -0,0 +1,40 @@
+include $(top_srcdir)/src/Common.am
+include $(top_srcdir)/src/TestHeaders.am
+
+noinst_LTLIBRARIES = libsslsquid.la libsslutil.la
+
+EXTRA_PROGRAMS = \
+       ssl_crtd
+
+if USE_SSL_CRTD
+SSL_CRTD = ssl_crtd
+SSL_CRTD_SOURCE = \
+    helper.cc \
+    helper.h
+else
+SSL_CRTD =
+SSL_CRTD_SOURCE =
+endif
+
+libsslsquid_la_SOURCES = \
+       context_storage.cc \
+       context_storage.h \
+       Config.cc \
+       Config.h
+
+libsslutil_la_SOURCES = \
+       support.cc \
+       support.h \
+       gadgets.cc \
+       gadgets.h \
+       crtd_message.cc \
+       crtd_message.h \
+       $(SSL_CRTD_SOURCE)
+
+libexec_PROGRAMS = \
+       $(SSL_CRTD)
+
+if USE_SSL_CRTD
+ssl_crtd_SOURCES = ssl_crtd.cc certificate_db.cc certificate_db.h
+ssl_crtd_LDADD = @SSLLIB@ -lsslutil $(COMPAT_LIB)
+endif
diff --git a/src/ssl/certificate_db.cc b/src/ssl/certificate_db.cc
new file mode 100644 (file)
index 0000000..be55da7
--- /dev/null
@@ -0,0 +1,486 @@
+/*
+ * $Id$
+ */
+
+#include "config.h"
+#include "ssl/certificate_db.h"
+#if HAVE_FSTREAM
+#include <fstream>
+#endif
+#if HAVE_STDEXCEPT
+#include <stdexcept>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+Ssl::FileLocker::FileLocker(std::string const & filename)
+        :    fd(-1)
+{
+#if _SQUID_MSWIN_
+    hFile = CreateFile(TEXT(filename.c_str()), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile != INVALID_HANDLE_VALUE)
+        LockFile(hFile, 0, 0, 1, 0);
+#else
+    fd = open(filename.c_str(), 0);
+    if (fd != -1)
+        flock(fd, LOCK_EX);
+#endif
+}
+
+Ssl::FileLocker::~FileLocker()
+{
+#ifdef _SQUID_MSWIN_
+    if (hFile != INVALID_HANDLE_VALUE) {
+        UnlockFile(hFile, 0, 0, 1, 0);
+        CloseHandle(hFile);
+    }
+#else
+    if (fd != -1) {
+        flock(fd, LOCK_UN);
+        close(fd);
+    }
+#endif
+}
+
+Ssl::CertificateDb::Row::Row()
+        :   width(cnlNumber)
+{
+    row = new char *[width + 1];
+    for (size_t i = 0; i < width + 1; i++)
+        row[i] = NULL;
+}
+
+Ssl::CertificateDb::Row::~Row()
+{
+    if (row) {
+        for (size_t i = 0; i < width + 1; i++) {
+            delete[](row[i]);
+        }
+        delete[](row);
+    }
+}
+
+void Ssl::CertificateDb::Row::reset()
+{
+    row = NULL;
+}
+
+void Ssl::CertificateDb::Row::setValue(size_t cell, char const * value)
+{
+    assert(cell < width);
+    if (row[cell]) {
+        free(row[cell]);
+    }
+    if (value) {
+        row[cell] = static_cast<char *>(malloc(sizeof(char) * (strlen(value) + 1)));
+        memcpy(row[cell], value, sizeof(char) * (strlen(value) + 1));
+    } else
+        row[cell] = NULL;
+}
+
+char ** Ssl::CertificateDb::Row::getRow()
+{
+    return row;
+}
+
+unsigned long Ssl::CertificateDb::index_serial_hash(const char **a)
+{
+    const char *n = a[Ssl::CertificateDb::cnlSerial];
+    while (*n == '0') n++;
+    return lh_strhash(n);
+}
+
+int Ssl::CertificateDb::index_serial_cmp(const char **a, const char **b)
+{
+    const char *aa, *bb;
+    for (aa = a[Ssl::CertificateDb::cnlSerial]; *aa == '0'; aa++);
+    for (bb = b[Ssl::CertificateDb::cnlSerial]; *bb == '0'; bb++);
+    return strcmp(aa, bb);
+}
+
+unsigned long Ssl::CertificateDb::index_name_hash(const char **a)
+{
+    return(lh_strhash(a[Ssl::CertificateDb::cnlName]));
+}
+
+int Ssl::CertificateDb::index_name_cmp(const char **a, const char **b)
+{
+    return(strcmp(a[Ssl::CertificateDb::cnlName], b[CertificateDb::cnlName]));
+}
+
+const std::string Ssl::CertificateDb::serial_file("serial");
+const std::string Ssl::CertificateDb::db_file("index.txt");
+const std::string Ssl::CertificateDb::cert_dir("certs");
+const std::string Ssl::CertificateDb::size_file("size");
+const size_t Ssl::CertificateDb::min_db_size(4096);
+
+Ssl::CertificateDb::CertificateDb(std::string const & aDb_path, size_t aMax_db_size, size_t aFs_block_size)
+        :  db_path(aDb_path),
+        serial_full(aDb_path + "/" + serial_file),
+        db_full(aDb_path + "/" + db_file),
+        cert_full(aDb_path + "/" + cert_dir),
+        size_full(aDb_path + "/" + size_file),
+        db(NULL),
+        max_db_size(aMax_db_size),
+        fs_block_size(aFs_block_size),
+        enabled_disk_store(true)
+{
+    if (db_path.empty() && !max_db_size)
+        enabled_disk_store = false;
+    else if ((db_path.empty() && max_db_size) || (!db_path.empty() && !max_db_size))
+        throw std::runtime_error("ssl_crtd is missing the required parameter. There should be -s and -M parameters together.");
+    else
+        load();
+}
+
+bool Ssl::CertificateDb::find(std::string const & host_name, Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey)
+{
+    FileLocker db_locker(db_full);
+    load();
+    return pure_find(host_name, cert, pkey);
+}
+
+bool Ssl::CertificateDb::addCertAndPrivateKey(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey)
+{
+    FileLocker db_locker(db_full);
+    load();
+    if (!db || !cert || !pkey || min_db_size > max_db_size)
+        return false;
+    Row row;
+    ASN1_INTEGER * ai = X509_get_serialNumber(cert.get());
+    std::string serial_string;
+    Ssl::BIGNUM_Pointer serial(ASN1_INTEGER_to_BN(ai, NULL));
+    {
+        TidyPointer<char, tidyFree> hex_bn(BN_bn2hex(serial.get()));
+        serial_string = std::string(hex_bn.get());
+    }
+    row.setValue(cnlSerial, serial_string.c_str());
+    char ** rrow = TXT_DB_get_by_index(db.get(), cnlSerial, row.getRow());
+    if (rrow != NULL)
+        return false;
+
+    {
+        TidyPointer<char, tidyFree> subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), NULL, 0));
+        if (pure_find(subject.get(), cert, pkey))
+            return true;
+    }
+    // check db size.
+    while (max_db_size < size()) {
+        if (!deleteInvalidCertificate())
+            break;
+    }
+
+    while (max_db_size < size()) {
+        deleteOldestCertificate();
+    }
+
+    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");
+    {
+        TidyPointer<char, tidyFree> subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), NULL, 0));
+        row.setValue(cnlName, subject.get());
+    }
+
+    if (!TXT_DB_insert(db.get(), row.getRow()))
+        return false;
+
+    row.reset();
+    std::string filename(cert_full + "/" + serial_string + ".pem");
+    FileLocker cert_locker(filename);
+    if (!writeCertAndPrivateKeyToFile(cert, pkey, filename.c_str()))
+        return false;
+    addSize(filename);
+
+    save();
+    return true;
+}
+
+BIGNUM * Ssl::CertificateDb::getCurrentSerialNumber()
+{
+    FileLocker serial_locker(serial_full);
+    // load serial number from file.
+    Ssl::BIO_Pointer file(BIO_new(BIO_s_file()));
+    if (!file)
+        return NULL;
+
+    if (BIO_rw_filename(file.get(), const_cast<char *>(serial_full.c_str())) <= 0)
+        return NULL;
+
+    Ssl::ASN1_INT_Pointer serial_ai(ASN1_INTEGER_new());
+    if (!serial_ai)
+        return NULL;
+
+    char buffer[1024];
+    if (!a2i_ASN1_INTEGER(file.get(), serial_ai.get(), buffer, sizeof(buffer)))
+        return NULL;
+
+    Ssl::BIGNUM_Pointer serial(ASN1_INTEGER_to_BN(serial_ai.get(), NULL));
+
+    if (!serial)
+        return NULL;
+
+    // increase serial number.
+    Ssl::BIGNUM_Pointer increased_serial(BN_dup(serial.get()));
+    if (!increased_serial)
+        return NULL;
+
+    BN_add_word(increased_serial.get(), 1);
+
+    // save increased serial number.
+    if (BIO_seek(file.get(), 0))
+        return NULL;
+
+    Ssl::ASN1_INT_Pointer increased_serial_ai(BN_to_ASN1_INTEGER(increased_serial.get(), NULL));
+    if (!increased_serial_ai)
+        return NULL;
+
+    i2a_ASN1_INTEGER(file.get(), increased_serial_ai.get());
+    BIO_puts(file.get(),"\n");
+
+    return serial.release();
+}
+
+void Ssl::CertificateDb::create(std::string const & db_path, int serial)
+{
+    if (db_path == "")
+        throw std::runtime_error("Path to db is empty");
+    std::string serial_full(db_path + "/" + serial_file);
+    std::string db_full(db_path + "/" + db_file);
+    std::string cert_full(db_path + "/" + cert_dir);
+    std::string size_full(db_path + "/" + size_file);
+
+#ifdef _SQUID_MSWIN_
+    if (mkdir(db_path.c_str()))
+#else
+    if (mkdir(db_path.c_str(), 0777))
+#endif
+        throw std::runtime_error("Cannot create " + db_path);
+
+#ifdef _SQUID_MSWIN_
+    if (mkdir(cert_full.c_str()))
+#else
+    if (mkdir(cert_full.c_str(), 0777))
+#endif
+        throw std::runtime_error("Cannot create " + cert_full);
+
+    Ssl::ASN1_INT_Pointer i(ASN1_INTEGER_new());
+    ASN1_INTEGER_set(i.get(), serial);
+
+    Ssl::BIO_Pointer file(BIO_new(BIO_s_file()));
+    if (!file)
+        throw std::runtime_error("SSL error");
+
+    if (BIO_write_filename(file.get(), const_cast<char *>(serial_full.c_str())) <= 0)
+        throw std::runtime_error("Cannot open " + cert_full + " to open");
+
+    i2a_ASN1_INTEGER(file.get(), i.get());
+
+    std::ofstream size(size_full.c_str());
+    if (size)
+        size << 0;
+    else
+        throw std::runtime_error("Cannot open " + size_full + " to open");
+    std::ofstream db(db_full.c_str());
+    if (!db)
+        throw std::runtime_error("Cannot open " + db_full + " to open");
+}
+
+void Ssl::CertificateDb::check(std::string const & db_path, size_t max_db_size)
+{
+    CertificateDb db(db_path, max_db_size, 0);
+}
+
+std::string Ssl::CertificateDb::getSNString() const
+{
+    FileLocker serial_locker(serial_full);
+    std::ifstream file(serial_full.c_str());
+    if (!file)
+        return "";
+    std::string serial;
+    file >> serial;
+    return serial;
+}
+
+bool Ssl::CertificateDb::pure_find(std::string const & host_name, Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey)
+{
+    if (!db)
+        return false;
+
+    Row row;
+    row.setValue(cnlName, host_name.c_str());
+
+    char **rrow = TXT_DB_get_by_index(db.get(), cnlName, row.getRow());
+    if (rrow == NULL)
+        return false;
+
+    if (!sslDateIsInTheFuture(rrow[cnlExp_date])) {
+        deleteByHostname(rrow[cnlName]);
+        return false;
+    }
+
+    // read cert and pkey from file.
+    std::string filename(cert_full + "/" + rrow[cnlSerial] + ".pem");
+    FileLocker cert_locker(filename);
+    readCertAndPrivateKeyFromFiles(cert, pkey, filename.c_str(), NULL);
+    if (!cert || !pkey)
+        return false;
+    return true;
+}
+
+size_t Ssl::CertificateDb::size() const
+{
+    FileLocker size_locker(size_full);
+    return readSize();
+}
+
+void Ssl::CertificateDb::addSize(std::string const & filename)
+{
+    FileLocker size_locker(size_full);
+    writeSize(readSize() + getFileSize(filename));
+}
+
+void Ssl::CertificateDb::subSize(std::string const & filename)
+{
+    FileLocker size_locker(size_full);
+    writeSize(readSize() - getFileSize(filename));
+}
+
+size_t Ssl::CertificateDb::readSize() const
+{
+    size_t db_size;
+    std::ifstream size_file(size_full.c_str());
+    if (!size_file && enabled_disk_store)
+        throw std::runtime_error("cannot read \"" + size_full + "\" file");
+    size_file >> db_size;
+    return db_size;
+}
+
+void Ssl::CertificateDb::writeSize(size_t db_size)
+{
+    std::ofstream size_file(size_full.c_str());
+    if (!size_file && enabled_disk_store)
+        throw std::runtime_error("cannot write \"" + size_full + "\" file");
+    size_file << db_size;
+}
+
+size_t Ssl::CertificateDb::getFileSize(std::string const & filename)
+{
+    std::ifstream file(filename.c_str(), std::ios::binary);
+    file.seekg(0, std::ios_base::end);
+    size_t file_size = file.tellg();
+    return ((file_size + fs_block_size - 1) / fs_block_size) * fs_block_size;
+}
+
+void Ssl::CertificateDb::load()
+{
+    // Load db from file.
+    Ssl::BIO_Pointer in(BIO_new(BIO_s_file()));
+    if (!in || BIO_read_filename(in.get(), db_full.c_str()) <= 0)
+        throw std::runtime_error("Uninitialized SSL certificate database directory: " + db_path + ". To initialize, run \"ssl_crtd -c -s " + db_path + "\".");
+
+    bool corrupt = false;
+    Ssl::TXT_DB_Pointer temp_db(TXT_DB_read(in.get(), cnlNumber));
+    if (!temp_db)
+        corrupt = true;
+
+    // Create indexes in db.
+    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)))
+        corrupt = true;
+
+    if (corrupt)
+        throw std::runtime_error("The SSL certificate database " + db_path + " is curruted. Please rebuild");
+
+    db.reset(temp_db.release());
+}
+
+void Ssl::CertificateDb::save()
+{
+    if (!db)
+        throw std::runtime_error("The certificates database is not loaded");;
+
+    // To save the db to file,  create a new BIO with BIO file methods.
+    Ssl::BIO_Pointer out(BIO_new(BIO_s_file()));
+    if (!out || !BIO_write_filename(out.get(), const_cast<char *>(db_full.c_str())))
+        throw std::runtime_error("Failed to initialize " + db_full + " file for writing");;
+
+    if (TXT_DB_write(out.get(), db.get()) < 0)
+        throw std::runtime_error("Failed to write " + db_full + " file");
+}
+
+bool Ssl::CertificateDb::deleteInvalidCertificate()
+{
+    if (!db)
+        return false;
+
+    bool removed_one = false;
+    for (int i = 0; i < sk_num(db.get()->data); i++) {
+        const char ** current_row = ((const char **)sk_value(db.get()->data, i));
+
+        if (!sslDateIsInTheFuture(current_row[cnlExp_date])) {
+            std::string filename(cert_full + "/" + current_row[cnlSerial] + ".pem");
+            FileLocker cert_locker(filename);
+            sk_delete(db.get()->data, i);
+            subSize(filename);
+            remove(filename.c_str());
+            removed_one = true;
+            break;
+        }
+    }
+
+    if (!removed_one)
+        return false;
+    return true;
+}
+
+bool Ssl::CertificateDb::deleteOldestCertificate()
+{
+    if (!db)
+        return false;
+
+    if (sk_num(db.get()->data) == 0)
+        return false;
+
+    std::string filename(cert_full + "/" + ((const char **)sk_value(db.get()->data, 0))[cnlSerial] + ".pem");
+    FileLocker cert_locker(filename);
+    sk_delete(db.get()->data, 0);
+    subSize(filename);
+    remove(filename.c_str());
+
+    return true;
+}
+
+bool Ssl::CertificateDb::deleteByHostname(std::string const & host)
+{
+    if (!db)
+        return false;
+
+    for (int i = 0; i < sk_num(db.get()->data); i++) {
+        const char ** current_row = ((const char **)sk_value(db.get()->data, i));
+        if (host == current_row[cnlName]) {
+            std::string filename(cert_full + "/" + current_row[cnlSerial] + ".pem");
+            FileLocker cert_locker(filename);
+            sk_delete(db.get()->data, i);
+            subSize(filename);
+            remove(filename.c_str());
+            return true;
+        }
+    }
+    return false;
+}
+
+bool Ssl::CertificateDb::IsEnabledDiskStore() const
+{
+    return enabled_disk_store;
+}
diff --git a/src/ssl/certificate_db.h b/src/ssl/certificate_db.h
new file mode 100644 (file)
index 0000000..0efbd7f
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_SSL_CERTIFICATE_DB_H
+#define SQUID_SSL_CERTIFICATE_DB_H
+
+#include "ssl/gadgets.h"
+#include "ssl/support.h"
+#if HAVE_STRING
+#include <string>
+#endif
+
+namespace Ssl
+{
+/// Cross platform file locker.
+class FileLocker
+{
+public:
+    /// Lock file
+    FileLocker(std::string const & aFilename);
+    /// Unlock file
+    ~FileLocker();
+private:
+#ifdef _SQUID_MSWIN_
+    HANDLE hFile; ///< Windows file handle.
+#else
+    int fd; ///< Linux file descriptor.
+#endif
+};
+
+/**
+ * Database class for storing SSL certificates and their private keys.
+ * A database consist by:
+ *     - A disk file to store current serial number
+ *     - A disk file to store the current database size
+ *     - A disk file which is a normal TXT_DB openSSL database
+ *     - A directory under which the certificates and their private keys stored.
+ *  The database before used must initialized with CertificateDb::create static method.
+ */
+class CertificateDb
+{
+public:
+    /// Names of db columns.
+    enum Columns {
+        cnlType = 0,
+        cnlExp_date,
+        cnlRev_date,
+        cnlSerial,
+        cnlFile,
+        cnlName,
+        cnlNumber
+    };
+
+    /// A wrapper for OpenSSL database row of TXT_DB database.
+    class Row
+    {
+    public:
+        /// Create row wrapper.
+        Row();
+        /// Delete all row.
+        ~Row();
+        void setValue(size_t number, char const * value); ///< Set cell's value in row
+        char ** getRow(); ///< Raw row
+        void reset(); ///< Abandon row and don't free memory
+    private:
+        char **row; ///< Raw row
+        size_t width; ///< Number of cells in the row
+    };
+
+    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, Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey);
+    /// Save certificate to disk.
+    bool addCertAndPrivateKey(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey);
+    /// Get a serial number to use for generating a new certificate.
+    BIGNUM * getCurrentSerialNumber();
+    /// Create and initialize a database  under the  db_path
+    static void create(std::string const & db_path, int serial);
+    /// Check the database stored under the db_path.
+    static void check(std::string const & db_path, size_t max_db_size);
+    std::string getSNString() const; ///< Get serial number as string.
+    bool IsEnabledDiskStore() const; ///< Check enabled of dist store.
+private:
+    void load(); ///< Load db from disk.
+    void save(); ///< Save db to disk.
+    size_t size() const; ///< Get db size on disk in bytes.
+    /// Increase db size by the given file size and update size_file
+    void addSize(std::string const & filename);
+    /// Decrease db size by the given file size and update size_file
+    void subSize(std::string const & filename);
+    size_t readSize() const; ///< Read size from file size_file
+    void writeSize(size_t db_size); ///< Write size to file size_file.
+    size_t getFileSize(std::string const & filename); ///< get file size on disk.
+    /// Only find certificate in current db and return it.
+    bool pure_find(std::string const & host_name, Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey);
+
+    bool deleteInvalidCertificate(); ///< Delete invalid certificate.
+    bool deleteOldestCertificate(); ///< Delete oldest certificate.
+    bool deleteByHostname(std::string const & host); ///< Delete using host name.
+
+    /// Callback hash function for serials. Used to create TXT_DB index of serials.
+    static unsigned long index_serial_hash(const char **a);
+    /// Callback compare function for serials. Used to create TXT_DB index of serials.
+    static int index_serial_cmp(const char **a, const char **b);
+    /// Callback hash function for names. Used to create TXT_DB index of names..
+    static unsigned long index_name_hash(const char **a);
+    /// Callback compare function for  names. Used to create TXT_DB index of names..
+    static int index_name_cmp(const char **a, const char **b);
+
+    /// Definitions required by openSSL, to use the index_* functions defined above
+    ///with TXT_DB_create_index.
+    static IMPLEMENT_LHASH_HASH_FN(index_serial_hash,const char **)
+    static IMPLEMENT_LHASH_COMP_FN(index_serial_cmp,const char **)
+    static IMPLEMENT_LHASH_HASH_FN(index_name_hash,const char **)
+    static IMPLEMENT_LHASH_COMP_FN(index_name_cmp,const char **)
+
+    static const std::string serial_file; ///< Base name of the file to store serial number.
+    static const std::string db_file; ///< Base name of the database index file.
+    static const std::string cert_dir; ///< Base name of the directory to store the certs.
+    static const std::string size_file; ///< Base name of the file to store db size.
+    /// Min size of disk db. If real size < min_db_size the  db will be disabled.
+    static const size_t min_db_size;
+
+    const std::string db_path; ///< The database directory.
+    const std::string serial_full; ///< Full path of the file to store serial number.
+    const std::string db_full; ///< Full path of the database index file.
+    const std::string cert_full; ///< Full path of the directory to store the certs.
+    const std::string size_full; ///< Full path of the file to store the db size.
+
+    TXT_DB_Pointer db; ///< Database with certificates info.
+    const size_t max_db_size; ///< Max size of db.
+    const size_t fs_block_size; ///< File system block size.
+
+    bool enabled_disk_store; ///< The storage on the disk is enabled.
+};
+
+} // namespace Ssl
+#endif // SQUID_SSL_CERTIFICATE_DB_H
diff --git a/src/ssl/context_storage.cc b/src/ssl/context_storage.cc
new file mode 100644 (file)
index 0000000..efcb3c7
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * $Id$
+ */
+
+#include "Store.h"
+#include "StoreEntryStream.h"
+#include "ssl/context_storage.h"
+#include "mgr/Registration.h"
+#if HAVE_LIMITS
+#include <limits>
+#endif
+
+Ssl::CertificateStorageAction::CertificateStorageAction(const Mgr::Command::Pointer &cmd)
+        :   Mgr::Action(cmd)
+{}
+
+Ssl::CertificateStorageAction::Pointer
+Ssl::CertificateStorageAction::Create(const Mgr::Command::Pointer &cmd)
+{
+    return new CertificateStorageAction(cmd);
+}
+
+void Ssl::CertificateStorageAction::dump (StoreEntry *sentry)
+{
+    StoreEntryStream stream(sentry);
+    const char delimiter = '\t';
+    const char endString = '\n';
+    // Page title.
+    stream << "Cached ssl certificates statistic.\n";
+    // Title of statistic table.
+    stream << "Port" << delimiter << "Max mem(KB)" << delimiter << "Cert number" << delimiter << "KB/cert" << delimiter << "Mem used(KB)" << delimiter << "Mem free(KB)" << endString;
+
+    // Add info for each port.
+    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_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 << 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()
+        :   reconfiguring(true)
+{
+    RegisterAction("cached_ssl_cert", "Statistic of cached generated ssl certificates", &CertificateStorageAction::Create, 0, 1);
+}
+
+Ssl::GlobalContextStorage::~GlobalContextStorage()
+{
+    for (std::map<Ip::Address, LocalContextStorage *>::iterator i = storage.begin(); i != storage.end(); i++) {
+        delete i->second;
+    }
+}
+
+void Ssl::GlobalContextStorage::addLocalStorage(Ip::Address const & address, size_t size_of_store)
+{
+    assert(reconfiguring);
+    configureStorage.insert(std::pair<Ip::Address, size_t>(address, size_of_store));
+}
+
+Ssl::LocalContextStorage & Ssl::GlobalContextStorage::getLocalStorage(Ip::Address const & address)
+{
+    reconfigureFinish();
+    std::map<Ip::Address, LocalContextStorage *>::iterator i = storage.find(address);
+    assert (i != storage.end());
+    return *(i->second);
+}
+
+void Ssl::GlobalContextStorage::reconfigureStart()
+{
+    reconfiguring = true;
+}
+
+void Ssl::GlobalContextStorage::reconfigureFinish()
+{
+    if (reconfiguring) {
+        reconfiguring = false;
+
+        // remove or change old local storages.
+        for (std::map<Ip::Address, LocalContextStorage *>::iterator i = storage.begin(); i != storage.end(); i++) {
+            std::map<Ip::Address, size_t>::iterator conf_i = configureStorage.find(i->first);
+            if (conf_i == configureStorage.end()) {
+                storage.erase(i);
+            } else {
+                i->second->SetSize(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)));
+            }
+        }
+    }
+}
+
+Ssl::GlobalContextStorage Ssl::TheGlobalContextStorage;
diff --git a/src/ssl/context_storage.h b/src/ssl/context_storage.h
new file mode 100644 (file)
index 0000000..92f35ea
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_SSL_CONTEXT_STORAGE_H
+#define SQUID_SSL_CONTEXT_STORAGE_H
+
+#if USE_SSL
+
+#include "SquidTime.h"
+#include "CacheManager.h"
+#include "mgr/Action.h"
+#include "mgr/Command.h"
+#if HAVE_MAP
+#include <map>
+#endif
+#if HAVE_LIST
+#include <list>
+#endif
+
+/// TODO: Replace on real size.
+#define SSL_CTX_SIZE 1024
+
+namespace  Ssl
+{
+
+/** Reports cached SSL certificate stats to Cache Manager.
+ * TODO: Use "Report" functions instead friend class.
+ */
+class CertificateStorageAction : public Mgr::Action
+{
+public:
+    CertificateStorageAction(const Mgr::Command::Pointer &cmd);
+    static Pointer Create(const Mgr::Command::Pointer &cmd);
+    virtual void dump (StoreEntry *sentry);
+    /**
+     * We do not support aggregation of information across workers
+     * TODO: aggregate these stats
+     */
+    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
+};
+
+
+/// Class for storing/manipulating LocalContextStorage per local listening address/port.
+class GlobalContextStorage
+{
+    friend class CertificateStorageAction;
+public:
+    GlobalContextStorage();
+    ~GlobalContextStorage();
+    /// Create new SSL context storage for the local listening address/port.
+    void addLocalStorage(Ip::Address const & address, size_t size_of_store);
+    /// Return the local storage for the given listening address/port.
+    LocalContextStorage & getLocalStorage(Ip::Address const & address);
+    /// When reconfigring should be called this method.
+    void reconfigureStart();
+private:
+    /// Called by getLocalStorage method
+    void reconfigureFinish();
+    bool reconfiguring; ///< True if system reconfiguring now.
+    /// Storage used on configure or reconfigure.
+    std::map<Ip::Address, size_t> configureStorage;
+    /// Map for storing all local ip address and their local storages.
+    std::map<Ip::Address, LocalContextStorage *> storage;
+};
+
+/// Global cache for store all SSL server certificates.
+extern GlobalContextStorage TheGlobalContextStorage;
+} //namespace Ssl
+#endif // USE_SSL
+
+#endif // SQUID_SSL_CONTEXT_STORAGE_H
diff --git a/src/ssl/crtd_message.cc b/src/ssl/crtd_message.cc
new file mode 100644 (file)
index 0000000..c837926
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * $Id$
+ */
+
+#include "config.h"
+#include "ssl/crtd_message.h"
+#if HAVE_CSTDLIB
+#include <cstdlib>
+#endif
+#if HAVE_CSTRING
+#include <cstring>
+#endif
+
+Ssl::CrtdMessage::CrtdMessage()
+        :   body_size(0), state(BEFORE_CODE)
+{}
+
+Ssl::CrtdMessage::ParseResult Ssl::CrtdMessage::parse(const char * buffer, size_t len)
+{
+    char const *current_pos = buffer;
+    while (current_pos != buffer + len && state != END) {
+        switch (state) {
+        case BEFORE_CODE: {
+            if (xisspace(*current_pos)) {
+                current_pos++;
+                break;
+            }
+            if (xisalpha(*current_pos)) {
+                state = CODE;
+                break;
+            }
+            clear();
+            return ERROR;
+        }
+        case CODE: {
+            if (xisalnum(*current_pos) || *current_pos == '_') {
+                current_block += *current_pos;
+                current_pos++;
+                break;
+            }
+            if (xisspace(*current_pos)) {
+                code = current_block;
+                current_block.clear();
+                state = BEFORE_LENGTH;
+                break;
+            }
+            clear();
+            return ERROR;
+        }
+        case BEFORE_LENGTH: {
+            if (xisspace(*current_pos)) {
+                current_pos++;
+                break;
+            }
+            if (xisdigit(*current_pos)) {
+                state = LENGTH;
+                break;
+            }
+            clear();
+            return ERROR;
+        }
+        case LENGTH: {
+            if (xisdigit(*current_pos)) {
+                current_block += *current_pos;
+                current_pos++;
+                break;
+            }
+            if (xisspace(*current_pos)) {
+                body_size = atoi(current_block.c_str());
+                current_block.clear();
+                state = BEFORE_BODY;
+                break;
+            }
+            clear();
+            return ERROR;
+        }
+        case BEFORE_BODY: {
+            if (body_size == 0) {
+                state = END;
+                break;
+            }
+            if (xisspace(*current_pos)) {
+                current_pos++;
+                break;
+            } else {
+                state = BODY;
+                break;
+            }
+        }
+        case BODY: {
+            size_t body_len = (static_cast<size_t>(buffer + len - current_pos) >= body_size - current_block.length())
+                              ? body_size - current_block.length()
+                              : static_cast<size_t>(buffer + len - current_pos);
+            current_block += std::string(current_pos, body_len);
+            current_pos += body_len;
+            if (current_block.length() == body_size) {
+                body = current_block;
+                state = END;
+            }
+            if (current_block.length() > body_size) {
+                clear();
+                return ERROR;
+            }
+            break;
+        }
+        case END: {
+            return OK;
+        }
+        }
+    }
+    if (state != END) return INCOMPLETE;
+    return OK;
+}
+
+std::string const & Ssl::CrtdMessage::getBody() const { return body; }
+
+std::string const & Ssl::CrtdMessage::getCode() const { return code; }
+
+void Ssl::CrtdMessage::setBody(std::string const & aBody) { body = aBody; }
+
+void Ssl::CrtdMessage::setCode(std::string const & aCode) { code = aCode; }
+
+
+std::string Ssl::CrtdMessage::compose() const
+{
+    if (code.empty()) return std::string();
+    char buffer[10];
+    snprintf(buffer, sizeof(buffer), "%zd", body.length());
+    return code + ' ' + buffer + ' ' + body + '\n';
+}
+
+void Ssl::CrtdMessage::clear()
+{
+    body_size = 0;
+    state = BEFORE_CODE;
+    body.clear();
+    code.clear();
+    current_block.clear();
+}
+
+void Ssl::CrtdMessage::parseBody(CrtdMessage::BodyParams & map, std::string & other_part) const
+{
+    other_part.clear();
+    // Copy string for using it as temp buffer.
+    std::string temp_body(body.c_str(), body.length());
+    char * buffer = const_cast<char *>(temp_body.c_str());
+    char * token = strtok(buffer, "\r\n");
+    while (token != NULL) {
+        std::string current_string(token);
+        size_t equal_pos = current_string.find('=');
+        if (equal_pos == std::string::npos) {
+            size_t offset_body_part = token - temp_body.c_str();
+            other_part = std::string(body.c_str() + offset_body_part, body.length() - offset_body_part);
+            break;
+        } else {
+            std::string param(current_string.c_str(), current_string.c_str() + equal_pos);
+            std::string value(current_string.c_str() + equal_pos + 1);
+            map.insert(std::make_pair(param, value));
+        }
+        token = strtok(NULL, "\r\n");
+    }
+}
+
+void Ssl::CrtdMessage::composeBody(CrtdMessage::BodyParams const & map, std::string const & other_part)
+{
+    body.clear();
+    for (BodyParams::const_iterator i = map.begin(); i != map.end(); i++) {
+        if (i != map.begin())
+            body += "\n";
+        body += i->first + "=" + i->second;
+    }
+    if (!other_part.empty())
+        body += other_part;
+}
+
+const std::string Ssl::CrtdMessage::code_new_certificate("new_certificate");
+const std::string Ssl::CrtdMessage::param_host("host");
diff --git a/src/ssl/crtd_message.h b/src/ssl/crtd_message.h
new file mode 100644 (file)
index 0000000..6fade2a
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_SSL_CRTD_MESSAGE_H
+#define SQUID_SSL_CRTD_MESSAGE_H
+
+#if HAVE_STRING
+#include <string>
+#endif
+#if HAVE_MAP
+#include <map>
+#endif
+
+namespace Ssl
+{
+/**
+ * This class is responsible for composing and parsing messages destined to, or comming
+ * from an ssl_crtd server. Format of these mesages is:
+ *   <response/request code> <whitespace> <body length> <whitespace> <body>
+ */
+class CrtdMessage
+{
+public:
+    typedef std::map<std::string, std::string> BodyParams;
+    /// Parse result codes.
+    enum ParseResult {
+        OK,
+        INCOMPLETE,
+        ERROR
+    };
+    CrtdMessage();
+    /**Parse buffer of length len
+     \retval OK          if parsing completes
+     \retval INCOMPLETE  if more data required
+     \retval ERROR       if there is an error.
+     */
+    ParseResult parse(const char * buffer, size_t len);
+    /// Current  body. If parsing is not finished the method returns incompleted body.
+    std::string const & getBody() const;
+    /// Current response/request code. If parsing is not finished the method may return incompleted code.
+    std::string const & getCode() const;
+    void setBody(std::string const & aBody); ///< Set new body to encode.
+    void setCode(std::string const & aCode); ///< Set new request/reply code to compose.
+    std::string compose() const; ///< Compose current (request) code and body to string.
+    /// Reset the class.
+    void clear();
+    /**
+     *Parse body data which has the form: \verbatim
+         param1=value1
+         param2=value2
+         The other multistring part of body.  \endverbatim
+     * The parameters of the body stored to map and the remaining part to other_part
+     */
+    void parseBody(BodyParams & map, std::string & other_part) const;
+    /**
+     *Compose parameters given by map with their values and the other part given by
+     * other_part to body data. The constructed body will have the form:  \verbatim
+         param1=value1
+         param2=value2
+         The other multistring part of body.  \endverbatim
+    */
+    void composeBody(BodyParams const & map, std::string const & other_part);
+    /// String code for "new_certificate" messages
+    static const std::string code_new_certificate;
+    /// Parameter name for passing hostname
+    static const std::string param_host;
+private:
+    enum ParseState {
+        BEFORE_CODE,
+        CODE,
+        BEFORE_LENGTH,
+        LENGTH,
+        BEFORE_BODY,
+        BODY,
+        END
+    };
+    size_t body_size; ///< The body size if exist or 0.
+    ParseState state; ///< Parsing state.
+    std::string body; ///< Current body.
+    std::string code; ///< Current response/request code.
+    std::string current_block; ///< Current block buffer.
+};
+
+} //namespace Ssl
+#endif // SQUID_SSL_CRTD_MESSAGE_H
diff --git a/src/ssl/gadgets.cc b/src/ssl/gadgets.cc
new file mode 100644 (file)
index 0000000..a18aee5
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * $Id$
+ */
+
+#include "config.h"
+#include "ssl/gadgets.h"
+
+/**
+ \ingroup ServerProtocolSSLInternal
+ * Add CN to subject in request.
+ */
+static bool addCnToRequest(Ssl::X509_REQ_Pointer & request, char const * cn)
+{
+    Ssl::X509_NAME_Pointer name(X509_REQ_get_subject_name(request.get()));
+    if (!name)
+        return false;
+    if (!X509_NAME_add_entry_by_txt(name.get(), "CN", MBSTRING_ASC, (unsigned char *)cn, -1, -1, 0))
+        return false;
+    name.release();
+    return true;
+}
+
+/**
+ \ingroup ServerProtocolSSLInternal
+ * Make request on sign using private key and hostname.
+ */
+static bool makeRequest(Ssl::X509_REQ_Pointer & request, Ssl::EVP_PKEY_Pointer const & pkey, char const * host)
+{
+    if (!X509_REQ_set_version(request.get(), 0L))
+        return false;
+
+    if (!addCnToRequest(request, host))
+        return false;
+
+    if (!X509_REQ_set_pubkey(request.get(), pkey.get()))
+        return false;
+    return true;
+}
+
+void Ssl::BIO_free_wrapper(BIO * bio)
+{
+    BIO_free(bio);
+}
+
+EVP_PKEY * Ssl::createSslPrivateKey()
+{
+    Ssl::EVP_PKEY_Pointer pkey(EVP_PKEY_new());
+
+    if (!pkey)
+        return NULL;
+
+    Ssl::RSA_Pointer rsa(RSA_generate_key(1024, RSA_F4, NULL, NULL));
+
+    if (!rsa)
+        return NULL;
+
+    if (!EVP_PKEY_assign_RSA(pkey.get(), (rsa.get())))
+        return NULL;
+
+    rsa.release();
+    return pkey.release();
+}
+
+X509_REQ * Ssl::createNewX509Request(Ssl::EVP_PKEY_Pointer const & pkey, const char * hostname)
+{
+    Ssl::X509_REQ_Pointer request(X509_REQ_new());
+
+    if (!request)
+        return NULL;
+
+    if (!makeRequest(request, pkey, hostname))
+        return NULL;
+    return request.release();
+}
+
+/**
+ \ingroup ServerProtocolSSLInternal
+ * Set serial random serial number or set random serial number.
+ */
+static bool setSerialNumber(ASN1_INTEGER *ai, BIGNUM const* serial)
+{
+    if (!ai)
+        return false;
+    Ssl::BIGNUM_Pointer bn(BN_new());
+    if (serial) {
+        bn.reset(BN_dup(serial));
+    } else {
+        if (!bn)
+            return false;
+
+        if (!BN_pseudo_rand(bn.get(), 64, 0, 0))
+            return false;
+    }
+
+    if (ai && !BN_to_ASN1_INTEGER(bn.get(), ai))
+        return false;
+    return true;
+}
+
+X509 * Ssl::signRequest(Ssl::X509_REQ_Pointer const & request, Ssl::X509_Pointer const & x509, Ssl::EVP_PKEY_Pointer const & pkey, ASN1_TIME * timeNotAfter, BIGNUM const * serial)
+{
+    Ssl::X509_Pointer cert(X509_new());
+    if (!cert)
+        return NULL;
+
+    if (!setSerialNumber(X509_get_serialNumber(cert.get()), serial))
+        return NULL;
+
+    if (!X509_set_issuer_name(cert.get(), x509.get() ? X509_get_subject_name(x509.get()) : X509_REQ_get_subject_name(request.get())))
+        return NULL;
+
+    if (!X509_gmtime_adj(X509_get_notBefore(cert.get()), (-2)*24*60*60))
+        return NULL;
+
+    if (timeNotAfter) {
+        if (!X509_set_notAfter(cert.get(), timeNotAfter))
+            return NULL;
+    } else if (!X509_gmtime_adj(X509_get_notAfter(cert.get()), 60*60*24*356*3))
+        return NULL;
+
+    if (!X509_set_subject_name(cert.get(), X509_REQ_get_subject_name(request.get())))
+        return NULL;
+
+    Ssl::EVP_PKEY_Pointer tmppkey(X509_REQ_get_pubkey(request.get()));
+
+    if (!tmppkey || !X509_set_pubkey(cert.get(), tmppkey.get()))
+        return NULL;
+
+    if (!X509_sign(cert.get(), pkey.get(), EVP_sha1()))
+        return NULL;
+
+    return cert.release();
+}
+
+bool Ssl::writeCertAndPrivateKeyToMemory(Ssl::X509_Pointer const & cert, Ssl::EVP_PKEY_Pointer const & pkey, std::string & bufferToWrite)
+{
+    bufferToWrite.clear();
+    if (!pkey || !cert)
+        return false;
+    BIO_Pointer bio(BIO_new(BIO_s_mem()));
+    if (!bio)
+        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;
+
+    char *ptr = NULL;
+    long len = BIO_get_mem_data(bio.get(), &ptr);
+    if (!ptr)
+        return false;
+
+    bufferToWrite = std::string(ptr, len);
+    return true;
+}
+
+bool Ssl::writeCertAndPrivateKeyToFile(Ssl::X509_Pointer 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_internal()));
+    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(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, char const * bufferToRead)
+{
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
+    BIO_puts(bio.get(), bufferToRead);
+
+    X509 * certPtr = NULL;
+    cert.reset(PEM_read_bio_X509(bio.get(), &certPtr, 0, 0));
+    if (!cert)
+        return false;
+
+    EVP_PKEY * pkeyPtr = NULL;
+    pkey.reset(PEM_read_bio_PrivateKey(bio.get(), &pkeyPtr, 0, 0));
+    if (!pkey)
+        return false;
+
+    return true;
+}
+
+bool Ssl::generateSslCertificateAndPrivateKey(char const *host, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey, Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, BIGNUM const * serial)
+{
+    pkey.reset(createSslPrivateKey());
+    if (!pkey)
+        return false;
+
+    Ssl::X509_REQ_Pointer request(createNewX509Request(pkey, host));
+    if (!request)
+        return false;
+
+    if (signedX509.get() && signedPkey.get())
+        cert.reset(signRequest(request, signedX509, signedPkey, X509_get_notAfter(signedX509.get()), serial));
+    else
+        cert.reset(signRequest(request, signedX509, pkey, NULL, serial));
+
+    if (!cert)
+        return false;
+
+    return true;
+}
+
+/**
+ \ingroup ServerProtocolSSLInternal
+ * Read certificate from file.
+ */
+static X509 * readSslX509Certificate(char const * certFilename)
+{
+    if (!certFilename)
+        return NULL;
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
+    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;
+}
+
+/**
+ \ingroup ServerProtocolSSLInternal
+ * Read private key from file. Make sure that this is not encrypted file.
+ */
+static EVP_PKEY * readSslPrivateKey(char const * keyFilename)
+{
+    if (!keyFilename)
+        return NULL;
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
+    if (!bio)
+        return NULL;
+    if (!BIO_read_filename(bio.get(), keyFilename))
+        return NULL;
+    EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL);
+    return pkey;
+}
+
+void Ssl::readCertAndPrivateKeyFromFiles(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, char const * certFilename, char const * keyFilename)
+{
+    if (keyFilename == NULL)
+        keyFilename = certFilename;
+    pkey.reset(readSslPrivateKey(keyFilename));
+    cert.reset(readSslX509Certificate(certFilename));
+    if (!pkey || !cert || !X509_check_private_key(cert.get(), pkey.get())) {
+        pkey.reset(NULL);
+        cert.reset(NULL);
+    }
+}
+
+bool Ssl::sslDateIsInTheFuture(char const * date)
+{
+    ASN1_UTCTIME tm;
+    tm.flags = 0;
+    tm.type = 23;
+    tm.data = (unsigned char *)date;
+    tm.length = strlen(date);
+
+    return (X509_cmp_current_time(&tm) > 0);
+}
diff --git a/src/ssl/gadgets.h b/src/ssl/gadgets.h
new file mode 100644 (file)
index 0000000..12f8f6d
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 2009/01/17
+ */
+
+#ifndef SQUID_SSL_GADGETS_H
+#define SQUID_SSL_GADGETS_H
+
+#include "base/TidyPointer.h"
+
+#if HAVE_OPENSSL_SSL_H
+#include <openssl/ssl.h>
+#endif
+#if HAVE_OPENSSL_TXT_DB_H
+#include <openssl/txt_db.h>
+#endif
+#if HAVE_STRING
+#include <string>
+#endif
+
+namespace Ssl
+{
+/**
+ \defgroup SslCrtdSslAPI ssl_crtd SSL api.
+ These functions must not depend on Squid runtime code such as debug()
+ because they are used by ssl_crtd.
+ */
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Function for BIO delete for Deleter template.
+*/
+void BIO_free_wrapper(BIO * bio);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * TidyPointer typedefs for  common SSL objects
+ */
+typedef TidyPointer<X509, X509_free> X509_Pointer;
+typedef TidyPointer<EVP_PKEY, EVP_PKEY_free> EVP_PKEY_Pointer;
+typedef TidyPointer<BIGNUM, BN_free> BIGNUM_Pointer;
+typedef TidyPointer<BIO, BIO_free_wrapper> BIO_Pointer;
+typedef TidyPointer<ASN1_INTEGER, ASN1_INTEGER_free> ASN1_INT_Pointer;
+typedef TidyPointer<TXT_DB, TXT_DB_free> TXT_DB_Pointer;
+typedef TidyPointer<X509_NAME, X509_NAME_free> X509_NAME_Pointer;
+typedef TidyPointer<RSA, RSA_free> RSA_Pointer;
+typedef TidyPointer<X509_REQ, X509_REQ_free> X509_REQ_Pointer;
+typedef TidyPointer<SSL_CTX, SSL_CTX_free> SSL_CTX_Pointer;
+typedef  TidyPointer<SSL, SSL_free> SSL_Pointer;
+
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Create 1024 bits rsa key.
+ */
+EVP_PKEY * createSslPrivateKey();
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Create request on certificate for a host.
+ */
+X509_REQ * createNewX509Request(EVP_PKEY_Pointer const & pkey, const char * hostname);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Write private key and SSL certificate to memory.
+ */
+bool writeCertAndPrivateKeyToMemory(X509_Pointer const & cert, EVP_PKEY_Pointer const & pkey, std::string & bufferToWrite);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Write private key and SSL certificate to file.
+ */
+bool writeCertAndPrivateKeyToFile(X509_Pointer const & cert, EVP_PKEY_Pointer const & pkey, char const * filename);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Write private key and SSL certificate to memory.
+ */
+bool readCertAndPrivateKeyFromMemory(X509_Pointer & cert, EVP_PKEY_Pointer & pkey, char const * bufferToRead);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Sign SSL request.
+ * \param x509 if this param equals NULL, returning certificate will be selfsigned.
+ * \return X509 Signed certificate.
+ */
+X509 * signRequest(X509_REQ_Pointer const & request, X509_Pointer const & x509, EVP_PKEY_Pointer const & pkey, ASN1_TIME * timeNotAfter, BIGNUM const * serial);
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Decide on the kind of certificate and generate a CA- or self-signed one.
+ * Return generated certificate and private key in resultX509 and resultPkey
+ * variables.
+ */
+bool generateSslCertificateAndPrivateKey(char const *host, X509_Pointer const & signedX509, EVP_PKEY_Pointer const & signedPkey, X509_Pointer & cert, EVP_PKEY_Pointer & pkey, BIGNUM const* serial);
+
+/**
+ \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(X509_Pointer & 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,
+ * return false.
+*/
+bool sslDateIsInTheFuture(char const * date);
+
+} // namespace Ssl
+#endif // SQUID_SSL_GADGETS_H
diff --git a/src/ssl/helper.cc b/src/ssl/helper.cc
new file mode 100644 (file)
index 0000000..e6d4140
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 2008/11/14
+ */
+
+#include "config.h"
+#include "ssl/Config.h"
+#include "ssl/helper.h"
+#include "SquidTime.h"
+#include "SwapDir.h"
+
+Ssl::Helper * Ssl::Helper::GetInstance()
+{
+    static Ssl::Helper sslHelper;
+    return &sslHelper;
+}
+
+Ssl::Helper::Helper()
+{
+    Init();
+}
+
+Ssl::Helper::~Helper()
+{
+    Shutdown();
+}
+
+void Ssl::Helper::Init()
+{
+    if (ssl_crtd == NULL)
+        ssl_crtd = new helper("ssl_crtd");
+    ssl_crtd->childs = Ssl::TheConfig.ssl_crtdChildren;
+    ssl_crtd->ipc_type = IPC_STREAM;
+    assert(ssl_crtd->cmdline == NULL);
+    {
+        char *tmp = xstrdup(Ssl::TheConfig.ssl_crtd);
+        char *tmp_begin = tmp;
+        char * token = NULL;
+        bool db_path_was_found = false;
+        bool block_size_was_found = false;
+        char buffer[20] = "2048";
+        while ((token = strwordtok(NULL, &tmp))) {
+            wordlistAdd(&ssl_crtd->cmdline, token);
+            if (!strcmp(token, "-b"))
+                block_size_was_found = true;
+            if (!strcmp(token, "-s")) {
+                db_path_was_found = true;
+            } else if (db_path_was_found) {
+                db_path_was_found = false;
+                int fs_block_size = 0;
+                storeDirGetBlkSize(token, &fs_block_size);
+                snprintf(buffer, sizeof(buffer), "%i", fs_block_size);
+            }
+        }
+        if (!block_size_was_found) {
+            wordlistAdd(&ssl_crtd->cmdline, "-b");
+            wordlistAdd(&ssl_crtd->cmdline, buffer);
+        }
+        safe_free(tmp_begin);
+    }
+    ssl_crtd->return_full_reply = true;
+    helperOpenServers(ssl_crtd);
+}
+
+void Ssl::Helper::Shutdown()
+{
+    if (!ssl_crtd)
+        return;
+    helperShutdown(ssl_crtd);
+    wordlistDestroy(&ssl_crtd->cmdline);
+    if (!shutting_down)
+        return;
+    delete ssl_crtd;
+    ssl_crtd = NULL;
+}
+
+void Ssl::Helper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void * data)
+{
+    static time_t first_warn = 0;
+
+    if (ssl_crtd->stats.queue_size >= (int)(ssl_crtd->childs.n_running * 2)) {
+        if (first_warn == 0)
+            first_warn = squid_curtime;
+        if (squid_curtime - first_warn > 3 * 60)
+            fatal("SSL servers not responding for 3 minutes");
+        debugs(34, 1, HERE << "Queue overload, rejecting");
+        callback(data, (char *)"error 45 Temporary network problem, please retry later");
+        return;
+    }
+
+    first_warn = 0;
+    helperSubmit(ssl_crtd, message.compose().c_str(), callback, data);
+}
diff --git a/src/ssl/helper.h b/src/ssl/helper.h
new file mode 100644 (file)
index 0000000..278ce2a
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * $Id$
+ */
+
+#ifndef SQUID_SSL_HELPER_H
+#define SQUID_SSL_HELPER_H
+
+#include "../helper.h"
+#include "ssl/crtd_message.h"
+
+namespace Ssl
+{
+/**
+ * Set of thread for ssl_crtd. This class is singleton. Use this class only
+ * over GetIntance() static method. This class use helper structure
+ * for threads management.
+ */
+class Helper
+{
+public:
+    static Helper * 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);
+private:
+    Helper();
+    ~Helper();
+
+    helper * ssl_crtd; ///< helper for management of ssl_crtd.
+};
+
+} //namespace Ssl
+#endif // SQUID_SSL_HELPER_H
diff --git a/src/ssl/ssl_crtd.cc b/src/ssl/ssl_crtd.cc
new file mode 100644 (file)
index 0000000..ca43cfb
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * $Id$
+ */
+
+#include "config.h"
+#include "helpers/defines.h"
+#include "ssl/gadgets.h"
+#include "ssl/crtd_message.h"
+#include "ssl/certificate_db.h"
+
+#if HAVE_CSTRING
+#include <cstring>
+#endif
+#if HAVE_SSTREAM
+#include <sstream>
+#endif
+#if HAVE_IOSTREAM
+#include <iostream>
+#endif
+#if HAVE_STDEXCEPT
+#include <stdexcept>
+#endif
+#if HAVE_STRING
+#include <string>
+#endif
+#if HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+/**
+ \defgroup ssl_crtd ssl_crtd
+ \ingroup ExternalPrograms
+ \par
+    Because the standart generation of ssl certificate for
+    sslBump feature, Squid must use external proccess to
+    actually make these calls. This process generate new ssl
+    certificates and worked with ssl certificates disk cache.
+    Typically there will be five ssl_crtd processes spawned
+    from Squid. Communication occurs via TCP sockets bound
+    to the loopback interface. The class in helper.h are
+    primally concerned with starting and stopping the ssl_crtd.
+    Reading and writing to and from the ssl_crtd occurs in the
+    \link IPCacheAPI IP\endlink and the dnsservers occurs in
+    the \link IPCacheAPI IP\endlink and \link FQDNCacheAPI
+    FQDN\endlink cache modules.
+
+ \section ssl_crtdInterface Command Line Interface
+ \verbatim
+usage: ssl_crtd -hv -s ssl_storage_path -M storage_max_size
+    -h                   Help
+    -v                   Version
+    -s ssl_storage_path  Path to specific disk storage of ssl server
+                         certificates.
+    -M storage_max_size  max size of ssl certificates storage.
+    -b fs_block_size     File system block size in bytes. Need for processing
+                         natural size of certificate on disk. Default value is
+                         2048 bytes."
+
+    After running write requests in the next format:
+    <request code><whitespace><body_len><whitespace><body>
+    There are two kind of request now:
+    new_certificate 14 host=host.dom
+        Create new private key and selfsigned certificate for "host.dom".
+
+    new_certificate xxx host=host.dom
+    -----BEGIN CERTIFICATE-----
+    ...
+    -----END CERTIFICATE-----
+    -----BEGIN RSA PRIVATE KEY-----
+    ...
+    -----END RSA PRIVATE KEY-----
+        Create new private key and certificate request for "host.dom".
+        Sign new request by received certificate and private key.
+
+usage: ssl_crtd -c -s ssl_store_path\n -n new_serial_number
+    -c                   Init ssl db directories and exit.
+    -n new_serial_number HEX serial number to use when initializing db.
+                         The default value of serial number is
+                         the number of seconds since Epoch minus 1200000000
+
+usage: ssl_crtd -g -s ssl_store_path
+    -g                   Show current serial number and exit.
+ \endverbatim
+ */
+
+static const char *const B_KBYTES_STR = "KB";
+static const char *const B_MBYTES_STR = "MB";
+static const char *const B_GBYTES_STR = "GB";
+static const char *const B_BYTES_STR = "B";
+
+/**
+  \ingroup ssl_crtd
+ * Get current time.
+*/
+time_t getCurrentTime(void)
+{
+    struct timeval current_time;
+#if GETTIMEOFDAY_NO_TZP
+    gettimeofday(&current_time);
+#else
+    gettimeofday(&current_time, NULL);
+#endif
+    return current_time.tv_sec;
+}
+
+/**
+  \ingroup ssl_crtd
+ * Parse bytes unit. It would be one of the next value: MB, GB, KB or B.
+ * This function is caseinsensitive.
+ */
+static size_t parseBytesUnits(const char * unit)
+{
+    if (!strncasecmp(unit, B_BYTES_STR, strlen(B_BYTES_STR)) ||
+            !strncasecmp(unit, "", strlen(unit)))
+        return 1;
+
+    if (!strncasecmp(unit, B_KBYTES_STR, strlen(B_KBYTES_STR)))
+        return 1 << 10;
+
+    if (!strncasecmp(unit, B_MBYTES_STR, strlen(B_MBYTES_STR)))
+        return 1 << 20;
+
+    if (!strncasecmp(unit, B_GBYTES_STR, strlen(B_GBYTES_STR)))
+        return 1 << 30;
+
+    return 0;
+}
+
+/**
+ \ingroup ssl_crtd
+ * Parse uninterrapted string of bytes value. It looks like "4MB".
+ */
+static bool parseBytesOptionValue(size_t * bptr, char const * value)
+{
+    // Find number from string beginning.
+    char const * number_begin = value;
+    char const * number_end = value;
+
+    while ((*number_end >= '0' && *number_end <= '9')) {
+        number_end++;
+    }
+
+    std::string number(number_begin, number_end - number_begin);
+    std::istringstream in(number);
+    int d = 0;
+    if (!(in >> d))
+        return false;
+
+    int m;
+    if ((m = parseBytesUnits(number_end)) == 0) {
+        return false;
+    }
+
+    *bptr = static_cast<size_t>(m * d);
+    if (static_cast<long>(*bptr * 2) != m * d * 2)
+        return false;
+
+    return true;
+}
+
+/**
+ \ingroup ssl_crtd
+ * Print help using response code.
+ */
+static void usage()
+{
+    std::string example_host_name = "host.dom";
+    std::string request_string = Ssl::CrtdMessage::param_host + "=" + example_host_name;
+    std::stringstream request_string_size_stream;
+    request_string_size_stream << request_string.length();
+    std::string help_string =
+        "usage: ssl_crtd -hv -s ssl_storage_path -M storage_max_size\n"
+        "\t-h                   Help\n"
+        "\t-v                   Version\n"
+        "\t-s ssl_storage_path  Path to specific disk storage of ssl server\n"
+        "\t                     certificates.\n"
+        "\t-M storage_max_size  max size of ssl certificates storage.\n"
+        "\t-b fs_block_size     File system block size in bytes. Need for processing\n"
+        "\t                     natural size of certificate on disk. Default value is\n"
+        "\t                     2048 bytes.\n"
+        "\n"
+        "After running write requests in the next format:\n"
+        "<request code><whitespace><body_len><whitespace><body>\n"
+        "There are two kind of request now:\n"
+        + Ssl::CrtdMessage::code_new_certificate + " " + request_string_size_stream.str() + " " + request_string + "\n" +
+        "\tCreate new private key and selfsigned certificate for \"host.dom\".\n"
+        + Ssl::CrtdMessage::code_new_certificate + " xxx " + request_string + "\n" +
+        "-----BEGIN CERTIFICATE-----\n"
+        "...\n"
+        "-----END CERTIFICATE-----\n"
+        "-----BEGIN RSA PRIVATE KEY-----\n"
+        "...\n"
+        "-----END RSA PRIVATE KEY-----\n"
+        "\tCreate new private key and certificate request for \"host.dom\"\n"
+        "\tSign new request by received certificate and private key.\n"
+        "usage: ssl_crtd -c -s ssl_store_path -n new_serial_number\n"
+        "\t-c                   Init ssl db directories and exit.\n"
+        "\t-n new_serial_number HEX serial number to use when initializing db.\n"
+        "\t                     The default value of serial number is\n"
+        "\t                     the number of seconds since Epoch minus 1200000000\n"
+        "usage: ssl_crtd -g -s ssl_store_path\n"
+        "\t-g                   Show current serial number and exit.";
+    std::cerr << help_string << std::endl;
+}
+
+/**
+ \ingroup ssl_crtd
+ * Proccess new request message.
+ */
+static bool proccessNewRequest(Ssl::CrtdMessage const & request_message, std::string const & db_path, size_t max_db_size, size_t fs_block_size)
+{
+    Ssl::CrtdMessage::BodyParams map;
+    std::string body_part;
+    request_message.parseBody(map, body_part);
+
+    Ssl::CrtdMessage::BodyParams::iterator i = map.find(Ssl::CrtdMessage::param_host);
+    if (i == map.end())
+        throw std::runtime_error("Cannot find \"" + Ssl::CrtdMessage::param_host + "\" parameter in request message.");
+    std::string host = i->second;
+
+    Ssl::CertificateDb db(db_path, max_db_size, fs_block_size);
+
+    Ssl::X509_Pointer cert;
+    Ssl::EVP_PKEY_Pointer pkey;
+    db.find("/CN=" + host, cert, pkey);
+
+    if (!cert || !pkey) {
+        Ssl::X509_Pointer certToSign;
+        Ssl::EVP_PKEY_Pointer pkeyToSign;
+        Ssl::readCertAndPrivateKeyFromMemory(certToSign, pkeyToSign, body_part.c_str());
+
+        Ssl::BIGNUM_Pointer serial(db.getCurrentSerialNumber());
+
+        if (!Ssl::generateSslCertificateAndPrivateKey(host.c_str(), certToSign, pkeyToSign, cert, pkey, serial.get()))
+            throw std::runtime_error("Cannot create ssl certificate or private key.");
+        if (!db.addCertAndPrivateKey(cert, pkey) && db.IsEnabledDiskStore())
+            throw std::runtime_error("Cannot add certificate to db.");
+    }
+
+    std::string bufferToWrite;
+    if (!Ssl::writeCertAndPrivateKeyToMemory(cert, pkey, bufferToWrite))
+        throw std::runtime_error("Cannot write ssl certificate or/and private key to memory.");
+
+    Ssl::CrtdMessage response_message;
+    response_message.setCode("ok");
+    response_message.setBody(bufferToWrite);
+
+    std::cout << response_message.compose();
+
+    return true;
+}
+
+/**
+ \ingroup ssl_crtd
+ * This is the external ssl_crtd process.
+ */
+int main(int argc, char *argv[])
+{
+    try {
+        int serial = (getCurrentTime() -  1200000000);
+        size_t max_db_size = 0;
+        size_t fs_block_size = 2048;
+        char c;
+        bool create_new_db = false;
+        bool show_sn = false;
+        std::string db_path;
+        // proccess options.
+        while ((c = getopt(argc, argv, "dcghvs:M:b:n:")) != -1) {
+            switch (c) {
+            case 'd':
+                debug_enabled = 1;
+                break;
+            case 'b':
+                if (!parseBytesOptionValue(&fs_block_size, optarg)) {
+                    throw std::runtime_error("Error when parsing -b options value");
+                }
+                break;
+            case 's':
+                db_path = optarg;
+                break;
+            case 'n': {
+                std::stringstream sn_stream(optarg);
+                sn_stream >> std::hex >> serial;
+                break;
+            }
+            case 'M':
+                if (!parseBytesOptionValue(&max_db_size, optarg)) {
+                    throw std::runtime_error("Error when parsing -M options value");
+                }
+                break;
+            case 'v':
+                std::cout << "ssl_crtd version " << VERSION << std::endl;
+                exit(0);
+                break;
+            case 'c':
+                create_new_db = true;
+                break;
+            case 'g':
+                show_sn = true;
+                break;
+            case 'h':
+                usage();
+                exit(0);
+            default:
+                exit(0);
+            }
+        }
+
+        if (create_new_db) {
+            std::cout << "Initialization SSL db..." << std::endl;
+            Ssl::CertificateDb::create(db_path, serial);
+            std::cout << "Done" << std::endl;
+            exit(0);
+        }
+
+        if (show_sn) {
+            Ssl::CertificateDb db(db_path, 4096, 0);
+            std::cout << db.getSNString() << std::endl;
+            exit(0);
+        }
+        {
+            Ssl::CertificateDb::check(db_path, max_db_size);
+        }
+        // proccess request.
+        for (;;) {
+            char request[HELPER_INPUT_BUFFER];
+            Ssl::CrtdMessage request_message;
+            Ssl::CrtdMessage::ParseResult parse_result = Ssl::CrtdMessage::INCOMPLETE;
+
+            while (parse_result == Ssl::CrtdMessage::INCOMPLETE) {
+                if (fgets(request, HELPER_INPUT_BUFFER, stdin) == NULL)
+                    return 1;
+                size_t gcount = strlen(request);
+                parse_result = request_message.parse(request, gcount);
+            }
+
+            if (parse_result == Ssl::CrtdMessage::ERROR) {
+                throw std::runtime_error("Cannot parse request message.");
+            } else if (request_message.getCode() == Ssl::CrtdMessage::code_new_certificate) {
+                proccessNewRequest(request_message, db_path, max_db_size, fs_block_size);
+            } else {
+                throw std::runtime_error("Unknown request code: \"" + request_message.getCode() + "\".");
+            }
+            std::cout.flush();
+        }
+    } catch (std::runtime_error & error) {
+        std::cerr << argv[0] << ": " << error.what() << std::endl;
+        return 0;
+    }
+    return 0;
+}
similarity index 94%
rename from src/ssl_support.cc
rename to src/ssl/ssl_support.cc
index 5907ccf6c1387a6e583d546c4985e7585a747549..a86afd0fcf0d71a47e4a8a19788cb7b4d98723fe 100644 (file)
@@ -42,6 +42,7 @@
 
 #include "fde.h"
 #include "acl/FilledChecklist.h"
+#include "ssl/gadgets.h"
 
 /**
  \defgroup ServerProtocolSSLInternal Server-Side SSL Internals
@@ -1206,4 +1207,58 @@ sslGetUserCertificateChainPEM(SSL *ssl)
     return str;
 }
 
+/// \ingroup ServerProtocolSSLInternal
+/// Create SSL context and apply ssl certificate and private key to it.
+static SSL_CTX * createSSLContext(Ssl::X509_Pointer & x509, Ssl::EVP_PKEY_Pointer & pkey)
+{
+    Ssl::SSL_CTX_Pointer sslContext(SSL_CTX_new(SSLv23_server_method()));
+
+    if (!SSL_CTX_use_certificate(sslContext.get(), x509.get()))
+        return NULL;
+
+    if (!SSL_CTX_use_PrivateKey(sslContext.get(), pkey.get()))
+        return NULL;
+    return sslContext.release();
+}
+
+SSL_CTX * Ssl::generateSslContextUsingPkeyAndCertFromMemory(const char * data)
+{
+    Ssl::X509_Pointer cert;
+    Ssl::EVP_PKEY_Pointer pkey;
+    if (!readCertAndPrivateKeyFromMemory(cert, pkey, data))
+        return NULL;
+
+    if (!cert || !pkey)
+        return NULL;
+
+    return createSSLContext(cert, pkey);
+}
+
+SSL_CTX * Ssl::generateSslContext(char const *host, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey)
+{
+    Ssl::X509_Pointer cert;
+    Ssl::EVP_PKEY_Pointer pkey;
+    if (!generateSslCertificateAndPrivateKey(host, signedX509, signedPkey, cert, pkey, NULL)) {
+        return NULL;
+    }
+    if (!cert)
+        return NULL;
+
+    if (!pkey)
+        return NULL;
+
+    return createSSLContext(cert, pkey);
+}
+
+bool Ssl::verifySslCertificateDate(SSL_CTX * sslContext)
+{
+    // Temporary ssl for getting X509 certificate from SSL_CTX.
+    Ssl::SSL_Pointer ssl(SSL_new(sslContext));
+    X509 * cert = SSL_get_certificate(ssl.get());
+    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);
+    return ret;
+}
+
 #endif /* USE_SSL */
similarity index 84%
rename from src/ssl_support.h
rename to src/ssl/ssl_support.h
index 58b43217ca769b2d593227bad04db06b53985707..e0a45fdfc1f0461e9bb62a2ce11fe418879d03bd 100644 (file)
@@ -36,6 +36,8 @@
 #define SQUID_SSL_SUPPORT_H
 
 #include "config.h"
+#include "ssl/gadgets.h"
+
 #if HAVE_OPENSSL_SSL_H
 #include <openssl/ssl.h>
 #endif
@@ -92,6 +94,30 @@ typedef int ssl_error_t;
 ssl_error_t sslParseErrorString(const char *name);
 const char *sslFindErrorString(ssl_error_t value);
 
+namespace Ssl
+{
+/**
+  \ingroup ServerProtocolSSLAPI
+  * Decide on the kind of certificate and generate a CA- or self-signed one
+*/
+SSL_CTX *generateSslContext(char const *host, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey);
+
+/**
+  \ingroup ServerProtocolSSLAPI
+  * Check date of certificate signature. If there is out of date error fucntion
+  * returns false, true otherwise.
+ */
+bool verifySslCertificateDate(SSL_CTX * sslContext);
+
+/**
+  \ingroup ServerProtocolSSLAPI
+  * Read private key and certificate from memory and generate SSL context
+  * using their.
+ */
+SSL_CTX * generateSslContextUsingPkeyAndCertFromMemory(const char * data);
+
+} //namespace Ssl
+
 // Custom SSL errors; assumes all official errors are positive
 #define SQUID_X509_V_ERR_DOMAIN_MISMATCH -1
 // All SSL errors range: from smallest (negative) custom to largest SSL error