Based on Squid v3.1 (r9820) === modified file 'configure.in' --- configure.in 2009-11-24 12:26:08 +0000 +++ configure.in 2010-01-05 08:46:09 +0000 @@ -1134,6 +1134,7 @@ dnl SSL is not enabled by default. AM_CONDITIONAL(ENABLE_SSL, false) +use_ssl=no dnl Default is to use OpenSSL when available AC_ARG_ENABLE(ssl, @@ -1142,6 +1143,7 @@ AC_MSG_NOTICE([SSL gatewaying using OpenSSL enabled]) AC_DEFINE(USE_SSL,1,[Define this to include code for SSL encryption.]) AM_CONDITIONAL(ENABLE_SSL, true) + use_ssl=yes case "$host_os" in mingw|mingw32) dnl Native Windows port of OpenSSL needs -lgdi32 @@ -1598,6 +1600,17 @@ AM_CONDITIONAL(USE_DNSSERVER, true) fi +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.]), +[ if test $use_ssl = yes ; then + AC_DEFINE(USE_SSL_CRTD,1,[Use sslserver processes instead of the internal generation ssl certificates]) + AM_CONDITIONAL(USE_SSL_CRTD, true) + fi +]) + dnl Select Default hosts file location AC_ARG_ENABLE(default-hostsfile, AS_HELP_STRING([--enable-default-hostsfile=path],[Select default location for hosts file. === modified file 'src/Makefile.am' --- src/Makefile.am 2009-11-21 22:20:01 +0000 +++ src/Makefile.am 2010-01-05 08:46:09 +0000 @@ -19,6 +19,18 @@ DnsLookupDetails.h \ DnsLookupDetails.cc +if USE_SSL_CRTD +SSL_CRTD = ssl_crtd +SSL_CRTD_SOURCE = \ + ssl_helper.cc \ + ssl_helper.h \ + ssl_certificate_db.cc \ + ssl_certificate_db.h +else +SSL_CRTD = +SSL_CRTD_SOURCE = +endif + SNMP_ALL_SOURCE = \ snmp_core.cc \ snmp_agent.cc @@ -108,7 +120,14 @@ SSL_ALL_SOURCE = \ ssl_support.cc \ - ssl_support.h + ssl_support.h \ + ssl_gadgets.cc \ + ssl_gadgets.h \ + ssl_context_storage.cc \ + ssl_context_storage.h \ + ssl_crtd_message.cc \ + ssl_crtd_message.h \ + $(SSL_CRTD_SOURCE) if ENABLE_SSL SSL_SOURCE = $(SSL_ALL_SOURCE) @@ -172,6 +191,7 @@ DiskIO/DiskDaemon/diskd \ unlinkd \ dnsserver \ + ssl_crtd \ recv-announce \ tests/testUfs \ tests/testCoss \ @@ -192,6 +212,7 @@ libexec_PROGRAMS = \ $(DNSSERVER) \ + $(SSL_CRTD) \ $(DISK_PROGRAMS) \ $(UNLINKD) @@ -237,6 +258,7 @@ $(DELAY_POOL_ALL_SOURCE) \ dns.cc \ dnsserver.cc \ + ssl_crtd.cc \ dns_internal.cc \ DnsLookupDetails.cc \ DnsLookupDetails.h \ @@ -557,7 +579,14 @@ unlinkd_SOURCES = unlinkd_daemon.cc SquidNew.cc - +ssl_crtd_SOURCES = ssl_crtd.cc \ + ssl_crtd_message.cc \ + ssl_crtd_message.h \ + ssl_gadgets.cc \ + ssl_gadgets.h \ + ssl_certificate_db.cc \ + ssl_certificate_db.h +ssl_crtd_LDADD = @SSLLIB@ dnsserver_SOURCES = dnsserver.cc SquidNew.cc recv_announce_SOURCES = recv-announce.cc SquidNew.cc @@ -713,6 +742,7 @@ 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 @@ -720,6 +750,7 @@ DEFAULT_PID_FILE = $(DEFAULT_PIDFILE) DEFAULT_NETDB_FILE = $(DEFAULT_LOG_PREFIX)/netdb.state DEFAULT_SWAP_DIR = $(localstatedir)/cache +DEFAULT_SSL_DB_DIR = $(localstatedir)/ssl_db DEFAULT_PINGER = $(libexecdir)/`echo pinger | sed '$(transform);s/$$/$(EXEEXT)/'` DEFAULT_UNLINKD = $(libexecdir)/`echo unlinkd | sed '$(transform);s/$$/$(EXEEXT)/'` DEFAULT_DISKD = $(libexecdir)/`echo diskd | sed '$(transform);s/$$/$(EXEEXT)/'` @@ -767,6 +798,7 @@ -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" \ @@ -776,6 +808,7 @@ -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" \ === modified file 'src/ProtoPort.cc' --- src/ProtoPort.cc 2009-02-01 10:09:23 +0000 +++ src/ProtoPort.cc 2010-01-05 08:46:09 +0000 @@ -6,11 +6,12 @@ #include "squid.h" #include "ProtoPort.h" +#include http_port_list::http_port_list(const char *aProtocol) #if USE_SSL : - http(*this) + http(*this), dynamicCertMemCacheSize(std::numeric_limits::max()) #endif { protocol = xstrdup(aProtocol); @@ -31,6 +32,8 @@ safe_free(capath); safe_free(dhfile); safe_free(sslflags); + safe_free(sslContextSessionId); + SSL_CTX_free(staticSslContext); #endif } === modified file 'src/ProtoPort.h' --- src/ProtoPort.h 2009-09-24 09:33:48 +0000 +++ src/ProtoPort.h 2010-01-05 08:46:09 +0000 @@ -7,6 +7,10 @@ //#include "typedefs.h" #include "cbdata.h" +#if USE_SSL +#include "ssl_gadgets.h" +#endif + struct http_port_list { http_port_list(const char *aProtocol); ~http_port_list(); @@ -52,8 +56,13 @@ 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_CTX *staticSslContext; ///< for HTTPS accelerator or static sslBump + AutoPtr signCert; /// < x509 certificate for signing generated certificates + AutoPtr signPkey; /// < private key for sighing generated certificates #endif CBDATA_CLASS2(http_port_list); === modified file 'src/base/Makefile.am' --- src/base/Makefile.am 2009-06-05 22:48:54 +0000 +++ src/base/Makefile.am 2010-01-05 08:46:09 +0000 @@ -11,4 +11,5 @@ AsyncJob.cc \ AsyncJobCalls.h \ AsyncCallQueue.cc \ - AsyncCallQueue.h + AsyncCallQueue.h \ + auto_ptr.h === added file 'src/base/auto_ptr.h' --- src/base/auto_ptr.h 1970-01-01 00:00:00 +0000 +++ src/base/auto_ptr.h 2010-01-05 08:46:09 +0000 @@ -0,0 +1,87 @@ +/* + * 2009/01/17 + */ + +#ifndef SQUID_AUTO_PTR_H +#define SQUID_AUTO_PTR_H + +#include "config.h" + +/// Base class for automatic delete different pointers after using. +template class BaseAutoPtr +{ +public: + /// Delete callback. + typedef void DCB (T * t); +protected: + BaseAutoPtr(T * t, DCB * aDeallocator) + : pointer(t), deallocator(aDeallocator) + {} +public: + bool operator !() + { + return !pointer; + } + T * get() + { + return pointer; + } + /// Address of pointer getter. + T ** addr() + { + return &pointer; + } + /// Reset pointer - delete last one and save new one. + void reset(T * t, DCB * aDeallocator) + { + deletePointer(); + pointer = t; + deallocator = aDeallocator; + } + + /// Release pointer. After that T pointer will be equal 0. + T * release() + { + T * ret = pointer; + pointer = NULL; + deallocator = NULL; + return ret; + } + /// Deallocate T pointer using special function or "free()" function. + ~BaseAutoPtr() + { + deletePointer(); + } +private: + /// Forbidden copy constructor. + BaseAutoPtr(BaseAutoPtr const &); + /// Forbidden assigment operator. + BaseAutoPtr & operator = (BaseAutoPtr const &); + /// Free pointer. + void deletePointer() + { + if (deallocator && pointer) { + deallocator(pointer); + } else if (!deallocator && pointer) { + free(pointer); + } + pointer = NULL; + deallocator = NULL; + } + /// Storing pointer. + T * pointer; + /// Function for deallocate T pointer from memory. It may be equal NULL. + DCB * deallocator; +}; + +/// Common auto pointer. +template class AutoPtr : public BaseAutoPtr +{ +public: + typedef void DCB (T * t); + AutoPtr(T * t, DCB * aDeallocator) + : BaseAutoPtr(t, aDeallocator) + {} +}; + +#endif // SQUID_AUTO_PTR_H === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2009-11-14 11:28:42 +0000 +++ src/cache_cf.cc 2010-01-05 08:46:09 +0000 @@ -51,6 +51,11 @@ #include "wordlist.h" #include "ident/Config.h" #include "ip/IpIntercept.h" +#include + +#if USE_SSL +#include "ssl_gadgets.h" +#endif #if HAVE_GLOB_H #include @@ -129,6 +134,10 @@ static void defaults_if_none(void); static int parse_line(char *); static void parseBytesLine(size_t * bptr, const char *units); +#if USE_SSL +/// Parse bytes from value. Value looks like "4MB". +static void parseBytesOptionValue(size_t * bptr, const char *units, char const * value); +#endif static size_t parseBytesUnits(const char *unit); static void free_all(void); void requirePathnameExists(const char *name, const char *path); @@ -719,7 +728,12 @@ 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 = 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); + + readCertAndPrivateKeyFromFiles(s->signCert, s->signPkey, s->cert, s->key); } } @@ -730,7 +744,10 @@ 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 = 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); } } @@ -893,6 +910,39 @@ self_destruct(); } +#if USE_SSL +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(m * d / u); + if (static_cast(*bptr) * 2 != m * d / u * 2) + self_destruct(); +} +#endif + static size_t parseBytesUnits(const char *unit) { @@ -3123,10 +3173,18 @@ 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, "sslBump") == 0) { s->sslBump = 1; // accelerated when bumped, otherwise not + } 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, xstrdup(token + 28)); #endif } else { self_destruct(); @@ -3253,11 +3311,17 @@ 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->sslBump) storeAppendPrintf(e, " sslBump"); + + if (s->generateHostCertificates) + storeAppendPrintf(e, " generate-host-certificates"); + + if (s->dynamicCertMemCacheSize != std::numeric_limits::max()) + storeAppendPrintf(e, "dynamic_cert_mem_cache_size=%lu%s\n", (unsigned long)s->dynamicCertMemCacheSize, B_BYTES_STR); #endif } === modified file 'src/cf.data.pre' --- src/cf.data.pre 2009-11-12 12:49:09 +0000 +++ src/cf.data.pre 2010-01-05 08:46:09 +0000 @@ -1152,6 +1152,11 @@ Squid, and treat them as unencrypted HTTP messages, becoming the man-in-the-middle. + This option enables the following certificate + generation parameters documented below: + generate-host-certificates and + dynamic_cert_mem_cache_size. + When this option is enabled, additional options become available to specify SSL-related properties of the client-side connection: cert, key, version, cipher, @@ -1162,6 +1167,25 @@ The ssl_bump option is required to fully enable the SslBump feature. + generate-host-certificates[=] + Dynamically create SSL server certificates for the + destination hosts of bumped CONNECT requests. If this + option is enable, cert and key options use to sign + generated certificate. 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. See the sslBump option + above for more information. + + dynamic_cert_mem_cache_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. + name= Specifies a internal name for the port. Defaults to the port specification (port or addr:port) @@ -1627,6 +1651,35 @@ 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: Config.Program.ssl_crtd +DOC_START + Specify the location and options of the executable for ssl_crtd process. + @DEFAULT_SSL_CRTD@ program SHOULD receive -s and -m options to correct + run. For more information use: + @DEFAULT_SSL_CRTD@ -h +DOC_END + +NAME: sslcrtd_children +TYPE: int +IFDEF: USE_SSL_CRTD +DEFAULT: 5 +LOC: Config.ssl_crtdChildren +DOC_START + The number of processes spawn to service ssl server. + The maximum is 32. The default is 5. + + You must have at least one ssl_crtd process. +DOC_END + +COMMENT_START OPTIONS WHICH AFFECT THE NEIGHBOR SELECTION ALGORITHM ----------------------------------------------------------------------------- COMMENT_END === modified file 'src/client_side.cc' --- src/client_side.cc 2009-11-21 11:01:43 +0000 +++ src/client_side.cc 2010-02-17 07:58:27 +0000 @@ -106,6 +106,18 @@ #include "ChunkedCodingParser.h" #include "rfc1738.h" +#include + +#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 LINGERING_CLOSE #define comm_close comm_lingering_close #endif @@ -3211,7 +3223,7 @@ 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; if (flag == COMM_ERR_CLOSING) { return; @@ -3272,24 +3284,110 @@ incoming_sockets_accepted++; } -bool -ConnStateData::switchToHttps() -{ - assert(!switchedToHttps_); - - //HTTPMSGLOCK(currentobject->http->request); - assert(areAllContextsForThisConnection()); - freeAllContexts(); - //currentobject->connIsFinished(); - - debugs(33, 5, HERE << "converting FD " << fd << " to SSL"); +void +ConnStateData::sslCrtdHandleReplyWrapper(void *data, char *reply) +{ + ConnStateData * state_data = (ConnStateData *)(data); + state_data->sslCrtdHandleReply(reply); +} + +void +ConnStateData::sslCrtdHandleReply(const char * reply) +{ + if (!reply) { + debugs(1, 1, HERE << "\"ssl_crtd\" helper return reply"); + } else { + SslCrtdMessage reply_message; + if (reply_message.parse(reply, strlen(reply)) != SslCrtdMessage::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(generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str()), true); + return; + } + } + } + getSslContextDone(NULL); +} + +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"); + LocalSslContextStorage & ssl_ctx_cache(TheGlobalSslContextStorage.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 (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."); + SslCrtdMessage request_message; + request_message.setCode(SslCrtdMessage::code_new_certificate); + SslCrtdMessage::BodyParams map; + map.insert(std::make_pair(SslCrtdMessage::param_host, host)); + std::string bufferToWrite; + writeCertAndPrivateKeyToMemory(port->signCert.get(), port->signPkey.get(), bufferToWrite); + request_message.composeBody(map, bufferToWrite); + SslHelper::GetInstance()->sslSubmit(request_message, sslCrtdHandleReplyWrapper, this); + return true; +#else + debugs(33, 5, HERE << "Generating SSL certificate for " << host); + dynCtx = generateSslContext(host, port->signCert.get(), port->signPkey.get()); + 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) { + LocalSslContextStorage & ssl_ctx_cache(TheGlobalSslContextStorage.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(33, 5, HERE << "Using static ssl context."); + sslContext = port->staticSslContext; + } else { + debugs(83, 1, "Closing SSL FD " << fd << " as lacking SSL context"); + comm_close(fd); + return false; + } + } // 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; @@ -3302,9 +3400,27 @@ commSetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, this, 0); switchedToHttps_ = true; + 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 */ @@ -3325,14 +3441,21 @@ } #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. + TheGlobalSslContextStorage.addLocalStorage(s->s, s->dynamicCertMemCacheSize == std::numeric_limits::max() ? 4194304 : s->dynamicCertMemCacheSize); + } #endif +#if USE_SSL_CRTD + SslHelper::GetInstance(); +#endif //USE_SSL_CRTD enter_suid(); @@ -3384,7 +3507,7 @@ continue; } - if (s->sslContext == NULL) { + if (!s->staticSslContext) { debugs(1, 1, "Ignoring https_port " << s->http.s << " due to SSL initialization failure."); continue; === modified file 'src/client_side.h' --- src/client_side.h 2009-09-03 12:15:55 +0000 +++ src/client_side.h 2010-01-05 08:46:09 +0000 @@ -259,7 +259,17 @@ 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; } @@ -282,6 +292,9 @@ bool closing_; bool switchedToHttps_; + /// Host name for generation ssl certificate. + String sslHostName; + BodyPipe::Pointer bodyPipe; // set when we are reading request body }; === modified file 'src/client_side_request.cc' --- src/client_side_request.cc 2009-09-24 09:33:48 +0000 +++ src/client_side_request.cc 2010-01-05 08:46:09 +0000 @@ -1149,7 +1149,7 @@ return; } - getConn()->switchToHttps(); + getConn()->switchToHttps(request->GetHost()); } void === modified file 'src/comm.cc' --- src/comm.cc 2009-11-21 11:01:43 +0000 +++ src/comm.cc 2010-01-05 08:46:09 +0000 @@ -1537,6 +1537,10 @@ F->ssl = NULL; } + if (F->dynamicSslContext) { + SSL_CTX_free(F->dynamicSslContext); + F->dynamicSslContext = NULL; + } #endif fd_close(fd); /* update fdstat */ === modified file 'src/defines.h' --- src/defines.h 2009-09-24 09:16:35 +0000 +++ src/defines.h 2010-01-05 08:46:09 +0000 @@ -69,9 +69,9 @@ #define COMM_DOBIND 0x10 #include "Debug.h" -#define do_debug(SECTION, LEVEL) ((Debug::level = (LEVEL)) > Debug::Levels[SECTION]) +#define do_debug(SECTION, LEVEL) ((Debug::level = (LEVEL)) <= Debug::Levels[SECTION]) #define debug(SECTION, LEVEL) \ - do_debug(SECTION, LEVEL) ? (void) 0 : _db_print + !do_debug(SECTION, LEVEL) ? (void) 0 : _db_print #define safe_free(x) if (x) { xxfree(x); x = NULL; } === modified file 'src/fde.h' --- src/fde.h 2009-01-16 12:14:02 +0000 +++ src/fde.h 2010-01-05 08:46:09 +0000 @@ -102,6 +102,7 @@ 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 { === modified file 'src/helper.cc' --- src/helper.cc 2009-09-03 12:15:55 +0000 +++ src/helper.cc 2010-01-05 08:46:09 +0000 @@ -838,6 +838,55 @@ cbdataFree(srv); } +/// Return buffer using call of callback function. +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; + void *cbdata; + + srv->requests[request_number] = NULL; + + r->callback = 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 = + 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) @@ -880,72 +929,29 @@ 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->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; - - srv->dispatch_time = r->dispatch_time; - - hlp->stats.avg_svc_time = - 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 " << - i << " from " << hlp->id_name << " #" << srv->index + 1 << - " '" << srv->rbuf << "'"); - - } - - 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; + if (hlp->is_return_full_reply) { + debugs(84, 3, HERE << "Return all 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"); + + if (t > srv->rbuf && t[-1] == '\r') + t[-1] = '\0'; + + *t++ = '\0'; + + if (hlp->concurrency) { + i = strtol(msg, &msg, 10); + + while (*msg && xisspace(*msg)) + msg++; + } + + helperReturnBuffer(i, srv, hlp, msg, t); } } === modified file 'src/helper.h' --- src/helper.h 2009-08-12 11:51:23 +0000 +++ src/helper.h 2010-01-05 08:46:09 +0000 @@ -73,6 +73,9 @@ int queue_size; int avg_svc_time; } stats; + /// If need to return all reply. Reply buffer from external program must + /// not contain '\0' simbols. '\0' symbol is end of buffer. + bool is_return_full_reply; }; struct _helper_stateful { === modified file 'src/main.cc' --- src/main.cc 2009-11-21 11:01:43 +0000 +++ src/main.cc 2010-01-05 08:46:09 +0000 @@ -79,6 +79,15 @@ #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 @@ -691,7 +700,12 @@ idnsShutdown(); #endif - +#if USE_SSL_CRTD + SslHelper::GetInstance()->Shutdown(); +#endif +#if USE_SSL + TheGlobalSslContextStorage.reconfigureStart(); +#endif redirectShutdown(); authenticateShutdown(); externalAclShutdown(); @@ -745,6 +759,9 @@ idnsInit(); #endif +#if USE_SSL_CRTD + SslHelper::GetInstance()->Init(); +#endif redirectInit(); authenticateInit(&Config.authConfiguration); @@ -1675,7 +1692,9 @@ idnsShutdown(); #endif - +#if USE_SSL_CRTD + SslHelper::GetInstance()->Shutdown(); +#endif redirectShutdown(); externalAclShutdown(); icpConnectionClose(); === added file 'src/ssl_certificate_db.cc' --- src/ssl_certificate_db.cc 1970-01-01 00:00:00 +0000 +++ src/ssl_certificate_db.cc 2010-02-16 16:16:37 +0000 @@ -0,0 +1,481 @@ +/* + * 2009/02/19 + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include "ssl_certificate_db.h" + +FileLocker::FileLocker(std::string const & filename) +: fd(-1) +{ +#ifdef _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 +} + +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 +} + +SslCertificateDb::DbRowWrapper::DbRowWrapper(size_t aNumber) +: number(aNumber) +{ + row = new char *[number + 1]; + for(size_t i = 0; i < number + 1; i++) + row[i] = NULL; +} + +SslCertificateDb::DbRowWrapper::~DbRowWrapper() +{ + if (row) { + for(size_t i = 0; i < number + 1; i++) { + if (row[i]) + delete[](row[i]); + } + delete[](row); + } +} + +void SslCertificateDb::DbRowWrapper::reset() +{ + row = NULL; +} + +void SslCertificateDb::DbRowWrapper::setValue(size_t number, char const * value) +{ + if (row[number]) { + free(row); + } + if (value) { + row[number] = static_cast(malloc(sizeof(char) * (strlen(value) + 1))); + memcpy(row[number], value, sizeof(char) * (strlen(value) + 1)); + } + else + row[number] = NULL; +} + +char ** SslCertificateDb::DbRowWrapper::getRow() +{ + return row; +} + +unsigned long SslCertificateDb::index_serial_hash(const char **a) +{ + const char *n; + n = a[SslCertificateDb::DbSerial]; + while (*n == '0') n++; + return lh_strhash(n); +} + +int SslCertificateDb::index_serial_cmp(const char **a, const char **b) +{ + const char *aa, *bb; + for (aa = a[SslCertificateDb::DbSerial]; *aa == '0'; aa++); + for (bb = b[SslCertificateDb::DbSerial]; *bb == '0'; bb++); + return strcmp(aa, bb); +} + +unsigned long SslCertificateDb::index_name_hash(const char **a) +{ + return(lh_strhash(a[SslCertificateDb::DbName])); +} + +int SslCertificateDb::index_name_cmp(const char **a, const char **b) +{ + return(strcmp(a[SslCertificateDb::DbName], b[SslCertificateDb::DbName])); +} + +const std::string SslCertificateDb::serial_file("serial"); +const std::string SslCertificateDb::db_file("index.txt"); +const std::string SslCertificateDb::cert_dir("certs"); +const std::string SslCertificateDb::size_file("size"); +const size_t SslCertificateDb::min_db_size(4096); + +SslCertificateDb::SslCertificateDb(std::string const & db_path, size_t aMax_db_size, size_t aFs_block_size) +: serial_full(db_path + "/" + serial_file), + db_full(db_path + "/" + db_file), + cert_full(db_path + "/" + cert_dir), + size_full(db_path + "/" + size_file), + db(NULL, 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 if (!db_path.empty() && max_db_size && !loadDb()) + throw std::runtime_error("Uninitialized SSL certificate database directory: " + db_path + ". To initialize, run \"ssl_crtd -c -s " + db_path + "\"."); +} + +bool SslCertificateDb::find(std::string const & host_name, AutoPtr & cert, AutoPtr & pkey) +{ + FileLocker db_locker(db_full); + loadDb(); + return pure_find(host_name, cert, pkey); +} + +bool SslCertificateDb::addCertAndPrivateKey(AutoPtr & cert, AutoPtr & pkey) +{ + FileLocker db_locker(db_full); + loadDb(); + if (!db || !cert || !pkey || min_db_size > max_db_size) + return false; + DbRowWrapper row(DbNumber); + ASN1_INTEGER * ai = X509_get_serialNumber(cert.get()); + std::string serial_string; + AutoPtr serial(ASN1_INTEGER_to_BN(ai, NULL)); + { + AutoPtr hex_bn(BN_bn2hex(serial.get()), NULL); + serial_string = std::string(hex_bn.get()); + } + row.setValue(DbSerial, serial_string.c_str()); + char ** rrow = TXT_DB_get_by_index(db.get(), DbSerial, row.getRow()); + if (rrow != NULL) + return false; + + { + AutoPtr subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), NULL, 0), NULL); + if (pure_find(subject.get(), cert, pkey)) + return true; + } + // check db size. + while (max_db_size < getDbSize()) { + if (!deleteInvalidCertificate()) + break; + } + + while (max_db_size < getDbSize()) { + deleteOldestCertificate(); + } + + row.setValue(DbType, "V"); + ASN1_UTCTIME * tm = X509_get_notAfter(cert.get()); + row.setValue(DbExp_date, std::string(reinterpret_cast(tm->data), tm->length).c_str()); + row.setValue(DbFile, "unknown"); + { + AutoPtr subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), NULL, 0), NULL); + row.setValue(DbName, 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.get(), pkey.get(), filename.c_str())) + return false; + addDbSize(filename); + + if (!saveDb()) + return false; + return true; +} + +BIGNUM * SslCertificateDb::getCurrentSerialNumber() +{ + FileLocker serial_locker(serial_full); + // load serial number from file. + AutoPtr file(BIO_new(BIO_s_file())); + if (!file) + return NULL; + + if (BIO_rw_filename(file.get(), const_cast(serial_full.c_str())) <= 0) + return NULL; + + AutoPtr serial_ai(ASN1_INTEGER_new(), ASN1_INTEGER_free); + if (!serial_ai) + return NULL; + + char buffer[1024]; + if (!a2i_ASN1_INTEGER(file.get(), serial_ai.get(), buffer, sizeof(buffer))) + return NULL; + + AutoPtr serial(ASN1_INTEGER_to_BN(serial_ai.get(), NULL)); + + if (!serial) + return NULL; + + // increase serial number. + AutoPtr 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; + + AutoPtr increased_serial_ai(BN_to_ASN1_INTEGER(increased_serial.get(), NULL), ASN1_INTEGER_free); + if (!increased_serial_ai) + return NULL; + + i2a_ASN1_INTEGER(file.get(), increased_serial_ai.get()); + BIO_puts(file.get(),"\n"); + + return serial.release(); +} + +void SslCertificateDb::cleanDb(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); + + AutoPtr i(ASN1_INTEGER_new(), ASN1_INTEGER_free); + ASN1_INTEGER_set(i.get(), serial); + + AutoPtr file(BIO_new(BIO_s_file())); + if (!file) + throw std::runtime_error("SSL error"); + + if (BIO_write_filename(file.get(), const_cast(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 SslCertificateDb::checkDb(std::string const & db_path, size_t max_db_size) +{ + SslCertificateDb db(db_path, max_db_size, 0); +} + +std::string SslCertificateDb::getSNString() +{ + FileLocker serial_locker(serial_full); + std::ifstream file(serial_full.c_str()); + if (!file) + return ""; + std::string serial; + file >> serial; + return serial; +} + +bool SslCertificateDb::pure_find(std::string const & host_name, AutoPtr & cert, AutoPtr & pkey) +{ + if (!db) + return false; + + DbRowWrapper row(DbNumber); + row.setValue(DbName, host_name.c_str()); + + char **rrow = TXT_DB_get_by_index(db.get(), DbName, row.getRow()); + if (rrow == NULL) + return false; + + if (!sslDateIsInTheFuture(rrow[DbExp_date])) { + deleteByHostname(rrow[DbName]); + return false; + } + + // read cert and pkey from file. + std::string filename(cert_full + "/" + rrow[DbSerial] + ".pem"); + FileLocker cert_locker(filename); + readCertAndPrivateKeyFromFiles(cert, pkey, filename.c_str(), NULL); + if (!cert || !pkey) + return false; + return true; +} + +size_t SslCertificateDb::getDbSize() +{ + FileLocker size_locker(size_full); + return readSize(); +} + +void SslCertificateDb::addDbSize(std::string const & filename) +{ + FileLocker size_locker(size_full); + writeSize(readSize() + getFileSize(filename)); +} + +void SslCertificateDb::subDbSize(std::string const & filename) +{ + FileLocker size_locker(size_full); + writeSize(readSize() - getFileSize(filename)); +} + +size_t SslCertificateDb::readSize() +{ + 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 SslCertificateDb::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 SslCertificateDb::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; +} + +bool SslCertificateDb::loadDb() +{ + // Load db from file. + AutoPtr in(BIO_new(BIO_s_file())); + if (!in) + return false; + if (BIO_read_filename(in.get(), db_full.c_str()) <= 0) + return false; + AutoPtr temp_db(TXT_DB_read(in.get(), DbNumber), TXT_DB_free); + + if (!temp_db) + return false; + + // Create indexes in db. + if (!TXT_DB_create_index(temp_db.get(), DbSerial, NULL, LHASH_HASH_FN(index_serial_hash), LHASH_COMP_FN(index_serial_cmp))) + return false; + + if (!TXT_DB_create_index(temp_db.get(), DbName, NULL, LHASH_HASH_FN(index_name_hash), LHASH_COMP_FN(index_name_cmp))) { + return false; + } + + db.reset(temp_db.release(), TXT_DB_free); + return true; +} + +bool SslCertificateDb::saveDb() +{ + if (!db) + return false; + // save db to file. + AutoPtr out(BIO_new(BIO_s_file())); + if (!out) + return false; + if (!BIO_write_filename(out.get(), const_cast(db_full.c_str()))) + return false; + + TXT_DB_write(out.get(), db.get()); + return true; +} + +bool SslCertificateDb::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[DbExp_date])) { + std::string filename(cert_full + "/" + current_row[DbSerial] + ".pem"); + FileLocker cert_locker(filename); + sk_delete(db.get()->data, i); + subDbSize(filename); + remove(filename.c_str()); + removed_one = true; + break; + } + } + + if (!removed_one) + return false; + return true; +} + +bool SslCertificateDb::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))[DbSerial] + ".pem"); + FileLocker cert_locker(filename); + sk_delete(db.get()->data, 0); + subDbSize(filename); + remove(filename.c_str()); + + return true; +} + +bool SslCertificateDb::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[DbName]) { + std::string filename(cert_full + "/" + current_row[DbSerial] + ".pem"); + FileLocker cert_locker(filename); + sk_delete(db.get()->data, i); + subDbSize(filename); + remove(filename.c_str()); + return true; + } + } + return false; +} + +bool SslCertificateDb::IsEnabledDiskStore() const +{ + return enabled_disk_store; +} === added file 'src/ssl_certificate_db.h' --- src/ssl_certificate_db.h 1970-01-01 00:00:00 +0000 +++ src/ssl_certificate_db.h 2010-01-05 08:46:09 +0000 @@ -0,0 +1,162 @@ +/* + * 2009/02/18 + */ + +#ifndef SQUID_SSL_CERTIFICATE_DB_H +#define SQUID_SSL_CERTIFICATE_DB_H + +#include +#include +#include "ssl_gadgets.h" +#include "ssl_support.h" + +/// Cross platform file locker. +class FileLocker +{ +public: + /// Lock file + FileLocker(std::string const & aFilename); + /// Unlock file + ~FileLocker(); +private: +#ifdef _SQUID_MSWIN_ + /// Windows file handle. + HANDLE hFile; +#else + /// Linux file descriptor. + int fd; +#endif +}; + +/// Data base for storing ssl certificates and their private keys. +class SslCertificateDb +{ +public: + /// Names of db columns. + enum DBColumns + { + DbType = 0, + DbExp_date, + DbRev_date, + DbSerial, + DbFile, + DbName, + DbNumber, + }; + + /// Row abstraction for openssl row. + class DbRowWrapper + { + public: + /// Create row wrapper with cells. + DbRowWrapper(size_t aNumber = DbNumber); + /// Delete all row. + ~DbRowWrapper(); + /// Set cell's value in row. + void setValue(size_t number, char const * value); + /// Row getter. + char ** getRow(); + /// Reset row and don't free memory. + void reset(); + private: + /// Row. + char **row; + /// Cell number into row. + size_t number; + }; + + SslCertificateDb(std::string const & db_path, size_t aMax_db_size, size_t aFs_block_size); + /// Find needed certificate into db. + bool find(std::string const & host_name, AutoPtr & cert, AutoPtr & pkey); + /// Save certificate to disk. + bool addCertAndPrivateKey(AutoPtr & cert, AutoPtr & pkey); + /// Get serial number for next certificate. + BIGNUM * getCurrentSerialNumber(); + /// Clean database. + static void cleanDb(std::string const & db_path, int serial); + /// Check that database was created. + static void checkDb(std::string const & db_path, size_t max_db_size); + /// Get serial number as string. + std::string getSNString(); + /// Check enabled of dist store. + bool IsEnabledDiskStore() const; +private: + /// Load db from file. + bool loadDb(); + /// Save db to file. + bool saveDb(); + + /// Get db size. + size_t getDbSize(); + /// Increase db size on size of file. + void addDbSize(std::string const & filename); + /// Decrease db size on size of file. + void subDbSize(std::string const & filename); + /// Read size from file. + size_t readSize(); + /// Write size to file. + void writeSize(size_t db_size); + /// get file size on disk. + size_t getFileSize(std::string const & filename); + /// Only find certificate in current db and return it. + bool pure_find(std::string const & host_name, AutoPtr & cert, AutoPtr & pkey); + + /// Delete invalid certificate. + bool deleteInvalidCertificate(); + /// Delete oldest certificate. + bool deleteOldestCertificate(); + /// Delete using host name. + bool deleteByHostname(std::string const & host); + + /// Hash function for serial number. + /// It needs to create serial index in db. + static unsigned long index_serial_hash(const char **a); + /// Compare function for serial number. + /// It needs to create serial index in db. + static int index_serial_cmp(const char **a, const char **b); + /// Hash function for name. + /// It needs to create name index in db. + static unsigned long index_name_hash(const char **a); + /// Compare function for name. + /// It needs to create name index in db. + static int index_name_cmp(const char **a, const char **b); + + /// Functions that neeed openssl for creating + /// Copy from openssl utilits code. + 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 **) + + /// Filename with serial number. + static const std::string serial_file; + /// Database filename. + static const std::string db_file; + /// Directory name for new certs. + static const std::string cert_dir; + /// Filename with db size. + static const std::string size_file; + /// Min size of disk db. If real size < min size of db will be disabled. + static const size_t min_db_size; + + /// Full path to file with serial number. + const std::string serial_full; + /// Full path to database file. + const std::string db_full; + /// Full path to directory for new certs. + const std::string cert_full; + /// Full path to file with db size. + const std::string size_full; + + /// Database with certificates info. + AutoPtr db; + /// Max size of db. + const size_t max_db_size; + /// File system block size. + const size_t fs_block_size; + + /// The storage on the disk is enabled. + bool enabled_disk_store; +}; + +#endif // SQUID_SSL_CERTIFICATE_DB_H === added file 'src/ssl_context_storage.cc' --- src/ssl_context_storage.cc 1970-01-01 00:00:00 +0000 +++ src/ssl_context_storage.cc 2010-01-05 08:46:09 +0000 @@ -0,0 +1,166 @@ +/* + * 2008/11/14 + */ + +#include +#include "Store.h" +#include "StoreEntryStream.h" +#include "ssl_context_storage.h" + +SslCertificateStorageAction::SslCertificateStorageAction() +: CacheManagerAction("cached_ssl_cert", "Statistic of cached generated ssl certificates", 0, 1) +{} + +void SslCertificateStorageAction::run (StoreEntry *sentry) +{ + StoreEntryStream stream(sentry); + char delimiter = '\t'; + 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::iterator i = TheGlobalSslContextStorage.storage.begin(); i != TheGlobalSslContextStorage.storage.end(); i++) + { + stream << i->first << delimiter; + LocalSslContextStorage & 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(); +} + +LocalSslContextStorage::LocalSslContextStorage(size_t aMax_memory) +: max_memory(aMax_memory), memory_used(0) +{} + +LocalSslContextStorage::~LocalSslContextStorage() +{ + for (QueueIterator i = lru_queue.begin(); i != lru_queue.end(); i++) { + delete *i; + } +} + +SSL_CTX * LocalSslContextStorage::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 * LocalSslContextStorage::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 LocalSslContextStorage::remove(char const * host_name) +{ + deleteAt(storage.find(host_name)); +} + +void LocalSslContextStorage::purgeOne() +{ + QueueIterator i = lru_queue.end(); + i--; + if (i != lru_queue.end()) { + remove((*i)->host_name.c_str()); + } +} + +void LocalSslContextStorage::deleteAt(LocalSslContextStorage::MapIterator i) +{ + if (i != storage.end()) { + + delete *(i->second); + lru_queue.erase(i->second); + storage.erase(i); + memory_used -= SSL_CTX_SIZE; + } +} + +void LocalSslContextStorage::SetSize(size_t aMax_memory) +{ + max_memory = aMax_memory; +} + +/////////////////////////////////////////////////////// + +GlobalSslContextStorage::GlobalSslContextStorage() +: reconfiguring(true) +{ + CacheManager *manager = CacheManager::GetInstance(); + manager->registerAction(new SslCertificateStorageAction()); +} + +GlobalSslContextStorage::~GlobalSslContextStorage() +{ + for (std::map::iterator i = storage.begin(); i != storage.end(); i++) { + delete i->second; + } +} + +void GlobalSslContextStorage::addLocalStorage(IpAddress const & address, size_t size_of_store) +{ + assert(reconfiguring); + configureStorage.insert(std::pair(address, size_of_store)); +} + +LocalSslContextStorage & GlobalSslContextStorage::getLocalStorage(IpAddress const & address) +{ + reconfigureFinish(); + std::map::iterator i = storage.find(address); + assert (i != storage.end()); + return *(i->second); +} + +void GlobalSslContextStorage::reconfigureStart() +{ + reconfiguring = true; +} + +void GlobalSslContextStorage::reconfigureFinish() +{ + if (reconfiguring){ + reconfiguring = false; + + // remove or change old local storages. + for (std::map::iterator i = storage.begin(); i != storage.end(); i++) { + std::map::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::iterator conf_i = configureStorage.begin(); conf_i != configureStorage.end(); conf_i++ ) { + if (storage.find(conf_i->first) == storage.end()) { + storage.insert(std::pair(conf_i->first, new LocalSslContextStorage(conf_i->second))); + } + } + } +} + +GlobalSslContextStorage TheGlobalSslContextStorage; === added file 'src/ssl_context_storage.h' --- src/ssl_context_storage.h 1970-01-01 00:00:00 +0000 +++ src/ssl_context_storage.h 2010-01-05 08:46:09 +0000 @@ -0,0 +1,121 @@ +/* + * 2008/11/14 + */ + +#ifndef SQUID_SSL_CONTEXT_STORAGE_H +#define SQUID_SSL_CONTEXT_STORAGE_H + +#if USE_SSL + +#include +#include +#include +#include "SquidTime.h" +#include "CacheManager.h" + +/// TODO: Replace on real size. +#define SSL_CTX_SIZE 1024 + +/// Class for representation statictic of cached ssl certificates on new page in Cache Manager. +/// TODO: Use "Report" functions instead friend class. +class SslCertificateStorageAction : public CacheManagerAction +{ +public: + SslCertificateStorageAction(); + virtual void run (StoreEntry *sentry); +}; + +/// Memory cache for store generated Ssl context. Cache has limited size. If need to add new +/// element and size of cache is maximum one Ssl context will be deleted from cache. It select +/// by using LRU algorithm. +class LocalSslContextStorage +{ +friend class SslCertificateStorageAction; +public: + /// Paker for store SSL_CTX * and host name together. + class Item + { + public: + Item(SSL_CTX * aSsl_ctx, std::string const & aName) + : ssl_ctx(aSsl_ctx), host_name(aName) + {} + /// Delete SSL_CTX * in here. + ~Item() { + SSL_CTX_free(ssl_ctx); + } + public: + /// Ssl certificate. + SSL_CTX * ssl_ctx; + /// Last using time. + std::string host_name; + }; + + /// Queue type. It store items in order of using. + typedef std::list Queue; + typedef Queue::iterator QueueIterator; + + /// Hash map type. It is used for fast find Queue element by host. + typedef std::map Map; + typedef Map::iterator MapIterator; + typedef std::pair MapPair; + + LocalSslContextStorage(size_t aMax_memory); + ~LocalSslContextStorage(); + /// Set maximum memory for this storage size. + void SetSize(size_t aMax_memory); + /// Return NULL if it is impossible to add SSL_CTX to starage (if max cache size equal 0). + SSL_CTX * add(char const * host_name, SSL_CTX * ssl_ctx); + /// Find SSL_CTX in storage by host name. Return null if there is no this certificate in storage. + /// Lru queue will be updated. + SSL_CTX * find(char const * host_name); + /// Delete not valid ssl context. + void remove(char const * host_name); + +private: + /// Delete oldest object. + void purgeOne(); + /// Delete object per iterator. It is used in deletePurge() and remove(...) methods. + void deleteAt(MapIterator i); + + /// Max cache size. + size_t max_memory; + /// Using cache size. + size_t memory_used; + /// SSL_CTX * with its host storage. + Map storage; + /// Lru queue. When ne element add to queue it push to front of queue. When element use (in find method) + /// it remove to front of queue too. + Queue lru_queue; +}; + + +/// Class for storage ports and LocalSslContextStorage associating with port. +class GlobalSslContextStorage +{ +friend class SslCertificateStorageAction; +public: + GlobalSslContextStorage(); + ~GlobalSslContextStorage(); + /// Create new Ssl context storage per port. + void addLocalStorage(IpAddress const & address, size_t size_of_store); + /// Local storage per port getter. + LocalSslContextStorage & getLocalStorage(IpAddress const & address); + /// When reconfigring should be called this method. + void reconfigureStart(); +private: + /// When system call getLocalStorage method this method will be called too. + void reconfigureFinish(); + /// True if system reconfiguring now. + bool reconfiguring; + /// Storage that using when there are configure or reconfigure. + std::map configureStorage; + /// Map for store all local ip address and their local storages. + std::map storage; +}; + +/// Global cache for store all ssl server certificates. +extern GlobalSslContextStorage TheGlobalSslContextStorage; + +#endif // USE_SSL + +#endif // SQUID_SSL_CONTEXT_STORAGE_H === added file 'src/ssl_crtd.cc' --- src/ssl_crtd.cc 1970-01-01 00:00:00 +0000 +++ src/ssl_crtd.cc 2010-02-16 05:25:34 +0000 @@ -0,0 +1,323 @@ +/* + * 2009/01/17 + */ + +#include "config.h" +#include +#include +#include +#include +#include +#if HAVE_GETOPT_H +#include +#endif +#include "ssl_gadgets.h" +#include "ssl_crtd_message.h" +#include "ssl_certificate_db.h" + +/** + \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 ssl_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: + + 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"; + +/// 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; +} + +/// 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; +} + +/// 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(m * d); + if (static_cast(*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 = SslCrtdMessage::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" + "\n" + "There are two kind of request now:\n" + + SslCrtdMessage::code_new_certificate + " " + request_string_size_stream.str() + " " + request_string + "\n" + + "\tCreate new private key and selfsigned certificate for \"host.dom\".\n" + + SslCrtdMessage::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(SslCrtdMessage const & request_message, std::string const & db_path, size_t max_db_size, size_t fs_block_size) +{ + SslCrtdMessage::BodyParams map; + std::string body_part; + request_message.parseBody(map, body_part); + + SslCrtdMessage::BodyParams::iterator i = map.find(SslCrtdMessage::param_host); + if (i == map.end()) + throw std::runtime_error("Cannot find \"" + SslCrtdMessage::param_host + "\" parameter in request message."); + std::string host = i->second; + + SslCertificateDb db(db_path, max_db_size, fs_block_size); + + AutoPtr cert; + AutoPtr pkey; + db.find("/CN=" + host, cert, pkey); + + if (!cert || !pkey) { + AutoPtr certToSign; + AutoPtr pkeyToSign; + readCertAndPrivateKeyFromMemory(certToSign, pkeyToSign, body_part.c_str()); + + AutoPtr serial(db.getCurrentSerialNumber()); + + if (!generateSslCertificateAndPrivateKey(host.c_str(), certToSign.get(), pkeyToSign.get(), 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 (!writeCertAndPrivateKeyToMemory(cert.get(), pkey.get(), bufferToWrite)) + throw std::runtime_error("Cannot write ssl certificate or/and private key to memory."); + + SslCrtdMessage 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, "cghvs:M:b:n:")) != -1) { + switch(c) { + 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; + SslCertificateDb::cleanDb(db_path, serial); + std::cout << "Done" << std::endl; + exit(0); + } + + if (show_sn) { + SslCertificateDb db(db_path, 4096, 0); + std::cout << db.getSNString() << std::endl; + exit(0); + } + { + SslCertificateDb::checkDb(db_path, max_db_size); + } + // proccess request. + for(;;) { + char request[512]; + size_t const REQ_SZ = sizeof(request); + SslCrtdMessage request_message; + SslCrtdMessage::ParseResult parse_result = SslCrtdMessage::INCOMPLETE; + + while (parse_result == SslCrtdMessage::INCOMPLETE) { + if (fgets(request, REQ_SZ, stdin) == NULL) + return 1; + size_t gcount = strlen(request); + parse_result = request_message.parse(request, gcount); + } + + if (parse_result == SslCrtdMessage::ERROR) { + throw std::runtime_error("Cannot parse request message."); + } else if (request_message.getCode() == SslCrtdMessage::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; +} === added file 'src/ssl_crtd_message.cc' --- src/ssl_crtd_message.cc 1970-01-01 00:00:00 +0000 +++ src/ssl_crtd_message.cc 2010-01-05 08:46:09 +0000 @@ -0,0 +1,183 @@ +/* + * 2009/01/16 + */ + +#include "config.h" +#include "ssl_crtd_message.h" +#include +#include + +SslCrtdMessage::SslCrtdMessage() +: body_size(0), current_pos(NULL), state(BEFORE_CODE) +{} + +SslCrtdMessage::ParseResult SslCrtdMessage::parse(const char * buffer, size_t len) +{ + 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(buffer + len - current_pos) >= body_size - current_block.length()) + ? body_size - current_block.length() + : static_cast(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 & SslCrtdMessage::getBody() const { return body; } + +std::string const & SslCrtdMessage::getCode() const { return code; } + +void SslCrtdMessage::setBody(std::string const & aBody) { body = aBody; } + +void SslCrtdMessage::setCode(std::string const & aCode) { code = aCode; } + + +std::string SslCrtdMessage::compose() const +{ + if (code.empty()) return std::string(); + char buffer[10]; + snprintf(buffer, sizeof(buffer), "%zd", body.length()); + return code + ' ' + buffer + '\n' + body + '\n'; +} + +void SslCrtdMessage::clear() +{ + body_size = 0; + current_pos = 0; + state = BEFORE_CODE; + body.clear(); + code.clear(); + current_block.clear(); +} + +void SslCrtdMessage::parseBody(SslCrtdMessage::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(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 SslCrtdMessage::composeBody(SslCrtdMessage::BodyParams const & map, std::string const & other_part) +{ + body.clear(); + for (BodyParams::const_iterator i = map.begin(); i != map.end(); i++) { + body += i->first + "=" + i->second + "\n"; + } + if (!other_part.empty()) + { + body += other_part; + } +} + +const std::string SslCrtdMessage::code_new_certificate("new_certificate"); +const std::string SslCrtdMessage::param_host("host"); === added file 'src/ssl_crtd_message.h' --- src/ssl_crtd_message.h 1970-01-01 00:00:00 +0000 +++ src/ssl_crtd_message.h 2010-01-05 08:46:09 +0000 @@ -0,0 +1,82 @@ +/* + * 2009/01/16 + */ + +#ifndef SQUID_SSL_CRTD_MESSAGE_H +#define SQUID_SSL_CRTD_MESSAGE_H + +#include +#include + +/// This class for serialization and desirealization message for (and from) server. Format of this mesages is the next: +/// +class SslCrtdMessage +{ +public: + typedef std::map BodyParams; + /// Parse result enum. It is returned by Parse function. + enum ParseResult + { + OK, + INCOMPLETE, + ERROR + }; + SslCrtdMessage(); + /// Parse buffer to class. + /// \return OK if parse will be end. INCOMPLETE if buffer hasn't all needed date and ERROR if there is some error. + ParseResult parse(const char * buffer, size_t len); + /// Existing body getter. If parse won't be finished it method return incomplete body. + std::string const & getBody() const; + /// Existing response/request code getter. If parse won't be finished it method return incomplete code. + std::string const & getCode() const; + /// Set new body to compose. + void setBody(std::string const & aBody); + /// Set new request/reply code to compose. + void setCode(std::string const & aCode); + /// Compose current class to string. + std::string compose() const; + /// Clear set all stats on begining position. If you want parse repeatedly you have to clear stats. + void clear(); + /// Parse body looks like: + /// param1=value1 + /// param2=value2 + /// The other multistring part of body. + /// to map contains params with their values and body part (may be empty) without parameters. + /// + void parseBody(BodyParams & map, std::string & other_part) const; + /// Compose params with their values and the other part of body (may be empty) from map to body. Body will look like: + /// param1=value1 + /// param2=value2 + /// The other multistring part of body. + void composeBody(BodyParams const & map, std::string const & other_part); + /// Single permissible code for messages now - new_certificate. + static const std::string code_new_certificate; + /// Single param for new_certificate message is host name. + static const std::string param_host; +private: + /// Internal parser state. It shows which block of messages parsing now. + enum ParseState + { + BEFORE_CODE, + CODE, + BEFORE_LENGTH, + LENGTH, + BEFORE_BODY, + BODY, + END + }; + /// body this if it defined. Otherwise 0. + size_t body_size; + /// Current position in buffer. + char const * current_pos; + /// Current block of message. + ParseState state; + /// Existing body. + std::string body; + /// Existing response/request code. + std::string code; + /// Current block buffer. + std::string current_block; +}; + +#endif // SQUID_SSL_CRTD_MESSAGE_H === added file 'src/ssl_gadgets.cc' --- src/ssl_gadgets.cc 1970-01-01 00:00:00 +0000 +++ src/ssl_gadgets.cc 2010-01-05 08:46:09 +0000 @@ -0,0 +1,262 @@ +/* + * 2009/01/17 + */ + +#include "ssl_gadgets.h" + +/// \ingroup ServerProtocolSSLInternal +/// Add CN to subject in request. +static bool addCnToRequest(X509_REQ * request, char const * cn) +{ + AutoPtr name(X509_REQ_get_subject_name(request), X509_NAME_free); + 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(X509_REQ * request, EVP_PKEY * pkey, char const * host) +{ + if (!X509_REQ_set_version(request, 0L)) + return false; + + if (!addCnToRequest(request, host)) + return false; + + if (!X509_REQ_set_pubkey(request, pkey)) + return false; + return true; +} + +void BIO_free_wrapper(BIO * bio) +{ + BIO_free(bio); +} + +EVP_PKEY * createSslPrivateKey() +{ + AutoPtr pkey(EVP_PKEY_new()); + + if (!pkey) + return NULL; + + AutoPtr rsa(RSA_generate_key(1024, RSA_F4, NULL, NULL), RSA_free); + + if (!rsa) + return NULL; + + if(!EVP_PKEY_assign_RSA(pkey.get(), (rsa.get()))) + return NULL; + + rsa.release(); + return pkey.release(); +} + +X509_REQ * createNewX509Request(EVP_PKEY * pkey, const char * hostname) +{ + AutoPtr request(X509_REQ_new(), X509_REQ_free); + + if (!request) + return NULL; + + if (!makeRequest(request.get(), 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 * serial) +{ + if (!ai) + return false; + AutoPtr 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 * signRequest(X509_REQ * request, X509 * x509, EVP_PKEY * pkey, ASN1_TIME * timeNotAfter, BIGNUM * serial) +{ + AutoPtr 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 ? X509_get_subject_name(x509) : X509_REQ_get_subject_name(request))) + 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))) + return NULL; + + AutoPtr tmppkey(X509_REQ_get_pubkey(request)); + + if (!tmppkey || !X509_set_pubkey(cert.get(), tmppkey.get())) + return NULL; + + if (!X509_sign(cert.get(), pkey, EVP_sha1())) + return NULL; + + return cert.release(); +} + +bool writeCertAndPrivateKeyToMemory(X509 * cert, EVP_PKEY * pkey, std::string & bufferToWrite) +{ + bufferToWrite.clear(); + if (!pkey || !cert) + return false; + AutoPtr bio(BIO_new(BIO_s_mem())); + if (!bio) + return false; + + if (!PEM_write_bio_X509(bio.get(), cert)) + return false; + + if (!PEM_write_bio_PrivateKey(bio.get(), pkey, 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 writeCertAndPrivateKeyToFile(X509 * cert, EVP_PKEY * pkey, char const * filename) +{ + if (!pkey || !cert) + return false; + + AutoPtr bio(BIO_new(BIO_s_file_internal())); + if (!bio) + return false; + if (!BIO_write_filename(bio.get(), const_cast(filename))) + return false; + + if (!PEM_write_bio_X509(bio.get(), cert)) + return false; + + if (!PEM_write_bio_PrivateKey(bio.get(), pkey, NULL, NULL, 0, NULL, NULL)) + return false; + + return true; +} + +bool readCertAndPrivateKeyFromMemory(AutoPtr & cert, AutoPtr & pkey, char const * bufferToRead) +{ + AutoPtr 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 generateSslCertificateAndPrivateKey(char const *host, X509 * signedX509, EVP_PKEY * signedPkey, AutoPtr & cert, AutoPtr & pkey, BIGNUM * serial) +{ + pkey.reset(createSslPrivateKey()); + if (!pkey) + return false; + + AutoPtr request(createNewX509Request(pkey.get(), host), X509_REQ_free); + if (!request) + return false; + + if (signedX509 && signedPkey) + cert.reset(signRequest(request.get(), signedX509, signedPkey, X509_get_notAfter(signedX509), serial)); + else + cert.reset(signRequest(request.get(), signedX509, pkey.get(), NULL, serial)); + + if (!cert) + return false; + + return true; +} + +/// \ingroup ServerProtocolSSLInternal +/// Read certificate from file. +static X509 * readSslX509Certificate(char const * certFilename) +{ + if (!certFilename) + return NULL; + AutoPtr 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; + AutoPtr 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 readCertAndPrivateKeyFromFiles(AutoPtr & cert, AutoPtr & 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 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); +} === added file 'src/ssl_gadgets.h' --- src/ssl_gadgets.h 1970-01-01 00:00:00 +0000 +++ src/ssl_gadgets.h 2010-01-05 08:46:09 +0000 @@ -0,0 +1,123 @@ +/* + * 2009/01/17 + */ + +#ifndef SQUID_SSL_GADGETS_H +#define SQUID_SSL_GADGETS_H + +#include "config.h" +#if HAVE_OPENSSL_SSL_H +#include +#endif + +#include +#include "base/auto_ptr.h" + +/** + \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 +/// Specilization of AutoPtr by X509. +template <> class AutoPtr : public BaseAutoPtr +{ +public: + AutoPtr(X509 * t = NULL) + : BaseAutoPtr(t, X509_free) + {} + void reset(X509 * t) + { + BaseAutoPtr::reset(t, X509_free); + } +}; + +/// \ingroup SslCrtdSslAPI +/// Specilization of AutoPtr by EVP_PKEY. +template <> class AutoPtr : public BaseAutoPtr +{ +public: + AutoPtr(EVP_PKEY * t = NULL) + : BaseAutoPtr(t, EVP_PKEY_free) + {} + void reset(EVP_PKEY * t) + { + BaseAutoPtr::reset(t, EVP_PKEY_free); + } +}; + +/// \ingroup SslCrtdSslAPI +/// Specilization of AutoPtr by BIGNUM. +template <> class AutoPtr : public BaseAutoPtr +{ +public: + AutoPtr(BIGNUM * t = NULL) + : BaseAutoPtr(t, BN_free) + {} + void reset(BIGNUM * t) + { + BaseAutoPtr::reset(t, BN_free); + } +}; + +/// \ingroup SslCrtdSslAPI +/// Specilization of AutoPtr by BIO. +template <> class AutoPtr : public BaseAutoPtr +{ +public: + AutoPtr(BIO * t = NULL) + : BaseAutoPtr(t, BIO_free_wrapper) + {} + void reset(BIO * t) + { + BaseAutoPtr::reset(t, BIO_free_wrapper); + } +}; + +/// \ingroup SslCrtdSslAPI +/// Create 1024 bits rsa key. +EVP_PKEY * createSslPrivateKey(); + +/// \ingroup SslCrtdSslAPI +/// Create request on certificate for a host. +X509_REQ * createNewX509Request(EVP_PKEY * pkey, const char * hostname); + +/// \ingroup SslCrtdSslAPI +/// Write private key and ssl certificate to memory. +bool writeCertAndPrivateKeyToMemory(X509 * cert, EVP_PKEY * pkey, std::string & bufferToWrite); + +/// \ingroup SslCrtdSslAPI +/// Write private key and ssl certificate to file. +bool writeCertAndPrivateKeyToFile(X509 * cert, EVP_PKEY * pkey, char const * filename); + +/// \ingroup SslCrtdSslAPI +/// Write private key and ssl certificate to memory. +bool readCertAndPrivateKeyFromMemory(AutoPtr & cert, AutoPtr & 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 * request, X509 * x509, EVP_PKEY * pkey, ASN1_TIME * timeNotAfter, BIGNUM * 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 * signedX509, EVP_PKEY * signedPkey, AutoPtr & cert, AutoPtr & pkey, BIGNUM * serial); + +/// Read certificate and private key from files. +/// \param certFilename name of file with certificate. +/// \param keyFilename name of file with private key. +/// \ingroup SslCrtdSslAPI +void readCertAndPrivateKeyFromFiles(AutoPtr & cert, AutoPtr & pkey, char const * certFilename, char const * keyFilename); + +/// Verify date. Date format it ASN1_UTCTIME. if there is out of date error, return false. +/// \ingroup SslCrtdSslAPI +bool sslDateIsInTheFuture(char const * date); + +#endif // SQUID_SSL_GADGETS_H === added file 'src/ssl_helper.cc' --- src/ssl_helper.cc 1970-01-01 00:00:00 +0000 +++ src/ssl_helper.cc 2010-01-05 08:46:09 +0000 @@ -0,0 +1,91 @@ +/* + * 2008/11/14 + */ + +#include "ssl_helper.h" +#include "SquidTime.h" +#include "SwapDir.h" + +SslHelper * SslHelper::GetInstance() +{ + static SslHelper sslHelper; + return &sslHelper; +} + +SslHelper::SslHelper() +{ + Init(); +} + +SslHelper::~SslHelper() +{ + Shutdown(); +} + +void SslHelper::Init() +{ + if (ssl_crtd == NULL) + ssl_crtd = helperCreate("ssl_crtd"); + ssl_crtd->n_to_start = Config.ssl_crtdChildren; + ssl_crtd->ipc_type = IPC_STREAM; + assert(ssl_crtd->cmdline == NULL); + { + char *tmp = xstrdup(Config.Program.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->is_return_full_reply = true; + helperOpenServers(ssl_crtd); +} + +void SslHelper::Shutdown() +{ + if (!ssl_crtd) + return; + helperShutdown(ssl_crtd); + wordlistDestroy(&ssl_crtd->cmdline); + if (!shutting_down) + return; + helperFree(ssl_crtd); + ssl_crtd = NULL; +} + +void SslHelper::sslSubmit(SslCrtdMessage const & message, HLPCB * callback, void * data) +{ + static time_t first_warn = 0; + + if (ssl_crtd->stats.queue_size >= ssl_crtd->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); +} === added file 'src/ssl_helper.h' --- src/ssl_helper.h 1970-01-01 00:00:00 +0000 +++ src/ssl_helper.h 2010-01-05 08:46:09 +0000 @@ -0,0 +1,31 @@ +/* + * 2008/11/14 + */ + +#ifndef SQUID_SSL_HELPER_H +#define SQUID_SSL_HELPER_H + +#include "helper.h" +#include "ssl_crtd_message.h" + +/// 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 SslHelper +{ +public: + /// Instance class. + static SslHelper * GetInstance(); + /// Init helper structure. + void Init(); + /// Shutdown helper structure. + void Shutdown(); + /// Submit ssl message to external ssl server. + void sslSubmit(SslCrtdMessage const & message, HLPCB * callback, void *data); +private: + SslHelper(); + ~SslHelper(); + /// helper for management of ssl_crtd. + helper * ssl_crtd; +}; + +#endif // SQUID_SSL_HELPER_H === modified file 'src/ssl_support.cc' --- src/ssl_support.cc 2009-04-24 10:10:27 +0000 +++ src/ssl_support.cc 2010-02-18 06:14:54 +0000 @@ -42,6 +42,7 @@ #include "fde.h" #include "acl/FilledChecklist.h" +#include "ssl_gadgets.h" /** \defgroup ServerProtocolSSLInternal Server-Side SSL Internals @@ -1181,4 +1182,58 @@ return str; } +/// \ingroup ServerProtocolSSLInternal +/// Create SSL context and apply ssl certificate and private key to it. +static SSL_CTX * createSSLContext(X509 * x509, EVP_PKEY * pkey) +{ + AutoPtr sslContext(SSL_CTX_new(SSLv23_server_method()), SSL_CTX_free); + + if (!SSL_CTX_use_certificate(sslContext.get(), x509)) + return NULL; + + if (!SSL_CTX_use_PrivateKey(sslContext.get(), pkey)) + return NULL; + return sslContext.release(); +} + +SSL_CTX * generateSslContextUsingPkeyAndCertFromMemory(const char * data) +{ + AutoPtr cert; + AutoPtr pkey; + if (!readCertAndPrivateKeyFromMemory(cert, pkey, data)) + return NULL; + + if (!cert || !pkey) + return NULL; + + return createSSLContext(cert.get(), pkey.get()); +} + +SSL_CTX * generateSslContext(char const *host, X509 * signedX509, EVP_PKEY * signedPkey) +{ + AutoPtr cert; + AutoPtr pkey; + if (!generateSslCertificateAndPrivateKey(host, signedX509, signedPkey, cert, pkey, NULL)) { + return NULL; + } + if (!cert) + return NULL; + + if (!pkey) + return NULL; + + return createSSLContext(cert.get(), pkey.get()); +} + +bool verifySslCertificateDate(SSL_CTX * sslContext) +{ + // Temporary ssl for getting X509 certificate from SSL_CTX. + AutoPtr ssl(SSL_new(sslContext), SSL_free); + 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 */ === modified file 'src/ssl_support.h' --- src/ssl_support.h 2009-02-01 10:09:23 +0000 +++ src/ssl_support.h 2010-01-05 08:46:09 +0000 @@ -89,6 +89,19 @@ ssl_error_t sslParseErrorString(const char *name); const char *sslFindErrorString(ssl_error_t value); +/// Decide on the kind of certificate and generate a CA- or self-signed one +/// \ingroup ServerProtocolSSLAPI +SSL_CTX *generateSslContext(char const *host, X509 * signedX509, EVP_PKEY * signedPkey); + +/// Check date of certificate signature. If there is out of date error fucntion +/// returns false, true otherwise. +/// \ingroup ServerProtocolSSLAPI +bool verifySslCertificateDate(SSL_CTX * sslContext); + +/// Read private key and certificate from memory and generate ssl context using their. +/// \ingroup ServerProtocolSSLAPI +SSL_CTX * generateSslContextUsingPkeyAndCertFromMemory(const char * data); + // 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 === modified file 'src/structs.h' --- src/structs.h 2009-09-27 00:28:52 +0000 +++ src/structs.h 2010-01-05 08:46:09 +0000 @@ -295,11 +295,21 @@ char *ssl_password; #endif +#if USE_SSL_CRTD + /// Name of external ssl_crtd application. + char *ssl_crtd; +#endif + } Program; #if USE_DNSSERVERS int dnsChildren; #endif +#if USE_SSL_CRTD + /// The number of processes spawn for ssl_crtd. + int ssl_crtdChildren; +#endif + int redirectChildren; int redirectConcurrency;