=== modified file 'configure.in' --- configure.in 2010-11-06 23:34:12 +0000 +++ configure.in 2010-11-09 19:49:22 +0000 @@ -1496,40 +1496,51 @@ SQUID_YESNO([$enableval],[unrecognized argument to --disable-ident-lookups: $enableval]) ]) AC_MSG_NOTICE([Support for Ident lookups enabled: ${enable_ident_lookups:=yes}]) SQUID_DEFINE_BOOL(USE_IDENT,$enable_ident_lookups,[Support for Ident (RFC 931) lookups]) squid_opt_use_dnsserver="no" AC_ARG_ENABLE(internal-dns, AS_HELP_STRING([--disable-internal-dns], [Prevents Squid from directly sending and receiving DNS messages, and instead enables the old external 'dnsserver' processes.]), [ if test "x$enableval" = "xno" ; then AC_MSG_WARN([Disabling Internal DNS queries]) squid_opt_use_dnsserver="yes" fi ]) SQUID_DEFINE_BOOL(USE_DNSSERVERS,$squid_opt_use_dnsserver, [Use dnsserver processes instead of the internal DNS protocol support]) 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.]), +[ if test "x$enable_ssl" = "xyes" ; then + AC_DEFINE(USE_SSL_CRTD,1,[Use ssl_crtd daemon]) + 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. See hosts_file directive in squid.conf for details]), [ if test "x$enableval" != "xnone" -a "x$enableval" != "xno" ; then if test \! -f "$enableval"; then AC_MSG_WARN([Unable to find file $enableval. I hope you know what you are doing.]) fi squid_opt_default_hosts=$enableval else squid_opt_default_hosts="none" fi ]) AC_MSG_NOTICE([Default hosts file set to: ${squid_opt_default_hosts:=/etc/hosts}]) DEFAULT_HOSTS=$squid_opt_default_hosts AC_SUBST(DEFAULT_HOSTS) # Select auth schemes modules to build AC_ARG_ENABLE(auth, @@ -3315,40 +3326,41 @@ lib/rfcnb/Makefile \ lib/smblib/Makefile \ scripts/Makefile \ src/Makefile \ src/base/Makefile \ src/acl/Makefile \ src/fs/Makefile \ src/repl/Makefile \ src/auth/Makefile \ src/adaptation/Makefile \ src/adaptation/icap/Makefile \ src/adaptation/ecap/Makefile \ src/comm/Makefile \ src/esi/Makefile \ src/eui/Makefile \ src/icmp/Makefile \ src/ident/Makefile \ src/ip/Makefile \ src/log/Makefile \ src/ipc/Makefile \ + src/ssl/Makefile \ src/mgr/Makefile \ contrib/Makefile \ snmplib/Makefile \ icons/Makefile \ errors/Makefile \ test-suite/Makefile \ doc/Makefile \ doc/manuals/Makefile \ helpers/Makefile \ helpers/basic_auth/Makefile \ helpers/basic_auth/DB/Makefile \ helpers/basic_auth/fake/Makefile \ helpers/basic_auth/getpwnam/Makefile \ helpers/basic_auth/LDAP/Makefile \ helpers/basic_auth/MSNT/Makefile \ helpers/basic_auth/MSNT-multi-domain/Makefile \ helpers/basic_auth/NCSA/Makefile \ helpers/basic_auth/NIS/Makefile \ helpers/basic_auth/PAM/Makefile \ helpers/basic_auth/POP3/Makefile \ === modified file 'src/Makefile.am' --- src/Makefile.am 2010-11-06 14:58:44 +0000 +++ src/Makefile.am 2010-11-09 20:36:25 +0000 @@ -24,40 +24,49 @@ MemBlob.h \ MemBlob.cc SNMP_ALL_SOURCE = \ snmp_core.cc \ snmp_agent.cc if USE_SNMP SNMP_SOURCE = $(SNMP_ALL_SOURCE) else SNMP_SOURCE = endif LOADABLE_MODULES_SOURCES = \ LoadableModule.h \ LoadableModule.cc \ LoadableModules.h \ LoadableModules.cc 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 if USE_ESI SUBDIRS += esi ESI_LOCAL_LIBS = \ esi/libesi.la \ $(top_builddir)/lib/libTrie/src/libTrie.a ESI_LIBS = $(ESI_LOCAL_LIBS) \ $(XMLLIB) \ $(EXPATLIB) else ESI_LIBS = endif DELAY_POOL_ALL_SOURCE = \ CommonPool.h \ CompositePoolNode.h \ delay_pools.cc \ @@ -97,50 +106,40 @@ endif if ENABLE_HTCP HTCPSOURCE = htcp.cc htcp.h endif if MAKE_LEAKFINDER LEAKFINDERSOURCE = LeakFinder.cc else LEAKFINDERSOURCE = endif if ENABLE_UNLINKD UNLINKDSOURCE = unlinkd.cc UNLINKD = unlinkd else UNLINKDSOURCE = UNLINKD = endif -SSL_ALL_SOURCE = \ - ssl_support.cc \ - ssl_support.h - -if ENABLE_SSL -SSL_SOURCE = $(SSL_ALL_SOURCE) -else -SSL_SOURCE = -endif - WIN32_ALL_SOURCE = \ win32.cc \ WinSvc.cc if ENABLE_WIN32SPECIFIC WIN32_SOURCE = win32.cc WINSVC_SOURCE = WinSvc.cc else WIN32_SOURCE = WINSVC_SOURCE = endif if USE_IPC_WIN32 IPC_SOURCE = ipc_win32.cc else IPC_SOURCE = ipc.cc endif AIO_WIN32_ALL_SOURCES = \ DiskIO/AIO/aio_win32.cc \ DiskIO/AIO/aio_win32.h @@ -413,41 +412,40 @@ peer_digest.cc \ peer_proxy_negotiate_auth.cc \ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ PeerSelectState.h \ PingData.h \ protos.h \ redirect.cc \ referer.cc \ refresh.cc \ RemovalPolicy.cc \ RemovalPolicy.h \ send-announce.cc \ $(SBUF_SOURCE) \ $(SNMP_SOURCE) \ squid.h \ SquidMath.h \ SquidMath.cc \ SquidNew.cc \ - $(SSL_SOURCE) \ stat.cc \ StatHist.cc \ String.cc \ stmem.cc \ stmem.h \ store.cc \ Store.h \ StoreFileSystem.cc \ StoreFileSystem.h \ StoreHashIndex.h \ store_io.cc \ StoreIOBuffer.h \ StoreIOState.cc \ StoreIOState.h \ store_client.cc \ StoreClient.h \ store_digest.cc \ store_dir.cc \ store_key_md5.cc \ store_log.cc \ @@ -500,42 +498,40 @@ EXTRA_squid_SOURCES = \ $(AIO_WIN32_ALL_SOURCES) \ $(all_AUTHMODULES) \ ConfigOption.h \ $(DELAY_POOL_ALL_SOURCE) \ dns.cc \ dnsserver.cc \ dns_internal.cc \ DnsLookupDetails.cc \ DnsLookupDetails.h \ htcp.cc \ htcp.h \ ipc.cc \ ipc_win32.cc \ ProfStats.cc \ LeakFinder.cc \ LeakFinder.h \ $(SNMP_ALL_SOURCE) \ $(UNLINKDSOURCE) \ - $(SSL_ALL_SOURCE) \ - $(WIN32_ALL_SOURCE) \ $(LOADABLE_MODULES_SOURCES) \ DiskIO/DiskThreads/aiops.cc \ DiskIO/DiskThreads/aiops_win32.cc noinst_HEADERS = \ client_side_request.cci \ MemBuf.cci \ MemBuf.h \ Store.cci \ StoreEntryStream.h \ String.cci \ SquidString.h \ SquidTime.h BUILT_SOURCES = \ cf_gen_defines.cci \ cf_parser.cci \ err_type.cc \ err_detail_type.cc \ globals.cc \ @@ -550,56 +546,58 @@ nodist_squid_SOURCES = \ $(DISKIO_GEN_SOURCE) \ $(BUILT_SOURCES) squid_LDADD = \ $(COMMON_LIBS) \ comm/libcomm-listener.la \ eui/libeui.la \ icmp/libicmp.la icmp/libicmp-core.la \ log/liblog.la \ $(XTRA_OBJS) \ $(DISK_LINKOBJS) \ $(REPL_OBJS) \ $(DISK_LIBS) \ $(DISK_OS_LIBS) \ $(CRYPTLIB) \ $(REGEXLIB) \ $(SNMPLIB) \ ${ADAPTATION_LIBS} \ $(ESI_LIBS) \ + $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(SSLLIB) \ $(EPOLL_LIBS) \ $(MINGW_LIBS) \ $(KRB5LIBS) \ $(COMPAT_LIB) \ $(XTRA_LIBS) squid_DEPENDENCIES = \ $(DISK_LIBS) \ $(DISK_LINKOBJS) \ $(REPL_OBJS) \ $(SNMPLIB) \ ${ADAPTATION_LIBS} \ $(ESI_LOCAL_LIBS) \ + $(SSL_LIBS) \ $(COMMON_LIBS) if USE_LOADABLE_MODULES squid_SOURCES += $(LOADABLE_MODULES_SOURCES) squid_LDADD += -L$(top_builddir) $(LIBLTDL) squid_LDFLAGS = -export-dynamic -dlopen force ## when static module linking is supported and enabled: ## squid_LDFLAGS = -all-static -dlopen self ## ## LTDL headers require their local include path... INCLUDES += $(INCLTDL) endif ## Kerberos libraries require their include path... INCLUDES += $(KRB5INCS) unlinkd_SOURCES = unlinkd_daemon.cc dnsserver_SOURCES = dnsserver.cc SquidNew.cc tests/stub_debug.cc test_tools.cc time.cc recv_announce_SOURCES = recv-announce.cc @@ -744,46 +742,48 @@ DiskIO/DiskThreads/DiskThreadsIOStrategy.cc \ DiskIO/DiskThreads/DiskThreadsIOStrategy.h DiskIO_DiskDaemon_diskd_SOURCES = DiskIO/DiskDaemon/diskd.cc nodist_DiskIO_DiskDaemon_diskd_SOURCES = time.cc DiskIO_DiskDaemon_diskd_LDADD = \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(COMPAT_LIB) \ $(XTRA_LIBS) DEFAULT_HTTP_PORT = 3128 DEFAULT_ICP_PORT = 3130 DEFAULT_PREFIX = $(prefix) DEFAULT_CONFIG_DIR = $(sysconfdir) DEFAULT_CONFIG_FILE = $(DEFAULT_CONFIG_DIR)/squid.conf DEFAULT_MIME_TABLE = $(DEFAULT_CONFIG_DIR)/mime.conf DEFAULT_DNSSERVER = $(libexecdir)/`echo dnsserver | sed '$(transform);s/$$/$(EXEEXT)/'` +DEFAULT_SSL_CRTD = $(libexecdir)/`echo ssl_crtd | sed '$(transform);s/$$/$(EXEEXT)/'` DEFAULT_LOG_PREFIX = $(DEFAULT_LOG_DIR) DEFAULT_CACHE_LOG = $(DEFAULT_LOG_PREFIX)/cache.log DEFAULT_ACCESS_LOG = $(DEFAULT_LOG_PREFIX)/access.log DEFAULT_STORE_LOG = $(DEFAULT_LOG_PREFIX)/store.log DEFAULT_NETDB_FILE = $(DEFAULT_LOG_PREFIX)/netdb.state DEFAULT_SWAP_DIR = $(localstatedir)/cache +DEFAULT_SSL_DB_DIR = $(localstatedir)/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)/'` DEFAULT_DISKD = $(libexecdir)/`echo diskd | sed '$(transform);s/$$/$(EXEEXT)/'` DEFAULT_ICON_DIR = $(datadir)/icons DEFAULT_ERROR_DIR = $(datadir)/errors # Make location configure settings available to the code DEFS += -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\" -DDEFAULT_SQUID_DATA_DIR=\"$(datadir)\" -DDEFAULT_SQUID_CONFIG_DIR=\"$(sysconfdir)\" snmp_core.o snmp_agent.o: ../snmplib/libsnmp.a $(top_srcdir)/include/cache_snmp.h globals.cc: globals.h mk-globals-c.awk $(AWK) -f $(srcdir)/mk-globals-c.awk < $(srcdir)/globals.h > $@ || ($(RM) -f $@ && exit 1) ## Generate files containing string arrays for various enums.... hier_code.cc: hier_code.h mk-string-arrays.awk $(AWK) -f $(srcdir)/mk-string-arrays.awk < $(srcdir)/hier_code.h > $@ || ($(RM) -f $@ && exit 1) err_type.cc: err_type.h mk-string-arrays.awk @@ -816,50 +816,52 @@ # squid.conf.default is built by cf_gen when making cf_parser.cci squid.conf.default squid.conf.documented: cf_parser.cci true cf_parser.cci: cf.data cf_gen$(EXEEXT) ./cf_gen cf.data $(srcdir)/cf.data.depend cf_gen_defines.cci: $(srcdir)/cf_gen_defines $(srcdir)/cf.data.pre $(AWK) -f $(srcdir)/cf_gen_defines <$(srcdir)/cf.data.pre >$@ || ($(RM) -f $@ && exit 1) ## FIXME: generate a sed command file from configure. Then this doesn't ## depend on the Makefile. cf.data: cf.data.pre Makefile sed \ -e "s%[@]DEFAULT_HTTP_PORT[@]%$(DEFAULT_HTTP_PORT)%g" \ -e "s%[@]DEFAULT_ICP_PORT[@]%$(DEFAULT_ICP_PORT)%g" \ -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_LOGFILED[@]%$(DEFAULT_LOGFILED)%g;" \ -e "s%[@]DEFAULT_CACHE_LOG[@]%$(DEFAULT_CACHE_LOG)%g" \ -e "s%[@]DEFAULT_ACCESS_LOG[@]%$(DEFAULT_ACCESS_LOG)%g" \ -e "s%[@]DEFAULT_STORE_LOG[@]%$(DEFAULT_STORE_LOG)%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" \ -e "s%[@]DEFAULT_HOSTS[@]%$(DEFAULT_HOSTS)%g" \ -e "s%[@]SQUID[@]%SQUID\ $(VERSION)%g" \ < $(srcdir)/cf.data.pre >$@ repl_modules.cc: repl_modules.sh Makefile $(SHELL) $(srcdir)/repl_modules.sh $(REPL_POLICIES) > repl_modules.cc include $(top_srcdir)/doc/manuals/Substitute.am squid.8: $(srcdir)/squid.8.in Makefile $(SUBSTITUTE) < $(srcdir)/squid.8.in > $@ man_MANS = squid.8 EXTRA_DIST += squid.8.in CLEANFILES += squid.8 install-data-local: install-sysconfDATA install-dataDATA @@ -1217,41 +1219,40 @@ mem_node.cc \ MemBuf.cc \ MemObject.cc \ mime.cc \ mime_header.cc \ neighbors.cc \ Packer.cc \ Parsing.cc \ pconn.cc \ peer_digest.cc \ peer_proxy_negotiate_auth.cc \ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ redirect.cc \ referer.cc \ refresh.cc \ RemovalPolicy.cc \ Server.cc \ $(SNMP_SOURCE) \ - $(SSL_SOURCE) \ SquidMath.h \ SquidMath.cc \ stat.cc \ StatHist.cc \ stmem.cc \ store.cc \ store_client.cc \ store_digest.cc \ store_dir.cc \ store_io.cc \ store_key_md5.cc \ store_log.cc \ store_rebuild.cc \ store_swapin.cc \ store_swapmeta.cc \ store_swapout.cc \ StoreFileSystem.cc \ StoreIOState.cc \ StoreMeta.cc \ StoreMetaMD5.cc \ @@ -1268,40 +1269,41 @@ url.cc \ URLScheme.cc \ urn.cc \ useragent.cc \ wccp2.cc \ whois.cc \ FadingCounter.cc \ $(WIN32_SOURCE) \ wordlist.cc nodist_tests_testCacheManager_SOURCES = \ $(BUILT_SOURCES) # comm.cc only requires comm/libcomm-listener.la until fdc_table is dead. tests_testCacheManager_LDADD = \ $(COMMON_LIBS) \ comm/libcomm-listener.la \ icmp/libicmp.la icmp/libicmp-core.la \ log/liblog.la \ $(REPL_OBJS) \ ${ADAPTATION_LIBS} \ $(ESI_LIBS) \ + $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(REGEXLIB) \ $(SNMPLIB) \ $(SQUID_CPPUNIT_LIBS) \ $(SQUID_CPPUNIT_LA) \ $(SSLLIB) \ $(KRB5LIBS) \ $(COMPAT_LIB) \ $(XTRA_LIBS) tests_testCacheManager_LDFLAGS = $(LIBADD_DL) tests_testCacheManager_DEPENDENCIES = \ $(REPL_OBJS) \ $(SQUID_CPPUNIT_LA) tests_testDiskIO_SOURCES = \ $(SWAP_TEST_SOURCES) \ tests/testDiskIO.cc \ tests/testDiskIO.h \ @@ -1421,41 +1423,40 @@ multicast.cc \ mem_node.cc \ MemBuf.cc \ MemObject.cc \ mime.cc \ mime_header.cc \ neighbors.cc \ Packer.cc \ Parsing.cc \ pconn.cc \ peer_digest.cc \ peer_proxy_negotiate_auth.cc \ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ redirect.cc \ referer.cc \ refresh.cc \ Server.cc \ $(SNMP_SOURCE) \ - $(SSL_SOURCE) \ SquidMath.h \ SquidMath.cc \ stat.cc \ StatHist.cc \ stmem.cc \ store.cc \ store_client.cc \ store_digest.cc \ store_dir.cc \ store_io.cc \ store_key_md5.cc \ store_log.cc \ store_rebuild.cc \ store_swapin.cc \ store_swapmeta.cc \ store_swapout.cc \ StoreFileSystem.cc \ StoreIOState.cc \ StoreMeta.cc \ StoreMetaMD5.cc \ @@ -1471,40 +1472,41 @@ SwapDir.cc \ url.cc \ URLScheme.cc \ urn.cc \ useragent.cc \ wccp2.cc \ whois.cc \ FadingCounter.cc \ $(WIN32_SOURCE) \ wordlist.cc nodist_tests_testEvent_SOURCES = \ $(BUILT_SOURCES) tests_testEvent_LDADD = \ $(COMMON_LIBS) \ icmp/libicmp.la icmp/libicmp-core.la \ comm/libcomm-listener.la \ log/liblog.la \ $(REPL_OBJS) \ ${ADAPTATION_LIBS} \ $(ESI_LIBS) \ + $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(REGEXLIB) \ $(SNMPLIB) \ $(SQUID_CPPUNIT_LIBS) \ $(SQUID_CPPUNIT_LA) \ $(SSLLIB) \ $(KRB5LIBS) \ $(COMPAT_LIB) \ $(XTRA_LIBS) tests_testEvent_LDFLAGS = $(LIBADD_DL) tests_testEvent_DEPENDENCIES = \ $(REPL_OBJS) \ $(SQUID_CPPUNIT_LA) ## Tests of the EventLoop module. tests_testEventLoop_SOURCES = \ $(ACL_REGISTRATION_SOURCES) \ debug.cc \ @@ -1584,41 +1586,40 @@ multicast.cc \ mem_node.cc \ MemBuf.cc \ MemObject.cc \ mime.cc \ mime_header.cc \ neighbors.cc \ Packer.cc \ Parsing.cc \ pconn.cc \ peer_digest.cc \ peer_proxy_negotiate_auth.cc \ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ redirect.cc \ referer.cc \ refresh.cc \ Server.cc \ $(SNMP_SOURCE) \ - $(SSL_SOURCE) \ SquidMath.h \ SquidMath.cc \ stat.cc \ StatHist.cc \ stmem.cc \ store.cc \ store_client.cc \ store_digest.cc \ store_dir.cc \ store_io.cc \ store_key_md5.cc \ store_log.cc \ store_rebuild.cc \ store_swapin.cc \ store_swapmeta.cc \ store_swapout.cc \ StoreFileSystem.cc \ StoreIOState.cc \ StoreMeta.cc \ StoreMetaMD5.cc \ @@ -1634,40 +1635,41 @@ SwapDir.cc \ url.cc \ URLScheme.cc \ urn.cc \ useragent.cc \ wccp2.cc \ whois.cc \ FadingCounter.cc \ $(WIN32_SOURCE) \ wordlist.cc nodist_tests_testEventLoop_SOURCES = \ $(BUILT_SOURCES) tests_testEventLoop_LDADD = \ $(COMMON_LIBS) \ icmp/libicmp.la icmp/libicmp-core.la \ comm/libcomm-listener.la \ log/liblog.la \ $(REPL_OBJS) \ ${ADAPTATION_LIBS} \ $(ESI_LIBS) \ + $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(REGEXLIB) \ $(SNMPLIB) \ $(SQUID_CPPUNIT_LIBS) \ $(SQUID_CPPUNIT_LA) \ $(SSLLIB) \ $(KRB5LIBS) \ $(COMPAT_LIB) \ $(XTRA_LIBS) tests_testEventLoop_LDFLAGS = $(LIBADD_DL) tests_testEventLoop_DEPENDENCIES = \ $(REPL_OBJS) \ $(SQUID_CPPUNIT_LA) tests_test_http_range_SOURCES = \ $(ACL_REGISTRATION_SOURCES) \ tests/test_http_range.cc \ BodyPipe.cc \ @@ -1738,41 +1740,40 @@ mem.cc \ mem_node.cc \ MemObject.cc \ mime.cc \ mime_header.cc \ multicast.cc \ neighbors.cc \ Parsing.cc \ peer_digest.cc \ peer_proxy_negotiate_auth.cc \ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ pconn.cc \ redirect.cc \ referer.cc \ refresh.cc \ RemovalPolicy.cc \ Server.cc \ $(SNMP_SOURCE) \ - $(SSL_SOURCE) \ SquidMath.h \ SquidMath.cc \ stat.cc \ StatHist.cc \ stmem.cc \ store.cc \ store_client.cc \ store_digest.cc \ store_dir.cc \ store_key_md5.cc \ store_io.cc \ store_log.cc \ store_rebuild.cc \ store_swapin.cc \ store_swapmeta.cc \ store_swapout.cc \ StoreFileSystem.cc \ StoreIOState.cc \ StoreMeta.cc \ StoreMetaMD5.cc \ @@ -1792,40 +1793,41 @@ URLScheme.cc \ urn.cc \ useragent.cc \ wccp2.cc \ whois.cc \ FadingCounter.cc \ $(WIN32_SOURCE) \ wordlist.cc \ Packer.cc \ MemBuf.cc nodist_tests_test_http_range_SOURCES = \ $(BUILT_SOURCES) tests_test_http_range_LDADD = \ $(COMMON_LIBS) \ icmp/libicmp.la icmp/libicmp-core.la \ comm/libcomm-listener.la \ log/liblog.la \ $(REPL_OBJS) \ ${ADAPTATION_LIBS} \ $(ESI_LIBS) \ + $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(REGEXLIB) \ $(SNMPLIB) \ $(SQUID_CPPUNIT_LIBS) \ $(SQUID_CPPUNIT_LA) \ $(SSLLIB) \ $(KRB5LIBS) \ $(COMPAT_LIB) \ $(XTRA_LIBS) tests_test_http_range_LDFLAGS = $(LIBADD_DL) tests_test_http_range_DEPENDENCIES = \ $(SQUID_CPPUNIT_LA) ## Tests of the HttpRequest module. tests_testHttpRequest_SOURCES = \ $(ACL_REGISTRATION_SOURCES) \ HttpRequest.cc \ @@ -1905,41 +1907,40 @@ mem_node.cc \ MemBuf.cc \ MemObject.cc \ mime.cc \ mime_header.cc \ neighbors.cc \ Packer.cc \ Parsing.cc \ pconn.cc \ peer_digest.cc \ peer_proxy_negotiate_auth.cc \ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ redirect.cc \ referer.cc \ refresh.cc \ RemovalPolicy.cc \ Server.cc \ $(SNMP_SOURCE) \ - $(SSL_SOURCE) \ SquidMath.h \ SquidMath.cc \ stat.cc \ StatHist.cc \ stmem.cc \ store.cc \ store_client.cc \ store_digest.cc \ store_dir.cc \ store_io.cc \ store_key_md5.cc \ store_log.cc \ store_rebuild.cc \ store_swapin.cc \ store_swapmeta.cc \ store_swapout.cc \ StoreFileSystem.cc \ StoreIOState.cc \ StoreMeta.cc \ StoreMetaMD5.cc \ @@ -1955,40 +1956,41 @@ SwapDir.cc \ url.cc \ URLScheme.cc \ urn.cc \ useragent.cc \ wccp2.cc \ whois.cc \ FadingCounter.cc \ $(WIN32_SOURCE) \ wordlist.cc nodist_tests_testHttpRequest_SOURCES = \ $(BUILT_SOURCES) tests_testHttpRequest_LDADD = \ $(COMMON_LIBS) \ icmp/libicmp.la icmp/libicmp-core.la \ comm/libcomm-listener.la \ log/liblog.la \ $(REPL_OBJS) \ ${ADAPTATION_LIBS} \ $(ESI_LIBS) \ + $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(REGEXLIB) \ $(SNMPLIB) \ $(SQUID_CPPUNIT_LIBS) \ $(SQUID_CPPUNIT_LA) \ $(SSLLIB) \ $(KRB5LIBS) \ $(COMPAT_LIB) \ $(XTRA_LIBS) tests_testHttpRequest_LDFLAGS = $(LIBADD_DL) tests_testHttpRequest_DEPENDENCIES = \ $(REPL_OBJS) \ $(SQUID_CPPUNIT_LA) # TODO:mime.cc drags in HttpReply.cc # delay pools need client_side_request.cc # store_key_md5 wants the method. STORE_TEST_SOURCES=\ @@ -2322,41 +2324,40 @@ multicast.cc \ mem_node.cc \ MemBuf.cc \ MemObject.cc \ mime.cc \ mime_header.cc \ neighbors.cc \ Packer.cc \ Parsing.cc \ pconn.cc \ peer_digest.cc \ peer_proxy_negotiate_auth.cc \ peer_select.cc \ peer_sourcehash.cc \ peer_userhash.cc \ redirect.cc \ referer.cc \ refresh.cc \ Server.cc \ $(SNMP_SOURCE) \ - $(SSL_SOURCE) \ SquidMath.h \ SquidMath.cc \ stat.cc \ StatHist.cc \ stmem.cc \ store.cc \ store_client.cc \ store_digest.cc \ store_dir.cc \ store_io.cc \ store_key_md5.cc \ store_log.cc \ store_rebuild.cc \ store_swapin.cc \ store_swapmeta.cc \ store_swapout.cc \ StoreFileSystem.cc \ StoreIOState.cc \ StoreMeta.cc \ StoreMetaMD5.cc \ @@ -2371,40 +2372,41 @@ tunnel.cc \ SwapDir.cc \ urn.cc \ useragent.cc \ wccp2.cc \ whois.cc \ FadingCounter.cc \ $(WIN32_SOURCE) \ wordlist.cc nodist_tests_testURL_SOURCES = \ $(BUILT_SOURCES) tests_testURL_LDADD = \ $(COMMON_LIBS) \ icmp/libicmp.la icmp/libicmp-core.la \ comm/libcomm-listener.la \ log/liblog.la \ $(REGEXLIB) \ $(REPL_OBJS) \ ${ADAPTATION_LIBS} \ $(ESI_LIBS) \ + $(SSL_LIBS) \ $(SNMPLIB) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(COMPAT_LIB) \ $(SQUID_CPPUNIT_LIBS) \ $(SQUID_CPPUNIT_LA) \ $(SSLLIB) \ $(KRB5LIBS) \ $(COMPAT_LIB) \ $(XTRA_LIBS) tests_testURL_LDFLAGS = $(LIBADD_DL) tests_testURL_DEPENDENCIES = \ $(REPL_OBJS) \ $(SQUID_CPPUNIT_LA) TESTS += testHeaders ## Special Universal .h dependency test script === modified file 'src/ProtoPort.cc' --- src/ProtoPort.cc 2009-12-31 02:35:01 +0000 +++ src/ProtoPort.cc 2010-11-10 11:33:44 +0000 @@ -1,44 +1,46 @@ /* * $Id$ */ #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); } http_port_list::~http_port_list() { delete listener; safe_free(name); safe_free(defaultsite); safe_free(protocol); #if USE_SSL safe_free(cert); safe_free(key); safe_free(options); safe_free(cipher); safe_free(cafile); safe_free(capath); safe_free(dhfile); safe_free(sslflags); + safe_free(sslContextSessionId); #endif } #if USE_SSL https_port_list::https_port_list(): http_port_list("https") { } #endif === modified file 'src/ProtoPort.h' --- src/ProtoPort.h 2010-04-17 02:29:04 +0000 +++ src/ProtoPort.h 2010-11-10 11:33:14 +0000 @@ -1,30 +1,34 @@ /* * $Id$ */ #ifndef SQUID_PROTO_PORT_H #define SQUID_PROTO_PORT_H //#include "typedefs.h" #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(); http_port_list *next; Ip::Address s; char *protocol; /* protocol name */ char *name; /* visible name */ char *defaultsite; /* default web site */ unsigned int intercepted:1; /**< intercepting proxy port */ unsigned int spoof_client_ip:1; /**< spoof client ip if possible */ unsigned int accel:1; /**< HTTP accelerator */ unsigned int allow_direct:1; /**< Allow direct forwarding in accelerator mode */ unsigned int vhost:1; /**< uses host header */ unsigned int sslBump:1; /**< intercepts CONNECT requests */ unsigned int ignore_cc:1; /**< Ignore request Cache-Control directives */ int vport; /* virtual port support, -1 for dynamic, >0 static*/ @@ -43,37 +47,42 @@ * If not NULL we are actively listening for client requests. * delete to close the socket. */ Comm::ListenStateData *listener; #if USE_SSL // XXX: temporary hack to ease move of SSL options to http_port http_port_list &http; char *cert; char *key; int version; char *cipher; char *options; char *clientca; char *cafile; char *capath; 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); }; #if USE_SSL struct https_port_list: public http_port_list { https_port_list(); }; #endif #endif /* SQUID_PROTO_PORT_H */ === modified file 'src/acl/Certificate.h' --- src/acl/Certificate.h 2009-03-08 21:53:27 +0000 +++ src/acl/Certificate.h 2010-11-09 15:37:15 +0000 @@ -21,41 +21,41 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * * Copyright (c) 2003, Robert Collins */ #ifndef SQUID_ACLCERTIFICATE_H #define SQUID_ACLCERTIFICATE_H #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 class ACLCertificateStrategy : public ACLStrategy { public: virtual int match (ACLData * &, ACLFilledChecklist *); static ACLCertificateStrategy *Instance(); /* Not implemented to prevent copies of the instance. */ /* Not private to prevent brain dead g+++ warnings about * private constructors with no friends */ ACLCertificateStrategy(ACLCertificateStrategy const &); private: static ACLCertificateStrategy Instance_; ACLCertificateStrategy() {} ACLCertificateStrategy&operator=(ACLCertificateStrategy const &); }; === modified file 'src/acl/CertificateData.h' --- src/acl/CertificateData.h 2009-03-31 12:39:30 +0000 +++ src/acl/CertificateData.h 2010-11-09 15:36:56 +0000 @@ -21,41 +21,41 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * * Copyright (c) 2003, Robert Collins */ #ifndef SQUID_ACLCERTIFICATEDATA_H #define SQUID_ACLCERTIFICATEDATA_H #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 class ACLCertificateData : public ACLData { public: MEMPROXY_CLASS(ACLCertificateData); ACLCertificateData(SSLGETATTRIBUTE *); ACLCertificateData(ACLCertificateData const &); ACLCertificateData &operator= (ACLCertificateData const &); virtual ~ACLCertificateData(); bool match(SSL *); wordlist *dump(); void parse(); bool empty() const; virtual ACLData *clone() const; char *attribute; === modified file 'src/acl/SslErrorData.h' --- src/acl/SslErrorData.h 2009-03-08 19:34:36 +0000 +++ src/acl/SslErrorData.h 2010-11-09 15:37:39 +0000 @@ -1,31 +1,31 @@ /* * $Id$ */ #ifndef SQUID_ACLSSL_ERRORDATA_H #define SQUID_ACLSSL_ERRORDATA_H #include "acl/Acl.h" #include "acl/Data.h" #include "CbDataList.h" -#include "ssl_support.h" +#include "ssl/support.h" class ACLSslErrorData : public ACLData { public: MEMPROXY_CLASS(ACLSslErrorData); ACLSslErrorData(); ACLSslErrorData(ACLSslErrorData const &); ACLSslErrorData &operator= (ACLSslErrorData const &); virtual ~ACLSslErrorData(); bool match(ssl_error_t); wordlist *dump(); void parse(); bool empty() const; virtual ACLData *clone() const; CbDataList *values; }; === modified file 'src/base/Makefile.am' --- src/base/Makefile.am 2010-10-07 07:53:45 +0000 +++ src/base/Makefile.am 2010-11-05 13:26:21 +0000 @@ -1,19 +1,20 @@ include $(top_srcdir)/src/Common.am include $(top_srcdir)/src/TestHeaders.am noinst_LTLIBRARIES = libbase.la libbase_la_SOURCES = \ AsyncCall.cc \ AsyncCall.h \ AsyncJob.h \ AsyncJob.cc \ AsyncJobCalls.h \ AsyncCallQueue.cc \ AsyncCallQueue.h \ + TidyPointer.h \ CbcPointer.h \ InstanceId.h \ Subscription.h \ TextException.cc \ TextException.h === added file 'src/base/TidyPointer.h' --- src/base/TidyPointer.h 1970-01-01 00:00:00 +0000 +++ src/base/TidyPointer.h 2010-11-10 11:51:32 +0000 @@ -0,0 +1,67 @@ +/* + * $Id$ + */ + +#ifndef SQUID_BASE_TIDYPOINTER_H +#define SQUID_BASE_TIDYPOINTER_H + +#include "config.h" + +/** + * A pointer that deletes the object it points to when the pointer's owner or + * context is gone. Similar to std::auto_ptr but without confusing assignment + * and with a customizable cleanup method. Prevents memory leaks in + * the presence of exceptions and processing short cuts. +*/ +template 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 const &); + /// Forbidden assigment operator. + TidyPointer & operator = (TidyPointer 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 void tidyFree(T *p) +{ + xfree(p); +} + +#endif // SQUID_BASE_TIDYPOINTER_H === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2010-10-29 00:12:28 +0000 +++ src/cache_cf.cc 2010-11-10 11:48:20 +0000 @@ -65,40 +65,44 @@ #include "Parsing.h" #include "ProtoPort.h" #include "rfc1738.h" #if SQUID_SNMP #include "snmp.h" #endif #include "Store.h" #include "StoreFileSystem.h" #include "SwapDir.h" #include "wordlist.h" #include "ipc/Kids.h" #if HAVE_GLOB_H #include #endif #if HAVE_LIMITS_H #include #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 void parse_adaptation_access_type(); #endif #if ICAP_CLIENT static void parse_icap_service_type(Adaptation::Icap::Config *); static void dump_icap_service_type(StoreEntry *, const char *, const Adaptation::Icap::Config &); static void free_icap_service_type(Adaptation::Icap::Config *); static void parse_icap_class_type(); static void parse_icap_access_type(); static void parse_icap_service_failure_limit(Adaptation::Icap::Config *); static void dump_icap_service_failure_limit(StoreEntry *, const char *, const Adaptation::Icap::Config &); static void free_icap_service_failure_limit(Adaptation::Icap::Config *); #endif #if USE_ECAP static void parse_ecap_service_type(Adaptation::Ecap::Config *); @@ -129,40 +133,43 @@ static void parse_access_log(customlog ** customlog_definitions); static int check_null_access_log(customlog *customlog_definitions); static void dump_logformat(StoreEntry * entry, const char *name, logformat * definitions); static void dump_access_log(StoreEntry * entry, const char *name, customlog * definitions); static void free_logformat(logformat ** definitions); static void free_access_log(customlog ** definitions); static void update_maxobjsize(void); static void configDoConfigure(void); static void parse_refreshpattern(refresh_t **); static int parseTimeUnits(const char *unit); static void parseTimeLine(time_t * tptr, const char *units); static void parse_ushort(u_short * var); static void parse_string(char **); static void default_all(void); static void defaults_if_none(void); static int parse_line(char *); static void parse_obsolete(const char *); static void parseBytesLine(size_t * bptr, const char *units); +#if USE_SSL +static void parseBytesOptionValue(size_t * bptr, const char *units, char const * value); +#endif #if !USE_DNSSERVERS static void parseBytesLineSigned(ssize_t * bptr, const char *units); #endif static size_t parseBytesUnits(const char *unit); static void free_all(void); void requirePathnameExists(const char *name, const char *path); static OBJH dump_config; #if USE_HTTP_VIOLATIONS static void dump_http_header_access(StoreEntry * entry, const char *name, header_mangler header[]); static void parse_http_header_access(header_mangler header[]); static void free_http_header_access(header_mangler header[]); static void dump_http_header_replace(StoreEntry * entry, const char *name, header_mangler header[]); static void parse_http_header_replace(header_mangler * header); static void free_http_header_replace(header_mangler * header); #endif static void parse_denyinfo(acl_deny_info_list ** var); static void dump_denyinfo(StoreEntry * entry, const char *name, acl_deny_info_list * var); static void free_denyinfo(acl_deny_info_list ** var); #if USE_WCCPv2 @@ -858,52 +865,62 @@ peer *p; for (p = Config.peers; p != NULL; p = p->next) { if (p->use_ssl) { debugs(3, 1, "Initializing cache_peer " << p->name << " SSL context"); p->sslContext = sslCreateClientContext(p->sslcert, p->sslkey, p->sslversion, p->sslcipher, p->ssloptions, p->sslflags, p->sslcafile, p->sslcapath, p->sslcrlfile); } } } { http_port_list *s; for (s = Config.Sockaddr.http; s != NULL; s = (http_port_list *) s->next) { if (!s->cert && !s->key) continue; 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); } } { https_port_list *s; 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 } /** Parse a line containing an obsolete directive. * To upgrade it where possible instead of just "Bungled config" for * directives which cannot be marked as simply aliases of the some name. * For example if the parameter order and content has changed. * Or if the directive has been completely removed. */ void parse_obsolete(const char *name) { // Directives which have been radically changed rather than removed if (!strcmp(name, "url_rewrite_concurrency")) { int cval; parse_int(&cval); debugs(3, DBG_CRITICAL, "WARNING: url_rewrite_concurrency upgrade overriding url_rewrite_children settings."); @@ -1096,40 +1113,78 @@ m = u; /* default to 'units' if none specified */ if (0.0 == d) (void) 0; else if ((token = strtok(NULL, w_space)) == NULL) debugs(3, 0, "WARNING: No units on '" << config_input_line << "', assuming " << d << " " << units ); else if ((m = parseBytesUnits(token)) == 0) { self_destruct(); return; } *bptr = static_cast(m * d / u); if (static_cast(*bptr) * 2 != m * d / u * 2) self_destruct(); } #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(m * d / u); + if (static_cast(*bptr) * 2 != m * d / u * 2) + self_destruct(); +} +#endif + static size_t parseBytesUnits(const char *unit) { if (!strncasecmp(unit, B_BYTES_STR, strlen(B_BYTES_STR))) 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; debugs(3, DBG_CRITICAL, "WARNING: Unknown bytes unit '" << unit << "'"); return 0; } @@ -3554,42 +3609,50 @@ s->cipher = xstrdup(token + 7); } else if (strncmp(token, "clientca=", 9) == 0) { safe_free(s->clientca); s->clientca = xstrdup(token + 9); } else if (strncmp(token, "cafile=", 7) == 0) { safe_free(s->cafile); s->cafile = xstrdup(token + 7); } else if (strncmp(token, "capath=", 7) == 0) { safe_free(s->capath); s->capath = xstrdup(token + 7); } else if (strncmp(token, "crlfile=", 8) == 0) { safe_free(s->crlfile); s->crlfile = xstrdup(token + 8); } else if (strncmp(token, "dhparams=", 9) == 0) { safe_free(s->dhfile); s->dhfile = xstrdup(token + 9); } else if (strncmp(token, "sslflags=", 9) == 0) { 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(); } } static http_port_list * create_http_port(char *portspec) { http_port_list *s = new http_port_list("http"); parse_http_port_specification(s, portspec); return s; } void add_http_port(char *portspec) { http_port_list *s = create_http_port(portspec); // we may need to merge better of the above returns a list with clones assert(s->next == NULL); @@ -3621,41 +3684,41 @@ memcpy( &(b->tcp_keepalive), &(a->tcp_keepalive), sizeof(a->tcp_keepalive)); #if 0 // AYJ: 2009-07-18: for now SSL does not clone. Configure separate ports with IPs and SSL settings #if USE_SSL // XXX: temporary hack to ease move of SSL options to http_port http_port_list &http; char *cert; char *key; int version; char *cipher; char *options; char *clientca; char *cafile; char *capath; char *crlfile; char *dhfile; char *sslflags; - char *sslcontext; + char *sslContextSessionId; SSL_CTX *sslContext; #endif #endif /*0*/ return b; } static void parse_http_port_list(http_port_list ** head) { char *token = strtok(NULL, w_space); if (!token) { self_destruct(); return; } http_port_list *s = create_http_port(token); @@ -3745,42 +3808,48 @@ storeAppendPrintf(e, " options=%s", s->options); if (s->cipher) storeAppendPrintf(e, " cipher=%s", s->cipher); if (s->cafile) storeAppendPrintf(e, " cafile=%s", s->cafile); if (s->capath) storeAppendPrintf(e, " capath=%s", s->capath); if (s->crlfile) storeAppendPrintf(e, " crlfile=%s", s->crlfile); if (s->dhfile) storeAppendPrintf(e, " dhparams=%s", s->dhfile); 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::max()) + storeAppendPrintf(e, "dynamic_cert_mem_cache_size=%lu%s\n", (unsigned long)s->dynamicCertMemCacheSize, B_BYTES_STR); #endif } static void dump_http_port_list(StoreEntry * e, const char *n, const http_port_list * s) { while (s) { dump_generic_http_port(e, n, s); storeAppendPrintf(e, "\n"); s = s->next; } } static void free_http_port_list(http_port_list ** head) { http_port_list *s; while ((s = *head) != NULL) { *head = s->next; === modified file 'src/cf.data.pre' --- src/cf.data.pre 2010-10-25 18:25:19 +0000 +++ src/cf.data.pre 2010-11-09 09:16:24 +0000 @@ -1373,40 +1373,58 @@ sslflags= Various flags modifying the use of SSL: DELAYED_AUTH Don't request client certificates immediately, but wait until acl processing requires a certificate (not yet implemented). NO_DEFAULT_CA Don't use the default CA lists built in to OpenSSL. NO_SESSION_REUSE Don't allow for session reuse. Each connection will result in a new SSL session. VERIFY_CRL Verify CRL lists when accepting client certificates. VERIFY_CRL_ALL Verify CRL lists for all certificates in the client certificate chain. sslcontext= SSL session ID context identifier. + 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=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: connection-auth[=on|off] use connection-auth=off to tell Squid to prevent forwarding Microsoft connection oriented authentication (NTLM, Negotiate and Kerberos) disable-pmtu-discovery= Control Path-MTU discovery usage: off lets OS decide on what to do (default). transparent disable PMTU discovery when transparent support is enabled. always disable always PMTU discovery. In many setups of transparently intercepting proxies Path-MTU discovery can not work on traffic towards the clients. This is the case when the intercepting device does not fully track connections and fails to forward ICMP must fragment messages to the cache server. If you @@ -1951,40 +1969,88 @@ NAME: sslpassword_program IFDEF: USE_SSL DEFAULT: none LOC: Config.Program.ssl_password TYPE: string DOC_START Specify a program used for entering SSL key passphrases when using encrypted SSL certificate keys. If not specified keys must either be unencrypted, or Squid started with the -N option to allow it to query interactively for the passphrase. The key file name is given as argument to the program allowing selection of the right password if you have multiple encrypted 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: 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: HelperChildConfig +IFDEF: USE_SSL_CRTD +DEFAULT: 32 startup=5 idle=1 +LOC: Config.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 ----------------------------------------------------------------------------- COMMENT_END NAME: cache_peer TYPE: peer DEFAULT: none LOC: Config.peers DOC_START To specify other caches in a hierarchy, use the format: cache_peer hostname type http-port icp-port [options] For example, # proxy icp # hostname type port port options # -------------------- -------- ----- ----- ----------- cache_peer parent.foo.net parent 3128 3130 default cache_peer sib1.foo.net sibling 3128 3130 proxy-only === modified file 'src/client_side.cc' --- src/client_side.cc 2010-10-26 00:17:17 +0000 +++ src/client_side.cc 2010-11-10 11:48:45 +0000 @@ -98,40 +98,52 @@ #include "eui/Config.h" #include "fde.h" #include "HttpHdrContRange.h" #include "HttpReply.h" #include "HttpRequest.h" #include "ident/Config.h" #include "ident/Ident.h" #include "ip/Intercept.h" #include "ipc/StartListening.h" #include "MemBuf.h" #include "MemObject.h" #include "ProtoPort.h" #include "rfc1738.h" #include "SquidTime.h" #include "Store.h" #if DELAY_POOLS #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 + +#include + #if LINGERING_CLOSE #define comm_close comm_lingering_close #endif /// dials clientHttpConnectionOpened or clientHttpsConnectionOpened call class ListeningStartedDialer: public CallDialer, public Ipc::StartListeningCb { public: typedef void (*Handler)(int fd, int errNo, http_port_list *portCfg); ListeningStartedDialer(Handler aHandler, http_port_list *aPortCfg): handler(aHandler), portCfg(aPortCfg) {} virtual void print(std::ostream &os) const { startPrint(os) << ", port=" << (void*)portCfg << ')'; } virtual bool canDial(AsyncCall &) const { return true; } virtual void dial(AsyncCall &) { (handler)(fd, errNo, portCfg); } @@ -3329,41 +3341,41 @@ debugs(83, 3, "clientNegotiateSSL: FD " << fd << " client certificate: issuer: " << X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0)); X509_free(client_cert); } else { debugs(83, 5, "clientNegotiateSSL: FD " << fd << " has no certificate."); } conn->readSomeData(); } /** handle a new HTTPS connection */ static void httpsAccept(int sock, int newfd, ConnectionDetail *details, comm_err_t flag, int xerrno, void *data) { https_port_list *s = (https_port_list *)data; - SSL_CTX *sslContext = s->sslContext; + SSL_CTX *sslContext = s->staticSslContext.get(); if (flag != COMM_OK) { errno = xerrno; debugs(33, 1, "httpsAccept: FD " << sock << ": accept failure: " << xstrerr(xerrno)); return; } SSL *ssl = NULL; if (!(ssl = httpsCreate(newfd, details, sslContext))) return; debugs(33, 5, "httpsAccept: FD " << newfd << " accepted, starting SSL negotiation."); fd_note(newfd, "client https connect"); ConnStateData *connState = connStateCreate(details->peer, details->me, newfd, &s->http); typedef CommCbMemFunT Dialer; AsyncCall::Pointer call = JobCallback(33, 5, Dialer, connState, ConnStateData::connStateClosed); comm_add_close_handler(newfd, call); @@ -3379,73 +3391,175 @@ if (Ident::TheConfig.identLookup) { ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, NULL); identChecklist.src_addr = details->peer; identChecklist.my_addr = details->me; if (identChecklist.fastCheck()) Ident::Start(details->me, details->peer, clientIdentDone, connState); } #endif if (s->http.tcp_keepalive.enabled) { commSetTcpKeepalive(newfd, s->http.tcp_keepalive.idle, s->http.tcp_keepalive.interval, s->http.tcp_keepalive.timeout); } commSetSelect(newfd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0); clientdbEstablished(details->peer, 1); incoming_sockets_accepted++; } +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 { + 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); +} + bool -ConnStateData::switchToHttps() +ConnStateData::getSslContextStart() { - assert(!switchedToHttps_); + 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"); + } - //HTTPMSGLOCK(currentobject->http->request); - assert(areAllContextsForThisConnection()); - freeAllContexts(); - //currentobject->connIsFinished(); +#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); +} - debugs(33, 5, HERE << "converting FD " << fd << " to SSL"); +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; // commSetTimeout() was called for this request before we switched. // Disable the client read handler until peer selection is complete commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0); 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 */ /// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed static bool OpenedHttpSocket(int fd, const char *msgIfFail) { if (fd < 0) { Must(NHttpSockets > 0); // we tried to open some --NHttpSockets; // there will be fewer sockets than planned Must(HttpSockets[NHttpSockets] < 0); // no extra fds received if (!NHttpSockets) // we could not open any listen sockets at all fatal(msgIfFail); return false; } return true; } /// find any unused HttpSockets[] slot and store fd there or return false @@ -3459,48 +3573,55 @@ } return found; } static void clientHttpConnectionsOpen(void) { http_port_list *s = NULL; #if USE_SSL int bumpCount = 0; // counts http_ports with sslBump option #endif for (s = Config.Sockaddr.http; s; s = s->next) { if (MAXHTTPPORTS == NHttpSockets) { debugs(1, 1, "WARNING: You have too many 'http_port' lines."); debugs(1, 1, " The limit is " << MAXHTTPPORTS); continue; } #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::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 ... */ const int openFlags = COMM_NONBLOCKING | (s->spoof_client_ip ? COMM_TRANSPARENT : 0); AsyncCall::Pointer callback = asyncCall(33,2, "clientHttpConnectionOpened", ListeningStartedDialer(&clientHttpConnectionOpened, s)); Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->s, openFlags, Ipc::fdnHttpSocket, callback); HttpSockets[NHttpSockets++] = -1; // set in clientHttpConnectionOpened } #if USE_SSL if (bumpCount && !Config.accessList.ssl_bump) debugs(33, 1, "WARNING: http_port(s) with SslBump found, but no " << std::endl << "\tssl_bump ACL configured. No requests will be " << "bumped."); @@ -3528,41 +3649,41 @@ (s->accel ? " accelerated" : "") << " HTTP connections at " << s->s << ", FD " << fd << "." ); Must(AddOpenedHttpSocket(fd)); // otherwise, we have received a fd we did not ask for } #if USE_SSL static void clientHttpsConnectionsOpen(void) { https_port_list *s; for (s = Config.Sockaddr.https; s; s = (https_port_list *)s->http.next) { if (MAXHTTPPORTS == NHttpSockets) { debugs(1, 1, "Ignoring 'https_port' lines exceeding the limit."); debugs(1, 1, "The limit is " << MAXHTTPPORTS << " HTTPS ports."); continue; } - if (s->sslContext == NULL) { + if (!s->staticSslContext) { debugs(1, 1, "Ignoring https_port " << s->http.s << " due to SSL initialization failure."); continue; } AsyncCall::Pointer call = asyncCall(33, 2, "clientHttpsConnectionOpened", ListeningStartedDialer(&clientHttpsConnectionOpened, &s->http)); Ipc::StartListening(SOCK_STREAM, IPPROTO_TCP, s->http.s, COMM_NONBLOCKING, Ipc::fdnHttpsSocket, call); HttpSockets[NHttpSockets++] = -1; } } /// process clientHttpsConnectionsOpen result static void clientHttpsConnectionOpened(int fd, int, http_port_list *s) { if (!OpenedHttpSocket(fd, "Cannot open HTTPS Port")) === modified file 'src/client_side.h' --- src/client_side.h 2010-09-10 20:56:24 +0000 +++ src/client_side.h 2010-11-09 22:29:49 +0000 @@ -258,56 +258,70 @@ int validatePinnedConnection(HttpRequest *request, const struct peer *peer); /** * returts the pinned peer if exists, NULL otherwise */ struct peer *pinnedPeer() const {return pinning.peer;} bool pinnedAuth() const {return pinning.auth;} // pining related comm callbacks void clientPinnedConnectionClosed(const CommCloseCbParams &io); // comm callbacks void clientReadRequest(const CommIoCbParams &io); void connStateClosed(const CommCloseCbParams &io); void requestTimeout(const CommTimeoutCbParams ¶ms); // AsyncJob API virtual bool doneAll() const { return BodyProducer::doneAll() && 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; } #endif protected: void startDechunkingRequest(); void finishDechunkingRequest(bool withSuccess); void abortChunkedRequestBody(const err_type error); err_type handleChunkedRequestBody(size_t &putSize); private: int connReadWasError(comm_err_t flag, int size, int xerrno); int connFinishedWithConn(int size); void clientMaybeReadData(int do_next_read); void clientAfterReadingRequests(int do_next_read); private: CBDATA_CLASS2(ConnStateData); bool transparent_; 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 }; /* convenience class while splitting up body handling */ /* temporary existence only - on stack use expected */ void setLogUri(ClientHttpRequest * http, char const *uri); const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end = NULL); #endif /* SQUID_CLIENTSIDE_H */ === modified file 'src/client_side_request.cc' --- src/client_side_request.cc 2010-10-21 08:13:41 +0000 +++ src/client_side_request.cc 2010-11-09 22:29:49 +0000 @@ -1172,41 +1172,41 @@ { ClientHttpRequest *r = static_cast(data); debugs(85, 5, HERE << "responded to CONNECT: " << r << " ? " << errflag); assert(r && cbdataReferenceValid(r)); r->sslBumpEstablish(errflag); } void ClientHttpRequest::sslBumpEstablish(comm_err_t errflag) { // Bail out quickly on COMM_ERR_CLOSING - close handlers will tidy up if (errflag == COMM_ERR_CLOSING) return; if (errflag) { getConn()->startClosing("CONNECT response failure in SslBump"); return; } - getConn()->switchToHttps(); + getConn()->switchToHttps(request->GetHost()); } void ClientHttpRequest::sslBumpStart() { debugs(85, 5, HERE << "ClientHttpRequest::sslBumpStart"); // send an HTTP 200 response to kick client SSL negotiation const int fd = getConn()->fd; debugs(33, 7, HERE << "Confirming CONNECT tunnel on FD " << fd); // TODO: Unify with tunnel.cc and add a Server(?) header static const char *const conn_established = "HTTP/1.0 200 Connection established\r\n\r\n"; comm_write(fd, conn_established, strlen(conn_established), &SslBumpEstablish, this, NULL); } #endif === modified file 'src/comm.cc' --- src/comm.cc 2010-11-03 16:28:34 +0000 +++ src/comm.cc 2010-11-09 22:29:49 +0000 @@ -1523,40 +1523,44 @@ #if USE_SSL fde *F = &fd_table[fd]; if (F->ssl) ssl_shutdown_method(fd); #endif } void comm_close_complete(int fd, void *data) { #if USE_SSL fde *F = &fd_table[fd]; if (F->ssl) { SSL_free(F->ssl); F->ssl = NULL; } + if (F->dynamicSslContext) { + SSL_CTX_free(F->dynamicSslContext); + F->dynamicSslContext = NULL; + } #endif fd_close(fd); /* update fdstat */ close(fd); statCounter.syscalls.sock.closes++; /* When an fd closes, give accept() a chance, if need be */ Comm::AcceptLimiter::Instance().kick(); } /* * Close the socket fd. * * + call write handlers with ERR_CLOSING * + call read handlers with ERR_CLOSING * + call closing handlers * * NOTE: COMM_ERR_CLOSING will NOT be called for CommReads' sitting in a * DeferredReadManager. === modified file 'src/fde.h' --- src/fde.h 2010-10-25 21:49:58 +0000 +++ src/fde.h 2010-11-09 22:29:49 +0000 @@ -96,40 +96,41 @@ ClientInfo * clientInfo;/* pointer to client info used in client write limiter or NULL if not present */ #endif unsigned epoll_state; struct _fde_disk disk; PF *read_handler; void *read_data; PF *write_handler; void *write_data; AsyncCall::Pointer timeoutHandler; time_t timeout; time_t writeStart; void *lifetime_data; AsyncCall::Pointer closeHandler; AsyncCall::Pointer halfClosedReader; /// read handler for half-closed fds CommWriteStateData *wstate; /* State data for comm_write */ READ_HANDLER *read_method; 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 { long handle; } win32; #endif tos_t tosFromServer; /**< Stores the TOS flags of the packets from the remote server. See FwdState::dispatch(). Note that this differs to tosToServer in that this is the value we *receive* from the, connection, whereas tosToServer is the value to set on packets *leaving* Squid. */ unsigned int nfmarkFromServer; /**< Stores the Netfilter mark value of the connection from the remote server. See FwdState::dispatch(). Note that this differs to nfmarkToServer in that this is the value we *receive* from the, connection, whereas nfmarkToServer is the value to set on packets *leaving* Squid. */ private: /** Clear the fde class back to NULL equivalent. */ inline void clear() { @@ -149,35 +150,36 @@ #if DELAY_POOLS clientInfo = NULL; #endif epoll_state = 0; memset(&disk, 0, sizeof(_fde_disk)); read_handler = NULL; read_data = NULL; write_handler = NULL; write_data = NULL; timeoutHandler = NULL; timeout = 0; writeStart = 0; lifetime_data = NULL; closeHandler = NULL; halfClosedReader = NULL; wstate = NULL; read_method = NULL; write_method = NULL; #if USE_SSL ssl = NULL; + dynamicSslContext = NULL; #endif #ifdef _SQUID_MSWIN_ win32.handle = NULL; #endif tosFromServer = '\0'; nfmarkFromServer = 0; } }; SQUIDCEXTERN int fdNFree(void); #define FD_READ_METHOD(fd, buf, len) (*fd_table[fd].read_method)(fd, buf, len) #define FD_WRITE_METHOD(fd, buf, len) (*fd_table[fd].write_method)(fd, buf, len) #endif /* SQUID_FDE_H */ === modified file 'src/helper.cc' --- src/helper.cc 2010-09-20 19:27:24 +0000 +++ src/helper.cc 2010-11-09 22:29:49 +0000 @@ -775,40 +775,89 @@ if ((r = srv->request)) { void *cbdata; if (cbdataReferenceValidDone(r->data, &cbdata)) r->callback(cbdata, srv, NULL); helperStatefulRequestFree(r); srv->request = NULL; } if (srv->data != NULL) hlp->datapool->freeOne(srv->data); cbdataReferenceDone(srv->parent); 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) { char *t = NULL; helper_server *srv = (helper_server *)data; helper *hlp = srv->parent; assert(cbdataReferenceValid(data)); /* Bail out early on COMM_ERR_CLOSING - close handlers will tidy up for us */ if (flag == COMM_ERR_CLOSING) { return; } assert(fd == srv->rfd); debugs(84, 5, "helperHandleRead: " << len << " bytes from " << hlp->id_name << " #" << srv->index + 1); if (flag != COMM_OK || len <= 0) { @@ -817,103 +866,63 @@ comm_close(fd); return; } srv->roffset += len; srv->rbuf[srv->roffset] = '\0'; debugs(84, 9, "helperHandleRead: '" << srv->rbuf << "'"); if (!srv->stats.pending) { /* someone spoke without being spoken to */ debugs(84, 1, "helperHandleRead: unexpected read from " << hlp->id_name << " #" << srv->index + 1 << ", " << (int)len << " bytes '" << srv->rbuf << "'"); srv->roffset = 0; 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; - - 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 " << - i << " from " << hlp->id_name << " #" << srv->index + 1 << - " '" << srv->rbuf << "'"); - - } - - srv->roffset -= (t - srv->rbuf); - memmove(srv->rbuf, t, srv->roffset + 1); + 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"); + + 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++; + } - 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); } } if (srv->rfd != -1) comm_read(fd, srv->rbuf + srv->roffset, srv->rbuf_sz - srv->roffset - 1, helperHandleRead, srv); } static void helperStatefulHandleRead(int fd, char *buf, size_t len, comm_err_t flag, int xerrno, void *data) { char *t = NULL; helper_stateful_server *srv = (helper_stateful_server *)data; helper_stateful_request *r; statefulhelper *hlp = srv->parent; assert(cbdataReferenceValid(data)); /* Bail out early on COMM_ERR_CLOSING - close handlers will tidy up for us */ if (flag == COMM_ERR_CLOSING) { return; === modified file 'src/helper.h' --- src/helper.h 2010-05-02 19:32:42 +0000 +++ src/helper.h 2010-11-09 22:29:49 +0000 @@ -52,40 +52,42 @@ inline helper(const char *name) : cmdline(NULL), id_name(name) {}; ~helper(); public: wordlist *cmdline; dlink_list servers; dlink_list queue; const char *id_name; HelperChildConfig childs; ///< Configuration settings for number running. int ipc_type; Ip::Address addr; time_t last_queue_warn; time_t last_restart; struct _stats { int requests; int replies; 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); }; class statefulhelper : public helper { public: inline statefulhelper(const char *name) : helper(name) {}; inline ~statefulhelper() {}; public: MemAllocator *datapool; HLPSAVAIL *IsAvailable; HLPSONEQ *OnEmptyQueue; private: CBDATA_CLASS2(statefulhelper); }; === modified file 'src/main.cc' --- src/main.cc 2010-11-01 05:44:28 +0000 +++ src/main.cc 2010-11-09 22:29:49 +0000 @@ -71,40 +71,49 @@ #include "comm_poll.h" #endif #if defined(USE_SELECT) || defined(USE_SELECT_WIN32) #include "comm_select.h" #endif #include "SquidTime.h" #include "SwapDir.h" #include "forward.h" #include "MemPool.h" #include "icmp/IcmpSquid.h" #include "icmp/net_db.h" #if DELAY_POOLS #include "ClientDelayConfig.h" #endif #if USE_LOADABLE_MODULES #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 #if USE_ECAP #include "adaptation/ecap/Config.h" #endif #if USE_ADAPTATION #include "adaptation/Config.h" #endif #if USE_SQUID_ESI #include "esi/Module.h" #endif #include "fs/Module.h" #if HAVE_PATHS_H #include #endif #if USE_WIN32_SERVICE #include "squid_windows.h" @@ -716,41 +725,46 @@ reconfiguring = 1; // Initiate asynchronous closing sequence serverConnectionsClose(); icpConnectionClose(); #if USE_HTCP htcpSocketClose(); #endif #if SQUID_SNMP snmpConnectionClose(); #endif #if USE_DNSSERVERS dnsShutdown(); #else idnsShutdown(); #endif - +#if USE_SSL_CRTD + Ssl::Helper::GetInstance()->Shutdown(); +#endif +#if USE_SSL + Ssl::TheGlobalContextStorage.reconfigureStart(); +#endif redirectShutdown(); authenticateReset(); externalAclShutdown(); storeDirCloseSwapLogs(); storeLogClose(); accessLogClose(); #if ICAP_CLIENT icapLogClose(); #endif useragentLogClose(); refererCloseLog(); eventAdd("mainReconfigureFinish", &mainReconfigureFinish, NULL, 0, 1, false); } static void mainReconfigureFinish(void *) { debugs(1, 3, "finishing reconfiguring"); @@ -802,40 +816,43 @@ #if USE_ECAP Adaptation::Ecap::TheConfig.finalize(); // must be after we load modules enableAdaptation = Adaptation::Ecap::TheConfig.onoff || enableAdaptation; #endif Adaptation::Config::Finalize(enableAdaptation); #endif #if ICAP_CLIENT icapLogOpen(); #endif storeLogOpen(); useragentOpenLog(); refererOpenLog(); #if USE_DNSSERVERS dnsInit(); #else idnsInit(); #endif +#if USE_SSL_CRTD + Ssl::Helper::GetInstance()->Init(); +#endif redirectInit(); authenticateInit(&Auth::TheConfig); externalAclInit(); if (IamPrimaryProcess()) { #if USE_WCCP wccpInit(); #endif #if USE_WCCPv2 wccp2Init(); #endif } serverConnectionsOpen(); neighbors_init(); @@ -1795,41 +1812,43 @@ SquidShutdown() { /* XXX: This function is called after the main loop has quit, which * means that no AsyncCalls would be called, including close handlers. * TODO: We need to close/shut/free everything that needs calls before * exiting the loop. */ #if USE_WIN32_SERVICE WIN32_svcstatusupdate(SERVICE_STOP_PENDING, 10000); #endif debugs(1, 1, "Shutting down..."); #if USE_DNSSERVERS dnsShutdown(); #else idnsShutdown(); #endif - +#if USE_SSL_CRTD + Ssl::Helper::GetInstance()->Shutdown(); +#endif redirectShutdown(); externalAclShutdown(); icpConnectionClose(); #if USE_HTCP htcpSocketClose(); #endif #if SQUID_SNMP snmpConnectionClose(); #endif #if USE_WCCP wccpConnectionClose(); #endif #if USE_WCCPv2 wccp2ConnectionClose(); #endif === modified file 'src/squid.h' --- src/squid.h 2010-11-01 05:44:28 +0000 +++ src/squid.h 2010-11-09 22:29:49 +0000 @@ -138,41 +138,41 @@ #ifndef MAXPATHLEN #define MAXPATHLEN SQUID_MAXPATHLEN #endif #if LEAK_CHECK_MODE #define LOCAL_ARRAY(type,name,size) \ static type *local_##name=NULL; \ type *name = local_##name ? local_##name : \ ( local_##name = (type *)xcalloc(size, sizeof(type)) ) #else #define LOCAL_ARRAY(type,name,size) static type name[size] #endif #if defined(_SQUID_NEXT_) && !defined(S_ISDIR) #define S_ISDIR(mode) (((mode) & (_S_IFMT)) == (_S_IFDIR)) #endif #include "md5.h" #if USE_SSL -#include "ssl_support.h" +#include "ssl/support.h" #endif #if SQUID_SNMP #include "cache_snmp.h" #endif #include "hash.h" #include "rfc3596.h" #include "defines.h" #include "enums.h" #include "typedefs.h" #include "util.h" #include "profiler/Profiler.h" #include "MemPool.h" #include "ip/Address.h" #include "structs.h" #include "protos.h" #include "globals.h" /* * I'm sick of having to keep doing this .. */ === added directory 'src/ssl' === added file 'src/ssl/Makefile.am' --- src/ssl/Makefile.am 1970-01-01 00:00:00 +0000 +++ src/ssl/Makefile.am 2010-11-10 14:36:16 +0000 @@ -0,0 +1,37 @@ +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 + +libsslutil_la_SOURCES = \ + support.cc \ + support.h \ + gadgets.cc \ + gadgets.h \ + crtd_message.cc \ + crtd_message.h \ + $(SSL_CRTD_SOURCE) + +libexec_PROGRAMS = \ + $(SSL_CRTD) + +ssl_crtd_SOURCES = ssl_crtd.cc certificate_db.cc certificate_db.h + +ssl_crtd_LDADD = @SSLLIB@ -lsslutil -L$(top_builddir)/compat -lcompat-squid === 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-11-10 12:12:20 +0000 @@ -0,0 +1,477 @@ +/* + * $Id$ + */ + +#include "config.h" +#include "ssl/certificate_db.h" +#include +#include +#include +#include +#include +#include + +Ssl::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 +} + +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(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 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 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(tm->data), tm->length).c_str()); + row.setValue(cnlFile, "unknown"); + { + TidyPointer 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(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(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() +{ + 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() +{ + 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() +{ + 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(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; +} === 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-11-10 12:20:42 +0000 @@ -0,0 +1,137 @@ +/* + * $Id$ + */ + +#ifndef SQUID_SSL_CERTIFICATE_DB_H +#define SQUID_SSL_CERTIFICATE_DB_H + +#include "ssl/gadgets.h" +#include "ssl/support.h" +#include + +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(); ///< 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(); ///< 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(); ///< 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 === 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-11-10 11:45:33 +0000 @@ -0,0 +1,180 @@ +/* + * $Id$ + */ + +#include "Store.h" +#include "StoreEntryStream.h" +#include "ssl/context_storage.h" +#include "mgr/Registration.h" +#include + +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::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::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(address, size_of_store)); +} + +Ssl::LocalContextStorage & Ssl::GlobalContextStorage::getLocalStorage(Ip::Address const & address) +{ + reconfigureFinish(); + std::map::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::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 LocalContextStorage(conf_i->second))); + } + } + } +} + +Ssl::GlobalContextStorage Ssl::TheGlobalContextStorage; === 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-11-10 15:02:04 +0000 @@ -0,0 +1,117 @@ +/* + * $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" +#include +#include +#include + +/// 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 Queue; + typedef Queue::iterator QueueIterator; + + /// host_name:queue_item mapping for fast lookups by host name + typedef std::map Map; + typedef Map::iterator MapIterator; + typedef std::pair 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 configureStorage; + /// Map for storing all local ip address and their local storages. + std::map storage; +}; + +/// Global cache for store all ssl server certificates. +extern GlobalContextStorage TheGlobalContextStorage; +} //namespace Ssl +#endif // USE_SSL + +#endif // SQUID_SSL_CONTEXT_STORAGE_H === 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-11-10 11:45:33 +0000 @@ -0,0 +1,173 @@ +/* + * $Id$ + */ + +#include "config.h" +#include "ssl/crtd_message.h" +#include +#include + +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(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 & 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(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"); === 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-11-10 11:45:33 +0000 @@ -0,0 +1,81 @@ +/* + * $Id$ + */ + +#ifndef SQUID_SSL_CRTD_MESSAGE_H +#define SQUID_SSL_CRTD_MESSAGE_H + +#include +#include + +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: + * + */ +class CrtdMessage +{ +public: + typedef std::map BodyParams; + /// Parse result codes. + enum ParseResult { + OK, + INCOMPLETE, + ERROR + }; + CrtdMessage(); + /**\brief Parse buffer of length len + * \return OK if parsing completes, INCOMPLETE if more data required + * and 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 === added file 'src/ssl/gadgets.cc' --- src/ssl/gadgets.cc 1970-01-01 00:00:00 +0000 +++ src/ssl/gadgets.cc 2010-11-10 11:45:33 +0000 @@ -0,0 +1,272 @@ +/* + * $Id$ + */ + +#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(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); +} === added file 'src/ssl/gadgets.h' --- src/ssl/gadgets.h 1970-01-01 00:00:00 +0000 +++ src/ssl/gadgets.h 2010-11-10 11:45:33 +0000 @@ -0,0 +1,111 @@ +/* + * 2009/01/17 + */ + +#ifndef SQUID_SSL_GADGETS_H +#define SQUID_SSL_GADGETS_H + +#include "config.h" +#include "base/TidyPointer.h" + +#if HAVE_OPENSSL_SSL_H +#include +#include +#endif + +#include + +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_Pointer; +typedef TidyPointer EVP_PKEY_Pointer; +typedef TidyPointer BIGNUM_Pointer; +typedef TidyPointer BIO_Pointer; +typedef TidyPointer ASN1_INT_Pointer; +typedef TidyPointer TXT_DB_Pointer; +typedef TidyPointer X509_NAME_Pointer; +typedef TidyPointer RSA_Pointer; +typedef TidyPointer X509_REQ_Pointer; +typedef TidyPointer SSL_CTX_Pointer; +typedef TidyPointer 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 === added file 'src/ssl/helper.cc' --- src/ssl/helper.cc 1970-01-01 00:00:00 +0000 +++ src/ssl/helper.cc 2010-11-10 11:45:33 +0000 @@ -0,0 +1,90 @@ +/* + * 2008/11/14 + */ + +#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 = 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->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); +} === added file 'src/ssl/helper.h' --- src/ssl/helper.h 1970-01-01 00:00:00 +0000 +++ src/ssl/helper.h 2010-11-10 11:45:33 +0000 @@ -0,0 +1,34 @@ +/* + * $Id$ + */ + +#ifndef SQUID_SSL_HELPER_H +#define SQUID_SSL_HELPER_H + +#include "../helper.h" +#include "ssl/crtd_message.h" + +namespace Ssl +{ +/** + * Set of thread for ssl_crtd. This class is singleton. Use this class only + * over GetIntance() static method. This class use helper structure + * for threads management. + */ +class Helper +{ +public: + static Helper * GetInstance(); ///< Instance class. + void Init(); ///< Init helper structure. + void Shutdown(); ///< Shutdown helper structure. + /// Submit ssl message to external ssl 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 === added file 'src/ssl/ssl_crtd.cc' --- src/ssl/ssl_crtd.cc 1970-01-01 00:00:00 +0000 +++ src/ssl/ssl_crtd.cc 2010-11-10 12:12:13 +0000 @@ -0,0 +1,338 @@ +/* + * $Id$ + */ + +#include "config.h" +#include "ssl/gadgets.h" +#include "ssl/crtd_message.h" +#include "ssl/certificate_db.h" + +#include +#include +#include +#include +#include +#if HAVE_GETOPT_H +#include +#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: + + 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(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 = 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" + "\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, "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; + 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[512]; + size_t const REQ_SZ = sizeof(request); + Ssl::CrtdMessage request_message; + Ssl::CrtdMessage::ParseResult parse_result = Ssl::CrtdMessage::INCOMPLETE; + + while (parse_result == Ssl::CrtdMessage::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 == 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; +} === renamed file 'src/ssl_support.cc' => 'src/ssl/support.cc' --- src/ssl_support.cc 2010-03-31 15:59:21 +0000 +++ src/ssl/support.cc 2010-11-10 11:45:33 +0000 @@ -25,40 +25,41 @@ * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "squid.h" /* MS Visual Studio Projects are monolithic, so we need the following * #if to exclude the SSL code from compile process when not needed. */ #if USE_SSL #include "fde.h" #include "acl/FilledChecklist.h" +#include "ssl/gadgets.h" /** \defgroup ServerProtocolSSLInternal Server-Side SSL Internals \ingroup ServerProtocolSSLAPI */ /// \ingroup ServerProtocolSSLInternal static int ssl_ask_password_cb(char *buf, int size, int rwflag, void *userdata) { FILE *in; int len = 0; char cmdline[1024]; snprintf(cmdline, sizeof(cmdline), "\"%s\" \"%s\"", Config.Program.ssl_password, (const char *)userdata); in = popen(cmdline, "r"); if (fgets(buf, size, in)) len = strlen(buf); @@ -1189,21 +1190,75 @@ return sslGetUserCertificatePEM(ssl); mem = BIO_new(BIO_s_mem()); for (i = 0; i < sk_X509_num(chain); i++) { X509 *cert = sk_X509_value(chain, i); PEM_write_bio_X509(mem, cert); } len = BIO_get_mem_data(mem, &ptr); str = (char *)xmalloc(len + 1); memcpy(str, ptr, len); str[len] = '\0'; BIO_free(mem); 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 */ === renamed file 'src/ssl_support.h' => 'src/ssl/support.h' --- src/ssl_support.h 2010-02-03 12:36:21 +0000 +++ src/ssl/support.h 2010-11-10 11:45:33 +0000 @@ -19,40 +19,42 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #ifndef SQUID_SSL_SUPPORT_H #define SQUID_SSL_SUPPORT_H #include "config.h" +#include "ssl/gadgets.h" + #if HAVE_OPENSSL_SSL_H #include #endif #if HAVE_OPENSSL_X509V3_H #include #endif #if HAVE_OPENSSL_ERR_H #include #endif #if HAVE_OPENSSL_ENGINE_H #include #endif /** \defgroup ServerProtocolSSLAPI Server-Side SSL API \ingroup ServerProtocol */ /// \ingroup ServerProtocolSSLAPI SSL_CTX *sslCreateServerContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options, const char *flags, const char *clientCA, const char *CAfile, const char *CApath, const char *CRLfile, const char *dhpath, const char *context); @@ -75,40 +77,64 @@ /// \ingroup ServerProtocolSSLAPI typedef char const *SSLGETATTRIBUTE(SSL *, const char *); /// \ingroup ServerProtocolSSLAPI SSLGETATTRIBUTE sslGetUserAttribute; /// \ingroup ServerProtocolSSLAPI SSLGETATTRIBUTE sslGetCAAttribute; /// \ingroup ServerProtocolSSLAPI const char *sslGetUserCertificatePEM(SSL *ssl); /// \ingroup ServerProtocolSSLAPI const char *sslGetUserCertificateChainPEM(SSL *ssl); typedef int ssl_error_t; ssl_error_t sslParseErrorString(const char *name); const char *sslFindErrorString(ssl_error_t value); +namespace Ssl +{ +/** + \ingroup ServerProtocolSSLAPI + * Decide on the kind of certificate and generate a CA- or self-signed one +*/ +SSL_CTX *generateSslContext(char const *host, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey); + +/** + \ingroup ServerProtocolSSLAPI + * Check date of certificate signature. If there is out of date error fucntion + * returns false, true otherwise. + */ +bool verifySslCertificateDate(SSL_CTX * sslContext); + +/** + \ingroup ServerProtocolSSLAPI + * Read private key and certificate from memory and generate ssl context + * using their. + */ +SSL_CTX * generateSslContextUsingPkeyAndCertFromMemory(const char * data); + +} //namespace Ssl + // Custom SSL errors; assumes all official errors are positive #define SQUID_X509_V_ERR_DOMAIN_MISMATCH -1 // All SSL errors range: from smallest (negative) custom to largest SSL error #define SQUID_SSL_ERROR_MIN SQUID_X509_V_ERR_DOMAIN_MISMATCH #define SQUID_SSL_ERROR_MAX INT_MAX #ifdef _SQUID_MSWIN_ #ifdef __cplusplus /** \cond AUTODOCS-IGNORE */ namespace Squid { /** \endcond */ /// \ingroup ServerProtocolSSLAPI inline int SSL_set_fd(SSL *ssl, int fd) { return ::SSL_set_fd(ssl, _get_osfhandle(fd)); === modified file 'src/structs.h' --- src/structs.h 2010-10-28 18:52:59 +0000 +++ src/structs.h 2010-11-09 22:29:49 +0000 @@ -287,45 +287,53 @@ char *visible_appname_string; char *effectiveGroup; struct { #if USE_DNSSERVERS char *dnsserver; #endif wordlist *redirect; #if USE_UNLINKD char *unlinkd; #endif char *diskd; #if USE_SSL char *ssl_password; #endif +#if USE_SSL_CRTD + char *ssl_crtd; ///< Name of external ssl_crtd application. +#endif + } Program; #if USE_DNSSERVERS HelperChildConfig dnsChildren; #endif +#if USE_SSL_CRTD + /// The number of processes spawn for ssl_crtd. + HelperChildConfig ssl_crtdChildren; +#endif HelperChildConfig redirectChildren; time_t authenticateGCInterval; time_t authenticateTTL; time_t authenticateIpTTL; struct { char *surrogate_id; } Accel; char *appendDomain; size_t appendDomainLen; char *pidFilename; char *netdbFilename; char *mimeTablePathname; char *etcHostsPath; char *visibleHostname; char *uniqueHostname; wordlist *hostnameAliases; char *errHtmlText;