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],
crypt.h \
cstdlib \
cstring \
+ list \
ctype.h \
errno.h \
execinfo.h \
iosfwd \
iomanip \
iostream \
+ fstream \
climits \
ip_compat.h \
ip_fil_compat.h \
map \
math.h \
memory.h \
+ memory \
mount.h \
netdb.h \
netinet/in.h \
openssl/x509v3.h \
netinet/tcp.h \
openssl/engine.h \
+ openssl/txt_db.h \
ostream \
paths.h \
poll.h \
src/ip/Makefile \
src/log/Makefile \
src/ipc/Makefile \
+ src/ssl/Makefile \
src/mgr/Makefile \
contrib/Makefile \
snmplib/Makefile \
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
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
SquidMath.h \
SquidMath.cc \
SquidNew.cc \
- $(SSL_SOURCE) \
stat.cc \
StatHist.cc \
String.cc \
LeakFinder.h \
$(SNMP_ALL_SOURCE) \
$(UNLINKDSOURCE) \
- $(SSL_ALL_SOURCE) \
$(WIN32_ALL_SOURCE) \
$(LOADABLE_MODULES_SOURCES) \
DiskIO/DiskThreads/aiops.cc \
$(SNMPLIB) \
${ADAPTATION_LIBS} \
$(ESI_LIBS) \
+ $(SSL_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
$(SNMPLIB) \
${ADAPTATION_LIBS} \
$(ESI_LOCAL_LIBS) \
+ $(SSL_LIBS) \
$(COMMON_LIBS)
if USE_LOADABLE_MODULES
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)/'`
-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" \
-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" \
RemovalPolicy.cc \
Server.cc \
$(SNMP_SOURCE) \
- $(SSL_SOURCE) \
SquidMath.h \
SquidMath.cc \
stat.cc \
$(REPL_OBJS) \
${ADAPTATION_LIBS} \
$(ESI_LIBS) \
+ $(SSL_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
refresh.cc \
Server.cc \
$(SNMP_SOURCE) \
- $(SSL_SOURCE) \
SquidMath.h \
SquidMath.cc \
stat.cc \
$(REPL_OBJS) \
${ADAPTATION_LIBS} \
$(ESI_LIBS) \
+ $(SSL_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
refresh.cc \
Server.cc \
$(SNMP_SOURCE) \
- $(SSL_SOURCE) \
SquidMath.h \
SquidMath.cc \
stat.cc \
$(REPL_OBJS) \
${ADAPTATION_LIBS} \
$(ESI_LIBS) \
+ $(SSL_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
RemovalPolicy.cc \
Server.cc \
$(SNMP_SOURCE) \
- $(SSL_SOURCE) \
SquidMath.h \
SquidMath.cc \
stat.cc \
$(REPL_OBJS) \
${ADAPTATION_LIBS} \
$(ESI_LIBS) \
+ $(SSL_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
RemovalPolicy.cc \
Server.cc \
$(SNMP_SOURCE) \
- $(SSL_SOURCE) \
SquidMath.h \
SquidMath.cc \
stat.cc \
$(REPL_OBJS) \
${ADAPTATION_LIBS} \
$(ESI_LIBS) \
+ $(SSL_LIBS) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
$(top_builddir)/lib/libmiscutil.la \
refresh.cc \
Server.cc \
$(SNMP_SOURCE) \
- $(SSL_SOURCE) \
SquidMath.h \
SquidMath.cc \
stat.cc \
$(REPL_OBJS) \
${ADAPTATION_LIBS} \
$(ESI_LIBS) \
+ $(SSL_LIBS) \
$(SNMPLIB) \
$(top_builddir)/lib/libmisccontainers.la \
$(top_builddir)/lib/libmiscencoding.la \
#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);
safe_free(capath);
safe_free(dhfile);
safe_free(sslflags);
+ safe_free(sslContextSessionId);
#endif
}
#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();
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);
#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
#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
#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>
{
AsyncJobCalls.h \
AsyncCallQueue.cc \
AsyncCallQueue.h \
+ TidyPointer.h \
CbcPointer.h \
InstanceId.h \
Subscription.h \
--- /dev/null
+/*
+ * $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
#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();
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
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);
}
}
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));
}
}
}
#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)
{
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();
char *crlfile;
char *dhfile;
char *sslflags;
- char *sslcontext;
+ char *sslContextSessionId;
SSL_CTX *sslContext;
#endif
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
}
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:
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
-----------------------------------------------------------------------------
#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
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;
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;
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
}
#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 ... */
continue;
}
- if (s->sslContext == NULL) {
+ if (!s->staticSslContext) {
debugs(1, 1, "Ignoring https_port " << s->http.s <<
" due to SSL initialization failure.");
continue;
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;
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; }
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
};
return;
}
- getConn()->switchToHttps();
+ getConn()->switchToHttps(request->GetHost());
}
void
F->ssl = NULL;
}
+ if (F->dynamicSslContext) {
+ SSL_CTX_free(F->dynamicSslContext);
+ F->dynamicSslContext = NULL;
+ }
#endif
fd_close(fd); /* update fdstat */
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 {
write_method = NULL;
#if USE_SSL
ssl = NULL;
+ dynamicSslContext = NULL;
#endif
#ifdef _SQUID_MSWIN_
win32.handle = NULL;
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)
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);
}
}
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);
#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
idnsShutdown();
#endif
-
+#if USE_SSL_CRTD
+ Ssl::Helper::GetInstance()->Shutdown();
+#endif
+#if USE_SSL
+ Ssl::TheGlobalContextStorage.reconfigureStart();
+#endif
redirectShutdown();
authenticateReset();
externalAclShutdown();
idnsInit();
#endif
+#if USE_SSL_CRTD
+ Ssl::Helper::GetInstance()->Init();
+#endif
redirectInit();
authenticateInit(&Auth::TheConfig);
idnsShutdown();
#endif
-
+#if USE_SSL_CRTD
+ Ssl::Helper::GetInstance()->Shutdown();
+#endif
redirectShutdown();
externalAclShutdown();
icpConnectionClose();
#include "md5.h"
#if USE_SSL
-#include "ssl_support.h"
+#include "ssl/support.h"
#endif
#if SQUID_SNMP
#include "cache_snmp.h"
--- /dev/null
+/*
+ * $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
+}
--- /dev/null
+/*
+ * $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
--- /dev/null
+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
--- /dev/null
+/*
+ * $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;
+}
--- /dev/null
+/*
+ * $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
--- /dev/null
+/*
+ * $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;
--- /dev/null
+/*
+ * $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
--- /dev/null
+/*
+ * $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");
--- /dev/null
+/*
+ * $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
--- /dev/null
+/*
+ * $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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * $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
--- /dev/null
+/*
+ * $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(¤t_time);
+#else
+ gettimeofday(¤t_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;
+}
#include "fde.h"
#include "acl/FilledChecklist.h"
+#include "ssl/gadgets.h"
/**
\defgroup ServerProtocolSSLInternal Server-Side SSL Internals
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 */
#define SQUID_SSL_SUPPORT_H
#include "config.h"
+#include "ssl/gadgets.h"
+
#if HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#endif
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