diff options
author | Ralf S. Engelschall <rse@apache.org> | 2001-05-04 21:54:25 +0000 |
---|---|---|
committer | Ralf S. Engelschall <rse@apache.org> | 2001-05-04 21:54:25 +0000 |
commit | f83672781de25207442ff908258219de1d103062 (patch) | |
tree | ba5717fc9753ff787e05f28007101951087c86eb | |
parent | 498c91aa40551ed31ae68b9f0655f0b3ce4a41de (diff) | |
download | httpd-f83672781de25207442ff908258219de1d103062.tar.gz |
Initial revision
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@88988 13f79535-47bb-0310-9956-ffa450edef68
42 files changed, 23834 insertions, 0 deletions
diff --git a/modules/ssl/Makefile.libdir b/modules/ssl/Makefile.libdir new file mode 100644 index 0000000000..a4a4c32080 --- /dev/null +++ b/modules/ssl/Makefile.libdir @@ -0,0 +1,15 @@ +## _ _ +## _ __ ___ ___ __| | ___ ___| | mod_ssl +## | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +## | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +## |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +## |_____| +## Makefile.libdir +## Apache 1.3 Configuration mechanism indicator file +## + +This is a place-holder which indicates to Apache's Configure script that it +shouldn't provide the default targets when building the Makefile in this +directory. Instead it'll just prepend all the important variable definitions, +and copy the Makefile.tmpl onto the end. + diff --git a/modules/ssl/Makefile.tmpl b/modules/ssl/Makefile.tmpl new file mode 100644 index 0000000000..38162731d7 --- /dev/null +++ b/modules/ssl/Makefile.tmpl @@ -0,0 +1,529 @@ +## _ _ +## _ __ ___ ___ __| | ___ ___| | mod_ssl +## | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +## | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +## |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +## |_____| +## Makefile.tmpl +## Apache 1.3 Makefile template for SSL module (Unix environment) +## + +## ==================================================================== +## Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## +## 1. Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## +## 2. Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following +## disclaimer in the documentation and/or other materials +## provided with the distribution. +## +## 3. All advertising materials mentioning features or use of this +## software must display the following acknowledgment: +## "This product includes software developed by +## Ralf S. Engelschall <rse@engelschall.com> for use in the +## mod_ssl project (http://www.modssl.org/)." +## +## 4. The names "mod_ssl" must not be used to endorse or promote +## products derived from this software without prior written +## permission. For written permission, please contact +## rse@engelschall.com. +## +## 5. Products derived from this software may not be called "mod_ssl" +## nor may "mod_ssl" appear in their names without prior +## written permission of Ralf S. Engelschall. +## +## 6. Redistributions of any form whatsoever must retain the following +## acknowledgment: +## "This product includes software developed by +## Ralf S. Engelschall <rse@engelschall.com> for use in the +## mod_ssl project (http://www.modssl.org/)." +## +## THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY +## EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR +## HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +## STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +## OF THE POSSIBILITY OF SUCH DAMAGE. +## ==================================================================== +## + # + # ``I cannot write a program which is + # as popular as one from Larry Wall. + # But I can write one which is from me.'' + # -- RSE + +LIB=libssl.$(LIBEXT) + +OBJS=\ + mod_ssl.o\ + ssl_engine_config.o\ + ssl_engine_compat.o\ + ssl_engine_ds.o\ + ssl_engine_dh.o\ + ssl_engine_init.o\ + ssl_engine_kernel.o\ + ssl_engine_rand.o\ + ssl_engine_io.o\ + ssl_engine_log.o\ + ssl_engine_mutex.o\ + ssl_engine_pphrase.o\ + ssl_engine_vars.o\ + ssl_engine_ext.o\ + ssl_scache.o\ + ssl_scache_dbm.o\ + ssl_scache_shmht.o\ + ssl_scache_shmcb.o\ + ssl_expr.o\ + ssl_expr_scan.o\ + ssl_expr_parse.o\ + ssl_expr_eval.o\ + ssl_util.o\ + ssl_util_ssl.o\ + ssl_util_sdbm.o\ + ssl_util_table.o\ + $(SSL_VENDOR_OBJS) + +OBJS_PIC=\ + mod_ssl.lo\ + ssl_engine_config.lo\ + ssl_engine_compat.lo\ + ssl_engine_ds.lo\ + ssl_engine_dh.lo\ + ssl_engine_init.lo\ + ssl_engine_kernel.lo\ + ssl_engine_rand.lo\ + ssl_engine_io.lo\ + ssl_engine_log.lo\ + ssl_engine_mutex.lo\ + ssl_engine_pphrase.lo\ + ssl_engine_vars.lo\ + ssl_engine_ext.lo\ + ssl_scache.lo\ + ssl_scache_dbm.lo\ + ssl_scache_shmht.lo\ + ssl_scache_shmcb.lo\ + ssl_expr.lo\ + ssl_expr_scan.lo\ + ssl_expr_parse.lo\ + ssl_expr_eval.lo\ + ssl_util.lo\ + ssl_util_ssl.lo\ + ssl_util_sdbm.lo\ + ssl_util_table.lo\ + $(SSL_VENDOR_OBJS_PIC) + +## +## END-USER AREA +## + +all: lib + +lib: $(LIB) + +libssl.a: $(OBJS) + rm -f $@ + ar cr $@ $(OBJS) + $(RANLIB) $@ + +libssl.so: $(OBJS_PIC) + rm -f $@ + $(LD_SHLIB) $(SSL_LDFLAGS) $(LDFLAGS_SHLIB) -o $@ $(OBJS_PIC) $(SSL_LIBS) $(LIBS_SHLIB) + +.SUFFIXES: .o .lo + +.c.o: + $(CC) -c $(INCLUDES) $(CFLAGS) $(SSL_CFLAGS) $(SSL_VERSION) $< + +.c.lo: + $(CC) -c $(INCLUDES) $(CFLAGS) $(CFLAGS_SHLIB) $(SSL_CFLAGS) $(SSL_VERSION) $< && mv $*.o $*.lo + +clean: + rm -f $(OBJS) $(OBJS_PIC) + rm -f libssl.a libssl.so + +realclean: clean + rm -f ssl_expr_parse.c ssl_expr_parse.h + rm -f ssl_expr_scan.c + +distclean: clean + -rm -f Makefile + +## +## DEVELOPER AREA +## We really don't expect end users to use these targets! +## + +ssl_expr_scan.c: ssl_expr_scan.l ssl_expr_parse.h + flex -Pssl_expr_yy -s -B ssl_expr_scan.l + sed -e '/$$Header:/d' <lex.ssl_expr_yy.c >ssl_expr_scan.c && rm -f lex.ssl_expr_yy.c + +ssl_expr_parse.c ssl_expr_parse.h: ssl_expr_parse.y + yacc -d ssl_expr_parse.y + sed -e 's;yy;ssl_expr_yy;g' \ + -e '/#if defined(c_plusplus) || defined(__cplusplus)/,/#endif/d' \ + <y.tab.c >ssl_expr_parse.c && rm -f y.tab.c + sed -e 's;yy;ssl_expr_yy;g' \ + <y.tab.h >ssl_expr_parse.h && rm -f y.tab.h + +nocons: + @$(MAKE) $(MFLAGS) $(MFLAGS_STATIC) \ + SSL_CFLAGS="`echo $(SSL_CFLAGS) |\ + sed -e 's;-DSSL_CONSERVATIVE;;'`" all + +cons: + @$(MAKE) $(MFLAGS) $(MFLAGS_STATIC) \ + SSL_CFLAGS="`echo $(SSL_CFLAGS) |\ + sed -e 's;-DSSL_CONSERVATIVE;;' \ + -e 's;^;-DSSL_CONSERVATIVE ;'`" all +noexp: + @$(MAKE) $(MFLAGS) $(MFLAGS_STATIC) \ + SSL_CFLAGS="`echo $(SSL_CFLAGS) |\ + sed -e 's;-DSSL_EXPERIMENTAL;;'`" all + +exp: + @$(MAKE) $(MFLAGS) $(MFLAGS_STATIC) \ + SSL_CFLAGS="`echo $(SSL_CFLAGS) |\ + sed -e 's;-DSSL_EXPERIMENTAL;;' \ + -e 's;^;-DSSL_EXPERIMENTAL ;'`" all + +depend: + cp Makefile.tmpl Makefile.tmpl.bak \ + && sed -ne '1,/^# DO NOT REMOVE/p' Makefile.tmpl > Makefile.new \ + && gcc -MM $(INCLUDES) $(CFLAGS) $(SSL_CFLAGS) *.c >> Makefile.new \ + && sed -e '1,$$s; $(INCDIR)/; $$(INCDIR)/;g' \ + -e '1,$$s; $(OSDIR)/; $$(OSDIR)/;g' \ + -e '1,$$s;^\([a-z0-9_]*\)\.o:;\1.o \1.lo:;g' Makefile.new \ + > Makefile.tmpl \ + && rm Makefile.new + +## +## DEPENDENCY AREA +## + +$(OBJS) $(OBJS_PIC): Makefile + +# DO NOT REMOVE +mod_ssl.o mod_ssl.lo: mod_ssl.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_compat.o ssl_engine_compat.lo: ssl_engine_compat.c mod_ssl.h \ + $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ + $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \ + $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_config.o ssl_engine_config.lo: ssl_engine_config.c mod_ssl.h \ + $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ + $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \ + $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_dh.o ssl_engine_dh.lo: ssl_engine_dh.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_ds.o ssl_engine_ds.lo: ssl_engine_ds.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_ext.o ssl_engine_ext.lo: ssl_engine_ext.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_init.o ssl_engine_init.lo: ssl_engine_init.c mod_ssl.h \ + $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ + $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \ + $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_io.o ssl_engine_io.lo: ssl_engine_io.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_kernel.o ssl_engine_kernel.lo: ssl_engine_kernel.c mod_ssl.h \ + $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ + $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \ + $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_log.o ssl_engine_log.lo: ssl_engine_log.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_mutex.o ssl_engine_mutex.lo: ssl_engine_mutex.c mod_ssl.h \ + $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ + $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \ + $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_pphrase.o ssl_engine_pphrase.lo: ssl_engine_pphrase.c mod_ssl.h \ + $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ + $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \ + $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_rand.o ssl_engine_rand.lo: ssl_engine_rand.c mod_ssl.h \ + $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ + $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \ + $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_engine_vars.o ssl_engine_vars.lo: ssl_engine_vars.c mod_ssl.h \ + $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ + $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \ + $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_expr.o ssl_expr.lo: ssl_expr.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_expr_eval.o ssl_expr_eval.lo: ssl_expr_eval.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_expr_parse.o ssl_expr_parse.lo: ssl_expr_parse.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_expr_scan.o ssl_expr_scan.lo: ssl_expr_scan.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h \ + ssl_expr_parse.h +ssl_scache.o ssl_scache.lo: ssl_scache.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_scache_dbm.o ssl_scache_dbm.lo: ssl_scache_dbm.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_scache_shmcb.o ssl_scache_shmcb.lo: ssl_scache_shmcb.c mod_ssl.h \ + $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ + $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \ + $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_scache_shmht.o ssl_scache_shmht.lo: ssl_scache_shmht.c mod_ssl.h \ + $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \ + $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \ + $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_util.o ssl_util.lo: ssl_util.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_util_sdbm.o ssl_util_sdbm.lo: ssl_util_sdbm.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_util_ssl.o ssl_util_ssl.lo: ssl_util_ssl.c mod_ssl.h $(INCDIR)/ap_config.h \ + $(INCDIR)/ap_mmn.h $(INCDIR)/ap_config_auto.h \ + $(OSDIR)/os.h $(OSDIR)/os-inline.c $(INCDIR)/ap_ctype.h \ + $(INCDIR)/httpd.h $(INCDIR)/ap_mm.h $(INCDIR)/ap_alloc.h \ + $(INCDIR)/ap_hook.h $(INCDIR)/ap_ctx.h $(INCDIR)/buff.h \ + $(INCDIR)/ap.h $(INCDIR)/util_uri.h \ + $(INCDIR)/http_config.h $(INCDIR)/http_conf_globals.h \ + $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \ + $(INCDIR)/http_main.h $(INCDIR)/http_core.h \ + $(INCDIR)/http_log.h $(INCDIR)/scoreboard.h \ + $(INCDIR)/util_md5.h $(INCDIR)/ap_md5.h \ + $(INCDIR)/fnmatch.h ssl_expr.h ssl_util_ssl.h ssl_util_table.h +ssl_util_table.o ssl_util_table.lo: ssl_util_table.c ssl_util_table.h diff --git a/modules/ssl/Makefile.win32 b/modules/ssl/Makefile.win32 new file mode 100644 index 0000000000..92781c182f --- /dev/null +++ b/modules/ssl/Makefile.win32 @@ -0,0 +1,133 @@ +## _ _ +## _ __ ___ ___ __| | ___ ___| | mod_ssl +## | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +## | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +## |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +## |_____| +## Makefile.win32 +## Apache 1.3 Makefile for SSL module (Win32 environment) +## + +## +## ==================================================================== +## Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## +## 1. Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## +## 2. Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following +## disclaimer in the documentation and/or other materials +## provided with the distribution. +## +## 3. All advertising materials mentioning features or use of this +## software must display the following acknowledgment: +## "This product includes software developed by +## Ralf S. Engelschall <rse@engelschall.com> for use in the +## mod_ssl project (http://www.modssl.org/)." +## +## 4. The names "mod_ssl" must not be used to endorse or promote +## products derived from this software without prior written +## permission. For written permission, please contact +## rse@engelschall.com. +## +## 5. Products derived from this software may not be called "mod_ssl" +## nor may "mod_ssl" appear in their names without prior +## written permission of Ralf S. Engelschall. +## +## 6. Redistributions of any form whatsoever must retain the following +## acknowledgment: +## "This product includes software developed by +## Ralf S. Engelschall <rse@engelschall.com> for use in the +## mod_ssl project (http://www.modssl.org/)." +## +## THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY +## EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR +## HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +## STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +## OF THE POSSIBILITY OF SUCH DAMAGE. +## ==================================================================== +## + +# the following variables are automatically +# adjusted by the configure.bat script. +SSL_INC = p:\ssl\work\win32\openssl\include +SSL_LIB = p:\ssl\work\win32\openssl\lib +MOD_SSL_VERS_NUM = 000000 +MOD_SSL_VERS_STR = 0.0.0 + +# build tools and flags +CC = cl.exe +CFLAGS = /nologo /c /O2 /MD /W3 /GX /DNDEBUG /DWIN32 /D_WINDOWS /DSHARED_MODULE /DEAPI +CFLAGS = $(CFLAGS) /DMOD_SSL=$(MOD_SSL_VERS_NUM) /DMOD_SSL_VERSION=\"$(MOD_SSL_VERS_STR)\" +CFLAGS = $(CFLAGS) /I..\..\include /I..\..\os\win32 /I$(SSL_INC) +LD = link.exe +LDFLAGS = /nologo +RM = del + +# name and extension of generated mod_ssl library file +LIBNAME = mod_ssl +LIBEXT = so +LIBFILE = $(LIBNAME).$(LIBEXT) + +# mod_ssl object files +OBJS=\ + mod_ssl.obj\ + ssl_engine_config.obj\ + ssl_engine_compat.obj\ + ssl_engine_ds.obj\ + ssl_engine_dh.obj\ + ssl_engine_init.obj\ + ssl_engine_kernel.obj\ + ssl_engine_rand.obj\ + ssl_engine_io.obj\ + ssl_engine_log.obj\ + ssl_engine_mutex.obj\ + ssl_engine_pphrase.obj\ + ssl_engine_vars.obj\ + ssl_engine_ext.obj\ + ssl_scache.obj\ + ssl_scache_dbm.obj\ + ssl_scache_shmcb.obj\ + ssl_scache_shmht.obj\ + ssl_expr.obj\ + ssl_expr_scan.obj\ + ssl_expr_parse.obj\ + ssl_expr_eval.obj\ + ssl_util.obj\ + ssl_util_ssl.obj\ + ssl_util_sdbm.obj\ + ssl_util_table.obj + +.c.obj: + $(CC) $(CFLAGS) $< + +all: $(LIBFILE) + +$(LIBNAME).lib: $(OBJS) + $(LD) $(LDFLAGS) /lib /out:$@ \ + $(OBJS) + +$(LIBNAME).so: $(OBJS) + $(LD) $(LDFLAGS) /dll /out:$@ \ + $(OBJS) \ + ..\..\Release\ApacheCore.lib \ + $(SSL_LIB)\ssleay32.lib \ + $(SSL_LIB)\libeay32.lib \ + wsock32.lib gdi32.lib + +clean: + -$(RM) $(LIBFILE) + -$(RM) $(OBJS) + diff --git a/modules/ssl/README b/modules/ssl/README new file mode 100644 index 0000000000..ca9e225bf8 --- /dev/null +++ b/modules/ssl/README @@ -0,0 +1,163 @@ + _ _ + _ __ ___ ___ __| | ___ ___| | + | '_ ` _ \ / _ \ / _` | / __/ __| | + | | | | | | (_) | (_| | \__ \__ \ | ``mod_ssl combines the flexibility of + |_| |_| |_|\___/ \__,_|___|___/___/_| Apache with the security of OpenSSL.'' + |_____| + mod_ssl ``Ralf Engelschall has released an + Apache Interface to OpenSSL excellent module that integrates + http://www.modssl.org/ Apache and SSLeay.'' + Version 2.8 -- Tim J. Hudson + + SYNOPSIS + + This Apache module provides strong cryptography for the Apache 1.3 webserver + via the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS + v1) protocols by the help of the SSL/TLS implementation library OpenSSL which + is based on SSLeay from Eric A. Young and Tim J. Hudson. The mod_ssl package + was created in April 1998 by Ralf S. Engelschall and was originally derived + from software developed by Ben Laurie for use in the Apache-SSL HTTP server + project. + + SOURCES + + Here is a short overview of the source files: + + Makefile.libdir ......... dummy for Apache config mechanism + Makefile.tmpl ........... Makefile template for Unix platform + Makefile.win32 .......... Makefile template for Win32 platform + libssl.module ........... stub called from the Apache config mechanism + libssl.version .......... file containing the mod_ssl version information + mod_ssl.c ............... main source file containing API structures + mod_ssl.h ............... common header file of mod_ssl + ssl_engine_compat.c ..... backward compatibility support + ssl_engine_config.c ..... module configuration handling + ssl_engine_dh.c ......... DSA/DH support + ssl_engine_ds.c ......... data structures + ssl_engine_ext.c ........ Extensions to other Apache parts + ssl_engine_init.c ....... module initialization + ssl_engine_io.c ......... I/O support + ssl_engine_kernel.c ..... SSL engine kernel + ssl_engine_log.c ........ logfile support + ssl_engine_mutex.c ...... mutual exclusion support + ssl_engine_pphrase.c .... pass-phrase handling + ssl_engine_rand.c ....... PRNG support + ssl_engine_vars.c ....... Variable Expansion support + ssl_expr.c .............. expression handling main source + ssl_expr.h .............. expression handling common header + ssl_expr_scan.c ......... expression scanner automaton (pre-generated) + ssl_expr_scan.l ......... expression scanner source + ssl_expr_parse.c ........ expression parser automaton (pre-generated) + ssl_expr_parse.h ........ expression parser header (pre-generated) + ssl_expr_parse.y ........ expression parser source + ssl_expr_eval.c ......... expression machine evaluation + ssl_scache.c ............ session cache abstraction layer + ssl_scache_dbm.c ........ session cache via DBM file + ssl_scache_shmcb.c ...... session cache via shared memory cyclic buffer + ssl_scache_shmht.c ...... session cache via shared memory hash table + ssl_util.c .............. utility functions + ssl_util_ssl.c .......... the OpenSSL companion source + ssl_util_ssl.h .......... the OpenSSL companion header + ssl_util_sdbm.c ......... the SDBM library source + ssl_util_sdbm.h ......... the SDBM library header + ssl_util_table.c ........ the hash table library source + ssl_util_table.h ........ the hash table library header + + The source files are written in clean ANSI C and pass the ``gcc -O -g + -ggdb3 -Wall -Wshadow -Wpointer-arith -Wcast-align -Wmissing-prototypes + -Wmissing-declarations -Wnested-externs -Winline'' compiler test + (assuming `gcc' is GCC 2.95.2 or newer) without any complains. When + you make changes or additions make sure the source still passes this + compiler test. + + FUNCTIONS + + Inside the source code you will be confronted with the following types of + functions which can be identified by their prefixes: + + ap_xxxx() ............... Apache API function + ssl_xxxx() .............. mod_ssl function + SSL_xxxx() .............. OpenSSL function (SSL library) + OpenSSL_xxxx() .......... OpenSSL function (SSL library) + X509_xxxx() ............. OpenSSL function (Crypto library) + PEM_xxxx() .............. OpenSSL function (Crypto library) + EVP_xxxx() .............. OpenSSL function (Crypto library) + RSA_xxxx() .............. OpenSSL function (Crypto library) + + DATA STRUCTURES + + Inside the source code you will be confronted with the following + data structures: + + ap_ctx .................. Apache EAPI Context + server_rec .............. Apache (Virtual) Server + conn_rec ................ Apache Connection + BUFF .................... Apache Connection Buffer + request_rec ............. Apache Request + SSLModConfig ............ mod_ssl (Global) Module Configuration + SSLSrvConfig ............ mod_ssl (Virtual) Server Configuration + SSLDirConfig ............ mod_ssl Directory Configuration + SSL_CTX ................. OpenSSL Context + SSL_METHOD .............. OpenSSL Protocol Method + SSL_CIPHER .............. OpenSSL Cipher + SSL_SESSION ............. OpenSSL Session + SSL ..................... OpenSSL Connection + BIO ..................... OpenSSL Connection Buffer + + For an overview how these are related and chained together have a look at the + page in README.dsov.{fig,ps}. It contains overview diagrams for those data + structures. It's designed for DIN A4 paper size, but you can easily generate + a smaller version inside XFig by specifing a magnification on the Export + panel. + + EXPERIMENTAL CODE + + Experimental code is always encapsulated as following: + + | #ifdef SSL_EXPERIMENTAL_xxxx + | ... + | #endif + + This way it is only compiled in when this define is enabled with + the APACI --enable-rule=SSL_EXPERIMENTAL option and as long as the + C pre-processor variable SSL_EXPERIMENTAL_xxxx_IGNORE is _NOT_ + defined (via CFLAGS). Or in other words: SSL_EXPERIMENTAL enables all + SSL_EXPERIMENTAL_xxxx variables, except if SSL_EXPERIMENTAL_xxxx_IGNORE + is already defined. Currently the following features are experimental: + + o SSL_EXPERIMENTAL_PERDIRCA + The ability to use SSLCACertificateFile and SSLCACertificatePath + in a per-directory context (.htaccess). This is provided by some nasty + reconfiguration hacks until OpenSSL has better support for this. It + should work on non-multithreaded platforms (all but Win32). + + o SSL_EXPERIMENTAL_PROXY + The ability to use various additional SSLProxyXXX directives in + oder to control extended client functionality in the HTTPS proxy + code. + + o SSL_EXPERIMENTAL_ENGINE + The ability to support the new forthcoming OpenSSL ENGINE stuff. + Until this development branch of OpenSSL is merged into the main + stream, you have to use openssl-engine-0.9.x.tar.gz for this. + mod_ssl automatically recognizes this OpenSSL variant and then can + activate external crypto devices through SSLCryptoDevice directive. + + VENDOR EXTENSIONS + + Inside the mod_ssl sources you can enable various EAPI vendor hooks + (`ap::mod_ssl::vendor::xxxx') by using the APACI --enable-rule=SSL_VENDOR + option. These hooks can be used to change or extend mod_ssl by a vendor + without patching the source code. Grep for `ap::mod_ssl::vendor::'. + Additionally vendors can add their own source code to files named + ssl_vendor.c, ssl_vendor_XXX.c, etc. The libssl.module script automatically + picks these up under configuration time and mod_ssl under run-time calls the + functions `void ssl_vendor_register(void)' and `void + ssl_vendor_unregister(void)' inside these objects to bootstrap them. + + An ssl_vendor.c should at least contain the following contents: + + | #include "mod_ssl.h" + | void ssl_vendor_register(void) { return; } + | void ssl_vendor_unregister(void) { return; } + diff --git a/modules/ssl/README.dsov.fig b/modules/ssl/README.dsov.fig new file mode 100644 index 0000000000..d8d03db247 --- /dev/null +++ b/modules/ssl/README.dsov.fig @@ -0,0 +1,346 @@ +#FIG 3.2 +Landscape +Center +Metric +Letter +100.00 +Single +-2 +1200 2 +0 32 #616561 +0 33 #b6b2b6 +0 34 #f7f3f7 +0 35 #cfcfcf +0 36 #ffffff +6 6345 2835 7155 3150 +6 6345 2970 7110 3150 +4 0 0 200 0 20 8 0.0000 4 120 585 6345 3105 "ssl_module")\001 +-6 +4 0 0 200 0 20 8 0.0000 4 120 660 6345 2970 ap_ctx_get(...,\001 +-6 +6 10800 2610 12240 3060 +4 0 0 200 0 20 8 0.0000 4 120 1170 10800 2745 ap_get_module_config(...\001 +4 0 0 200 0 20 8 0.0000 4 120 795 10800 2880 ->per_dir_config,\001 +4 0 0 200 0 20 8 0.0000 4 120 585 10800 3015 &ssl_module)\001 +-6 +6 7920 4770 9135 4995 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 9135 4995 7920 4995 7920 4770 9135 4770 9135 4995 +4 0 0 100 0 18 12 0.0000 4 180 1065 8010 4950 request_rec\001 +-6 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 6975 3330 7425 2520 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 7200 4230 9450 2520 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 7875 4905 7200 5220 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 6750 5130 6750 4545 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 6705 5445 7155 6120 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 7875 4815 7200 4590 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 9585 2565 11475 4230 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 10170 5130 11835 4545 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 7920 6075 9855 5400 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 9990 5445 10935 5625 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 10215 5310 10935 5310 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 11925 4590 11925 5085 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 9810 5490 9810 6840 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 9945 5445 10935 6030 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 8865 4725 10800 2565 +2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2 + 675 6075 5850 6075 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 675 6525 675 6075 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 0 1.00 60.00 120.00 + 5850 6075 5850 6525 +2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2 + 900 5625 5625 5625 +2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2 + 1125 5175 5400 5175 +2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2 + 1350 4725 5175 4725 +2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2 + 1575 4275 4950 4275 +2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2 + 1800 3825 4725 3825 +2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2 + 2025 3375 4500 3375 +2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2 + 2250 2925 4275 2925 +2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2 + 2475 2475 4050 2475 +2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2 + 2700 2025 3825 2025 +2 1 0 3 0 34 200 0 20 0.000 0 0 -1 0 0 2 + 2925 1575 3600 1575 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 900 6075 900 5625 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 1125 6525 1125 5175 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 1350 5175 1350 4725 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 1575 4725 1575 4275 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 1800 6525 1800 3825 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 2025 3825 2025 3375 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 2250 3375 2250 2925 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 2475 2925 2475 2475 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 0 1.00 60.00 120.00 + 5625 5625 5625 6075 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 0 1.00 60.00 120.00 + 5400 5175 5400 6525 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 0 1.00 60.00 120.00 + 5175 4725 5175 5175 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 0 1.00 60.00 120.00 + 4950 4275 4950 4725 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 0 1.00 60.00 120.00 + 4725 3825 4725 6525 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 0 1.00 60.00 120.00 + 4500 3375 4500 3825 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 0 1.00 60.00 120.00 + 4275 2925 4275 3375 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 0 1.00 60.00 120.00 + 4050 2475 4050 2925 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 2700 6525 2700 2025 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 0 1.00 60.00 120.00 + 3825 2025 3825 6525 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 0 1.00 60.00 120.00 + 3600 1575 3600 2025 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 2925 2025 2925 1575 +2 1 0 4 0 0 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 4.00 60.00 120.00 + 540 6525 6300 6525 +2 3 0 1 7 7 800 0 20 0.000 0 0 -1 0 0 9 + 675 6525 5850 6525 5850 6075 5625 6075 5625 5625 900 5625 + 900 6075 675 6075 675 6525 +2 3 0 1 34 34 700 0 20 0.000 0 0 -1 0 0 13 + 1125 6525 5355 6525 5400 5175 5175 5175 5175 4725 4950 4725 + 4950 4275 1575 4275 1575 4725 1350 4725 1350 5175 1125 5175 + 1125 6525 +2 3 0 1 35 35 500 0 20 0.000 0 0 -1 0 0 17 + 1800 6525 4725 6525 4725 3825 4500 3825 4500 3375 4275 3375 + 4275 2925 4050 2925 4050 2475 2475 2475 2475 2925 2250 2925 + 2250 3375 2025 3375 2025 3825 1800 3825 1800 6525 +2 3 0 1 33 33 400 0 20 0.000 0 0 -1 0 0 9 + 2700 6525 3825 6525 3825 2025 3600 2025 3600 1575 2925 1575 + 2925 2025 2700 2025 2700 6525 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2 + 2 0 1.00 60.00 120.00 + 2 0 1.00 60.00 120.00 + 2700 6750 3825 6750 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2 + 2 0 1.00 60.00 120.00 + 2 0 1.00 60.00 120.00 + 1125 7200 5400 7200 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2 + 2 0 1.00 60.00 120.00 + 2 0 1.00 60.00 120.00 + 1800 6975 4725 6975 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 1 2 + 2 0 1.00 60.00 120.00 + 2 0 1.00 60.00 120.00 + 675 7425 5850 7425 +2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2 + 675 6570 675 7650 +2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2 + 1125 6570 1125 7650 +2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2 + 1800 6570 1800 7650 +2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2 + 2700 6570 2700 7650 +2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2 + 3825 6570 3825 7650 +2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2 + 4725 6570 4725 7650 +2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2 + 5400 6570 5400 7650 +2 1 2 1 0 34 200 0 20 3.000 0 1 -1 0 0 2 + 5850 6570 5850 7650 +2 4 0 2 0 7 100 0 -1 0.000 0 0 20 0 0 5 + 12600 8550 450 8550 450 225 12600 225 12600 8550 +2 4 0 1 0 34 200 0 20 0.000 0 0 20 0 0 5 + 12600 1350 450 1350 450 225 12600 225 12600 1350 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 10170 2475 8775 2475 8775 2250 10170 2250 10170 2475 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 11925 2475 10575 2475 10575 2250 11925 2250 11925 2475 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 12375 4500 11430 4500 11430 4275 12375 4275 12375 4500 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 12375 5400 10980 5400 10980 5175 12375 5175 12375 5400 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 10170 5400 9675 5400 9675 5175 10170 5175 10170 5400 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 7875 6300 7200 6300 7200 6075 7875 6075 7875 6300 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 8190 2475 6750 2475 6750 2250 8190 2250 8190 2475 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 7605 3600 6300 3600 6300 3375 7605 3375 7605 3600 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 7335 4500 6300 4500 6300 4275 7335 4275 7335 4500 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 7200 5400 6300 5400 6300 5175 7200 5175 7200 5400 +2 1 0 6 7 7 600 0 -1 0.000 0 0 -1 0 0 2 + 9450 4500 6075 1935 +2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2 + 9450 4500 12465 2205 +2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2 + 9450 4500 9450 7785 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 9630 5310 7245 5310 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 11385 4365 7380 4365 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 12240 5805 10980 5805 10980 5580 12240 5580 12240 5805 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 12375 6210 10980 6210 10980 5985 12375 5985 12375 6210 +2 1 0 1 0 34 200 0 20 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 11205 6885 9900 5445 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 12285 7155 10530 7155 10530 6930 12285 6930 12285 7155 +2 4 0 1 35 35 200 0 20 0.000 0 0 4 0 0 5 + 10170 7155 9630 7155 9630 6930 10170 6930 10170 7155 +2 1 0 6 7 7 600 0 -1 0.000 0 0 4 0 0 2 + 12510 6435 9450 6435 +2 1 0 1 0 34 300 0 20 0.000 0 0 7 1 0 4 + 1 1 1.00 60.00 120.00 + 12375 4455 12510 4635 12510 6210 11970 6885 +2 1 2 1 0 34 200 0 20 1.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 9850 5143 9175 4918 +3 1 0 1 34 34 800 0 20 0.000 0 0 0 41 + 7380 1710 6390 2115 5535 2115 6075 3015 5670 3465 6165 3915 + 5715 4410 6030 5040 6030 5310 6480 5715 6390 6255 6975 6300 + 7065 6975 7965 6750 8100 7560 8955 7290 9360 7740 9720 7560 + 10755 8145 12060 8280 12375 7650 12420 7200 12510 7065 12330 6660 + 12510 6390 12420 5940 12375 5400 12510 5220 12510 4725 12600 4275 + 12375 3645 12105 3240 12150 2745 12375 2700 12330 1980 11790 1575 + 11250 1935 10125 1485 8955 2070 7785 1620 7695 1575 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 1.000 1.000 1.000 1.000 1.000 1.000 1.000 + 1.000 +4 0 0 100 0 0 12 0.0000 4 180 1440 10575 675 Ralf S. Engelschall\001 +4 0 0 100 0 18 20 0.0000 4 270 3840 4275 675 Apache+mod_ssl+OpenSSL\001 +4 0 0 100 0 0 10 0.0000 4 135 1320 10575 855 rse@engelschall.com\001 +4 0 0 100 0 0 10 0.0000 4 135 1410 10575 1035 www.engelschall.com\001 +4 0 0 100 0 0 12 0.0000 4 135 870 900 675 Version 1.3\001 +4 0 0 100 0 0 12 0.0000 4 180 1035 900 855 12-Apr-1999\001 +4 0 0 200 0 20 8 0.0000 4 60 390 6210 4680 ->server\001 +4 0 0 200 0 20 8 0.0000 4 120 855 8280 6120 ap_ctx_get(...,"ssl")\001 +4 0 0 200 0 20 8 0.0000 4 120 1170 7740 2700 ap_get_module_config(...\001 +4 0 0 200 0 20 8 0.0000 4 120 810 7740 2835 ->module_config,\001 +4 0 0 200 0 20 8 0.0000 4 120 585 7740 2970 &ssl_module)\001 +4 0 0 100 0 18 20 0.0000 4 270 1200 9000 8100 Chaining\001 +4 0 0 100 0 18 20 0.0000 4 210 1095 2745 8100 Lifetime\001 +4 0 0 100 0 18 12 0.0000 4 180 1215 810 6255 ap_global_ctx\001 +4 0 0 100 0 18 12 0.0000 4 180 1305 990 5805 SSLModConfig\001 +4 0 0 100 0 18 12 0.0000 4 180 840 4050 4455 SSL_CTX\001 +4 0 0 100 0 18 12 0.0000 4 150 975 4455 5355 server_rec\001 +4 0 0 100 0 18 12 0.0000 4 180 1260 3870 4905 SSLSrvConfig\001 +4 0 0 100 0 18 12 0.0000 4 135 480 1845 4005 BUFF\001 +4 0 0 100 0 18 12 0.0000 4 150 810 2070 3555 conn_rec\001 +4 0 0 100 0 18 12 0.0000 4 135 345 2295 3105 BIO\001 +4 0 0 100 0 18 12 0.0000 4 135 375 2565 2655 SSL\001 +4 0 0 100 0 18 12 0.0000 4 180 1185 3645 1620 SSLDirConfig\001 +4 0 0 100 0 18 12 0.0000 4 180 1065 3915 2070 request_rec\001 +4 0 0 200 0 0 8 0.0000 4 120 1440 900 7560 Startup, Runtime, Shutdown\001 +4 0 0 200 0 0 8 0.0000 4 105 975 1350 7335 Configuration Time\001 +4 0 0 200 0 0 8 0.0000 4 90 1050 2025 7110 Connection Duration\001 +4 0 0 200 0 0 8 0.0000 4 120 885 2835 6885 Request Duration\001 +4 0 0 200 0 18 20 0.0000 4 195 90 6345 6795 t\001 +4 0 0 200 0 20 8 0.0000 4 90 345 7110 5985 ->client\001 +4 0 0 100 0 18 12 0.0000 4 180 1305 6795 2430 SSLModConfig\001 +4 0 0 100 0 18 12 0.0000 4 180 1260 8865 2430 SSLSrvConfig\001 +4 0 0 100 0 18 12 0.0000 4 180 1215 6345 3555 ap_global_ctx\001 +4 0 0 100 0 18 12 0.0000 4 150 975 6345 4455 server_rec\001 +4 0 0 100 0 18 12 0.0000 4 150 810 6345 5355 conn_rec\001 +4 0 0 100 0 18 12 0.0000 4 135 375 9720 5355 SSL\001 +4 0 0 100 0 18 12 0.0000 4 180 1185 10665 2430 SSLDirConfig\001 +4 0 0 100 0 18 12 0.0000 4 135 480 7290 6255 BUFF\001 +4 0 0 100 0 18 12 0.0000 4 180 1305 11025 5355 SSL_METHOD\001 +4 0 0 100 0 18 12 0.0000 4 180 840 11475 4455 SSL_CTX\001 +4 0 0 100 0 18 24 0.0000 4 285 4365 3915 1080 Data Structure Overview\001 +4 0 0 200 0 20 8 0.0000 4 90 615 7065 5085 ->connection\001 +4 0 0 200 0 20 8 0.0000 4 60 390 7065 4770 ->server\001 +4 0 0 200 0 20 8 0.0000 4 120 960 8010 5445 SSL_get_app_data()\001 +4 0 0 200 0 20 8 0.0000 4 120 510 10530 4050 ->pSSLCtx\001 +4 0 0 200 0 20 8 0.0000 4 120 1215 7875 4275 SSL_CTX_get_app_data()\001 +4 0 0 200 0 20 8 0.0000 4 120 1155 10305 5535 SSL_get_current_cipher()\001 +4 0 0 100 0 18 12 0.0000 4 180 1170 11025 5760 SSL_CIPHER\001 +4 0 0 100 0 18 12 0.0000 4 180 1350 10980 6165 SSL_SESSION\001 +4 0 0 200 0 20 8 0.0000 4 120 840 10440 5940 SSL_get_session()\001 +4 0 0 100 0 18 12 0.0000 4 180 1665 10575 7110 X509_STORE_CTX\001 +4 0 0 100 0 18 12 0.0000 4 135 345 9720 7110 BIO\001 +4 0 0 200 0 20 8 0.0000 4 120 840 9540 7335 SSL_get_{r,w}bio()\001 +4 0 0 100 0 18 20 0.0000 4 270 1170 8730 3465 mod_ssl\001 +4 0 0 100 0 18 20 0.0000 4 270 1050 8145 6750 Apache\001 +4 0 0 200 0 20 8 0.0000 4 120 945 10125 4680 SSL_get_SSL_CTX()\001 +4 0 0 200 0 20 8 0.0000 4 120 1170 10350 5175 SSL_get_SSL_METHOD()\001 +4 0 0 200 0 20 8 0.0000 4 90 465 11745 4770 ->method\001 +4 0 0 200 0 20 8 0.0000 4 120 1665 9945 6480 X509_STORE_CTX_get_app_data()\001 +4 0 0 200 0 20 8 0.0000 4 120 1215 10980 6705 SSL_CTX_get_cert_store()\001 +4 0 0 200 0 20 8 0.0000 4 120 1020 8280 5130 SSL_get_app_data2()\001 +4 0 0 100 0 18 20 0.0000 4 270 1290 10710 7605 OpenSSL\001 +4 0 0 100 0 18 12 0.0000 4 180 720 10710 7785 [Crypto]\001 +4 0 0 100 0 18 20 0.0000 4 270 1290 10935 3645 OpenSSL\001 +4 0 0 100 0 18 12 0.0000 4 180 495 10935 3825 [SSL]\001 diff --git a/modules/ssl/README.dsov.ps b/modules/ssl/README.dsov.ps new file mode 100644 index 0000000000..def19dbecf --- /dev/null +++ b/modules/ssl/README.dsov.ps @@ -0,0 +1,1138 @@ +%!PS-Adobe-2.0 +%%Title: README.dsov.ps +%%Creator: fig2dev Version 3.2 Patchlevel 1 +%%CreationDate: Mon Apr 12 17:09:11 1999 +%%For: rse@en1.engelschall.com (Ralf S. Engelschall) +%%Orientation: Landscape +%%BoundingBox: 59 37 553 755 +%%Pages: 1 +%%BeginSetup +%%IncludeFeature: *PageSize Letter +%%EndSetup +%%Magnification: 0.9340 +%%EndComments +/$F2psDict 200 dict def +$F2psDict begin +$F2psDict /mtrx matrix put +/col-1 {0 setgray} bind def +/col0 {0.000 0.000 0.000 srgb} bind def +/col1 {0.000 0.000 1.000 srgb} bind def +/col2 {0.000 1.000 0.000 srgb} bind def +/col3 {0.000 1.000 1.000 srgb} bind def +/col4 {1.000 0.000 0.000 srgb} bind def +/col5 {1.000 0.000 1.000 srgb} bind def +/col6 {1.000 1.000 0.000 srgb} bind def +/col7 {1.000 1.000 1.000 srgb} bind def +/col8 {0.000 0.000 0.560 srgb} bind def +/col9 {0.000 0.000 0.690 srgb} bind def +/col10 {0.000 0.000 0.820 srgb} bind def +/col11 {0.530 0.810 1.000 srgb} bind def +/col12 {0.000 0.560 0.000 srgb} bind def +/col13 {0.000 0.690 0.000 srgb} bind def +/col14 {0.000 0.820 0.000 srgb} bind def +/col15 {0.000 0.560 0.560 srgb} bind def +/col16 {0.000 0.690 0.690 srgb} bind def +/col17 {0.000 0.820 0.820 srgb} bind def +/col18 {0.560 0.000 0.000 srgb} bind def +/col19 {0.690 0.000 0.000 srgb} bind def +/col20 {0.820 0.000 0.000 srgb} bind def +/col21 {0.560 0.000 0.560 srgb} bind def +/col22 {0.690 0.000 0.690 srgb} bind def +/col23 {0.820 0.000 0.820 srgb} bind def +/col24 {0.500 0.190 0.000 srgb} bind def +/col25 {0.630 0.250 0.000 srgb} bind def +/col26 {0.750 0.380 0.000 srgb} bind def +/col27 {1.000 0.500 0.500 srgb} bind def +/col28 {1.000 0.630 0.630 srgb} bind def +/col29 {1.000 0.750 0.750 srgb} bind def +/col30 {1.000 0.880 0.880 srgb} bind def +/col31 {1.000 0.840 0.000 srgb} bind def +/col32 {0.380 0.396 0.380 srgb} bind def +/col33 {0.714 0.698 0.714 srgb} bind def +/col34 {0.969 0.953 0.969 srgb} bind def +/col35 {0.812 0.812 0.812 srgb} bind def +/col36 {1.000 1.000 1.000 srgb} bind def + +end +save +48.0 12.0 translate + 90 rotate +1 -1 scale + +/cp {closepath} bind def +/ef {eofill} bind def +/gr {grestore} bind def +/gs {gsave} bind def +/sa {save} bind def +/rs {restore} bind def +/l {lineto} bind def +/m {moveto} bind def +/rm {rmoveto} bind def +/n {newpath} bind def +/s {stroke} bind def +/sh {show} bind def +/slc {setlinecap} bind def +/slj {setlinejoin} bind def +/slw {setlinewidth} bind def +/srgb {setrgbcolor} bind def +/rot {rotate} bind def +/sc {scale} bind def +/sd {setdash} bind def +/ff {findfont} bind def +/sf {setfont} bind def +/scf {scalefont} bind def +/sw {stringwidth} bind def +/tr {translate} bind def +/tnt {dup dup currentrgbcolor + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} + bind def +/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul + 4 -2 roll mul srgb} bind def +/reencdict 12 dict def /ReEncode { reencdict begin +/newcodesandnames exch def /newfontname exch def /basefontname exch def +/basefontdict basefontname findfont def /newfont basefontdict maxlength dict def +basefontdict { exch dup /FID ne { dup /Encoding eq +{ exch dup length array copy newfont 3 1 roll put } +{ exch newfont 3 1 roll put } ifelse } { pop pop } ifelse } forall +newfont /FontName newfontname put newcodesandnames aload pop +128 1 255 { newfont /Encoding get exch /.notdef put } for +newcodesandnames length 2 idiv { newfont /Encoding get 3 1 roll put } repeat +newfontname newfont definefont pop end } def +/isovec [ +8#200 /grave 8#201 /acute 8#202 /circumflex 8#203 /tilde +8#204 /macron 8#205 /breve 8#206 /dotaccent 8#207 /dieresis +8#210 /ring 8#211 /cedilla 8#212 /hungarumlaut 8#213 /ogonek 8#214 /caron +8#220 /dotlessi 8#230 /oe 8#231 /OE +8#240 /space 8#241 /exclamdown 8#242 /cent 8#243 /sterling +8#244 /currency 8#245 /yen 8#246 /brokenbar 8#247 /section 8#250 /dieresis +8#251 /copyright 8#252 /ordfeminine 8#253 /guillemotleft 8#254 /logicalnot +8#255 /endash 8#256 /registered 8#257 /macron 8#260 /degree 8#261 /plusminus +8#262 /twosuperior 8#263 /threesuperior 8#264 /acute 8#265 /mu 8#266 /paragraph +8#267 /periodcentered 8#270 /cedilla 8#271 /onesuperior 8#272 /ordmasculine +8#273 /guillemotright 8#274 /onequarter 8#275 /onehalf +8#276 /threequarters 8#277 /questiondown 8#300 /Agrave 8#301 /Aacute +8#302 /Acircumflex 8#303 /Atilde 8#304 /Adieresis 8#305 /Aring +8#306 /AE 8#307 /Ccedilla 8#310 /Egrave 8#311 /Eacute +8#312 /Ecircumflex 8#313 /Edieresis 8#314 /Igrave 8#315 /Iacute +8#316 /Icircumflex 8#317 /Idieresis 8#320 /Eth 8#321 /Ntilde 8#322 /Ograve +8#323 /Oacute 8#324 /Ocircumflex 8#325 /Otilde 8#326 /Odieresis 8#327 /multiply +8#330 /Oslash 8#331 /Ugrave 8#332 /Uacute 8#333 /Ucircumflex +8#334 /Udieresis 8#335 /Yacute 8#336 /Thorn 8#337 /germandbls 8#340 /agrave +8#341 /aacute 8#342 /acircumflex 8#343 /atilde 8#344 /adieresis 8#345 /aring +8#346 /ae 8#347 /ccedilla 8#350 /egrave 8#351 /eacute +8#352 /ecircumflex 8#353 /edieresis 8#354 /igrave 8#355 /iacute +8#356 /icircumflex 8#357 /idieresis 8#360 /eth 8#361 /ntilde 8#362 /ograve +8#363 /oacute 8#364 /ocircumflex 8#365 /otilde 8#366 /odieresis 8#367 /divide +8#370 /oslash 8#371 /ugrave 8#372 /uacute 8#373 /ucircumflex +8#374 /udieresis 8#375 /yacute 8#376 /thorn 8#377 /ydieresis] def +/Times-Roman /Times-Roman-iso isovec ReEncode +/Helvetica-Bold /Helvetica-Bold-iso isovec ReEncode +/Helvetica-Narrow /Helvetica-Narrow-iso isovec ReEncode +/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def +/$F2psEnd {$F2psEnteredState restore end} def +%%EndProlog + +$F2psBegin +10 setmiterlimit +n -1000 9572 m -1000 -1000 l 13622 -1000 l 13622 9572 l cp clip + 0.05883 0.05883 sc +%%Page: 1 1 +% Polyline +7.500 slw +n 6413 2048 m 6380 2054 l 6348 2061 l 6315 2067 l 6283 2073 l 6250 2079 l + 6217 2084 l 6185 2090 l 6152 2095 l 6120 2101 l 6088 2107 l + 6057 2113 l 6027 2120 l 5998 2126 l 5970 2134 l 5943 2141 l + 5918 2149 l 5894 2158 l 5873 2167 l 5853 2177 l 5835 2187 l + 5819 2198 l 5805 2210 l 5793 2222 l 5782 2235 l 5774 2250 l + 5768 2265 l 5763 2281 l 5760 2299 l 5759 2318 l 5759 2339 l + 5761 2360 l 5764 2383 l 5768 2408 l 5774 2433 l 5780 2460 l + 5788 2488 l 5797 2516 l 5806 2546 l 5815 2575 l 5825 2606 l + 5836 2636 l 5846 2666 l 5856 2696 l 5866 2726 l 5875 2755 l + 5884 2784 l 5892 2812 l 5899 2839 l 5905 2866 l 5910 2891 l + 5915 2916 l 5918 2940 l 5919 2968 l 5920 2995 l 5919 3022 l + 5916 3048 l 5912 3075 l 5908 3101 l 5902 3127 l 5895 3153 l + 5887 3179 l 5880 3205 l 5871 3230 l 5863 3254 l 5855 3278 l + 5848 3302 l 5841 3324 l 5834 3346 l 5829 3367 l 5824 3388 l + 5821 3408 l 5819 3427 l 5819 3446 l 5820 3465 l 5823 3484 l + 5827 3503 l 5833 3522 l 5840 3542 l 5848 3562 l 5858 3582 l + 5868 3603 l 5880 3625 l 5891 3647 l 5904 3669 l 5916 3691 l + 5929 3713 l 5941 3736 l 5953 3758 l 5964 3779 l 5974 3801 l + 5983 3822 l 5991 3843 l 5997 3863 l 6002 3883 l 6006 3903 l + 6008 3923 l 6008 3942 l 6006 3962 l 6003 3983 l 5998 4004 l + 5992 4025 l 5985 4048 l 5977 4070 l 5968 4094 l 5958 4118 l + 5947 4142 l 5936 4167 l 5925 4192 l 5913 4216 l 5902 4241 l + 5892 4266 l 5882 4291 l 5872 4315 l 5864 4339 l 5857 4362 l + 5851 4386 l 5846 4409 l 5843 4433 l 5840 4456 l 5840 4480 l + 5840 4505 l 5842 4530 l 5845 4556 l 5849 4582 l 5854 4609 l + 5860 4636 l 5867 4664 l 5875 4692 l 5883 4720 l 5892 4747 l + 5901 4774 l 5910 4801 l 5920 4827 l 5929 4852 l 5938 4875 l + 5947 4898 l 5955 4920 l 5963 4941 l 5971 4961 l 5978 4980 l + 5985 5002 l 5992 5024 l 5999 5046 l 6005 5067 l 6010 5088 l + 6016 5109 l 6022 5129 l 6027 5150 l 6033 5170 l 6039 5190 l + 6045 5209 l 6052 5228 l 6059 5246 l 6067 5264 l 6075 5281 l + 6084 5298 l 6094 5315 l 6105 5333 l 6115 5347 l 6125 5361 l + 6137 5376 l 6149 5392 l 6162 5408 l 6176 5425 l 6191 5443 l + 6206 5461 l 6221 5480 l 6237 5499 l 6253 5519 l 6269 5539 l + 6284 5559 l 6299 5579 l 6313 5599 l 6327 5619 l 6340 5639 l + 6352 5659 l 6363 5679 l 6373 5698 l 6382 5718 l 6390 5738 l + 6398 5759 l 6404 5782 l 6410 5805 l 6415 5828 l 6420 5852 l + 6424 5877 l 6428 5902 l 6431 5927 l 6435 5952 l 6438 5977 l + 6442 6001 l 6446 6025 l 6450 6048 l 6455 6069 l 6461 6090 l + 6467 6109 l 6474 6127 l 6483 6143 l 6492 6159 l 6503 6173 l + 6515 6185 l 6528 6197 l 6543 6209 l 6560 6220 l 6578 6230 l + 6598 6240 l 6619 6250 l 6641 6260 l 6663 6270 l 6687 6281 l + 6710 6291 l 6733 6302 l 6757 6312 l 6779 6324 l 6801 6335 l + 6821 6348 l 6841 6361 l 6859 6374 l 6876 6389 l 6893 6405 l + 6906 6421 l 6919 6437 l 6932 6455 l 6944 6475 l 6955 6495 l + 6967 6516 l 6979 6538 l 6991 6561 l 7003 6584 l 7015 6608 l + 7027 6631 l 7040 6654 l 7053 6677 l 7067 6699 l 7081 6720 l + 7096 6739 l 7111 6758 l 7127 6774 l 7144 6789 l 7161 6803 l + 7180 6815 l 7200 6825 l 7220 6833 l 7240 6840 l 7263 6845 l + 7286 6850 l 7311 6854 l 7338 6857 l 7365 6859 l 7394 6861 l + 7424 6862 l 7454 6864 l 7485 6865 l 7516 6866 l 7547 6867 l + 7578 6868 l 7609 6870 l 7639 6872 l 7668 6875 l 7696 6879 l + 7723 6883 l 7748 6889 l 7773 6895 l 7795 6903 l 7817 6912 l + 7838 6923 l 7857 6934 l 7875 6948 l 7892 6963 l 7909 6980 l + 7926 6998 l 7941 7017 l 7957 7038 l 7972 7060 l 7987 7083 l + 8002 7106 l 8017 7130 l 8031 7154 l 8046 7178 l 8061 7202 l + 8075 7225 l 8090 7247 l 8105 7269 l 8120 7289 l 8135 7308 l + 8151 7326 l 8167 7342 l 8184 7356 l 8202 7369 l 8220 7380 l + 8239 7390 l 8260 7397 l 8282 7404 l 8305 7409 l 8330 7413 l + 8356 7416 l 8383 7418 l 8412 7420 l 8441 7420 l 8471 7419 l + 8502 7418 l 8534 7417 l 8565 7415 l 8597 7413 l 8629 7411 l + 8660 7409 l 8690 7407 l 8720 7405 l 8749 7404 l 8777 7404 l + 8804 7404 l 8830 7405 l 8856 7407 l 8880 7410 l 8906 7414 l + 8931 7420 l 8956 7427 l 8981 7435 l 9005 7444 l 9029 7455 l + 9053 7466 l 9077 7478 l 9100 7491 l 9123 7504 l 9146 7517 l + 9168 7531 l 9190 7544 l 9210 7557 l 9230 7570 l 9250 7582 l + 9268 7593 l 9286 7604 l 9304 7613 l 9320 7621 l 9336 7629 l + 9353 7635 l 9370 7641 l 9388 7645 l 9406 7648 l 9425 7650 l + 9444 7652 l 9464 7653 l 9485 7653 l 9508 7653 l 9531 7653 l + 9555 7653 l 9579 7653 l 9605 7654 l 9631 7655 l 9658 7656 l + 9685 7659 l 9713 7662 l 9742 7666 l 9771 7672 l 9801 7679 l + 9833 7688 l 9853 7694 l 9874 7700 l 9895 7708 l 9918 7716 l + 9941 7725 l 9966 7734 l 9991 7745 l 10017 7755 l 10045 7767 l + 10073 7779 l 10102 7791 l 10132 7804 l 10163 7818 l 10194 7831 l + 10227 7845 l 10259 7860 l 10293 7874 l 10326 7889 l 10360 7903 l + 10394 7918 l 10429 7932 l 10463 7947 l 10497 7961 l 10531 7974 l + 10565 7988 l 10599 8001 l 10633 8013 l 10667 8025 l 10700 8037 l + 10733 8049 l 10767 8059 l 10800 8070 l 10834 8080 l 10868 8090 l + 10902 8099 l 10937 8108 l 10973 8117 l 11009 8125 l 11045 8133 l + 11083 8141 l 11120 8148 l 11158 8155 l 11197 8161 l 11236 8167 l + 11275 8172 l 11313 8177 l 11352 8181 l 11391 8184 l 11429 8187 l + 11467 8190 l 11504 8191 l 11540 8192 l 11576 8192 l 11610 8192 l + 11644 8191 l 11676 8189 l 11707 8187 l 11738 8184 l 11767 8180 l + 11794 8176 l 11821 8171 l 11847 8165 l 11871 8159 l 11895 8153 l + 11923 8143 l 11950 8133 l 11976 8122 l 12001 8109 l 12025 8096 l + 12048 8081 l 12071 8065 l 12092 8048 l 12113 8031 l 12133 8012 l + 12153 7992 l 12171 7972 l 12188 7951 l 12205 7930 l 12220 7909 l + 12235 7887 l 12248 7865 l 12260 7843 l 12272 7822 l 12282 7800 l + 12292 7779 l 12301 7759 l 12309 7739 l 12316 7719 l 12323 7699 l + 12330 7680 l 12338 7655 l 12345 7631 l 12352 7607 l 12359 7582 l + 12365 7558 l 12371 7533 l 12377 7508 l 12382 7484 l 12388 7460 l + 12392 7436 l 12397 7414 l 12401 7391 l 12405 7370 l 12409 7350 l + 12412 7331 l 12415 7313 l 12418 7297 l 12421 7281 l 12424 7266 l + 12428 7253 l 12432 7234 l 12437 7216 l 12442 7199 l 12446 7183 l + 12451 7166 l 12456 7150 l 12460 7134 l 12463 7117 l 12466 7101 l + 12468 7086 l 12469 7070 l 12469 7054 l 12467 7037 l 12465 7020 l + 12462 7006 l 12459 6991 l 12455 6975 l 12450 6958 l 12445 6940 l + 12440 6921 l 12434 6901 l 12428 6880 l 12422 6859 l 12416 6838 l + 12411 6817 l 12406 6796 l 12401 6776 l 12397 6756 l 12394 6736 l + 12392 6718 l 12390 6700 l 12390 6683 l 12390 6665 l 12392 6649 l + 12394 6631 l 12397 6614 l 12401 6597 l 12406 6579 l 12411 6561 l + 12416 6542 l 12422 6524 l 12428 6505 l 12434 6487 l 12440 6468 l + 12445 6450 l 12450 6432 l 12455 6414 l 12459 6396 l 12462 6378 l + 12465 6360 l 12467 6343 l 12468 6326 l 12469 6308 l 12469 6289 l + 12468 6269 l 12468 6249 l 12466 6227 l 12464 6205 l 12462 6182 l + 12460 6159 l 12457 6135 l 12454 6111 l 12451 6087 l 12447 6063 l + 12444 6040 l 12441 6016 l 12437 5993 l 12434 5970 l 12431 5948 l + 12428 5925 l 12424 5902 l 12421 5879 l 12419 5855 l 12416 5831 l + 12413 5806 l 12411 5781 l 12408 5755 l 12406 5729 l 12404 5702 l + 12403 5676 l 12401 5651 l 12400 5625 l 12400 5601 l 12399 5578 l + 12399 5555 l 12400 5534 l 12401 5514 l 12402 5495 l 12403 5477 l + 12405 5460 l 12408 5440 l 12411 5421 l 12416 5402 l 12420 5384 l + 12426 5365 l 12431 5347 l 12437 5329 l 12444 5311 l 12450 5293 l + 12456 5275 l 12462 5258 l 12468 5240 l 12474 5222 l 12479 5205 l + 12483 5186 l 12488 5168 l 12490 5152 l 12493 5135 l 12496 5117 l + 12498 5099 l 12500 5079 l 12502 5058 l 12504 5036 l 12506 5014 l + 12507 4990 l 12509 4966 l 12510 4942 l 12512 4918 l 12513 4893 l + 12515 4869 l 12516 4845 l 12518 4822 l 12520 4799 l 12521 4776 l + 12523 4754 l 12525 4733 l 12527 4713 l 12529 4693 l 12531 4673 l + 12534 4653 l 12536 4632 l 12539 4610 l 12541 4588 l 12543 4566 l + 12546 4543 l 12548 4520 l 12550 4497 l 12552 4473 l 12553 4450 l + 12554 4426 l 12555 4403 l 12555 4380 l 12555 4357 l 12555 4334 l + 12554 4312 l 12552 4290 l 12550 4267 l 12548 4245 l 12545 4224 l + 12541 4203 l 12537 4181 l 12533 4159 l 12528 4136 l 12523 4112 l + 12517 4088 l 12510 4064 l 12503 4038 l 12496 4013 l 12488 3987 l + 12479 3961 l 12471 3935 l 12462 3909 l 12452 3884 l 12443 3859 l + 12434 3835 l 12424 3811 l 12415 3788 l 12405 3766 l 12396 3744 l + 12386 3723 l 12377 3702 l 12368 3683 l 12357 3661 l 12347 3640 l + 12336 3619 l 12325 3598 l 12314 3576 l 12303 3555 l 12291 3533 l + 12280 3511 l 12269 3489 l 12257 3467 l 12246 3446 l 12235 3424 l + 12225 3402 l 12215 3381 l 12206 3360 l 12197 3340 l 12189 3320 l + 12181 3301 l 12174 3281 l 12168 3262 l 12162 3244 l 12158 3225 l + 12153 3204 l 12149 3183 l 12145 3162 l 12142 3139 l 12140 3117 l + 12138 3094 l 12137 3071 l 12137 3047 l 12138 3024 l 12139 3001 l + 12141 2978 l 12143 2956 l 12146 2935 l 12150 2915 l 12154 2896 l + 12158 2879 l 12163 2862 l 12168 2847 l 12174 2833 l 12180 2820 l + 12188 2805 l 12197 2792 l 12206 2779 l 12216 2766 l 12227 2754 l + 12238 2742 l 12249 2730 l 12260 2717 l 12272 2704 l 12282 2691 l + 12292 2676 l 12302 2661 l 12310 2645 l 12318 2627 l 12324 2608 l + 12330 2588 l 12334 2571 l 12336 2553 l 12339 2534 l 12341 2513 l + 12342 2491 l 12343 2467 l 12343 2442 l 12342 2416 l 12340 2389 l + 12338 2360 l 12335 2332 l 12331 2303 l 12326 2273 l 12320 2244 l + 12314 2215 l 12307 2187 l 12299 2159 l 12290 2132 l 12280 2106 l + 12270 2081 l 12259 2056 l 12248 2033 l 12236 2011 l 12224 1990 l + 12210 1970 l 12196 1949 l 12181 1929 l 12164 1910 l 12147 1890 l + 12129 1871 l 12110 1853 l 12090 1835 l 12070 1818 l 12049 1802 l + 12027 1787 l 12005 1773 l 11983 1761 l 11961 1749 l 11939 1739 l + 11917 1730 l 11895 1722 l 11874 1716 l 11852 1710 l 11831 1707 l + 11811 1704 l 11790 1703 l 11769 1702 l 11748 1703 l 11727 1705 l + 11706 1708 l 11683 1711 l 11660 1716 l 11636 1721 l 11612 1727 l + 11587 1733 l 11560 1740 l 11534 1747 l 11506 1754 l 11479 1761 l + 11450 1768 l 11422 1774 l 11393 1780 l 11364 1786 l 11334 1791 l + 11305 1795 l 11275 1798 l 11245 1800 l 11215 1801 l 11184 1801 l + 11153 1800 l 11128 1798 l 11104 1796 l 11078 1793 l 11052 1790 l + 11025 1785 l 10997 1781 l 10968 1776 l 10939 1770 l 10908 1764 l + 10877 1758 l 10844 1751 l 10811 1744 l 10778 1737 l 10743 1730 l + 10708 1722 l 10673 1715 l 10637 1708 l 10601 1701 l 10565 1695 l + 10530 1688 l 10494 1682 l 10458 1677 l 10422 1672 l 10387 1668 l + 10352 1664 l 10318 1661 l 10284 1658 l 10250 1657 l 10216 1656 l + 10183 1655 l 10150 1656 l 10118 1658 l 10087 1660 l 10055 1663 l + 10024 1666 l 9992 1671 l 9960 1676 l 9927 1682 l 9894 1688 l + 9861 1695 l 9827 1703 l 9792 1711 l 9757 1720 l 9721 1729 l + 9685 1738 l 9649 1748 l 9613 1757 l 9576 1767 l 9539 1778 l + 9502 1788 l 9465 1798 l 9429 1807 l 9392 1817 l 9356 1826 l + 9320 1835 l 9285 1844 l 9250 1852 l 9216 1860 l 9182 1867 l + 9148 1873 l 9115 1879 l 9082 1884 l 9050 1889 l 9018 1892 l + 8987 1895 l 8955 1898 l 8919 1899 l 8883 1900 l 8847 1899 l + 8811 1898 l 8774 1896 l 8737 1893 l 8699 1889 l 8661 1884 l + 8623 1878 l 8585 1872 l 8546 1865 l 8508 1857 l 8470 1849 l + 8432 1840 l 8395 1830 l 8358 1821 l 8322 1811 l 8287 1801 l + 8254 1790 l 8221 1780 l 8189 1770 l 8159 1760 l 8130 1750 l + 8102 1740 l 8076 1730 l 8051 1721 l 8028 1712 l 8006 1703 l + 7985 1695 l 7965 1688 l 7931 1674 l 7899 1662 l 7871 1650 l + 7844 1640 l 7820 1631 l 7798 1623 l 7778 1617 l 7760 1611 l + 7743 1607 l 7728 1603 l 7715 1601 l 7702 1600 l 7691 1600 l + 7680 1601 l 7669 1603 l 7658 1605 l 7648 1607 l 7638 1610 l + 7627 1613 l 7615 1617 l 7601 1621 l 7587 1626 l 7571 1632 l + 7554 1638 l 7536 1645 l 7517 1653 l 7496 1661 l 7474 1670 l + 7452 1679 l 7428 1689 l 7403 1699 l 7378 1709 l 7352 1720 l + 7325 1731 l 7297 1743 l 7268 1755 l 7247 1763 l 7226 1772 l + 7204 1781 l 7182 1790 l 7158 1800 l 7133 1810 l 7108 1820 l + 7081 1831 l 7053 1842 l 7025 1853 l 6996 1864 l 6966 1875 l + 6935 1886 l 6904 1898 l 6873 1909 l 6841 1921 l 6809 1932 l + 6776 1943 l 6744 1954 l 6712 1964 l 6680 1974 l 6649 1984 l + 6618 1994 l 6587 2003 l 6557 2011 l 6527 2019 l 6498 2027 l + 6469 2034 l 6441 2041 l cp gs col34 1.00 shd ef gr gs col34 s gr +% Polyline +n 675 6525 m 5850 6525 l 5850 6075 l 5625 6075 l 5625 5625 l 900 5625 l + 900 6075 l 675 6075 l cp gs col7 1.00 shd ef gr gs col7 s gr +% Polyline +n 1125 6525 m 5355 6525 l 5400 5175 l 5175 5175 l 5175 4725 l 4950 4725 l + 4950 4275 l 1575 4275 l 1575 4725 l 1350 4725 l 1350 5175 l + 1125 5175 l cp gs col34 1.00 shd ef gr gs col34 s gr +% Polyline +75.000 slw +n 9450 4500 m 12465 2205 l gs col7 s gr +% Polyline +n 9450 4500 m 9450 7785 l gs col7 s gr +% Polyline +n 9450 4500 m 6075 1935 l gs col7 s gr +% Polyline +n 12510 6435 m 9450 6435 l gs col7 s gr +% Polyline +7.500 slw +n 1800 6525 m 4725 6525 l 4725 3825 l 4500 3825 l 4500 3375 l 4275 3375 l + 4275 2925 l 4050 2925 l 4050 2475 l 2475 2475 l 2475 2925 l + 2250 2925 l 2250 3375 l 2025 3375 l 2025 3825 l 1800 3825 l + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 2700 6525 m 3825 6525 l 3825 2025 l 3600 2025 l 3600 1575 l 2925 1575 l + 2925 2025 l 2700 2025 l cp gs col33 1.00 shd ef gr gs col33 s gr +% Polyline +gs clippath +12068 6810 m 11970 6885 l 12022 6773 l 11937 6878 l 11984 6915 l cp +clip +n 12375 4455 m 12510 4635 l 12510 6210 l 11970 6885 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 12068 6810 m 11970 6885 l 12022 6773 l 12045 6791 l 12068 6810 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +7113 6004 m 7155 6120 l 7063 6037 l 7138 6149 l 7188 6116 l cp +clip +n 6705 5445 m 7155 6120 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 7113 6004 m 7155 6120 l 7063 6037 l 7088 6020 l 7113 6004 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +7304 4656 m 7200 4590 l 7323 4599 l 7195 4557 l 7176 4614 l cp +clip +n 7875 4815 m 7200 4590 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 7304 4656 m 7200 4590 l 7323 4599 l 7314 4628 l 7304 4656 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +11405 4128 m 11475 4230 l 11365 4173 l 11466 4262 l 11506 4217 l cp +clip +n 9585 2565 m 11475 4230 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 11405 4128 m 11475 4230 l 11365 4173 l 11385 4151 l 11405 4128 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +11712 4556 m 11835 4545 l 11732 4613 l 11859 4568 l 11839 4512 l cp +clip +n 10170 5130 m 11835 4545 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 11712 4556 m 11835 4545 l 11732 4613 l 11722 4585 l 11712 4556 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +9732 5411 m 9855 5400 l 9752 5468 l 9879 5423 l 9859 5367 l cp +clip +n 7920 6075 m 9855 5400 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 9732 5411 m 9855 5400 l 9752 5468 l 9742 5440 l 9732 5411 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +10823 5573 m 10935 5625 l 10812 5632 l 10944 5657 l 10955 5598 l cp +clip +n 9990 5445 m 10935 5625 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 10823 5573 m 10935 5625 l 10812 5632 l 10817 5603 l 10823 5573 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +10815 5280 m 10935 5310 l 10815 5340 l 10950 5340 l 10950 5280 l cp +clip +n 10215 5310 m 10935 5310 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 10815 5280 m 10935 5310 l 10815 5340 l 10815 5310 l 10815 5280 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +11955 4965 m 11925 5085 l 11895 4965 l 11895 5100 l 11955 5100 l cp +clip +n 11925 4590 m 11925 5085 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 11955 4965 m 11925 5085 l 11895 4965 l 11925 4965 l 11955 4965 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +9840 6720 m 9810 6840 l 9780 6720 l 9780 6855 l 9840 6855 l cp +clip +n 9810 5490 m 9810 6840 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 9840 6720 m 9810 6840 l 9780 6720 l 9810 6720 l 9840 6720 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +10847 5943 m 10935 6030 l 10816 5995 l 10933 6063 l 10963 6012 l cp +clip +n 9945 5445 m 10935 6030 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 10847 5943 m 10935 6030 l 10816 5995 l 10832 5969 l 10847 5943 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +10698 2634 m 10800 2565 l 10742 2674 l 10832 2574 l 10788 2534 l cp +clip +n 8865 4725 m 10800 2565 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 10698 2634 m 10800 2565 l 10742 2674 l 10720 2654 l 10698 2634 l cp gs 0.00 setgray ef gr col0 s +% Polyline +30.000 slw +n 675 6075 m 5850 6075 l gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +7.500 slw + [15 15] 15 sd +gs clippath +645 6195 m 675 6075 l 705 6195 l 705 6060 l 645 6060 l cp +clip +n 675 6525 m 675 6075 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 645 6195 m 675 6075 l 705 6195 l 675 6195 l 645 6195 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +5880 6405 m 5850 6525 l 5820 6405 l 5820 6540 l 5880 6540 l cp +clip +n 5850 6075 m 5850 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 5880 6405 m 5850 6525 l 5820 6405 l 5850 6405 l 5880 6405 l cp gs col7 1.00 shd ef gr col0 s +% Polyline +30.000 slw +n 900 5625 m 5625 5625 l gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +n 1125 5175 m 5400 5175 l gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +n 1350 4725 m 5175 4725 l gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +n 1575 4275 m 4950 4275 l gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +n 1800 3825 m 4725 3825 l gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +n 2025 3375 m 4500 3375 l gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +n 2250 2925 m 4275 2925 l gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +n 2475 2475 m 4050 2475 l gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +n 2700 2025 m 3825 2025 l gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +n 2925 1575 m 3600 1575 l gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +7.500 slw + [15 15] 15 sd +gs clippath +870 5745 m 900 5625 l 930 5745 l 930 5610 l 870 5610 l cp +clip +n 900 6075 m 900 5625 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 870 5745 m 900 5625 l 930 5745 l 900 5745 l 870 5745 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +1095 5295 m 1125 5175 l 1155 5295 l 1155 5160 l 1095 5160 l cp +clip +n 1125 6525 m 1125 5175 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 1095 5295 m 1125 5175 l 1155 5295 l 1125 5295 l 1095 5295 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +1320 4845 m 1350 4725 l 1380 4845 l 1380 4710 l 1320 4710 l cp +clip +n 1350 5175 m 1350 4725 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 1320 4845 m 1350 4725 l 1380 4845 l 1350 4845 l 1320 4845 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +1545 4395 m 1575 4275 l 1605 4395 l 1605 4260 l 1545 4260 l cp +clip +n 1575 4725 m 1575 4275 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 1545 4395 m 1575 4275 l 1605 4395 l 1575 4395 l 1545 4395 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +1770 3945 m 1800 3825 l 1830 3945 l 1830 3810 l 1770 3810 l cp +clip +n 1800 6525 m 1800 3825 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 1770 3945 m 1800 3825 l 1830 3945 l 1800 3945 l 1770 3945 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +1995 3495 m 2025 3375 l 2055 3495 l 2055 3360 l 1995 3360 l cp +clip +n 2025 3825 m 2025 3375 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 1995 3495 m 2025 3375 l 2055 3495 l 2025 3495 l 1995 3495 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +2220 3045 m 2250 2925 l 2280 3045 l 2280 2910 l 2220 2910 l cp +clip +n 2250 3375 m 2250 2925 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 2220 3045 m 2250 2925 l 2280 3045 l 2250 3045 l 2220 3045 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +2445 2595 m 2475 2475 l 2505 2595 l 2505 2460 l 2445 2460 l cp +clip +n 2475 2925 m 2475 2475 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 2445 2595 m 2475 2475 l 2505 2595 l 2475 2595 l 2445 2595 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +5655 5955 m 5625 6075 l 5595 5955 l 5595 6090 l 5655 6090 l cp +clip +n 5625 5625 m 5625 6075 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 5655 5955 m 5625 6075 l 5595 5955 l 5625 5955 l 5655 5955 l cp gs col7 1.00 shd ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +5430 6405 m 5400 6525 l 5370 6405 l 5370 6540 l 5430 6540 l cp +clip +n 5400 5175 m 5400 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 5430 6405 m 5400 6525 l 5370 6405 l 5400 6405 l 5430 6405 l cp gs col7 1.00 shd ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +5205 5055 m 5175 5175 l 5145 5055 l 5145 5190 l 5205 5190 l cp +clip +n 5175 4725 m 5175 5175 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 5205 5055 m 5175 5175 l 5145 5055 l 5175 5055 l 5205 5055 l cp gs col7 1.00 shd ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +4980 4605 m 4950 4725 l 4920 4605 l 4920 4740 l 4980 4740 l cp +clip +n 4950 4275 m 4950 4725 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 4980 4605 m 4950 4725 l 4920 4605 l 4950 4605 l 4980 4605 l cp gs col7 1.00 shd ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +4755 6405 m 4725 6525 l 4695 6405 l 4695 6540 l 4755 6540 l cp +clip +n 4725 3825 m 4725 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 4755 6405 m 4725 6525 l 4695 6405 l 4725 6405 l 4755 6405 l cp gs col7 1.00 shd ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +4530 3705 m 4500 3825 l 4470 3705 l 4470 3840 l 4530 3840 l cp +clip +n 4500 3375 m 4500 3825 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 4530 3705 m 4500 3825 l 4470 3705 l 4500 3705 l 4530 3705 l cp gs col7 1.00 shd ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +4305 3255 m 4275 3375 l 4245 3255 l 4245 3390 l 4305 3390 l cp +clip +n 4275 2925 m 4275 3375 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 4305 3255 m 4275 3375 l 4245 3255 l 4275 3255 l 4305 3255 l cp gs col7 1.00 shd ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +4080 2805 m 4050 2925 l 4020 2805 l 4020 2940 l 4080 2940 l cp +clip +n 4050 2475 m 4050 2925 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 4080 2805 m 4050 2925 l 4020 2805 l 4050 2805 l 4080 2805 l cp gs col7 1.00 shd ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +2670 2145 m 2700 2025 l 2730 2145 l 2730 2010 l 2670 2010 l cp +clip +n 2700 6525 m 2700 2025 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 2670 2145 m 2700 2025 l 2730 2145 l 2700 2145 l 2670 2145 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +3855 6405 m 3825 6525 l 3795 6405 l 3795 6540 l 3855 6540 l cp +clip +n 3825 2025 m 3825 6525 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 3855 6405 m 3825 6525 l 3795 6405 l 3825 6405 l 3855 6405 l cp gs col7 1.00 shd ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +3630 1905 m 3600 2025 l 3570 1905 l 3570 2040 l 3630 2040 l cp +clip +n 3600 1575 m 3600 2025 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 3630 1905 m 3600 2025 l 3570 1905 l 3600 1905 l 3630 1905 l cp gs col7 1.00 shd ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +2895 1695 m 2925 1575 l 2955 1695 l 2955 1560 l 2895 1560 l cp +clip +n 2925 2025 m 2925 1575 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 2895 1695 m 2925 1575 l 2955 1695 l 2925 1695 l 2895 1695 l cp gs 0.00 setgray ef gr col0 s +% Polyline +45.000 slw +gs clippath +6087 6495 m 6207 6525 l 6087 6555 l 6360 6555 l 6360 6495 l cp +clip +n 540 6525 m 6300 6525 l gs 0.00 setgray ef gr gs col0 s gr gr + +% arrowhead +n 6087 6495 m 6207 6525 l 6087 6555 l 6087 6525 l 6087 6495 l cp gs 0.00 setgray ef gr col0 s +% Polyline +7.500 slw +gs clippath +3681 6720 m 3825 6750 l 3681 6780 l 3840 6780 l 3840 6720 l cp +2844 6780 m 2700 6750 l 2844 6720 l 2685 6720 l 2685 6780 l cp +clip +n 2700 6750 m 3825 6750 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 2844 6780 m 2700 6750 l 2844 6720 l 2820 6750 l 2844 6780 l cp gs col7 1.00 shd ef gr col0 s +% arrowhead +n 3681 6720 m 3825 6750 l 3681 6780 l 3705 6750 l 3681 6720 l cp gs col7 1.00 shd ef gr col0 s +% Polyline +gs clippath +5256 7170 m 5400 7200 l 5256 7230 l 5415 7230 l 5415 7170 l cp +1269 7230 m 1125 7200 l 1269 7170 l 1110 7170 l 1110 7230 l cp +clip +n 1125 7200 m 5400 7200 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 1269 7230 m 1125 7200 l 1269 7170 l 1245 7200 l 1269 7230 l cp gs col7 1.00 shd ef gr col0 s +% arrowhead +n 5256 7170 m 5400 7200 l 5256 7230 l 5280 7200 l 5256 7170 l cp gs col7 1.00 shd ef gr col0 s +% Polyline +gs clippath +4581 6945 m 4725 6975 l 4581 7005 l 4740 7005 l 4740 6945 l cp +1944 7005 m 1800 6975 l 1944 6945 l 1785 6945 l 1785 7005 l cp +clip +n 1800 6975 m 4725 6975 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 1944 7005 m 1800 6975 l 1944 6945 l 1920 6975 l 1944 7005 l cp gs col7 1.00 shd ef gr col0 s +% arrowhead +n 4581 6945 m 4725 6975 l 4581 7005 l 4605 6975 l 4581 6945 l cp gs col7 1.00 shd ef gr col0 s +% Polyline +gs clippath +5706 7395 m 5850 7425 l 5706 7455 l 5865 7455 l 5865 7395 l cp +819 7455 m 675 7425 l 819 7395 l 660 7395 l 660 7455 l cp +clip +n 675 7425 m 5850 7425 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 819 7455 m 675 7425 l 819 7395 l 795 7425 l 819 7455 l cp gs col7 1.00 shd ef gr col0 s +% arrowhead +n 5706 7395 m 5850 7425 l 5706 7455 l 5730 7425 l 5706 7395 l cp gs col7 1.00 shd ef gr col0 s +% Polyline +1 slc + [15 45] 45 sd +n 675 6570 m 675 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 1125 6570 m 1125 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 1800 6570 m 1800 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 2700 6570 m 2700 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 3825 6570 m 3825 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 4725 6570 m 4725 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 5400 6570 m 5400 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd +% Polyline + [15 45] 45 sd +n 5850 6570 m 5850 7650 l gs col34 1.00 shd ef gr gs col0 s gr [] 0 sd +% Polyline +0 slc +n 750 225 m 450 225 450 1050 300 arcto 4 {pop} repeat + 450 1350 12300 1350 300 arcto 4 {pop} repeat + 12600 1350 12600 525 300 arcto 4 {pop} repeat + 12600 225 750 225 300 arcto 4 {pop} repeat + cp gs col34 1.00 shd ef gr gs col0 s gr +% Polyline +n 8835 2250 m 8775 2250 8775 2415 60 arcto 4 {pop} repeat + 8775 2475 10110 2475 60 arcto 4 {pop} repeat + 10170 2475 10170 2310 60 arcto 4 {pop} repeat + 10170 2250 8835 2250 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 10635 2250 m 10575 2250 10575 2415 60 arcto 4 {pop} repeat + 10575 2475 11865 2475 60 arcto 4 {pop} repeat + 11925 2475 11925 2310 60 arcto 4 {pop} repeat + 11925 2250 10635 2250 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 11490 4275 m 11430 4275 11430 4440 60 arcto 4 {pop} repeat + 11430 4500 12315 4500 60 arcto 4 {pop} repeat + 12375 4500 12375 4335 60 arcto 4 {pop} repeat + 12375 4275 11490 4275 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 11040 5175 m 10980 5175 10980 5340 60 arcto 4 {pop} repeat + 10980 5400 12315 5400 60 arcto 4 {pop} repeat + 12375 5400 12375 5235 60 arcto 4 {pop} repeat + 12375 5175 11040 5175 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 9735 5175 m 9675 5175 9675 5340 60 arcto 4 {pop} repeat + 9675 5400 10110 5400 60 arcto 4 {pop} repeat + 10170 5400 10170 5235 60 arcto 4 {pop} repeat + 10170 5175 9735 5175 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 7260 6075 m 7200 6075 7200 6240 60 arcto 4 {pop} repeat + 7200 6300 7815 6300 60 arcto 4 {pop} repeat + 7875 6300 7875 6135 60 arcto 4 {pop} repeat + 7875 6075 7260 6075 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 6810 2250 m 6750 2250 6750 2415 60 arcto 4 {pop} repeat + 6750 2475 8130 2475 60 arcto 4 {pop} repeat + 8190 2475 8190 2310 60 arcto 4 {pop} repeat + 8190 2250 6810 2250 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 6360 3375 m 6300 3375 6300 3540 60 arcto 4 {pop} repeat + 6300 3600 7545 3600 60 arcto 4 {pop} repeat + 7605 3600 7605 3435 60 arcto 4 {pop} repeat + 7605 3375 6360 3375 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 6360 4275 m 6300 4275 6300 4440 60 arcto 4 {pop} repeat + 6300 4500 7275 4500 60 arcto 4 {pop} repeat + 7335 4500 7335 4335 60 arcto 4 {pop} repeat + 7335 4275 6360 4275 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 6360 5175 m 6300 5175 6300 5340 60 arcto 4 {pop} repeat + 6300 5400 7140 5400 60 arcto 4 {pop} repeat + 7200 5400 7200 5235 60 arcto 4 {pop} repeat + 7200 5175 6360 5175 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +gs clippath +7365 5340 m 7245 5310 l 7365 5280 l 7230 5280 l 7230 5340 l cp +clip +n 9630 5310 m 7245 5310 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 7365 5340 m 7245 5310 l 7365 5280 l 7365 5310 l 7365 5340 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +7500 4395 m 7380 4365 l 7500 4335 l 7365 4335 l 7365 4395 l cp +clip +n 11385 4365 m 7380 4365 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 7500 4395 m 7380 4365 l 7500 4335 l 7500 4365 l 7500 4395 l cp gs 0.00 setgray ef gr col0 s +% Polyline +n 11040 5580 m 10980 5580 10980 5745 60 arcto 4 {pop} repeat + 10980 5805 12180 5805 60 arcto 4 {pop} repeat + 12240 5805 12240 5640 60 arcto 4 {pop} repeat + 12240 5580 11040 5580 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 11040 5985 m 10980 5985 10980 6150 60 arcto 4 {pop} repeat + 10980 6210 12315 6210 60 arcto 4 {pop} repeat + 12375 6210 12375 6045 60 arcto 4 {pop} repeat + 12375 5985 11040 5985 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +gs clippath +9958 5554 m 9900 5445 l 10003 5514 l 9912 5414 l 9868 5454 l cp +clip +n 11205 6885 m 9900 5445 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 9958 5554 m 9900 5445 l 10003 5514 l 9981 5534 l 9958 5554 l cp gs 0.00 setgray ef gr col0 s +% Polyline +n 10590 6930 m 10530 6930 10530 7095 60 arcto 4 {pop} repeat + 10530 7155 12225 7155 60 arcto 4 {pop} repeat + 12285 7155 12285 6990 60 arcto 4 {pop} repeat + 12285 6930 10590 6930 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +n 9690 6930 m 9630 6930 9630 7095 60 arcto 4 {pop} repeat + 9630 7155 10110 7155 60 arcto 4 {pop} repeat + 10170 7155 10170 6990 60 arcto 4 {pop} repeat + 10170 6930 9690 6930 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +/Times-Roman-iso ff 120.00 scf sf +900 7560 m +gs 1 -1 sc (Startup, Runtime, Shutdown) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +6345 2970 m +gs 1 -1 sc (ap_ctx_get\(...,) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +10800 2745 m +gs 1 -1 sc (ap_get_module_config\(...) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +10800 2880 m +gs 1 -1 sc (->per_dir_config,) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +10800 3015 m +gs 1 -1 sc (&ssl_module\)) col0 sh gr +% Polyline +n 7980 4770 m 7920 4770 7920 4935 60 arcto 4 {pop} repeat + 7920 4995 9075 4995 60 arcto 4 {pop} repeat + 9135 4995 9135 4830 60 arcto 4 {pop} repeat + 9135 4770 7980 4770 60 arcto 4 {pop} repeat + cp gs col35 1.00 shd ef gr gs col35 s gr +% Polyline +gs clippath +7340 2610 m 7425 2520 l 7393 2639 l 7459 2521 l 7406 2492 l cp +clip +n 6975 3330 m 7425 2520 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 7340 2610 m 7425 2520 l 7393 2639 l 7367 2625 l 7340 2610 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +9336 2569 m 9450 2520 l 9373 2616 l 9480 2535 l 9444 2487 l cp +clip +n 7200 4230 m 9450 2520 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 9336 2569 m 9450 2520 l 9373 2616 l 9354 2593 l 9336 2569 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +7321 5196 m 7200 5220 l 7296 5142 l 7174 5199 l 7199 5254 l cp +clip +n 7875 4905 m 7200 5220 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 7321 5196 m 7200 5220 l 7296 5142 l 7309 5169 l 7321 5196 l cp gs 0.00 setgray ef gr col0 s +% Polyline +gs clippath +6720 4665 m 6750 4545 l 6780 4665 l 6780 4530 l 6720 4530 l cp +clip +n 6750 5130 m 6750 4545 l gs col34 1.00 shd ef gr gs col0 s gr gr + +% arrowhead +n 6720 4665 m 6750 4545 l 6780 4665 l 6750 4665 l 6720 4665 l cp gs 0.00 setgray ef gr col0 s +% Polyline + [15 15] 15 sd +gs clippath +9279 4984 m 9175 4918 l 9298 4927 l 9170 4885 l 9151 4942 l cp +clip +n 9850 5143 m 9175 4918 l gs col34 1.00 shd ef gr gs col0 s gr gr + [] 0 sd +% arrowhead +n 9279 4984 m 9175 4918 l 9298 4927 l 9289 4956 l 9279 4984 l cp gs 0.00 setgray ef gr col0 s +/Helvetica-Narrow-iso ff 120.00 scf sf +6210 4680 m +gs 1 -1 sc (->server) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +8280 6120 m +gs 1 -1 sc (ap_ctx_get\(...,"ssl"\)) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +7740 2700 m +gs 1 -1 sc (ap_get_module_config\(...) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +7740 2835 m +gs 1 -1 sc (->module_config,) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +7740 2970 m +gs 1 -1 sc (&ssl_module\)) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +6345 3105 m +gs 1 -1 sc ("ssl_module"\)) col0 sh gr +/Times-Roman-iso ff 120.00 scf sf +1350 7335 m +gs 1 -1 sc (Configuration Time) col0 sh gr +/Times-Roman-iso ff 120.00 scf sf +2025 7110 m +gs 1 -1 sc (Connection Duration) col0 sh gr +/Times-Roman-iso ff 120.00 scf sf +2835 6885 m +gs 1 -1 sc (Request Duration) col0 sh gr +/Helvetica-Bold-iso ff 300.00 scf sf +6345 6795 m +gs 1 -1 sc (t) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +7110 5985 m +gs 1 -1 sc (->client) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +7065 5085 m +gs 1 -1 sc (->connection) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +7065 4770 m +gs 1 -1 sc (->server) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +8010 5445 m +gs 1 -1 sc (SSL_get_app_data\(\)) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +10530 4050 m +gs 1 -1 sc (->pSSLCtx) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +7875 4275 m +gs 1 -1 sc (SSL_CTX_get_app_data\(\)) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +10305 5535 m +gs 1 -1 sc (SSL_get_current_cipher\(\)) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +10440 5940 m +gs 1 -1 sc (SSL_get_session\(\)) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +9540 7335 m +gs 1 -1 sc (SSL_get_{r,w}bio\(\)) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +10125 4680 m +gs 1 -1 sc (SSL_get_SSL_CTX\(\)) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +10350 5175 m +gs 1 -1 sc (SSL_get_SSL_METHOD\(\)) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +11745 4770 m +gs 1 -1 sc (->method) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +9945 6480 m +gs 1 -1 sc (X509_STORE_CTX_get_app_data\(\)) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +10980 6705 m +gs 1 -1 sc (SSL_CTX_get_cert_store\(\)) col0 sh gr +/Helvetica-Narrow-iso ff 120.00 scf sf +8280 5130 m +gs 1 -1 sc (SSL_get_app_data2\(\)) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +3645 1620 m +gs 1 -1 sc (SSLDirConfig) col0 sh gr +/Helvetica-Bold-iso ff 300.00 scf sf +10935 3645 m +gs 1 -1 sc (OpenSSL) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +10935 3825 m +gs 1 -1 sc ([SSL]) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +11025 5760 m +gs 1 -1 sc (SSL_CIPHER) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +10980 6165 m +gs 1 -1 sc (SSL_SESSION) col0 sh gr +/Helvetica-Bold-iso ff 300.00 scf sf +10710 7605 m +gs 1 -1 sc (OpenSSL) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +10575 7110 m +gs 1 -1 sc (X509_STORE_CTX) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +6795 2430 m +gs 1 -1 sc (SSLModConfig) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +8865 2430 m +gs 1 -1 sc (SSLSrvConfig) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +6345 3555 m +gs 1 -1 sc (ap_global_ctx) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +6345 4455 m +gs 1 -1 sc (server_rec) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +6345 5355 m +gs 1 -1 sc (conn_rec) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +9720 5355 m +gs 1 -1 sc (SSL) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +10665 2430 m +gs 1 -1 sc (SSLDirConfig) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +7290 6255 m +gs 1 -1 sc (BUFF) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +11025 5355 m +gs 1 -1 sc (SSL_METHOD) col0 sh gr +% Polyline +15.000 slw +n 750 225 m 450 225 450 8250 300 arcto 4 {pop} repeat + 450 8550 12300 8550 300 arcto 4 {pop} repeat + 12600 8550 12600 525 300 arcto 4 {pop} repeat + 12600 225 750 225 300 arcto 4 {pop} repeat + cp gs col0 s gr +/Helvetica-Bold-iso ff 180.00 scf sf +11475 4455 m +gs 1 -1 sc (SSL_CTX) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +8010 4950 m +gs 1 -1 sc (request_rec) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +10575 675 m +gs 1 -1 sc (Ralf S. Engelschall) col0 sh gr +/Helvetica-Bold-iso ff 300.00 scf sf +4275 675 m +gs 1 -1 sc (Apache+mod_ssl+OpenSSL) col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +10575 855 m +gs 1 -1 sc (rse@engelschall.com) col0 sh gr +/Times-Roman-iso ff 150.00 scf sf +10575 1035 m +gs 1 -1 sc (www.engelschall.com) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +900 675 m +gs 1 -1 sc (Version 1.3) col0 sh gr +/Times-Roman-iso ff 180.00 scf sf +900 855 m +gs 1 -1 sc (12-Apr-1999) col0 sh gr +/Helvetica-Bold-iso ff 360.00 scf sf +3915 1080 m +gs 1 -1 sc (Data Structure Overview) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +9720 7110 m +gs 1 -1 sc (BIO) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +10710 7785 m +gs 1 -1 sc ([Crypto]) col0 sh gr +/Helvetica-Bold-iso ff 300.00 scf sf +8730 3465 m +gs 1 -1 sc (mod_ssl) col0 sh gr +/Helvetica-Bold-iso ff 300.00 scf sf +8145 6750 m +gs 1 -1 sc (Apache) col0 sh gr +/Helvetica-Bold-iso ff 300.00 scf sf +9000 8100 m +gs 1 -1 sc (Chaining) col0 sh gr +/Helvetica-Bold-iso ff 300.00 scf sf +2745 8100 m +gs 1 -1 sc (Lifetime) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +810 6255 m +gs 1 -1 sc (ap_global_ctx) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +990 5805 m +gs 1 -1 sc (SSLModConfig) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +4050 4455 m +gs 1 -1 sc (SSL_CTX) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +4455 5355 m +gs 1 -1 sc (server_rec) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +3870 4905 m +gs 1 -1 sc (SSLSrvConfig) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +1845 4005 m +gs 1 -1 sc (BUFF) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +2070 3555 m +gs 1 -1 sc (conn_rec) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +2295 3105 m +gs 1 -1 sc (BIO) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +2565 2655 m +gs 1 -1 sc (SSL) col0 sh gr +/Helvetica-Bold-iso ff 180.00 scf sf +3915 2070 m +gs 1 -1 sc (request_rec) col0 sh gr +$F2psEnd +rs +showpage diff --git a/modules/ssl/libssl.module b/modules/ssl/libssl.module new file mode 100644 index 0000000000..72eeabb345 --- /dev/null +++ b/modules/ssl/libssl.module @@ -0,0 +1,497 @@ +## _ _ +## _ __ ___ ___ __| | ___ ___| | mod_ssl +## | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +## | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +## |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +## |_____| +## libssl.module +## Apache 1.3 Configuration mechanism module stub +## + +## +## ==================================================================== +## Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## +## 1. Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## +## 2. Redistributions in binary form must reproduce the above copyright +## notice, this list of conditions and the following +## disclaimer in the documentation and/or other materials +## provided with the distribution. +## +## 3. All advertising materials mentioning features or use of this +## software must display the following acknowledgment: +## "This product includes software developed by +## Ralf S. Engelschall <rse@engelschall.com> for use in the +## mod_ssl project (http://www.modssl.org/)." +## +## 4. The names "mod_ssl" must not be used to endorse or promote +## products derived from this software without prior written +## permission. For written permission, please contact +## rse@engelschall.com. +## +## 5. Products derived from this software may not be called "mod_ssl" +## nor may "mod_ssl" appear in their names without prior +## written permission of Ralf S. Engelschall. +## +## 6. Redistributions of any form whatsoever must retain the following +## acknowledgment: +## "This product includes software developed by +## Ralf S. Engelschall <rse@engelschall.com> for use in the +## mod_ssl project (http://www.modssl.org/)." +## +## THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY +## EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR +## HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +## STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +## OF THE POSSIBILITY OF SUCH DAMAGE. +## ==================================================================== +## + + # ``What you are missing, I suppose, is that I'm not + # prepared to give equal rights to Ralf on the basis + # that he's spent a few hours doing what he thinks is + # better than what I've spent the last 4 years on, + # and so he isn't prepared to cooperate with me.'' + # -- Ben Laurie, Apache-SSL author + +Name: ssl_module +ConfigStart + + # + # interface to the src/Configure script + # + my_dir="`echo ${modfile} | sed -e 's:/[^/]*$::'`" + my_version="$my_dir/libssl.version" + my_outfile="Makefile.config" + my_prefix=" +" + my_prefixe=" " + SSL_CFLAGS='' + SSL_LDFLAGS='' + SSL_LIBS='' + + # + # find a reasonable Bourne Shell for sub-shell calls + # + SH=sh + if [ -f /bin/bash ]; then + SH=/bin/bash + elif [ -f /bin/sh5 ]; then + SH=/bin/sh5 + elif [ -f /bin/sh ]; then + SH=/bin/sh + fi + + # + # determine mod_ssl author version + # + A_ID=`cat $my_version | sed -e 's; .*;;'` + A_NAME=`echo $A_ID | sed -e 's;/.*;;'` + A_VER=`echo $A_ID | sed -e 's;.*/;;'` + A_VER_STR=`echo $A_VER | sed -e 's;-.*;;'` + case $A_VER_STR in + *.*b* ) + A_VER_HEX=`echo "$A_VER_STR" | sed -e 's/b.*//' | awk -F. '{ printf("%d%02d", $1, $2); }' && + echo "$A_VER_STR" | sed -e 's/.*b//' | awk '{ printf("0%02d", $1); }'` + ;; + *.*.* ) + A_VER_HEX=`echo "$A_VER_STR" | awk -F. '{ printf("%d%02d1%02d", $1, $2, $3); }'` + ;; + esac + echo "$my_prefix SSL interface: $A_NAME/$A_VER_STR" + SSL_VERSION="-DMOD_SSL_VERSION=\\\"$A_VER_STR\\\"" + + # + # determine optional mod_ssl product version + # + if [ ".`egrep '.*/.* .*/.*' $my_version`" != . ]; then + P_ID=`cat $my_version | sed -e 's;.* ;;'` + P_NAME=`echo $P_ID | sed -e 's;/.*;;'` + P_VER=`echo $P_ID | sed -e 's;.*/;;'` + P_VER_STR=`echo $P_VER | sed -e 's;-.*;;'` + case $P_VER_STR in + *.*b* ) + P_VER_HEX=`echo "$P_VER_STR" | sed -e 's/b.*//' | awk -F. '{ printf("%d%02d", $1, $2); }' && + echo "$P_VER_STR" | sed -e 's/.*b//' | awk '{ printf("0%02d", $1); }'` + ;; + *.*.* ) + P_VER_HEX=`echo "$P_VER_STR" | awk -F. '{ printf("%d%02d1%02d", $1, $2, $3); }'` + ;; + esac + echo "$my_prefix SSL product: $P_NAME/$P_VER_STR" + SSL_VERSION="$SSL_VERSION -DSSL_PRODUCT_NAME=\\\"$P_NAME\\\"" + SSL_VERSION="$SSL_VERSION -DSSL_PRODUCT_VERSION=\\\"$P_VER_STR\\\"" + fi + + # + # determine object build type + # + case $modfile in + *.so ) my_buildtype="DSO" ;; + * ) my_buildtype="OBJ" ;; + esac + echo "$my_prefix SSL interface build type: $my_buildtype" + + # + # determine SSL rules + # + if [ ".$APXS_MODE" = .YES ]; then + my_rule_SSL_COMPAT=$SSL_COMPAT + my_rule_SSL_SDBM=$SSL_SDBM + my_rule_SSL_EXPERIMENTAL=$SSL_EXPERIMENTAL + my_rule_SSL_CONSERVATIVE=$SSL_CONSERVATIVE + my_rule_SSL_VENDOR=$SSL_VENDOR + else + my_rule_SSL_COMPAT=`$SH helpers/CutRule SSL_COMPAT $file` + my_rule_SSL_SDBM=`$SH helpers/CutRule SSL_SDBM $file` + my_rule_SSL_EXPERIMENTAL=`$SH helpers/CutRule SSL_EXPERIMENTAL $file` + my_rule_SSL_CONSERVATIVE=`$SH helpers/CutRule SSL_CONSERVATIVE $file` + my_rule_SSL_VENDOR=`$SH helpers/CutRule SSL_VENDOR $file` + fi + + # + # determine compatibility mode + # + if [ ".$my_rule_SSL_COMPAT" = .yes ]; then + echo "$my_prefix SSL interface compatibility: enabled" + SSL_CFLAGS="$SSL_CFLAGS -DSSL_COMPAT" + else + echo "$my_prefix SSL interface compatibility: disabled" + fi + + # + # determine experimental mode + # + if [ ".$my_rule_SSL_EXPERIMENTAL" = .yes ]; then + echo "$my_prefix SSL interface experimental code: enabled" + SSL_CFLAGS="$SSL_CFLAGS -DSSL_EXPERIMENTAL" + else + echo "$my_prefix SSL interface experimental code: disabled" + fi + + # + # determine conservative mode + # + if [ ".$my_rule_SSL_CONSERVATIVE" = .yes ]; then + echo "$my_prefix SSL interface conservative code: enabled" + SSL_CFLAGS="$SSL_CFLAGS -DSSL_CONSERVATIVE" + else + echo "$my_prefix SSL interface conservative code: disabled" + fi + + # + # determine vendor mode + # + SSL_VENDOR_OBJS='' + SSL_VENDOR_OBJS_PIC='' + if [ ".$my_rule_SSL_VENDOR" = .yes ]; then + echo "$my_prefix SSL interface vendor extensions: enabled" + SSL_CFLAGS="$SSL_CFLAGS -DSSL_VENDOR" + my_src="`cd $my_dir && echo ssl_vendor*.c`" + if [ ".$my_src" != . -a ".$my_src" != ".ssl_vendor*.c" ]; then + SSL_CFLAGS="$SSL_CFLAGS -DSSL_VENDOR_OBJS" + SSL_VENDOR_OBJS="`echo $my_src | sed -e 's;\.c;.o;g'`" + SSL_VENDOR_OBJS_PIC="`echo $my_src | sed -e 's;\.c;.lo;g'`" + echo "$my_prefix SSL interface vendor objects: $SSL_VENDOR_OBJS" + fi + else + echo "$my_prefix SSL interface vendor extensions: disabled" + fi + + # + # determine DBM support library + # (src/Configure has DBM_LIB predefined for some platforms) + # + if [ ".$APXS_MODE" != .YES ]; then + SSL_DBM_NAME='' + # 1. check for predefined DBM lib + if [ ".$DBM_LIB" != . ]; then + LIBS_ORIG="$LIBS" + LIBS="$LIBS $DBM_LIB" + if $SH helpers/TestCompile func dbm_open; then + SSL_DBM_NAME="Configured DBM ($DBM_LIB)" + SSL_DBM_FLAG="$DBM_LIB" + fi + LIBS="$LIBS_ORIG" + fi + # 2. check for various vendor DBM libs + if [ ".$SSL_DBM_NAME" = . ]; then + if $SH helpers/TestCompile func dbm_open; then + SSL_DBM_NAME='Vendor DBM (libc)' + SSL_DBM_FLAG='' + elif $SH helpers/TestCompile lib dbm dbm_open; then + SSL_DBM_NAME='Vendor DBM (libdbm)' + SSL_DBM_FLAG='-ldbm' + elif $SH helpers/TestCompile lib ndbm dbm_open; then + SSL_DBM_NAME='Vendor DBM (libndbm)' + SSL_DBM_FLAG='-lndbm' + fi + fi + # 3. let the SSL_SDBM rule override decisions + if [ ".$my_rule_SSL_SDBM" = .yes ]; then + # force us to fallback to SDBM + SSL_DBM_NAME='' + fi + if [ ".$my_rule_SSL_SDBM" = .no ]; then + # for us to never use SDBM, but be + # careful when no DBM was found at all + if [ ".$SSL_DBM_NAME" = . ]; then + echo "Error: SDBM is needed, because no custom or vendor DBM library available!" 1>&2 + echo "Hint: Allow us to choose SDBM by changing the rule SSL_SDBM, please." 1>&2 + exit 1 + fi + fi + # 4. override decision on a few brain-dead platforms + if [ ".$my_rule_SSL_SDBM" = .default ]; then + case "$OS" in + Linux ) + # force Linux boxes to use builtin SDBM per default because + # of too much broken vendor DBM libraries on this platform + SSL_DBM_NAME='' + ;; + esac + fi + # 5. finally configure the chosen DBM lib + if [ ".$SSL_DBM_NAME" != . ]; then + echo "$my_prefix SSL interface plugin: $SSL_DBM_NAME" + my_dbm_already_used=`echo $LIBS | grep -- " $SSL_DBM_FLAG"` + if [ ".$my_buildtype" = .OBJ -a ".$my_dbm_already_used" != . ]; then + : + else + SSL_LIBS="$SSL_LIBS $SSL_DBM_FLAG" + fi + else + echo "$my_prefix SSL interface plugin: Built-in SDBM" + SSL_CFLAGS="$SSL_CFLAGS -DSSL_USE_SDBM" + fi + fi + + # + # determine SSL_BASE + # + if [ ".$SSL_BASE" = . ]; then + SSL_BASE=`egrep '^SSL_BASE=' $file | tail -1 | awk -F= '{print $2}'` + if [ ".$SSL_BASE" = . ]; then + if [ -d /usr/local/ssl ]; then + SSL_BASE="/usr/local/ssl" + else + SSL_BASE="SYSTEM" + fi + fi + fi + case $SSL_BASE in + SYSTEM ) ;; + /* ) ;; + * ) SSL_BASE="`cd ../$SSL_BASE; pwd`" ;; + esac + if [ ".$SSL_BASE" = .SYSTEM ]; then + echo "$my_prefix SSL library path: [SYSTEM]" + else + if [ ! -d "$SSL_BASE" ]; then + echo "Error: Cannot find SSL installation in $SSL_BASE" 1>&2 + echo "Hint: Please provide us with the location of OpenSSL" 1>&2 + echo " via the environment variable SSL_BASE." 1>&2 + exit 1 + fi + echo "$my_prefix SSL library path: $SSL_BASE" + fi + + # + # determine location of OpenSSL binaries + # (we still search also for `ssleay' to allow us to + # better complain about the actually installed version) + # + SSL_BINDIR="" + if [ ".$SSL_BASE" = .SYSTEM ]; then + for name in openssl ssleay; do + for p in . `echo $PATH | sed -e 's/:/ /g'`; do + if [ -f "$p/$name" ]; then + SSL_PROGRAM="$p/$name" + SSL_BINDIR="$p" + break + fi + done + if [ ".$SSL_BINDIR" != . ]; then + break; + fi + done + if [ ".$SSL_BINDIR" = . ]; then + echo "Error: Cannot find SSL binaries in $PATH" 1>&2 + exit 1 + fi + else + for name in openssl ssleay; do + if [ -f "$SSL_BASE/bin/$name" ]; then + SSL_PROGRAM="$SSL_BASE/bin/$name" + SSL_BINDIR='$(SSL_BASE)/bin' + break; + fi + if [ -f "$SSL_BASE/apps/$name" ]; then + SSL_PROGRAM="$SSL_BASE/apps/$name" + SSL_BINDIR='$(SSL_BASE)/apps' + break; + fi + done + if [ ".$SSL_BINDIR" = . ]; then + echo "Error: Cannot find SSL binaries under $SSL_BASE" 1>&2 + exit 1 + fi + fi + + # + # SSL version + # + SSL_VERSION_ID="`$SSL_PROGRAM version`" + echo "$my_prefix SSL library version: $SSL_VERSION_ID" + case $SSL_VERSION_ID in + *0.[5678].*|*0.9.[012]* ) + echo "Error: OpenSSL VERSIONS BELOW 0.9.3 ARE NO LONGER SUPPORTED." + echo "Hint: Use OpenSSL version 0.9.3 or higher!" + exit 1 + ;; + esac + + # + # SSL engine support + # + case $SSL_VERSION_ID in + *0.9.6*engine* | *0.9.6a*engine* | *0.9.[789]* ) + SSL_CFLAGS="$SSL_CFLAGS -DSSL_ENGINE" + ;; + esac + + # + # determine location of OpenSSL headers + # + if [ ".$SSL_BASE" = .SYSTEM ]; then + SSL_INCDIR="" + for p in . /usr/include /usr/include/ssl/ /usr/local/include /usr/local/include/ssl; do + if [ -f "$p/openssl/ssl.h" ]; then + SSL_INCDIR="$p" + break + fi + done + if [ ".$SSL_INCDIR" = . ]; then + echo "Error: Cannot find SSL header files in any of the following dirs:" 1>&2 + echo "Error: . /usr/include /usr/include/ssl/ /usr/local/include /usr/local/include/ssl" 1>&2 + exit 1 + fi + else + if [ -f "$SSL_BASE/include/openssl/ssl.h" ]; then + SSL_INCDIR='$(SSL_BASE)/include' + else + echo "Error: Cannot find SSL header files under $SSL_BASE" 1>&2 + exit 1 + fi + fi + if [ ".$SSL_INCDIR" != "./usr/include" ]; then + SSL_CFLAGS="$SSL_CFLAGS -I\$(SSL_INCDIR)" + fi + + # + # determine location of OpenSSL libraries + # + if [ ".$SSL_BASE" = .SYSTEM ]; then + SSL_LIBDIR="" + for p in . /lib /usr/lib /usr/local/lib; do + if [ -f "$p/libssl.a" -o -f "$p/libssl.so" ]; then + SSL_LIBDIR="$p" + my_real_ssl_libdir="$p" + break + fi + done + if [ ".$SSL_LIBDIR" = . ]; then + echo "Error: Cannot find SSL library files in any of the following dirs:" 1>&2 + echo "Error: . /lib /usr/lib /usr/local/lib" 1>&2 + exit 1 + fi + else + if [ -f "$SSL_BASE/libssl.a" -o -f "$SSL_BASE/libssl.so" ]; then + SSL_LIBDIR='$(SSL_BASE)' + my_real_ssl_libdir="$SSL_BASE" + elif [ -f "$SSL_BASE/lib/libssl.a" -o -f "$SSL_BASE/lib/libssl.so" ]; then + SSL_LIBDIR='$(SSL_BASE)/lib' + my_real_ssl_libdir="$SSL_BASE/lib" + else + echo "Error: Cannot find SSL library files under $SSL_BASE" 1>&2 + exit 1 + fi + fi + SSL_LDFLAGS="$SSL_LDFLAGS -L\$(SSL_LIBDIR)" + SSL_LIBS="$SSL_LIBS -lssl -lcrypto" + + # + # SSL installation type + # + case $SSL_BINDIR in + */apps ) my_type="source tree only" ;; + * ) my_type="installed package" ;; + esac + case $SSL_BASE in + SYSTEM ) my_note="(system-wide)" ;; + * ) my_note="(stand-alone)" ;; + esac + echo "$my_prefix SSL library type: $my_type $my_note" + + # + # Special GCC/DSO support + # + # Under some platforms where GCC is used we have to link the DSO + # (libssl.so) explicitly against the GCC library (libgcc) to avoid + # problems with missing symbols like __umoddi3, etc. + # + # Notice: When GCC is installed as "cc" we assume it's really + # well incorporated into the system and no hack is + # needed (like on FreeBSD, Linux, etc.) + # + if [ ".$my_buildtype" = .DSO ]; then + my_CC=`echo "$CC" | sed -e 's/ .*//'` + case $my_CC in + gcc|*/gcc|egcs|*/egcs|egcc|*/egcc|pgcc|*/pgcc ) + gcclibdir="`$CC --print-libgcc-file-name | sed -e 's;/[^/]*$;;'`" + SSL_LIBS="$SSL_LIBS -L$gcclibdir -lgcc" + ;; + esac + fi + + # + # adjust the Apache build environment + # + echo "SSL_BASE=$SSL_BASE" >>$my_outfile + echo "SSL_BINDIR=$SSL_BINDIR" >>$my_outfile + echo "SSL_INCDIR=$SSL_INCDIR" >>$my_outfile + echo "SSL_LIBDIR=$SSL_LIBDIR" >>$my_outfile + echo "SSL_PROGRAM=$SSL_PROGRAM" >>$my_outfile + echo "SSL_VERSION=$SSL_VERSION" >>$my_outfile + echo "SSL_CFLAGS=$SSL_CFLAGS" >>$my_outfile + echo "SSL_VENDOR_OBJS=$SSL_VENDOR_OBJS" >>$my_outfile + echo "SSL_VENDOR_OBJS_PIC=$SSL_VENDOR_OBJS_PIC" >>$my_outfile + if [ ".$my_buildtype" = .DSO ]; then + # under DSO we link ourself + echo "SSL_LIBS=$SSL_LIBS" >>$my_outfile + echo "SSL_LDFLAGS=$SSL_LDFLAGS" >>$my_outfile + else + # else we are linked with httpd + LDFLAGS="$LDFLAGS $SSL_LDFLAGS" + LIBS="$LIBS $SSL_LIBS" + fi + CFLAGS="$CFLAGS -DMOD_SSL=$A_VER_HEX" + if [ ".$P_ID" != . ]; then + CFLAGS="$CFLAGS -DSSL_PRODUCT=$P_VER_HEX" + fi + RULE_EAPI=yes + +ConfigEnd + diff --git a/modules/ssl/libssl.version b/modules/ssl/libssl.version new file mode 100644 index 0000000000..a053095e44 --- /dev/null +++ b/modules/ssl/libssl.version @@ -0,0 +1 @@ +mod_ssl/2.8.3-1.3.19 diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c new file mode 100644 index 0000000000..2d1b5e1b0a --- /dev/null +++ b/modules/ssl/mod_ssl.c @@ -0,0 +1,248 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** mod_ssl.c +** Apache API interface structures +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``I'll be surprised if + others think that what you + are doing is honourable.'' + -- Ben Laurie, Apache-SSL author */ +#include "mod_ssl.h" + +/* _________________________________________________________________ +** +** Apache API glue structures +** _________________________________________________________________ +*/ + +/* + * identify the module to SCCS `what' and RCS `ident' commands + */ +static char const sccsid[] = "@(#) mod_ssl/" MOD_SSL_VERSION " >"; +static char const rcsid[] = "$Id: mod_ssl.c,v 1.1 2001/05/04 21:54:42 rse Exp $"; + +/* + * the table of configuration directives we provide + */ +static command_rec ssl_config_cmds[] = { + /* + * Global (main-server) context configuration directives + */ + AP_SRV_CMD(Mutex, TAKE1, + "SSL lock for handling internal mutual exclusions " + "(`none', `file:/path/to/file')") + AP_SRV_CMD(PassPhraseDialog, TAKE1, + "SSL dialog mechanism for the pass phrase query " + "(`builtin', `exec:/path/to/program')") + AP_SRV_CMD(SessionCache, TAKE1, + "SSL Session Cache storage " + "(`none', `dbm:/path/to/file')") +#ifdef SSL_EXPERIMENTAL_ENGINE + AP_SRV_CMD(CryptoDevice, TAKE1, + "SSL external Crypto Device usage " + "(`builtin', `...')") +#endif + AP_SRV_CMD(RandomSeed, TAKE23, + "SSL Pseudo Random Number Generator (PRNG) seeding source " + "(`startup|connect builtin|file:/path|exec:/path [bytes]')") + + /* + * Per-server context configuration directives + */ + AP_SRV_CMD(Engine, FLAG, + "SSL switch for the protocol engine " + "(`on', `off')") + AP_ALL_CMD(CipherSuite, TAKE1, + "Colon-delimited list of permitted SSL Ciphers " + "(`XXX:...:XXX' - see manual)") + AP_SRV_CMD(CertificateFile, TAKE1, + "SSL Server Certificate file " + "(`/path/to/file' - PEM or DER encoded)") + AP_SRV_CMD(CertificateKeyFile, TAKE1, + "SSL Server Private Key file " + "(`/path/to/file' - PEM or DER encoded)") + AP_SRV_CMD(CertificateChainFile, TAKE1, + "SSL Server CA Certificate Chain file " + "(`/path/to/file' - PEM encoded)") +#ifdef SSL_EXPERIMENTAL_PERDIRCA + AP_ALL_CMD(CACertificatePath, TAKE1, + "SSL CA Certificate path " + "(`/path/to/dir' - contains PEM encoded files)") + AP_ALL_CMD(CACertificateFile, TAKE1, + "SSL CA Certificate file " + "(`/path/to/file' - PEM encoded)") +#else + AP_SRV_CMD(CACertificatePath, TAKE1, + "SSL CA Certificate path " + "(`/path/to/dir' - contains PEM encoded files)") + AP_SRV_CMD(CACertificateFile, TAKE1, + "SSL CA Certificate file " + "(`/path/to/file' - PEM encoded)") +#endif + AP_SRV_CMD(CARevocationPath, TAKE1, + "SSL CA Certificate Revocation List (CRL) path " + "(`/path/to/dir' - contains PEM encoded files)") + AP_SRV_CMD(CARevocationFile, TAKE1, + "SSL CA Certificate Revocation List (CRL) file " + "(`/path/to/file' - PEM encoded)") + AP_ALL_CMD(VerifyClient, TAKE1, + "SSL Client verify type " + "(`none', `optional', `require', `optional_no_ca')") + AP_ALL_CMD(VerifyDepth, TAKE1, + "SSL Client verify depth " + "(`N' - number of intermediate certificates)") + AP_SRV_CMD(SessionCacheTimeout, TAKE1, + "SSL Session Cache object lifetime " + "(`N' - number of seconds)") + AP_SRV_CMD(Log, TAKE1, + "SSL logfile for SSL-related messages " + "(`/path/to/file', `|/path/to/program')") + AP_SRV_CMD(LogLevel, TAKE1, + "SSL logfile verbosity level " + "(`none', `error', `warn', `info', `debug')") + AP_SRV_CMD(Protocol, RAW_ARGS, + "Enable or disable various SSL protocols" + "(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)") + +#ifdef SSL_EXPERIMENTAL_PROXY + /* + * Proxy configuration for remote SSL connections + */ + AP_SRV_CMD(ProxyProtocol, RAW_ARGS, + "SSL Proxy: enable or disable SSL protocol flavors " + "(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)") + AP_SRV_CMD(ProxyCipherSuite, TAKE1, + "SSL Proxy: colon-delimited list of permitted SSL ciphers " + "(`XXX:...:XXX' - see manual)") + AP_SRV_CMD(ProxyVerify, FLAG, + "SSL Proxy: whether to verify the remote certificate " + "(`on' or `off')") + AP_SRV_CMD(ProxyVerifyDepth, TAKE1, + "SSL Proxy: maximum certificate verification depth " + "(`N' - number of intermediate certificates)") + AP_SRV_CMD(ProxyCACertificateFile, TAKE1, + "SSL Proxy: file containing server certificates " + "(`/path/to/file' - PEM encoded certificates)") + AP_SRV_CMD(ProxyCACertificatePath, TAKE1, + "SSL Proxy: directory containing server certificates " + "(`/path/to/dir' - contains PEM encoded certificates)") + AP_SRV_CMD(ProxyMachineCertificateFile, TAKE1, + "SSL Proxy: file containing client certificates " + "(`/path/to/file' - PEM encoded certificates)") + AP_SRV_CMD(ProxyMachineCertificatePath, TAKE1, + "SSL Proxy: directory containing client certificates " + "(`/path/to/dir' - contains PEM encoded certificates)") +#endif + + /* + * Per-directory context configuration directives + */ + AP_DIR_CMD(Options, OPTIONS, RAW_ARGS, + "Set one of more options to configure the SSL engine" + "(`[+-]option[=value] ...' - see manual)") + AP_DIR_CMD(RequireSSL, AUTHCFG, NO_ARGS, + "Require the SSL protocol for the per-directory context " + "(no arguments)") + AP_DIR_CMD(Require, AUTHCFG, RAW_ARGS, + "Require a boolean expresion to evaluate to true for granting access" + "(arbitrary complex boolean expression - see manual)") + + AP_END_CMD +}; + +static const handler_rec ssl_config_handler[] = { + { "mod_ssl:content-handler", ssl_hook_Handler }, + { NULL, NULL } +}; + +/* + * the main Apache API config structure + */ +module MODULE_VAR_EXPORT ssl_module = { + STANDARD_MODULE_STUFF, + + /* Standard API (always present) */ + + ssl_init_Module, /* module initializer */ + ssl_config_perdir_create, /* create per-dir config structures */ + ssl_config_perdir_merge, /* merge per-dir config structures */ + ssl_config_server_create, /* create per-server config structures */ + ssl_config_server_merge, /* merge per-server config structures */ + ssl_config_cmds, /* table of config file commands */ + ssl_config_handler, /* [#8] MIME-typed-dispatched handlers */ + ssl_hook_Translate, /* [#1] URI to filename translation */ + ssl_hook_Auth, /* [#4] validate user id from request */ + ssl_hook_UserCheck, /* [#5] check if the user is ok _here_ */ + ssl_hook_Access, /* [#3] check access by host address */ + NULL, /* [#6] determine MIME type */ + ssl_hook_Fixup, /* [#7] pre-run fixups */ + NULL, /* [#9] log a transaction */ + NULL, /* [#2] header parser */ + ssl_init_Child, /* child_init */ + NULL, /* child_exit */ + ssl_hook_ReadReq, /* [#0] post read-request */ + + /* Extended API (forced to be enabled with mod_ssl) */ + + ssl_hook_AddModule, /* after modules was added to core */ + ssl_hook_RemoveModule, /* before module is removed from core */ + ssl_hook_RewriteCommand, /* configuration command rewriting */ + ssl_hook_NewConnection, /* socket connection open */ + ssl_hook_CloseConnection /* socket connection close */ +}; + diff --git a/modules/ssl/mod_ssl.h b/modules/ssl/mod_ssl.h new file mode 100644 index 0000000000..8731ef9de4 --- /dev/null +++ b/modules/ssl/mod_ssl.h @@ -0,0 +1,854 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** mod_ssl.h +** Global header +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``The Apache Group: a collection + of talented individuals who are + trying to perfect the art of + never finishing something.'' + -- Rob Hartill */ +#ifndef MOD_SSL_H +#define MOD_SSL_H 1 + +/* + * Check whether Extended API (EAPI) is enabled + */ +#ifndef EAPI +#error "mod_ssl requires Extended API (EAPI)" +#endif + +/* + * Optionally enable the experimental stuff, but allow the user to + * override the decision which experimental parts are included by using + * CFLAGS="-DSSL_EXPERIMENTAL_xxxx_IGNORE". + */ +#ifdef SSL_EXPERIMENTAL +#ifndef SSL_EXPERIMENTAL_PERDIRCA_IGNORE +#define SSL_EXPERIMENTAL_PERDIRCA +#endif +#ifndef SSL_EXPERIMENTAL_PROXY_IGNORE +#define SSL_EXPERIMENTAL_PROXY +#endif +#ifdef SSL_ENGINE +#ifndef SSL_EXPERIMENTAL_ENGINE_IGNORE +#define SSL_EXPERIMENTAL_ENGINE +#endif +#endif +#endif /* SSL_EXPERIMENTAL */ + +/* + * Power up our brain... + */ + +/* OS headers */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#ifndef WIN32 +#include <sys/time.h> +#endif +#ifdef WIN32 +#include <wincrypt.h> +#include <winsock2.h> +#endif + +/* OpenSSL headers */ +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/pem.h> +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/rand.h> +#ifdef SSL_EXPERIMENTAL_ENGINE +#include <openssl/engine.h> +#endif + +/* Apache headers */ +#define CORE_PRIVATE +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_conf_globals.h" +#include "http_protocol.h" +#include "http_request.h" +#include "http_main.h" +#include "http_core.h" +#include "http_log.h" +#include "scoreboard.h" +#include "util_md5.h" +#include "fnmatch.h" +#undef CORE_PRIVATE + +/* mod_ssl headers */ +#include "ssl_expr.h" +#include "ssl_util_ssl.h" +#include "ssl_util_table.h" + +/* + * Provide reasonable default for some defines + */ +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif +#ifndef PFALSE +#define PFALSE ((void *)FALSE) +#endif +#ifndef PTRUE +#define PTRUE ((void *)TRUE) +#endif +#ifndef UNSET +#define UNSET (-1) +#endif +#ifndef NUL +#define NUL '\0' +#endif +#ifndef RAND_MAX +#include <limits.h> +#define RAND_MAX INT_MAX +#endif + +/* + * Provide reasonable defines for some types + */ +#ifndef BOOL +#define BOOL unsigned int +#endif +#ifndef UCHAR +#define UCHAR unsigned char +#endif + +/* + * Provide useful shorthands + */ +#define strEQ(s1,s2) (strcmp(s1,s2) == 0) +#define strNE(s1,s2) (strcmp(s1,s2) != 0) +#define strEQn(s1,s2,n) (strncmp(s1,s2,n) == 0) +#define strNEn(s1,s2,n) (strncmp(s1,s2,n) != 0) + +#define strcEQ(s1,s2) (strcasecmp(s1,s2) == 0) +#define strcNE(s1,s2) (strcasecmp(s1,s2) != 0) +#define strcEQn(s1,s2,n) (strncasecmp(s1,s2,n) == 0) +#define strcNEn(s1,s2,n) (strncasecmp(s1,s2,n) != 0) + +#define strIsEmpty(s) (s == NULL || s[0] == NUL) + +#define cfgMerge(el,unset) new->el = add->el == unset ? base->el : add->el +#define cfgMergeArray(el) new->el = ap_append_arrays(p, add->el, base->el) +#define cfgMergeTable(el) new->el = ap_overlay_tables(p, add->el, base->el) +#define cfgMergeCtx(el) new->el = ap_ctx_overlay(p, add->el, base->el) +#define cfgMergeString(el) cfgMerge(el, NULL) +#define cfgMergeBool(el) cfgMerge(el, UNSET) +#define cfgMergeInt(el) cfgMerge(el, UNSET) + +#define myModConfig() (SSLModConfigRec *)ap_ctx_get(ap_global_ctx, "ssl_module") +#define mySrvConfig(srv) (SSLSrvConfigRec *)ap_get_module_config(srv->module_config, &ssl_module) +#define myDirConfig(req) (SSLDirConfigRec *)ap_get_module_config(req->per_dir_config, &ssl_module) + +#define myCtxVarSet(mc,num,val) mc->rCtx.pV##num = val +#define myCtxVarGet(mc,num,type) (type)(mc->rCtx.pV##num) + +#define AP_ALL_CMD(name, args, desc) \ + { "SSL"#name, ssl_cmd_SSL##name, NULL, RSRC_CONF|OR_AUTHCFG, args, desc }, +#define AP_SRV_CMD(name, args, desc) \ + { "SSL"#name, ssl_cmd_SSL##name, NULL, RSRC_CONF, args, desc }, +#define AP_DIR_CMD(name, type, args, desc) \ + { "SSL"#name, ssl_cmd_SSL##name, NULL, OR_##type, args, desc }, +#define AP_END_CMD \ + { NULL } + +/* + * SSL Logging + */ +#define SSL_LOG_NONE (1<<0) +#define SSL_LOG_ERROR (1<<1) +#define SSL_LOG_WARN (1<<2) +#define SSL_LOG_INFO (1<<3) +#define SSL_LOG_TRACE (1<<4) +#define SSL_LOG_DEBUG (1<<5) +#define SSL_LOG_MASK (SSL_LOG_ERROR|SSL_LOG_WARN|SSL_LOG_INFO|SSL_LOG_TRACE|SSL_LOG_DEBUG) + +#define SSL_ADD_NONE (1<<8) +#define SSL_ADD_ERRNO (1<<9) +#define SSL_ADD_SSLERR (1<<10) +#define SSL_NO_TIMESTAMP (1<<11) +#define SSL_NO_LEVELID (1<<12) +#define SSL_NO_NEWLINE (1<<13) + +/* + * Defaults for the configuration + */ +#ifndef SSL_SESSION_CACHE_TIMEOUT +#define SSL_SESSION_CACHE_TIMEOUT 300 +#endif + +/* + * Support for file locking: Try to determine whether we should use fcntl() or + * flock(). Would be better ap_config.h could provide this... :-( + */ +#if defined(USE_FCNTL_SERIALIZED_ACCEPT) +#define SSL_USE_FCNTL 1 +#include <fcntl.h> +#endif +#if defined(USE_FLOCK_SERIALIZED_ACCEPT) +#define SSL_USE_FLOCK 1 +#include <sys/file.h> +#endif +#if !defined(SSL_USE_FCNTL) && !defined(SSL_USE_FLOCK) +#define SSL_USE_FLOCK 1 +#if !defined(MPE) && !defined(WIN32) +#include <sys/file.h> +#endif +#ifndef LOCK_UN +#undef SSL_USE_FLOCK +#define SSL_USE_FCNTL 1 +#include <fcntl.h> +#endif +#endif +#ifdef AIX +#undef SSL_USE_FLOCK +#define SSL_USE_FCNTL 1 +#include <fcntl.h> +#endif + +/* + * Support for Mutex + */ +#ifndef WIN32 +#define SSL_MUTEX_LOCK_MODE ( S_IRUSR|S_IWUSR ) +#else +#define SSL_MUTEX_LOCK_MODE (_S_IREAD|_S_IWRITE ) +#endif +#if defined(USE_SYSVSEM_SERIALIZED_ACCEPT) ||\ + (defined(__FreeBSD__) && defined(__FreeBSD_version) &&\ + __FreeBSD_version >= 300000) ||\ + (defined(LINUX) && defined(__GLIBC__) && defined(__GLIBC_MINOR__) &&\ + LINUX >= 2 && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) ||\ + defined(SOLARIS2) || defined(__hpux) ||\ + (defined (__digital__) && defined (__unix__)) +#define SSL_CAN_USE_SEM +#define SSL_HAVE_IPCSEM +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/sem.h> +/* + * Some platforms have a `union semun' pre-defined but Single Unix + * Specification (SUSv2) says in semctl(2): `If required, it is of + * type union semun, which the application program must explicitly + * declare'. So we define it always ourself to avoid problems (but under + * a different name to avoid a namespace clash). + */ +union ssl_ipc_semun { + long val; + struct semid_ds *buf; + unsigned short int *array; +}; +#endif +#ifdef WIN32 +#define SSL_CAN_USE_SEM +#define SSL_HAVE_W32SEM +#include "multithread.h" +#include <process.h> +#endif + +/* + * Support for MM library + */ +#ifndef WIN32 +#define SSL_MM_FILE_MODE ( S_IRUSR|S_IWUSR ) +#else +#define SSL_MM_FILE_MODE ( _S_IREAD|_S_IWRITE ) +#endif + +/* + * Support for DBM library + */ +#ifndef WIN32 +#define SSL_DBM_FILE_MODE ( S_IRUSR|S_IWUSR ) +#else +#define SSL_USE_SDBM +#define SSL_DBM_FILE_MODE ( _S_IREAD|_S_IWRITE ) +#endif + +#ifdef SSL_USE_SDBM +#include "ssl_util_sdbm.h" +#define ssl_dbm_open sdbm_open +#define ssl_dbm_close sdbm_close +#define ssl_dbm_store sdbm_store +#define ssl_dbm_fetch sdbm_fetch +#define ssl_dbm_delete sdbm_delete +#define ssl_dbm_firstkey sdbm_firstkey +#define ssl_dbm_nextkey sdbm_nextkey +#define SSL_DBM_FILE_SUFFIX_DIR ".dir" +#define SSL_DBM_FILE_SUFFIX_PAG ".pag" +#else /* !SSL_USE_SDBM */ +#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) \ + && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 +#include <db1/ndbm.h> +#else +#include <ndbm.h> +#endif +#define ssl_dbm_open dbm_open +#define ssl_dbm_close dbm_close +#define ssl_dbm_store dbm_store +#define ssl_dbm_fetch dbm_fetch +#define ssl_dbm_delete dbm_delete +#define ssl_dbm_firstkey dbm_firstkey +#define ssl_dbm_nextkey dbm_nextkey +#if !defined(SSL_DBM_FILE_SUFFIX_DIR) && !defined(SSL_DBM_FILE_SUFFIX_PAG) +#if defined(DBM_SUFFIX) +#define SSL_DBM_FILE_SUFFIX_DIR DBM_SUFFIX +#define SSL_DBM_FILE_SUFFIX_PAG DBM_SUFFIX +#elif defined(__FreeBSD__) || (defined(DB_LOCK) && defined(DB_SHMEM)) +#define SSL_DBM_FILE_SUFFIX_DIR ".db" +#define SSL_DBM_FILE_SUFFIX_PAG ".db" +#else +#define SSL_DBM_FILE_SUFFIX_DIR ".dir" +#define SSL_DBM_FILE_SUFFIX_PAG ".pag" +#endif +#endif +#endif /* !SSL_USE_SDBM */ + +/* + * Check for OpenSSL version + */ +#if SSL_LIBRARY_VERSION < 0x00903100 +#error "mod_ssl requires OpenSSL 0.9.3 or higher" +#endif + +/* + * The own data structures + */ +typedef struct { + pool *pPool; + pool *pSubPool; + array_header *aData; +} ssl_ds_array; + +typedef struct { + pool *pPool; + pool *pSubPool; + array_header *aKey; + array_header *aData; +} ssl_ds_table; + +/* + * Define the certificate algorithm types + */ + +typedef int ssl_algo_t; + +#define SSL_ALGO_UNKNOWN (0) +#define SSL_ALGO_RSA (1<<0) +#define SSL_ALGO_DSA (1<<1) +#define SSL_ALGO_ALL (SSL_ALGO_RSA|SSL_ALGO_DSA) + +#define SSL_AIDX_RSA (0) +#define SSL_AIDX_DSA (1) +#define SSL_AIDX_MAX (2) + +/* + * Define IDs for the temporary RSA keys and DH params + */ + +#define SSL_TKP_GEN (0) +#define SSL_TKP_ALLOC (1) +#define SSL_TKP_FREE (2) + +#define SSL_TKPIDX_RSA512 (0) +#define SSL_TKPIDX_RSA1024 (1) +#define SSL_TKPIDX_DH512 (2) +#define SSL_TKPIDX_DH1024 (3) +#define SSL_TKPIDX_MAX (4) + +/* + * Define the SSL options + */ +#define SSL_OPT_NONE (0) +#define SSL_OPT_RELSET (1<<0) +#define SSL_OPT_STDENVVARS (1<<1) +#define SSL_OPT_COMPATENVVARS (1<<2) +#define SSL_OPT_EXPORTCERTDATA (1<<3) +#define SSL_OPT_FAKEBASICAUTH (1<<4) +#define SSL_OPT_STRICTREQUIRE (1<<5) +#define SSL_OPT_OPTRENEGOTIATE (1<<6) +#define SSL_OPT_ALL (SSL_OPT_STDENVVARS|SSL_OPT_COMPATENVVAR|SSL_OPT_EXPORTCERTDATA|SSL_OPT_FAKEBASICAUTH|SSL_OPT_STRICTREQUIRE|SSL_OPT_OPTRENEGOTIATE) +typedef int ssl_opt_t; + +/* + * Define the SSL Protocol options + */ +#define SSL_PROTOCOL_NONE (0) +#define SSL_PROTOCOL_SSLV2 (1<<0) +#define SSL_PROTOCOL_SSLV3 (1<<1) +#define SSL_PROTOCOL_TLSV1 (1<<2) +#define SSL_PROTOCOL_ALL (SSL_PROTOCOL_SSLV2|SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1) +typedef int ssl_proto_t; + +/* + * Define the SSL verify levels + */ +typedef enum { + SSL_CVERIFY_UNSET = UNSET, + SSL_CVERIFY_NONE = 0, + SSL_CVERIFY_OPTIONAL = 1, + SSL_CVERIFY_REQUIRE = 2, + SSL_CVERIFY_OPTIONAL_NO_CA = 3 +} ssl_verify_t; + +/* + * Define the SSL pass phrase dialog types + */ +typedef enum { + SSL_PPTYPE_UNSET = UNSET, + SSL_PPTYPE_BUILTIN = 0, + SSL_PPTYPE_FILTER = 1 +} ssl_pphrase_t; + +/* + * Define the Path Checking modes + */ +#define SSL_PCM_EXISTS 1 +#define SSL_PCM_ISREG 2 +#define SSL_PCM_ISDIR 4 +#define SSL_PCM_ISNONZERO 8 +typedef unsigned int ssl_pathcheck_t; + +/* + * Define the SSL session cache modes and structures + */ +typedef enum { + SSL_SCMODE_UNSET = UNSET, + SSL_SCMODE_NONE = 0, + SSL_SCMODE_DBM = 1, + SSL_SCMODE_SHMHT = 2, + SSL_SCMODE_SHMCB = 3 +} ssl_scmode_t; + +/* + * Define the SSL mutex modes + */ +typedef enum { + SSL_MUTEXMODE_UNSET = UNSET, + SSL_MUTEXMODE_NONE = 0, + SSL_MUTEXMODE_FILE = 1, + SSL_MUTEXMODE_SEM = 2 +} ssl_mutexmode_t; + +/* + * Define the SSL requirement structure + */ +typedef struct { + char *cpExpr; + ssl_expr *mpExpr; +} ssl_require_t; + +/* + * Define the SSL random number generator seeding source + */ +typedef enum { + SSL_RSCTX_STARTUP = 1, + SSL_RSCTX_CONNECT = 2 +} ssl_rsctx_t; +typedef enum { + SSL_RSSRC_BUILTIN = 1, + SSL_RSSRC_FILE = 2, + SSL_RSSRC_EXEC = 3 +#if SSL_LIBRARY_VERSION >= 0x00905100 + ,SSL_RSSRC_EGD = 4 +#endif +} ssl_rssrc_t; +typedef struct { + ssl_rsctx_t nCtx; + ssl_rssrc_t nSrc; + char *cpPath; + int nBytes; +} ssl_randseed_t; + +/* + * Define the structure of an ASN.1 anything + */ +typedef struct { + long int nData; + unsigned char *cpData; +} ssl_asn1_t; + +/* + * Define the mod_ssl per-module configuration structure + * (i.e. the global configuration for each httpd process) + */ + +typedef struct { + pool *pPool; + BOOL bFixed; + int nInitCount; + int nSessionCacheMode; + char *szSessionCacheDataFile; + int nSessionCacheDataSize; + AP_MM *pSessionCacheDataMM; + table_t *tSessionCacheDataTable; + ssl_mutexmode_t nMutexMode; + char *szMutexFile; + int nMutexFD; + int nMutexSEMID; + array_header *aRandSeed; + ssl_ds_table *tTmpKeys; + void *pTmpKeys[SSL_TKPIDX_MAX]; + ssl_ds_table *tPublicCert; + ssl_ds_table *tPrivateKey; +#ifdef SSL_EXPERIMENTAL_ENGINE + char *szCryptoDevice; +#endif + struct { + void *pV1, *pV2, *pV3, *pV4, *pV5, *pV6, *pV7, *pV8, *pV9, *pV10; + } rCtx; +#ifdef SSL_VENDOR + ap_ctx *ctx; +#endif +} SSLModConfigRec; + +/* + * Define the mod_ssl per-server configuration structure + * (i.e. the configuration for the main server + * and all <VirtualHost> contexts) + */ +typedef struct { + BOOL bEnabled; + char *szPublicCertFile[SSL_AIDX_MAX]; + char *szPrivateKeyFile[SSL_AIDX_MAX]; + char *szCertificateChain; + char *szCACertificatePath; + char *szCACertificateFile; + char *szLogFile; + char *szCipherSuite; + FILE *fileLogFile; + int nLogLevel; + int nVerifyDepth; + ssl_verify_t nVerifyClient; + X509 *pPublicCert[SSL_AIDX_MAX]; + EVP_PKEY *pPrivateKey[SSL_AIDX_MAX]; + SSL_CTX *pSSLCtx; + int nSessionCacheTimeout; + int nPassPhraseDialogType; + char *szPassPhraseDialogPath; + ssl_proto_t nProtocol; + char *szCARevocationPath; + char *szCARevocationFile; + X509_STORE *pRevocationStore; +#ifdef SSL_EXPERIMENTAL_PROXY + /* Configuration details for proxy operation */ + ssl_proto_t nProxyProtocol; + int bProxyVerify; + int nProxyVerifyDepth; + char *szProxyCACertificatePath; + char *szProxyCACertificateFile; + char *szProxyClientCertificateFile; + char *szProxyClientCertificatePath; + char *szProxyCipherSuite; + SSL_CTX *pSSLProxyCtx; + STACK_OF(X509_INFO) *skProxyClientCerts; +#endif +#ifdef SSL_VENDOR + ap_ctx *ctx; +#endif +} SSLSrvConfigRec; + +/* + * Define the mod_ssl per-directory configuration structure + * (i.e. the local configuration for all <Directory> + * and .htaccess contexts) + */ +typedef struct { + BOOL bSSLRequired; + array_header *aRequirement; + ssl_opt_t nOptions; + ssl_opt_t nOptionsAdd; + ssl_opt_t nOptionsDel; + char *szCipherSuite; + ssl_verify_t nVerifyClient; + int nVerifyDepth; +#ifdef SSL_EXPERIMENTAL_PERDIRCA + char *szCACertificatePath; + char *szCACertificateFile; +#endif +#ifdef SSL_VENDOR + ap_ctx *ctx; +#endif +} SSLDirConfigRec; + +/* + * function prototypes + */ + +/* API glue structures */ +extern module MODULE_VAR_EXPORT ssl_module; + +/* configuration handling */ +void ssl_config_global_create(void); +void ssl_config_global_fix(void); +BOOL ssl_config_global_isfixed(void); +void *ssl_config_server_create(pool *, server_rec *); +void *ssl_config_server_merge(pool *, void *, void *); +void *ssl_config_perdir_create(pool *, char *); +void *ssl_config_perdir_merge(pool *, void *, void *); +const char *ssl_cmd_SSLMutex(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLPassPhraseDialog(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLCryptoDevice(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLRandomSeed(cmd_parms *, char *, char *, char *, char *); +const char *ssl_cmd_SSLEngine(cmd_parms *, char *, int); +const char *ssl_cmd_SSLCipherSuite(cmd_parms *, SSLDirConfigRec *, char *); +const char *ssl_cmd_SSLCertificateFile(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLCertificateChainFile(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLCACertificatePath(cmd_parms *, SSLDirConfigRec *, char *); +const char *ssl_cmd_SSLCACertificateFile(cmd_parms *, SSLDirConfigRec *, char *); +const char *ssl_cmd_SSLCARevocationPath(cmd_parms *, SSLDirConfigRec *, char *); +const char *ssl_cmd_SSLCARevocationFile(cmd_parms *, SSLDirConfigRec *, char *); +const char *ssl_cmd_SSLVerifyClient(cmd_parms *, SSLDirConfigRec *, char *); +const char *ssl_cmd_SSLVerifyDepth(cmd_parms *, SSLDirConfigRec *, char *); +const char *ssl_cmd_SSLSessionCache(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLSessionCacheTimeout(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLLog(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLLogLevel(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLProtocol(cmd_parms *, char *, const char *); +const char *ssl_cmd_SSLOptions(cmd_parms *, SSLDirConfigRec *, const char *); +const char *ssl_cmd_SSLRequireSSL(cmd_parms *, SSLDirConfigRec *, char *); +const char *ssl_cmd_SSLRequire(cmd_parms *, SSLDirConfigRec *, char *); +#ifdef SSL_EXPERIMENTAL_PROXY +const char *ssl_cmd_SSLProxyProtocol(cmd_parms *, char *, const char *); +const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLProxyVerify(cmd_parms *, char *, int); +const char *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLProxyCACertificateFile(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *, char *, char *); +const char *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *, char *, char *); +#endif + +/* module initialization */ +void ssl_init_Module(server_rec *, pool *); +void ssl_init_SSLLibrary(void); +void ssl_init_Engine(server_rec *, pool *); +void ssl_init_TmpKeysHandle(int, server_rec *, pool *); +void ssl_init_ConfigureServer(server_rec *, pool *, SSLSrvConfigRec *); +void ssl_init_CheckServers(server_rec *, pool *); +STACK_OF(X509_NAME) + *ssl_init_FindCAList(server_rec *, pool *, char *, char *); +void ssl_init_Child(server_rec *, pool *); +void ssl_init_ChildKill(void *); +void ssl_init_ModuleKill(void *); + +/* Apache API hooks */ +void ssl_hook_AddModule(module *); +void ssl_hook_RemoveModule(module *); +char *ssl_hook_RewriteCommand(cmd_parms *, void *, const char *); +void ssl_hook_NewConnection(conn_rec *); +void ssl_hook_TimeoutConnection(int); +void ssl_hook_CloseConnection(conn_rec *); +int ssl_hook_Translate(request_rec *); +int ssl_hook_Auth(request_rec *); +int ssl_hook_UserCheck(request_rec *); +int ssl_hook_Access(request_rec *); +int ssl_hook_Fixup(request_rec *); +int ssl_hook_ReadReq(request_rec *); +int ssl_hook_Handler(request_rec *); + +/* OpenSSL callbacks */ +RSA *ssl_callback_TmpRSA(SSL *, int, int); +DH *ssl_callback_TmpDH(SSL *, int, int); +int ssl_callback_SSLVerify(int, X509_STORE_CTX *); +int ssl_callback_SSLVerify_CRL(int, X509_STORE_CTX *, server_rec *); +int ssl_callback_NewSessionCacheEntry(SSL *, SSL_SESSION *); +SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *, unsigned char *, int, int *); +void ssl_callback_DelSessionCacheEntry(SSL_CTX *, SSL_SESSION *); +void ssl_callback_LogTracingState(SSL *, int, int); + +/* Session Cache Support */ +void ssl_scache_init(server_rec *, pool *); +void ssl_scache_kill(server_rec *); +BOOL ssl_scache_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *); +SSL_SESSION *ssl_scache_retrieve(server_rec *, UCHAR *, int); +void ssl_scache_remove(server_rec *, UCHAR *, int); +void ssl_scache_expire(server_rec *); +void ssl_scache_status(server_rec *, pool *, void (*)(char *, void *), void *); +char *ssl_scache_id2sz(UCHAR *, int); +void ssl_scache_dbm_init(server_rec *, pool *); +void ssl_scache_dbm_kill(server_rec *); +BOOL ssl_scache_dbm_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *); +SSL_SESSION *ssl_scache_dbm_retrieve(server_rec *, UCHAR *, int); +void ssl_scache_dbm_remove(server_rec *, UCHAR *, int); +void ssl_scache_dbm_expire(server_rec *); +void ssl_scache_dbm_status(server_rec *, pool *, void (*)(char *, void *), void *); +void ssl_scache_shmht_init(server_rec *, pool *); +void ssl_scache_shmht_kill(server_rec *); +BOOL ssl_scache_shmht_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *); +SSL_SESSION *ssl_scache_shmht_retrieve(server_rec *, UCHAR *, int); +void ssl_scache_shmht_remove(server_rec *, UCHAR *, int); +void ssl_scache_shmht_expire(server_rec *); +void ssl_scache_shmht_status(server_rec *, pool *, void (*)(char *, void *), void *); +void ssl_scache_shmcb_init(server_rec *, pool *); +void ssl_scache_shmcb_kill(server_rec *); +BOOL ssl_scache_shmcb_store(server_rec *, UCHAR *, int, time_t, SSL_SESSION *); +SSL_SESSION *ssl_scache_shmcb_retrieve(server_rec *, UCHAR *, int); +void ssl_scache_shmcb_remove(server_rec *, UCHAR *, int); +void ssl_scache_shmcb_expire(server_rec *); +void ssl_scache_shmcb_status(server_rec *, pool *, void (*)(char *, void *), void *); + +/* Pass Phrase Support */ +void ssl_pphrase_Handle(server_rec *, pool *); +int ssl_pphrase_Handle_CB(char *, int, int); + +/* Diffie-Hellman Parameter Support */ +DH *ssl_dh_GetTmpParam(int); +DH *ssl_dh_GetParamFromFile(char *); + +/* Data Structures */ +ssl_ds_array *ssl_ds_array_make(pool *, int); +BOOL ssl_ds_array_isempty(ssl_ds_array *); +void *ssl_ds_array_push(ssl_ds_array *); +void *ssl_ds_array_get(ssl_ds_array *, int); +void ssl_ds_array_wipeout(ssl_ds_array *); +void ssl_ds_array_kill(ssl_ds_array *); +ssl_ds_table *ssl_ds_table_make(pool *, int); +BOOL ssl_ds_table_isempty(ssl_ds_table *); +void *ssl_ds_table_push(ssl_ds_table *, char *); +void *ssl_ds_table_get(ssl_ds_table *, char *); +void ssl_ds_table_wipeout(ssl_ds_table *); +void ssl_ds_table_kill(ssl_ds_table *); + +/* Mutex Support */ +void ssl_mutex_init(server_rec *, pool *); +void ssl_mutex_reinit(server_rec *, pool *); +void ssl_mutex_on(server_rec *); +void ssl_mutex_off(server_rec *); +void ssl_mutex_kill(server_rec *s); +void ssl_mutex_file_create(server_rec *, pool *); +void ssl_mutex_file_open(server_rec *, pool *); +void ssl_mutex_file_remove(void *); +BOOL ssl_mutex_file_acquire(void); +BOOL ssl_mutex_file_release(void); +void ssl_mutex_sem_create(server_rec *, pool *); +void ssl_mutex_sem_open(server_rec *, pool *); +void ssl_mutex_sem_remove(void *); +BOOL ssl_mutex_sem_acquire(void); +BOOL ssl_mutex_sem_release(void); + +/* Logfile Support */ +void ssl_log_open(server_rec *, server_rec *, pool *); +BOOL ssl_log_applies(server_rec *, int); +void ssl_log(server_rec *, int, const char *, ...); +void ssl_die(void); + +/* Variables */ +void ssl_var_register(void); +void ssl_var_unregister(void); +char *ssl_var_lookup(pool *, server_rec *, conn_rec *, request_rec *, char *); + +/* I/O */ +void ssl_io_register(void); +void ssl_io_unregister(void); +long ssl_io_data_cb(BIO *, int, const char *, int, long, long); +#ifndef SSL_CONSERVATIVE +void ssl_io_suck(request_rec *, SSL *); +#endif + +/* PRNG */ +int ssl_rand_seed(server_rec *, pool *, ssl_rsctx_t, char *); + +/* Extensions */ +void ssl_ext_register(void); +void ssl_ext_unregister(void); + +/* Compatibility */ +#ifdef SSL_COMPAT +char *ssl_compat_directive(server_rec *, pool *, const char *); +void ssl_compat_variables(request_rec *); +#endif + +/* Utility Functions */ +char *ssl_util_server_root_relative(pool *, char *, char *); +char *ssl_util_vhostid(pool *, server_rec *); +void ssl_util_strupper(char *); +void ssl_util_uuencode(char *, const char *, BOOL); +void ssl_util_uuencode_binary(unsigned char *, const unsigned char *, int, BOOL); +FILE *ssl_util_ppopen(server_rec *, pool *, char *); +int ssl_util_ppopen_child(void *, child_info *); +void ssl_util_ppclose(server_rec *, pool *, FILE *); +char *ssl_util_readfilter(server_rec *, pool *, char *); +BOOL ssl_util_path_check(ssl_pathcheck_t, char *); +ssl_algo_t ssl_util_algotypeof(X509 *, EVP_PKEY *); +char *ssl_util_algotypestr(ssl_algo_t); +char *ssl_util_ptxtsub(pool *, const char *, const char *, char *); +void ssl_util_thread_setup(void); + +/* Vendor extension support */ +#if defined(SSL_VENDOR) && defined(SSL_VENDOR_OBJS) +void ssl_vendor_register(void); +void ssl_vendor_unregister(void); +#endif + +#endif /* MOD_SSL_H */ diff --git a/modules/ssl/ssl_engine_compat.c b/modules/ssl/ssl_engine_compat.c new file mode 100644 index 0000000000..a9fe50fdfe --- /dev/null +++ b/modules/ssl/ssl_engine_compat.c @@ -0,0 +1,511 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_compat.c +** Backward Compatibility +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + + /* ``Backward compatibility is for + users who don't want to live + on the bleeding edge.'' + -- Unknown */ +#ifdef SSL_COMPAT + +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Backward Compatibility +** _________________________________________________________________ +*/ + +/* + * The mapping of obsolete directives to official ones... + */ + +static char *ssl_compat_RequireSSL(pool *, const char *, const char *, const char *); +static char *ssl_compat_SSLSessionLockFile(pool *, const char *, const char *, const char *); +static char *ssl_compat_SSLCacheDisable(pool *, const char *, const char *, const char *); +static char *ssl_compat_SSLRequireCipher(pool *, const char *, const char *, const char *); +static char *ssl_compat_SSLBanCipher(pool *, const char *, const char *, const char *); +static char *ssl_compat_SSL_SessionDir(pool *, const char *, const char *, const char *); +static char *ssl_compat_words2list(pool *, const char *); + +#define CRM_BEGIN /* nop */ +#define CRM_ENTRY(what,action) { what, action }, +#define CRM_END { NULL, NULL, NULL, NULL, NULL, NULL } +#define CRM_CMD(cmd) cmd, NULL, NULL +#define CRM_STR(str) NULL, str, NULL +#define CRM_PAT(cmd) NULL, NULL, pat +#define CRM_LOG(msg) msg, NULL, NULL +#define CRM_SUB(new) NULL, new, NULL +#define CRM_CAL(fct) NULL, NULL, fct + +static struct { + char *cpCommand; + char *cpSubstring; + char *cpPattern; + char *cpMessage; + char *cpSubst; + char *(*fpSubst)(pool *, const char *, const char *, const char *); +} ssl_cmd_rewrite_map[] = { + CRM_BEGIN + + /* + * Apache-SSL 1.x & mod_ssl 2.0.x backward compatibility + */ + CRM_ENTRY( CRM_CMD("SSLEnable"), CRM_SUB("SSLEngine on") ) + CRM_ENTRY( CRM_CMD("SSLDisable"), CRM_SUB("SSLEngine off") ) + CRM_ENTRY( CRM_CMD("SSLLogFile"), CRM_SUB("SSLLog") ) + CRM_ENTRY( CRM_CMD("SSLRequiredCiphers"), CRM_SUB("SSLCipherSuite") ) + CRM_ENTRY( CRM_CMD("SSLRequireCipher"), CRM_CAL(ssl_compat_SSLRequireCipher) ) + CRM_ENTRY( CRM_CMD("SSLBanCipher"), CRM_CAL(ssl_compat_SSLBanCipher) ) + CRM_ENTRY( CRM_CMD("SSLFakeBasicAuth"), CRM_SUB("SSLOptions +FakeBasicAuth") ) + CRM_ENTRY( CRM_CMD("SSLCacheServerPath"), CRM_LOG("Use SSLSessionCache instead") ) + CRM_ENTRY( CRM_CMD("SSLCacheServerPort"), CRM_LOG("Use SSLSessionCache instead") ) + + /* + * Apache-SSL 1.x backward compatibility + */ + CRM_ENTRY( CRM_CMD("SSLExportClientCertificates"), CRM_SUB("SSLOptions +ExportCertData") ) + CRM_ENTRY( CRM_CMD("SSLCacheServerRunDir"), CRM_LOG("Not needed for mod_ssl") ) + + /* + * Sioux 1.x backward compatibility + */ + CRM_ENTRY( CRM_CMD("SSL_CertFile"), CRM_SUB("SSLCertificateFile") ) + CRM_ENTRY( CRM_CMD("SSL_KeyFile"), CRM_SUB("SSLCertificateKeyFile") ) + CRM_ENTRY( CRM_CMD("SSL_CipherSuite"), CRM_SUB("SSLCipherSuite") ) + CRM_ENTRY( CRM_CMD("SSL_X509VerifyDir"), CRM_SUB("SSLCACertificatePath") ) + CRM_ENTRY( CRM_CMD("SSL_Log"), CRM_SUB("SSLLogFile") ) + CRM_ENTRY( CRM_CMD("SSL_Connect"), CRM_SUB("SSLEngine") ) + CRM_ENTRY( CRM_CMD("SSL_ClientAuth"), CRM_SUB("SSLVerifyClient") ) + CRM_ENTRY( CRM_CMD("SSL_X509VerifyDepth"), CRM_SUB("SSLVerifyDepth") ) + CRM_ENTRY( CRM_CMD("SSL_FetchKeyPhraseFrom"), CRM_LOG("Use SSLPassPhraseDialog instead") ) + CRM_ENTRY( CRM_CMD("SSL_SessionDir"), CRM_CAL(ssl_compat_SSL_SessionDir) ) + CRM_ENTRY( CRM_CMD("SSL_Require"), CRM_LOG("Use SSLRequire instead (Syntax!)")) + CRM_ENTRY( CRM_CMD("SSL_CertFileType"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSL_KeyFileType"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSL_X509VerifyPolicy"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSL_LogX509Attributes"), CRM_LOG("Not supported by mod_ssl") ) + + /* + * Stronghold 2.x backward compatibility + */ + CRM_ENTRY( CRM_CMD("StrongholdAccelerator"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("StrongholdKey"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("StrongholdLicenseFile"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSLFlag"), CRM_SUB("SSLEngine") ) + CRM_ENTRY( CRM_CMD("SSLClientCAfile"), CRM_SUB("SSLCACertificateFile") ) + CRM_ENTRY( CRM_CMD("SSLSessionLockFile"), CRM_CAL(ssl_compat_SSLSessionLockFile) ) + CRM_ENTRY( CRM_CMD("SSLCacheDisable"), CRM_CAL(ssl_compat_SSLCacheDisable) ) + CRM_ENTRY( CRM_CMD("RequireSSL"), CRM_CAL(ssl_compat_RequireSSL) ) + CRM_ENTRY( CRM_CMD("SSLCipherList"), CRM_SUB("SSLCipherSuite") ) + CRM_ENTRY( CRM_CMD("SSLErrorFile"), CRM_LOG("Not needed for mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSLRoot"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSL_CertificateLogDir"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("AuthCertDir"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSL_Group"), CRM_LOG("Not supported by mod_ssl") ) +#ifndef SSL_EXPERIMENTAL_PROXY + CRM_ENTRY( CRM_CMD("SSLProxyMachineCertPath"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSLProxyMachineCertFile"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSLProxyCACertificatePath"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSLProxyCACertificateFile"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSLProxyVerifyDepth"), CRM_LOG("Not supported by mod_ssl") ) + CRM_ENTRY( CRM_CMD("SSLProxyCipherList"), CRM_LOG("Not supported by mod_ssl") ) +#else + CRM_ENTRY( CRM_CMD("SSLProxyCipherList"), CRM_SUB("SSLProxyCipherSuite") ) +#endif + + CRM_END +}; + +static char *ssl_compat_RequireSSL( + pool *p, const char *oline, const char *cmd, const char *args) +{ + char *cp; + + for (cp = (char *)args; ap_isspace(*cp); cp++) + ; + if (strcEQ(cp, "on")) + return "SSLRequireSSL"; + return ""; +} + +static char *ssl_compat_SSLSessionLockFile( + pool *p, const char *oline, const char *cmd, const char *args) +{ + char *cp; + + for (cp = (char *)args; ap_isspace(*cp); cp++) + ; + return ap_pstrcat(p, "SSLMutex file:", cp, NULL); +} + +static char *ssl_compat_SSLCacheDisable( + pool *p, const char *oline, const char *cmd, const char *args) +{ + char *cp; + + for (cp = (char *)args; ap_isspace(*cp); cp++) + ; + if (strcEQ(cp, "on")) + return "SSLSessionCache none"; + return ""; +} + +static char *ssl_compat_SSLRequireCipher(pool *p, const char *oline, const char *cmd, const char *args) +{ + return ap_pstrcat(p, "SSLRequire %{SSL_CIPHER} in {", + ssl_compat_words2list(p, args), + "}", NULL); +} + +static char *ssl_compat_SSLBanCipher(pool *p, const char *oline, const char *cmd, const char *args) +{ + return ap_pstrcat(p, "SSLRequire not (%{SSL_CIPHER} in {", + ssl_compat_words2list(p, args), + "})", NULL); +} + +static char *ssl_compat_SSL_SessionDir( + pool *p, const char *oline, const char *cmd, const char *args) +{ + char *cp; + + for (cp = (char *)args; ap_isspace(*cp); cp++) + ; + return ap_pstrcat(p, "SSLSessionCache dir:", cp, NULL); +} + +static char *ssl_compat_words2list(pool *p, const char *oline) +{ + char *line; + char *cpB; + char *cpE; + char *cpI; + char *cpO; + char n; + + /* + * Step 1: Determine borders + */ + cpB = (char *)oline; + while (*cpB == ' ' || *cpB == '\t') + cpB++; + cpE = cpB+strlen(cpB); + while (cpE > cpB && (*(cpE-1) == ' ' || *(cpE-1) == '\t')) + cpE--; + + /* + * Step 2: Determine final size and allocate buffer + */ + for (cpI = cpB, n = 1; cpI < cpE; cpI++) + if ((*cpI == ' ' || *cpI == '\t') && + (cpI > cpB && *(cpI-1) != ' ' && *(cpI-1) != '\t')) + n++; + line = ap_palloc(p, (cpE-cpB)+(n*2)+n+1); + cpI = cpB; + cpO = line; + while (cpI < cpE) { + if ( (*cpI != ' ' && *cpI != '\t') + && ( cpI == cpB + || ( cpI > cpB + && (*(cpI-1) == ' ' || *(cpI-1) == '\t')))) { + *cpO++ = '"'; + *cpO++ = *cpI++; + } + else if ( (*cpI == ' ' || *cpI == '\t') + && ( cpI > cpB + && (*(cpI-1) != ' ' && *(cpI-1) != '\t'))) { + *cpO++ = '"'; + *cpO++ = ','; + *cpO++ = *cpI++; + } + else { + *cpO++ = *cpI++; + } + } + if (cpI > cpB && (*(cpI-1) != ' ' && *(cpI-1) != '\t')) + *cpO++ = '"'; + *cpO++ = NUL; + return line; +} + +char *ssl_compat_directive(server_rec *s, pool *p, const char *oline) +{ + int i; + char *line; + char *cp; + char caCmd[1024]; + char *cpArgs; + int match; + + /* + * Skip comment lines + */ + cp = (char *)oline; + while ((*cp == ' ' || *cp == '\t' || *cp == '\n') && (*cp != NUL)) + cp++; + if (*cp == '#' || *cp == NUL) + return NULL; + + /* + * Extract directive name + */ + cp = (char *)oline; + for (i = 0; *cp != ' ' && *cp != '\t' && *cp != NUL && i < 1024; ) + caCmd[i++] = *cp++; + caCmd[i] = NUL; + cpArgs = cp; + + /* + * Apply rewriting map + */ + line = NULL; + for (i = 0; !(ssl_cmd_rewrite_map[i].cpCommand == NULL && + ssl_cmd_rewrite_map[i].cpPattern == NULL ); i++) { + /* + * Matching + */ + match = FALSE; + if (ssl_cmd_rewrite_map[i].cpCommand != NULL) { + if (strcEQ(ssl_cmd_rewrite_map[i].cpCommand, caCmd)) + match = TRUE; + } + else if (ssl_cmd_rewrite_map[i].cpSubstring != NULL) { + if (strstr(oline, ssl_cmd_rewrite_map[i].cpSubstring) != NULL) + match = TRUE; + } + else if (ssl_cmd_rewrite_map[i].cpPattern != NULL) { + if (ap_fnmatch(ssl_cmd_rewrite_map[i].cpPattern, oline, 0)) + match = TRUE; + } + + /* + * Action Processing + */ + if (match) { + if (ssl_cmd_rewrite_map[i].cpMessage != NULL) { + ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, s, + "mod_ssl:Compat: OBSOLETE '%s' => %s", + oline, ssl_cmd_rewrite_map[i].cpMessage); + line = ""; + break; + } + else if (ssl_cmd_rewrite_map[i].cpSubst != NULL) { + if (ssl_cmd_rewrite_map[i].cpCommand != NULL) + line = ap_pstrcat(p, ssl_cmd_rewrite_map[i].cpSubst, + cpArgs, NULL); + else if (ssl_cmd_rewrite_map[i].cpSubstring != NULL) + line = ssl_util_ptxtsub(p, oline, ssl_cmd_rewrite_map[i].cpSubstring, + ssl_cmd_rewrite_map[i].cpSubst); + else + line = ssl_cmd_rewrite_map[i].cpSubst; + break; + } + else if (ssl_cmd_rewrite_map[i].fpSubst != NULL) { + line = ((char *(*)(pool *, const char *, const char *, const char *)) + (ssl_cmd_rewrite_map[i].fpSubst))(p, oline, caCmd, cpArgs); + break; + } + } + } + if (line != NULL && line[0] != NUL) + ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, s, + "mod_ssl:Compat: MAPPED '%s' => '%s'", oline, line); + return line; +} + +/* + * The mapping of obsolete environment variables to official ones... + */ + +#define VRM_BEGIN /* nop */ +#define VRM_ENTRY(var,action) { var, action }, +#define VRM_END { NULL, NULL, NULL } +#define VRM_VAR(old) old +#define VRM_SUB(new) new, NULL +#define VRM_LOG(msg) NULL, msg + +static struct { + char *cpOld; + char *cpNew; + char *cpMsg; +} ssl_var_rewrite_map[] = { + VRM_BEGIN + + /* + * Apache-SSL 1.x, mod_ssl 2.0.x, Sioux 1.x + * and Stronghold 2.x backward compatibility + */ + VRM_ENTRY( VRM_VAR("SSL_PROTOCOL_VERSION"), VRM_SUB("SSL_PROTOCOL") ) + VRM_ENTRY( VRM_VAR("SSLEAY_VERSION"), VRM_SUB("SSL_VERSION_LIBRARY") ) + VRM_ENTRY( VRM_VAR("HTTPS_SECRETKEYSIZE"), VRM_SUB("SSL_CIPHER_USEKEYSIZE") ) + VRM_ENTRY( VRM_VAR("HTTPS_KEYSIZE"), VRM_SUB("SSL_CIPHER_ALGKEYSIZE") ) + VRM_ENTRY( VRM_VAR("HTTPS_CIPHER"), VRM_SUB("SSL_CIPHER") ) + VRM_ENTRY( VRM_VAR("HTTPS_EXPORT"), VRM_SUB("SSL_CIPHER_EXPORT") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_KEY_SIZE"), VRM_SUB("SSL_CIPHER_ALGKEYSIZE") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_CERTIFICATE"), VRM_SUB("SSL_SERVER_CERT") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_CERT_START"), VRM_SUB("SSL_SERVER_V_START") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_CERT_END"), VRM_SUB("SSL_SERVER_V_END") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_CERT_SERIAL"), VRM_SUB("SSL_SERVER_M_SERIAL") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_SIGNATURE_ALGORITHM"),VRM_SUB("SSL_SERVER_A_SIG") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_DN"), VRM_SUB("SSL_SERVER_S_DN") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_CN"), VRM_SUB("SSL_SERVER_S_DN_CN") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_EMAIL"), VRM_SUB("SSL_SERVER_S_DN_Email") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_O"), VRM_SUB("SSL_SERVER_S_DN_O") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_OU"), VRM_SUB("SSL_SERVER_S_DN_OU") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_C"), VRM_SUB("SSL_SERVER_S_DN_C") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_SP"), VRM_SUB("SSL_SERVER_S_DN_SP") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_L"), VRM_SUB("SSL_SERVER_S_DN_L") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_IDN"), VRM_SUB("SSL_SERVER_I_DN") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_ICN"), VRM_SUB("SSL_SERVER_I_DN_CN") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_IEMAIL"), VRM_SUB("SSL_SERVER_I_DN_Email") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_IO"), VRM_SUB("SSL_SERVER_I_DN_O") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_IOU"), VRM_SUB("SSL_SERVER_I_DN_OU") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_IC"), VRM_SUB("SSL_SERVER_I_DN_C") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_ISP"), VRM_SUB("SSL_SERVER_I_DN_SP") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_IL"), VRM_SUB("SSL_SERVER_I_DN_L") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_CERTIFICATE"), VRM_SUB("SSL_CLIENT_CERT") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_CERT_START"), VRM_SUB("SSL_CLIENT_V_START") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_CERT_END"), VRM_SUB("SSL_CLIENT_V_END") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_CERT_SERIAL"), VRM_SUB("SSL_CLIENT_M_SERIAL") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_SIGNATURE_ALGORITHM"),VRM_SUB("SSL_CLIENT_A_SIG") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_DN"), VRM_SUB("SSL_CLIENT_S_DN") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_CN"), VRM_SUB("SSL_CLIENT_S_DN_CN") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_EMAIL"), VRM_SUB("SSL_CLIENT_S_DN_Email") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_O"), VRM_SUB("SSL_CLIENT_S_DN_O") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_OU"), VRM_SUB("SSL_CLIENT_S_DN_OU") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_C"), VRM_SUB("SSL_CLIENT_S_DN_C") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_SP"), VRM_SUB("SSL_CLIENT_S_DN_SP") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_L"), VRM_SUB("SSL_CLIENT_S_DN_L") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_IDN"), VRM_SUB("SSL_CLIENT_I_DN") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_ICN"), VRM_SUB("SSL_CLIENT_I_DN_CN") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_IEMAIL"), VRM_SUB("SSL_CLIENT_I_DN_Email") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_IO"), VRM_SUB("SSL_CLIENT_I_DN_O") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_IOU"), VRM_SUB("SSL_CLIENT_I_DN_OU") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_IC"), VRM_SUB("SSL_CLIENT_I_DN_C") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_ISP"), VRM_SUB("SSL_CLIENT_I_DN_SP") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_IL"), VRM_SUB("SSL_CLIENT_I_DN_L") ) + VRM_ENTRY( VRM_VAR("SSL_EXPORT"), VRM_SUB("SSL_CIPHER_EXPORT") ) + VRM_ENTRY( VRM_VAR("SSL_KEYSIZE"), VRM_SUB("SSL_CIPHER_ALGKEYSIZE") ) + VRM_ENTRY( VRM_VAR("SSL_SECRETKEYSIZE"), VRM_SUB("SSL_CIPHER_USEKEYSIZE") ) + VRM_ENTRY( VRM_VAR("SSL_SSLEAY_VERSION"), VRM_SUB("SSL_VERSION_LIBRARY") ) + + VRM_ENTRY( VRM_VAR("SSL_STRONG_CRYPTO"), VRM_LOG("Not supported by mod_ssl") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_KEY_EXP"), VRM_LOG("Not supported by mod_ssl") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_KEY_SIZE"), VRM_LOG("Not supported by mod_ssl") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_KEY_ALGORITHM"), VRM_LOG("Not supported by mod_ssl") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_SESSIONDIR"), VRM_LOG("Not supported by mod_ssl") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_CERTIFICATELOGDIR"), VRM_LOG("Not supported by mod_ssl") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_CERTFILE"), VRM_LOG("Not supported by mod_ssl") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_KEYFILE"), VRM_LOG("Not supported by mod_ssl") ) + VRM_ENTRY( VRM_VAR("SSL_SERVER_KEYFILETYPE"), VRM_LOG("Not supported by mod_ssl") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_KEY_EXP"), VRM_LOG("Not supported by mod_ssl") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_KEY_ALGORITHM"), VRM_LOG("Not supported by mod_ssl") ) + VRM_ENTRY( VRM_VAR("SSL_CLIENT_KEY_SIZE"), VRM_LOG("Not supported by mod_ssl") ) + + VRM_END +}; + +void ssl_compat_variables(request_rec *r) +{ + char *cpOld; + char *cpNew; + char *cpMsg; + char *cpVal; + int i; + + for (i = 0; ssl_var_rewrite_map[i].cpOld != NULL; i++) { + cpOld = ssl_var_rewrite_map[i].cpOld; + cpMsg = ssl_var_rewrite_map[i].cpMsg; + cpNew = ssl_var_rewrite_map[i].cpNew; + if (cpNew != NULL) { + cpVal = ssl_var_lookup(r->pool, r->server, r->connection, r, cpNew); + if (!strIsEmpty(cpVal)) + ap_table_set(r->subprocess_env, cpOld, cpVal); + } + else if (cpMsg != NULL) { +#ifdef SSL_VENDOR + /* + * something that isn't provided by mod_ssl, so at least + * let vendor extensions provide a reasonable value first. + */ + cpVal = NULL; + ap_hook_use("ap::mod_ssl::vendor::compat_variables_lookup", + AP_HOOK_SIG3(ptr,ptr,ptr), + AP_HOOK_DECLINE(NULL), + &cpVal, r, cpOld); + if (cpVal != NULL) { + ap_table_set(r->subprocess_env, cpOld, cpVal); + continue; + } +#endif + + /* + * we cannot print a message, so we set at least + * the variables content to the compat message + */ + ap_table_set(r->subprocess_env, cpOld, cpMsg); + } + } + return; +} + +#endif /* SSL_COMPAT */ diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c new file mode 100644 index 0000000000..c47340b223 --- /dev/null +++ b/modules/ssl/ssl_engine_config.c @@ -0,0 +1,1093 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_config.c +** Apache Configuration Directives +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + + /* ``Damned if you do, + damned if you don't.'' + -- Unknown */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Support for Global Configuration +** _________________________________________________________________ +*/ + +void ssl_hook_AddModule(module *m) +{ + if (m == &ssl_module) { + /* + * Announce us for the configuration files + */ + ap_add_config_define("MOD_SSL"); + + /* + * Link ourself into the Apache kernel + */ + ssl_var_register(); + ssl_ext_register(); + ssl_io_register(); +#if defined(SSL_VENDOR) && defined(SSL_VENDOR_OBJS) + ssl_vendor_register(); +#endif + } + return; +} + +void ssl_hook_RemoveModule(module *m) +{ + if (m == &ssl_module) { + /* + * Unlink ourself from the Apache kernel + */ + ssl_var_unregister(); + ssl_ext_unregister(); + ssl_io_unregister(); +#if defined(SSL_VENDOR) && defined(SSL_VENDOR_OBJS) + ssl_vendor_unregister(); +#endif + } + return; +} + +void ssl_config_global_create(void) +{ + pool *pPool; + SSLModConfigRec *mc; + + mc = ap_ctx_get(ap_global_ctx, "ssl_module"); + if (mc == NULL) { + /* + * allocate an own subpool which survives server restarts + */ + pPool = ap_make_sub_pool(NULL); + mc = (SSLModConfigRec *)ap_palloc(pPool, sizeof(SSLModConfigRec)); + mc->pPool = pPool; + mc->bFixed = FALSE; + + /* + * initialize per-module configuration + */ + mc->nInitCount = 0; + mc->nSessionCacheMode = SSL_SCMODE_UNSET; + mc->szSessionCacheDataFile = NULL; + mc->nSessionCacheDataSize = 0; + mc->pSessionCacheDataMM = NULL; + mc->tSessionCacheDataTable = NULL; + mc->nMutexMode = SSL_MUTEXMODE_UNSET; + mc->szMutexFile = NULL; + mc->nMutexFD = -1; + mc->nMutexSEMID = -1; + mc->aRandSeed = ap_make_array(pPool, 4, sizeof(ssl_randseed_t)); + mc->tPrivateKey = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t)); + mc->tPublicCert = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t)); + mc->tTmpKeys = ssl_ds_table_make(pPool, sizeof(ssl_asn1_t)); +#ifdef SSL_EXPERIMENTAL_ENGINE + mc->szCryptoDevice = NULL; +#endif + + (void)memset(mc->pTmpKeys, 0, SSL_TKPIDX_MAX*sizeof(void *)); + +#ifdef SSL_VENDOR + mc->ctx = ap_ctx_new(pPool); + ap_hook_use("ap::mod_ssl::vendor::config_global_create", + AP_HOOK_SIG2(void,ptr), AP_HOOK_MODE_ALL, mc); +#endif + + /* + * And push it into Apache's global context + */ + ap_ctx_set(ap_global_ctx, "ssl_module", mc); + } + return; +} + +void ssl_config_global_fix(void) +{ + SSLModConfigRec *mc = myModConfig(); + mc->bFixed = TRUE; + return; +} + +BOOL ssl_config_global_isfixed(void) +{ + SSLModConfigRec *mc = myModConfig(); + return (mc->bFixed); +} + + +/* _________________________________________________________________ +** +** Configuration handling +** _________________________________________________________________ +*/ + +/* + * Create per-server SSL configuration + */ +void *ssl_config_server_create(pool *p, server_rec *s) +{ + SSLSrvConfigRec *sc; + + ssl_config_global_create(); + + sc = ap_palloc(p, sizeof(SSLSrvConfigRec)); + sc->bEnabled = UNSET; + sc->szCACertificatePath = NULL; + sc->szCACertificateFile = NULL; + sc->szCertificateChain = NULL; + sc->szLogFile = NULL; + sc->szCipherSuite = NULL; + sc->nLogLevel = SSL_LOG_NONE; + sc->nVerifyDepth = UNSET; + sc->nVerifyClient = SSL_CVERIFY_UNSET; + sc->nSessionCacheTimeout = UNSET; + sc->nPassPhraseDialogType = SSL_PPTYPE_UNSET; + sc->szPassPhraseDialogPath = NULL; + sc->nProtocol = SSL_PROTOCOL_ALL; + sc->fileLogFile = NULL; + sc->pSSLCtx = NULL; + sc->szCARevocationPath = NULL; + sc->szCARevocationFile = NULL; + sc->pRevocationStore = NULL; + +#ifdef SSL_EXPERIMENTAL_PROXY + sc->nProxyVerifyDepth = UNSET; + sc->szProxyCACertificatePath = NULL; + sc->szProxyCACertificateFile = NULL; + sc->szProxyClientCertificateFile = NULL; + sc->szProxyClientCertificatePath = NULL; + sc->szProxyCipherSuite = NULL; + sc->nProxyProtocol = SSL_PROTOCOL_ALL & ~SSL_PROTOCOL_TLSV1; + sc->bProxyVerify = UNSET; + sc->pSSLProxyCtx = NULL; +#endif + + (void)memset(sc->szPublicCertFile, 0, SSL_AIDX_MAX*sizeof(char *)); + (void)memset(sc->szPrivateKeyFile, 0, SSL_AIDX_MAX*sizeof(char *)); + (void)memset(sc->pPublicCert, 0, SSL_AIDX_MAX*sizeof(X509 *)); + (void)memset(sc->pPrivateKey, 0, SSL_AIDX_MAX*sizeof(EVP_PKEY *)); + +#ifdef SSL_VENDOR + sc->ctx = ap_ctx_new(p); + ap_hook_use("ap::mod_ssl::vendor::config_server_create", + AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_MODE_ALL, + p, s, sc); +#endif + + return sc; +} + +/* + * Merge per-server SSL configurations + */ +void *ssl_config_server_merge(pool *p, void *basev, void *addv) +{ + SSLSrvConfigRec *base = (SSLSrvConfigRec *)basev; + SSLSrvConfigRec *add = (SSLSrvConfigRec *)addv; + SSLSrvConfigRec *new = (SSLSrvConfigRec *)ap_palloc(p, sizeof(SSLSrvConfigRec)); + int i; + + cfgMergeBool(bEnabled); + cfgMergeString(szCACertificatePath); + cfgMergeString(szCACertificateFile); + cfgMergeString(szCertificateChain); + cfgMergeString(szLogFile); + cfgMergeString(szCipherSuite); + cfgMerge(nLogLevel, SSL_LOG_NONE); + cfgMergeInt(nVerifyDepth); + cfgMerge(nVerifyClient, SSL_CVERIFY_UNSET); + cfgMergeInt(nSessionCacheTimeout); + cfgMerge(nPassPhraseDialogType, SSL_PPTYPE_UNSET); + cfgMergeString(szPassPhraseDialogPath); + cfgMerge(nProtocol, SSL_PROTOCOL_ALL); + cfgMerge(fileLogFile, NULL); + cfgMerge(pSSLCtx, NULL); + cfgMerge(szCARevocationPath, NULL); + cfgMerge(szCARevocationFile, NULL); + cfgMerge(pRevocationStore, NULL); + + for (i = 0; i < SSL_AIDX_MAX; i++) { + cfgMergeString(szPublicCertFile[i]); + cfgMergeString(szPrivateKeyFile[i]); + cfgMerge(pPublicCert[i], NULL); + cfgMerge(pPrivateKey[i], NULL); + } + +#ifdef SSL_VENDOR + cfgMergeCtx(ctx); + ap_hook_use("ap::mod_ssl::vendor::config_server_merge", + AP_HOOK_SIG5(void,ptr,ptr,ptr,ptr), AP_HOOK_MODE_ALL, + p, base, add, new); +#endif + +#ifdef SSL_EXPERIMENTAL_PROXY + cfgMergeInt(nProxyVerifyDepth); + cfgMergeString(szProxyCACertificatePath); + cfgMergeString(szProxyCACertificateFile); + cfgMergeString(szProxyClientCertificateFile); + cfgMergeString(szProxyClientCertificatePath); + cfgMergeString(szProxyCipherSuite); + cfgMerge(nProxyProtocol, (SSL_PROTOCOL_ALL & ~SSL_PROTOCOL_TLSV1)); + cfgMergeBool(bProxyVerify); + cfgMerge(pSSLProxyCtx, NULL); +#endif + + return new; +} + +/* + * Create per-directory SSL configuration + */ +void *ssl_config_perdir_create(pool *p, char *dir) +{ + SSLDirConfigRec *dc = ap_palloc(p, sizeof(SSLDirConfigRec)); + + dc->bSSLRequired = FALSE; + dc->aRequirement = ap_make_array(p, 4, sizeof(ssl_require_t)); + dc->nOptions = SSL_OPT_NONE|SSL_OPT_RELSET; + dc->nOptionsAdd = SSL_OPT_NONE; + dc->nOptionsDel = SSL_OPT_NONE; + + dc->szCipherSuite = NULL; + dc->nVerifyClient = SSL_CVERIFY_UNSET; + dc->nVerifyDepth = UNSET; +#ifdef SSL_EXPERIMENTAL_PERDIRCA + dc->szCACertificatePath = NULL; + dc->szCACertificateFile = NULL; +#endif + +#ifdef SSL_VENDOR + dc->ctx = ap_ctx_new(p); + ap_hook_use("ap::mod_ssl::vendor::config_perdir_create", + AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_MODE_ALL, + p, dir, dc); +#endif + + return dc; +} + +/* + * Merge per-directory SSL configurations + */ +void *ssl_config_perdir_merge(pool *p, void *basev, void *addv) +{ + SSLDirConfigRec *base = (SSLDirConfigRec *)basev; + SSLDirConfigRec *add = (SSLDirConfigRec *)addv; + SSLDirConfigRec *new = (SSLDirConfigRec *)ap_palloc(p, + sizeof(SSLDirConfigRec)); + + cfgMerge(bSSLRequired, FALSE); + cfgMergeArray(aRequirement); + + if (add->nOptions & SSL_OPT_RELSET) { + new->nOptionsAdd = (base->nOptionsAdd & ~(add->nOptionsDel)) | add->nOptionsAdd; + new->nOptionsDel = (base->nOptionsDel & ~(add->nOptionsAdd)) | add->nOptionsDel; + new->nOptions = (base->nOptions & ~(new->nOptionsDel)) | new->nOptionsAdd; + } + else { + new->nOptions = add->nOptions; + new->nOptionsAdd = add->nOptionsAdd; + new->nOptionsDel = add->nOptionsDel; + } + + cfgMergeString(szCipherSuite); + cfgMerge(nVerifyClient, SSL_CVERIFY_UNSET); + cfgMergeInt(nVerifyDepth); +#ifdef SSL_EXPERIMENTAL_PERDIRCA + cfgMergeString(szCACertificatePath); + cfgMergeString(szCACertificateFile); +#endif + +#ifdef SSL_VENDOR + cfgMergeCtx(ctx); + ap_hook_use("ap::mod_ssl::vendor::config_perdir_merge", + AP_HOOK_SIG5(void,ptr,ptr,ptr,ptr), AP_HOOK_MODE_ALL, + p, base, add, new); +#endif + + return new; +} + +/* + * Directive Rewriting + */ + +char *ssl_hook_RewriteCommand(cmd_parms *cmd, void *config, const char *cmd_line) +{ +#ifdef SSL_COMPAT + return ssl_compat_directive(cmd->server, cmd->pool, cmd_line); +#else + return NULL; +#endif +} + +/* + * Configuration functions for particular directives + */ + +const char *ssl_cmd_SSLMutex( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + const char *err; + SSLModConfigRec *mc = myModConfig(); + + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL) + return err; + if (ssl_config_global_isfixed()) + return NULL; + if (strcEQ(arg, "none")) { + mc->nMutexMode = SSL_MUTEXMODE_NONE; + } + else if (strlen(arg) > 5 && strcEQn(arg, "file:", 5)) { +#ifndef WIN32 + mc->nMutexMode = SSL_MUTEXMODE_FILE; + mc->szMutexFile = ap_psprintf(mc->pPool, "%s.%lu", + ssl_util_server_root_relative(cmd->pool, "mutex", arg+5), + (unsigned long)getpid()); +#else + return "SSLMutex: Lockfiles not available on this platform"; +#endif + } + else if (strcEQ(arg, "sem")) { +#ifdef SSL_CAN_USE_SEM + mc->nMutexMode = SSL_MUTEXMODE_SEM; +#else + return "SSLMutex: Semaphores not available on this platform"; +#endif + } + else + return "SSLMutex: Invalid argument"; + return NULL; +} + +const char *ssl_cmd_SSLPassPhraseDialog( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL) + return err; + if (strcEQ(arg, "builtin")) { + sc->nPassPhraseDialogType = SSL_PPTYPE_BUILTIN; + sc->szPassPhraseDialogPath = NULL; + } + else if (strlen(arg) > 5 && strEQn(arg, "exec:", 5)) { + sc->nPassPhraseDialogType = SSL_PPTYPE_FILTER; + sc->szPassPhraseDialogPath = ssl_util_server_root_relative(cmd->pool, "dialog", arg+5); + if (!ssl_util_path_check(SSL_PCM_EXISTS, sc->szPassPhraseDialogPath)) + return ap_pstrcat(cmd->pool, "SSLPassPhraseDialog: file '", + sc->szPassPhraseDialogPath, "' not exists", NULL); + } + else + return "SSLPassPhraseDialog: Invalid argument"; + return NULL; +} + +#ifdef SSL_EXPERIMENTAL_ENGINE +const char *ssl_cmd_SSLCryptoDevice( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLModConfigRec *mc = myModConfig(); + const char *err; + ENGINE *e; +#if SSL_LIBRARY_VERSION >= 0x00907000 + static int loaded_engines = FALSE; + + /* early loading to make sure the engines are already + available for ENGINE_by_id() above... */ + if (!loaded_engines) { + ENGINE_load_builtin_engines(); + loaded_engines = TRUE; + } +#endif + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL) + return err; + if (strcEQ(arg, "builtin")) { + mc->szCryptoDevice = NULL; + } + else if ((e = ENGINE_by_id(arg)) != NULL) { + mc->szCryptoDevice = arg; + ENGINE_free(e); + } + else + return "SSLCryptoDevice: Invalid argument"; + return NULL; +} +#endif + +const char *ssl_cmd_SSLRandomSeed( + cmd_parms *cmd, char *struct_ptr, char *arg1, char *arg2, char *arg3) +{ + SSLModConfigRec *mc = myModConfig(); + const char *err; + ssl_randseed_t *pRS; + + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL) + return err; + if (ssl_config_global_isfixed()) + return NULL; + pRS = ap_push_array(mc->aRandSeed); + if (strcEQ(arg1, "startup")) + pRS->nCtx = SSL_RSCTX_STARTUP; + else if (strcEQ(arg1, "connect")) + pRS->nCtx = SSL_RSCTX_CONNECT; + else + return ap_pstrcat(cmd->pool, "SSLRandomSeed: " + "invalid context: `", arg1, "'"); + if (strlen(arg2) > 5 && strEQn(arg2, "file:", 5)) { + pRS->nSrc = SSL_RSSRC_FILE; + pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2+5)); + } + else if (strlen(arg2) > 5 && strEQn(arg2, "exec:", 5)) { + pRS->nSrc = SSL_RSSRC_EXEC; + pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2+5)); + } +#if SSL_LIBRARY_VERSION >= 0x00905100 + else if (strlen(arg2) > 4 && strEQn(arg2, "egd:", 4)) { + pRS->nSrc = SSL_RSSRC_EGD; + pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2+4)); + } +#endif + else if (strcEQ(arg2, "builtin")) { + pRS->nSrc = SSL_RSSRC_BUILTIN; + pRS->cpPath = NULL; + } + else { + pRS->nSrc = SSL_RSSRC_FILE; + pRS->cpPath = ap_pstrdup(mc->pPool, ssl_util_server_root_relative(cmd->pool, "random", arg2)); + } + if (pRS->nSrc != SSL_RSSRC_BUILTIN) + if (!ssl_util_path_check(SSL_PCM_EXISTS, pRS->cpPath)) + return ap_pstrcat(cmd->pool, "SSLRandomSeed: source path '", + pRS->cpPath, "' not exists", NULL); + if (arg3 == NULL) + pRS->nBytes = 0; /* read whole file */ + else { + if (pRS->nSrc == SSL_RSSRC_BUILTIN) + return "SSLRandomSeed: byte specification not " + "allowed for builtin seed source"; + pRS->nBytes = atoi(arg3); + if (pRS->nBytes < 0) + return "SSLRandomSeed: invalid number of bytes specified"; + } + return NULL; +} + +const char *ssl_cmd_SSLEngine( + cmd_parms *cmd, char *struct_ptr, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->bEnabled = (flag ? TRUE : FALSE); + return NULL; +} + +const char *ssl_cmd_SSLCipherSuite( + cmd_parms *cmd, SSLDirConfigRec *dc, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + if (cmd->path == NULL || dc == NULL) + sc->szCipherSuite = arg; + else + dc->szCipherSuite = arg; + return NULL; +} + +const char *ssl_cmd_SSLCertificateFile( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + char *cpPath; + int i; + + cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg); + if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath)) + return ap_pstrcat(cmd->pool, "SSLCertificateFile: file '", + cpPath, "' not exists or empty", NULL); + for (i = 0; i < SSL_AIDX_MAX && sc->szPublicCertFile[i] != NULL; i++) + ; + if (i == SSL_AIDX_MAX) + return ap_psprintf(cmd->pool, "SSLCertificateFile: only up to %d " + "different certificates per virtual host allowed", + SSL_AIDX_MAX); + sc->szPublicCertFile[i] = cpPath; + return NULL; +} + +const char *ssl_cmd_SSLCertificateKeyFile( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + char *cpPath; + int i; + + cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg); + if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath)) + return ap_pstrcat(cmd->pool, "SSLCertificateKeyFile: file '", + cpPath, "' not exists or empty", NULL); + for (i = 0; i < SSL_AIDX_MAX && sc->szPrivateKeyFile[i] != NULL; i++) + ; + if (i == SSL_AIDX_MAX) + return ap_psprintf(cmd->pool, "SSLCertificateKeyFile: only up to %d " + "different private keys per virtual host allowed", + SSL_AIDX_MAX); + sc->szPrivateKeyFile[i] = cpPath; + return NULL; +} + +const char *ssl_cmd_SSLCertificateChainFile( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + char *cpPath; + + cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg); + if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath)) + return ap_pstrcat(cmd->pool, "SSLCertificateChainFile: file '", + cpPath, "' not exists or empty", NULL); + sc->szCertificateChain = cpPath; + return NULL; +} + +const char *ssl_cmd_SSLCACertificatePath( + cmd_parms *cmd, SSLDirConfigRec *dc, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + char *cpPath; + + cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg); + if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath)) + return ap_pstrcat(cmd->pool, "SSLCACertificatePath: directory '", + cpPath, "' not exists", NULL); +#ifdef SSL_EXPERIMENTAL_PERDIRCA + if (cmd->path == NULL || dc == NULL) + sc->szCACertificatePath = cpPath; + else + dc->szCACertificatePath = cpPath; +#else + sc->szCACertificatePath = cpPath; +#endif + return NULL; +} + +const char *ssl_cmd_SSLCACertificateFile( + cmd_parms *cmd, SSLDirConfigRec *dc, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + char *cpPath; + + cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg); + if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath)) + return ap_pstrcat(cmd->pool, "SSLCACertificateFile: file '", + cpPath, "' not exists or empty", NULL); +#ifdef SSL_EXPERIMENTAL_PERDIRCA + if (cmd->path == NULL || dc == NULL) + sc->szCACertificateFile = cpPath; + else + dc->szCACertificateFile = cpPath; +#else + sc->szCACertificateFile = cpPath; +#endif + return NULL; +} + +const char *ssl_cmd_SSLCARevocationPath( + cmd_parms *cmd, SSLDirConfigRec *dc, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + char *cpPath; + + cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg); + if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath)) + return ap_pstrcat(cmd->pool, "SSLCARecocationPath: directory '", + cpPath, "' not exists", NULL); + sc->szCARevocationPath = cpPath; + return NULL; +} + +const char *ssl_cmd_SSLCARevocationFile( + cmd_parms *cmd, SSLDirConfigRec *dc, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + char *cpPath; + + cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg); + if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath)) + return ap_pstrcat(cmd->pool, "SSLCARevocationFile: file '", + cpPath, "' not exists or empty", NULL); + sc->szCARevocationFile = cpPath; + return NULL; +} + +const char *ssl_cmd_SSLVerifyClient( + cmd_parms *cmd, SSLDirConfigRec *dc, char *level) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + ssl_verify_t id; + + if (strEQ(level, "0") || strcEQ(level, "none")) + id = SSL_CVERIFY_NONE; + else if (strEQ(level, "1") || strcEQ(level, "optional")) + id = SSL_CVERIFY_OPTIONAL; + else if (strEQ(level, "2") || strcEQ(level, "require")) + id = SSL_CVERIFY_REQUIRE; + else if (strEQ(level, "3") || strcEQ(level, "optional_no_ca")) + id = SSL_CVERIFY_OPTIONAL_NO_CA; + else + return "SSLVerifyClient: Invalid argument"; + if (cmd->path == NULL || dc == NULL) + sc->nVerifyClient = id; + else + dc->nVerifyClient = id; + return NULL; +} + +const char *ssl_cmd_SSLVerifyDepth( + cmd_parms *cmd, SSLDirConfigRec *dc, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + int d; + + d = atoi(arg); + if (d < 0) + return "SSLVerifyDepth: Invalid argument"; + if (cmd->path == NULL || dc == NULL) + sc->nVerifyDepth = d; + else + dc->nVerifyDepth = d; + return NULL; +} + +const char *ssl_cmd_SSLSessionCache( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + const char *err; + SSLModConfigRec *mc = myModConfig(); + char *cp, *cp2; + int maxsize; + + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY)) != NULL) + return err; + if (ssl_config_global_isfixed()) + return NULL; + if (strcEQ(arg, "none")) { + mc->nSessionCacheMode = SSL_SCMODE_NONE; + mc->szSessionCacheDataFile = NULL; + } + else if (strlen(arg) > 4 && strcEQn(arg, "dbm:", 4)) { + mc->nSessionCacheMode = SSL_SCMODE_DBM; + mc->szSessionCacheDataFile = ap_pstrdup(mc->pPool, + ssl_util_server_root_relative(cmd->pool, "scache", arg+4)); + } + else if ( (strlen(arg) > 4 && strcEQn(arg, "shm:", 4)) + || (strlen(arg) > 6 && strcEQn(arg, "shmht:", 6))) { + if (!ap_mm_useable()) + return "SSLSessionCache: shared memory cache not useable on this platform"; + mc->nSessionCacheMode = SSL_SCMODE_SHMHT; + cp = strchr(arg, ':'); + mc->szSessionCacheDataFile = ap_pstrdup(mc->pPool, + ssl_util_server_root_relative(cmd->pool, "scache", cp+1)); + mc->tSessionCacheDataTable = NULL; + mc->nSessionCacheDataSize = 1024*512; /* 512KB */ + if ((cp = strchr(mc->szSessionCacheDataFile, '(')) != NULL) { + *cp++ = NUL; + if ((cp2 = strchr(cp, ')')) == NULL) + return "SSLSessionCache: Invalid argument: no closing parenthesis"; + *cp2 = NUL; + mc->nSessionCacheDataSize = atoi(cp); + if (mc->nSessionCacheDataSize <= 8192) + return "SSLSessionCache: Invalid argument: size has to be >= 8192 bytes"; + maxsize = ap_mm_core_maxsegsize(); + if (mc->nSessionCacheDataSize >= maxsize) + return ap_psprintf(cmd->pool, "SSLSessionCache: Invalid argument: " + "size has to be < %d bytes on this platform", maxsize); + } + } + else if (strlen(arg) > 6 && strcEQn(arg, "shmcb:", 6)) { + if (!ap_mm_useable()) + return "SSLSessionCache: shared memory cache not useable on this platform"; + mc->nSessionCacheMode = SSL_SCMODE_SHMCB; + mc->szSessionCacheDataFile = ap_pstrdup(mc->pPool, + ap_server_root_relative(cmd->pool, arg+6)); + mc->tSessionCacheDataTable = NULL; + mc->nSessionCacheDataSize = 1024*512; /* 512KB */ + if ((cp = strchr(mc->szSessionCacheDataFile, '(')) != NULL) { + *cp++ = NUL; + if ((cp2 = strchr(cp, ')')) == NULL) + return "SSLSessionCache: Invalid argument: no closing parenthesis"; + *cp2 = NUL; + mc->nSessionCacheDataSize = atoi(cp); + if (mc->nSessionCacheDataSize <= 8192) + return "SSLSessionCache: Invalid argument: size has to be >= 8192 bytes"; + maxsize = ap_mm_core_maxsegsize(); + if (mc->nSessionCacheDataSize >= maxsize) + return ap_psprintf(cmd->pool, "SSLSessionCache: Invalid argument: " + "size has to be < %d bytes on this platform", maxsize); + } + } + else +#ifdef SSL_VENDOR + if (!ap_hook_use("ap::mod_ssl::vendor::cmd_sslsessioncache", + AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_MODE_ALL, + cmd, arg, mc)) +#endif + return "SSLSessionCache: Invalid argument"; + return NULL; +} + +const char *ssl_cmd_SSLSessionCacheTimeout( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->nSessionCacheTimeout = atoi(arg); + if (sc->nSessionCacheTimeout < 0) + return "SSLSessionCacheTimeout: Invalid argument"; + return NULL; +} + +const char *ssl_cmd_SSLLog( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_DIRECTORY + |NOT_IN_LOCATION|NOT_IN_FILES )) != NULL) + return err; + sc->szLogFile = arg; + return NULL; +} + +const char *ssl_cmd_SSLLogLevel( + cmd_parms *cmd, char *struct_ptr, char *level) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_DIRECTORY + |NOT_IN_LOCATION|NOT_IN_FILES )) != NULL) + return err; + if (strcEQ(level, "none")) + sc->nLogLevel = SSL_LOG_NONE; + else if (strcEQ(level, "error")) + sc->nLogLevel = SSL_LOG_ERROR; + else if (strcEQ(level, "warn")) + sc->nLogLevel = SSL_LOG_WARN; + else if (strcEQ(level, "info")) + sc->nLogLevel = SSL_LOG_INFO; + else if (strcEQ(level, "trace")) + sc->nLogLevel = SSL_LOG_TRACE; + else if (strcEQ(level, "debug")) + sc->nLogLevel = SSL_LOG_DEBUG; + else + return "SSLLogLevel: Invalid argument"; + return NULL; +} + +const char *ssl_cmd_SSLOptions( + cmd_parms *cmd, SSLDirConfigRec *dc, const char *cpLine) +{ + ssl_opt_t opt; + int first; + char action; + char *w; + + first = TRUE; + while (cpLine[0] != NUL) { + w = ap_getword_conf(cmd->pool, &cpLine); + action = NUL; + + if (*w == '+' || *w == '-') { + action = *(w++); + } + else if (first) { + dc->nOptions = SSL_OPT_NONE; + first = FALSE; + } + + if (strcEQ(w, "StdEnvVars")) + opt = SSL_OPT_STDENVVARS; + else if (strcEQ(w, "CompatEnvVars")) + opt = SSL_OPT_COMPATENVVARS; + else if (strcEQ(w, "ExportCertData")) + opt = SSL_OPT_EXPORTCERTDATA; + else if (strcEQ(w, "FakeBasicAuth")) + opt = SSL_OPT_FAKEBASICAUTH; + else if (strcEQ(w, "StrictRequire")) + opt = SSL_OPT_STRICTREQUIRE; + else if (strcEQ(w, "OptRenegotiate")) + opt = SSL_OPT_OPTRENEGOTIATE; + else + return ap_pstrcat(cmd->pool, "SSLOptions: Illegal option '", w, "'", NULL); + + if (action == '-') { + dc->nOptionsAdd &= ~opt; + dc->nOptionsDel |= opt; + dc->nOptions &= ~opt; + } + else if (action == '+') { + dc->nOptionsAdd |= opt; + dc->nOptionsDel &= ~opt; + dc->nOptions |= opt; + } + else { + dc->nOptions = opt; + dc->nOptionsAdd = opt; + dc->nOptionsDel = SSL_OPT_NONE; + } + } + return NULL; +} + +const char *ssl_cmd_SSLRequireSSL( + cmd_parms *cmd, SSLDirConfigRec *dc, char *cipher) +{ + dc->bSSLRequired = TRUE; + return NULL; +} + +const char *ssl_cmd_SSLRequire( + cmd_parms *cmd, SSLDirConfigRec *dc, char *cpExpr) +{ + ssl_expr *mpExpr; + ssl_require_t *pReqRec; + + if ((mpExpr = ssl_expr_comp(cmd->pool, cpExpr)) == NULL) + return ap_pstrcat(cmd->pool, "SSLRequire: ", ssl_expr_get_error(), NULL); + pReqRec = ap_push_array(dc->aRequirement); + pReqRec->cpExpr = ap_pstrdup(cmd->pool, cpExpr); + pReqRec->mpExpr = mpExpr; + return NULL; +} + +const char *ssl_cmd_SSLProtocol( + cmd_parms *cmd, char *struct_ptr, const char *opt) +{ + SSLSrvConfigRec *sc; + ssl_proto_t options, thisopt; + char action; + char *w; + + sc = mySrvConfig(cmd->server); + options = SSL_PROTOCOL_NONE; + while (opt[0] != NUL) { + w = ap_getword_conf(cmd->pool, &opt); + + action = NUL; + if (*w == '+' || *w == '-') + action = *(w++); + + if (strcEQ(w, "SSLv2")) + thisopt = SSL_PROTOCOL_SSLV2; + else if (strcEQ(w, "SSLv3")) + thisopt = SSL_PROTOCOL_SSLV3; + else if (strcEQ(w, "TLSv1")) + thisopt = SSL_PROTOCOL_TLSV1; + else if (strcEQ(w, "all")) + thisopt = SSL_PROTOCOL_ALL; + else + return ap_pstrcat(cmd->pool, "SSLProtocol: Illegal protocol '", w, "'", NULL); + + if (action == '-') + options &= ~thisopt; + else if (action == '+') + options |= thisopt; + else + options = thisopt; + } + sc->nProtocol = options; + return NULL; +} + +#ifdef SSL_EXPERIMENTAL_PROXY + +const char *ssl_cmd_SSLProxyProtocol( + cmd_parms *cmd, char *struct_ptr, const char *opt) +{ + SSLSrvConfigRec *sc; + ssl_proto_t options, thisopt; + char action; + char *w; + + sc = mySrvConfig(cmd->server); + options = SSL_PROTOCOL_NONE; + while (opt[0] != NUL) { + w = ap_getword_conf(cmd->pool, &opt); + + action = NUL; + if (*w == '+' || *w == '-') + action = *(w++); + + if (strcEQ(w, "SSLv2")) + thisopt = SSL_PROTOCOL_SSLV2; + else if (strcEQ(w, "SSLv3")) + thisopt = SSL_PROTOCOL_SSLV3; + else if (strcEQ(w, "TLSv1")) + thisopt = SSL_PROTOCOL_TLSV1; + else if (strcEQ(w, "all")) + thisopt = SSL_PROTOCOL_ALL; + else + return ap_pstrcat(cmd->pool, "SSLProxyProtocol: " + "Illegal protocol '", w, "'", NULL); + if (action == '-') + options &= ~thisopt; + else if (action == '+') + options |= thisopt; + else + options = thisopt; + } + sc->nProxyProtocol = options; + return NULL; +} + +const char *ssl_cmd_SSLProxyCipherSuite( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->szProxyCipherSuite = arg; + return NULL; +} + +const char *ssl_cmd_SSLProxyVerify( + cmd_parms *cmd, char *struct_ptr, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->bProxyVerify = (flag ? TRUE : FALSE); + return NULL; +} + +const char *ssl_cmd_SSLProxyVerifyDepth( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + int d; + + d = atoi(arg); + if (d < 0) + return "SSLProxyVerifyDepth: Invalid argument"; + sc->nProxyVerifyDepth = d; + return NULL; +} + +const char *ssl_cmd_SSLProxyCACertificateFile( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + char *cpPath; + + cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg); + if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath)) + return ap_pstrcat(cmd->pool, "SSLProxyCACertificateFile: file '", + cpPath, "' not exists or empty", NULL); + sc->szProxyCACertificateFile = cpPath; + return NULL; +} + +const char *ssl_cmd_SSLProxyCACertificatePath( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + char *cpPath; + + cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg); + if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath)) + return ap_pstrcat(cmd->pool, "SSLProxyCACertificatePath: directory '", + cpPath, "' does not exists", NULL); + sc->szProxyCACertificatePath = cpPath; + return NULL; +} + +const char *ssl_cmd_SSLProxyMachineCertificateFile( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + char *cpPath; + + cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg); + if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO, cpPath)) + return ap_pstrcat(cmd->pool, "SSLProxyMachineCertFile: file '", + cpPath, "' not exists or empty", NULL); + sc->szProxyClientCertificateFile = cpPath; + return NULL; +} + +const char *ssl_cmd_SSLProxyMachineCertificatePath( + cmd_parms *cmd, char *struct_ptr, char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + char *cpPath; + + cpPath = ssl_util_server_root_relative(cmd->pool, "certkey", arg); + if (!ssl_util_path_check(SSL_PCM_EXISTS|SSL_PCM_ISDIR, cpPath)) + return ap_pstrcat(cmd->pool, "SSLProxyMachineCertPath: directory '", + cpPath, "' does not exists", NULL); + sc->szProxyClientCertificatePath = cpPath; + return NULL; +} + +#endif /* SSL_EXPERIMENTAL_PROXY */ + diff --git a/modules/ssl/ssl_engine_dh.c b/modules/ssl/ssl_engine_dh.c new file mode 100644 index 0000000000..84f49e6657 --- /dev/null +++ b/modules/ssl/ssl_engine_dh.c @@ -0,0 +1,255 @@ +#if 0 +=pod +#endif +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_dh.c +** Diffie-Hellman Built-in Temporary Parameters +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include "mod_ssl.h" + +/* ----BEGIN GENERATED SECTION-------- */ + +/* +** Diffie-Hellman-Parameters: (512 bit) +** prime: +** 00:d4:bc:d5:24:06:f6:9b:35:99:4b:88:de:5d:b8: +** 96:82:c8:15:7f:62:d8:f3:36:33:ee:57:72:f1:1f: +** 05:ab:22:d6:b5:14:5b:9f:24:1e:5a:cc:31:ff:09: +** 0a:4b:c7:11:48:97:6f:76:79:50:94:e7:1e:79:03: +** 52:9f:5a:82:4b +** generator: 2 (0x2) +** Diffie-Hellman-Parameters: (1024 bit) +** prime: +** 00:e6:96:9d:3d:49:5b:e3:2c:7c:f1:80:c3:bd:d4: +** 79:8e:91:b7:81:82:51:bb:05:5e:2a:20:64:90:4a: +** 79:a7:70:fa:15:a2:59:cb:d5:23:a6:a6:ef:09:c4: +** 30:48:d5:a2:2f:97:1f:3c:20:12:9b:48:00:0e:6e: +** dd:06:1c:bc:05:3e:37:1d:79:4e:53:27:df:61:1e: +** bb:be:1b:ac:9b:5c:60:44:cf:02:3d:76:e0:5e:ea: +** 9b:ad:99:1b:13:a6:3c:97:4e:9e:f1:83:9e:b5:db: +** 12:51:36:f7:26:2e:56:a8:87:15:38:df:d8:23:c6: +** 50:50:85:e2:1f:0d:d5:c8:6b +** generator: 2 (0x2) +*/ + +static unsigned char dh512_p[] = +{ + 0xD4, 0xBC, 0xD5, 0x24, 0x06, 0xF6, 0x9B, 0x35, 0x99, 0x4B, 0x88, 0xDE, + 0x5D, 0xB8, 0x96, 0x82, 0xC8, 0x15, 0x7F, 0x62, 0xD8, 0xF3, 0x36, 0x33, + 0xEE, 0x57, 0x72, 0xF1, 0x1F, 0x05, 0xAB, 0x22, 0xD6, 0xB5, 0x14, 0x5B, + 0x9F, 0x24, 0x1E, 0x5A, 0xCC, 0x31, 0xFF, 0x09, 0x0A, 0x4B, 0xC7, 0x11, + 0x48, 0x97, 0x6F, 0x76, 0x79, 0x50, 0x94, 0xE7, 0x1E, 0x79, 0x03, 0x52, + 0x9F, 0x5A, 0x82, 0x4B, +}; +static unsigned char dh512_g[] = +{ + 0x02, +}; + +static DH *get_dh512() +{ + DH *dh; + + if ((dh = DH_new()) == NULL) + return (NULL); + dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL); + dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL); + if ((dh->p == NULL) || (dh->g == NULL)) + return (NULL); + return (dh); +} +static unsigned char dh1024_p[] = +{ + 0xE6, 0x96, 0x9D, 0x3D, 0x49, 0x5B, 0xE3, 0x2C, 0x7C, 0xF1, 0x80, 0xC3, + 0xBD, 0xD4, 0x79, 0x8E, 0x91, 0xB7, 0x81, 0x82, 0x51, 0xBB, 0x05, 0x5E, + 0x2A, 0x20, 0x64, 0x90, 0x4A, 0x79, 0xA7, 0x70, 0xFA, 0x15, 0xA2, 0x59, + 0xCB, 0xD5, 0x23, 0xA6, 0xA6, 0xEF, 0x09, 0xC4, 0x30, 0x48, 0xD5, 0xA2, + 0x2F, 0x97, 0x1F, 0x3C, 0x20, 0x12, 0x9B, 0x48, 0x00, 0x0E, 0x6E, 0xDD, + 0x06, 0x1C, 0xBC, 0x05, 0x3E, 0x37, 0x1D, 0x79, 0x4E, 0x53, 0x27, 0xDF, + 0x61, 0x1E, 0xBB, 0xBE, 0x1B, 0xAC, 0x9B, 0x5C, 0x60, 0x44, 0xCF, 0x02, + 0x3D, 0x76, 0xE0, 0x5E, 0xEA, 0x9B, 0xAD, 0x99, 0x1B, 0x13, 0xA6, 0x3C, + 0x97, 0x4E, 0x9E, 0xF1, 0x83, 0x9E, 0xB5, 0xDB, 0x12, 0x51, 0x36, 0xF7, + 0x26, 0x2E, 0x56, 0xA8, 0x87, 0x15, 0x38, 0xDF, 0xD8, 0x23, 0xC6, 0x50, + 0x50, 0x85, 0xE2, 0x1F, 0x0D, 0xD5, 0xC8, 0x6B, +}; +static unsigned char dh1024_g[] = +{ + 0x02, +}; + +static DH *get_dh1024() +{ + DH *dh; + + if ((dh = DH_new()) == NULL) + return (NULL); + dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL); + dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL); + if ((dh->p == NULL) || (dh->g == NULL)) + return (NULL); + return (dh); +} +/* ----END GENERATED SECTION---------- */ + +DH *ssl_dh_GetTmpParam(int nKeyLen) +{ + DH *dh; + + if (nKeyLen == 512) + dh = get_dh512(); + else if (nKeyLen == 1024) + dh = get_dh1024(); + else + dh = get_dh1024(); + return dh; +} + +DH *ssl_dh_GetParamFromFile(char *file) +{ + DH *dh = NULL; + BIO *bio; + + if ((bio = BIO_new_file(file, "r")) == NULL) + return NULL; +#if SSL_LIBRARY_VERSION < 0x00904000 + dh = PEM_read_bio_DHparams(bio, NULL, NULL); +#else + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); +#endif + BIO_free(bio); + return (dh); +} + +/* +=cut +## +## Embedded Perl script for generating the temporary DH parameters +## + +require 5.003; +use strict; + +# configuration +my $file = $0; +my $begin = '----BEGIN GENERATED SECTION--------'; +my $end = '----END GENERATED SECTION----------'; + +# read ourself and keep a backup +open(FP, "<$file") || die; +my $source = ''; +$source .= $_ while (<FP>); +close(FP); +open(FP, ">$file.bak") || die; +print FP $source; +close(FP); + +# generate the DH parameters +print "1. Generate 512 and 1024 bit Diffie-Hellman parameters (p, g)\n"; +my $rand = ''; +foreach $file (qw(/var/log/messages /var/adm/messages + /kernel /vmunix /vmlinuz /etc/hosts /etc/resolv.conf)) { + if (-f $file) { + $rand = $file if ($rand eq ''); + $rand .= ":$file" if ($rand ne ''); + } +} +$rand = "-rand $rand" if ($rand ne ''); +system("openssl gendh $rand -out dh512.pem 512"); +system("openssl gendh $rand -out dh1024.pem 1024"); + +# generate DH param info +my $dhinfo = ''; +open(FP, "openssl dh -noout -text -in dh512.pem |") || die; +$dhinfo .= $_ while (<FP>); +close(FP); +open(FP, "openssl dh -noout -text -in dh1024.pem |") || die; +$dhinfo .= $_ while (<FP>); +close(FP); +$dhinfo =~ s|^|** |mg; +$dhinfo = "\n\/\*\n$dhinfo\*\/\n\n"; + +# generate C source from DH params +my $dhsource = ''; +open(FP, "openssl dh -noout -C -in dh512.pem | indent | expand -8 |") || die; +$dhsource .= $_ while (<FP>); +close(FP); +open(FP, "openssl dh -noout -C -in dh1024.pem | indent | expand -8 |") || die; +$dhsource .= $_ while (<FP>); +close(FP); +$dhsource =~ s|(DH\s+\*get_dh)|static $1|sg; + +# generate output +my $o = $dhinfo . $dhsource; + +# insert the generated code at the target location +$source =~ s|(\/\* $begin.+?\n).*\n(.*?\/\* $end)|$1$o$2|s; + +# and update the source on disk +print "Updating file `$file'\n"; +open(FP, ">$file") || die; +print FP $source; +close(FP); + +# cleanup +unlink("dh512.pem"); +unlink("dh1024.pem"); + +=pod +*/ diff --git a/modules/ssl/ssl_engine_ds.c b/modules/ssl/ssl_engine_ds.c new file mode 100644 index 0000000000..f0f9e00e48 --- /dev/null +++ b/modules/ssl/ssl_engine_ds.c @@ -0,0 +1,195 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_ds.c +** Additional Data Structures +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``If you can't do it in + C or assembly language, + it isn't worth doing.'' + -- Unknown */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Data Structures which store _arbitrary_ data +** _________________________________________________________________ +*/ + +ssl_ds_array *ssl_ds_array_make(pool *p, int size) +{ + ssl_ds_array *a; + + if ((a = (ssl_ds_array *)ap_palloc(p, sizeof(ssl_ds_array))) == NULL) + return NULL; + a->pPool = p; + if ((a->pSubPool = ap_make_sub_pool(p)) == NULL) + return NULL; + a->aData = ap_make_array(a->pSubPool, 2, size); + return a; +} + +BOOL ssl_ds_array_isempty(ssl_ds_array *a) +{ + if (a == NULL || a->aData == NULL || a->aData->nelts == 0) + return TRUE; + else + return FALSE; +} + +void *ssl_ds_array_push(ssl_ds_array *a) +{ + void *d; + + d = (void *)ap_push_array(a->aData); + return d; +} + +void *ssl_ds_array_get(ssl_ds_array *a, int n) +{ + void *d; + + if (n < 0 || n >= a->aData->nelts) + return NULL; + d = (void *)(a->aData->elts+(a->aData->elt_size*n)); + return d; +} + +void ssl_ds_array_wipeout(ssl_ds_array *a) +{ + if (a->aData->nelts > 0) + memset(a->aData->elts, 0, a->aData->elt_size*a->aData->nelts); + return; +} + +void ssl_ds_array_kill(ssl_ds_array *a) +{ + ap_destroy_pool(a->pSubPool); + a->pSubPool = NULL; + a->aData = NULL; + return; +} + +ssl_ds_table *ssl_ds_table_make(pool *p, int size) +{ + ssl_ds_table *t; + + if ((t = (ssl_ds_table *)ap_palloc(p, sizeof(ssl_ds_table))) == NULL) + return NULL; + t->pPool = p; + if ((t->pSubPool = ap_make_sub_pool(p)) == NULL) + return NULL; + t->aKey = ap_make_array(t->pSubPool, 2, MAX_STRING_LEN); + t->aData = ap_make_array(t->pSubPool, 2, size); + return t; +} + +BOOL ssl_ds_table_isempty(ssl_ds_table *t) +{ + if (t == NULL || t->aKey == NULL || t->aKey->nelts == 0) + return TRUE; + else + return FALSE; +} + +void *ssl_ds_table_push(ssl_ds_table *t, char *key) +{ + char *k; + void *d; + + k = (char *)ap_push_array(t->aKey); + d = (void *)ap_push_array(t->aData); + ap_cpystrn(k, key, t->aKey->elt_size); + return d; +} + +void *ssl_ds_table_get(ssl_ds_table *t, char *key) +{ + char *k; + void *d; + int i; + + d = NULL; + for (i = 0; i < t->aKey->nelts; i++) { + k = (t->aKey->elts+(t->aKey->elt_size*i)); + if (strEQ(k, key)) { + d = (void *)(t->aData->elts+(t->aData->elt_size*i)); + break; + } + } + return d; +} + +void ssl_ds_table_wipeout(ssl_ds_table *t) +{ + if (t->aKey->nelts > 0) { + memset(t->aKey->elts, 0, t->aKey->elt_size*t->aKey->nelts); + memset(t->aData->elts, 0, t->aData->elt_size*t->aData->nelts); + } + return; +} + +void ssl_ds_table_kill(ssl_ds_table *t) +{ + ap_destroy_pool(t->pSubPool); + t->pSubPool = NULL; + t->aKey = NULL; + t->aData = NULL; + return; +} + diff --git a/modules/ssl/ssl_engine_ext.c b/modules/ssl/ssl_engine_ext.c new file mode 100644 index 0000000000..81b283ce40 --- /dev/null +++ b/modules/ssl/ssl_engine_ext.c @@ -0,0 +1,808 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_ext.c +** Extensions to other Apache parts +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``Only those who attempt the absurd + can achieve the impossible.'' + -- Unknown */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** SSL Extensions +** _________________________________________________________________ +*/ + +static void ssl_ext_mlc_register(void); +static void ssl_ext_mlc_unregister(void); +static void ssl_ext_mr_register(void); +static void ssl_ext_mr_unregister(void); +static void ssl_ext_mp_register(void); +static void ssl_ext_mp_unregister(void); +static void ssl_ext_ms_register(void); +static void ssl_ext_ms_unregister(void); + +void ssl_ext_register(void) +{ + ssl_ext_mlc_register(); + ssl_ext_mr_register(); + ssl_ext_mp_register(); + ssl_ext_ms_register(); + return; +} + +void ssl_ext_unregister(void) +{ + ssl_ext_mlc_unregister(); + ssl_ext_mr_unregister(); + ssl_ext_mp_unregister(); + ssl_ext_ms_unregister(); + return; +} + +/* _________________________________________________________________ +** +** SSL Extension to mod_log_config +** _________________________________________________________________ +*/ + +static char *ssl_ext_mlc_log_c(request_rec *r, char *a); +static char *ssl_ext_mlc_log_x(request_rec *r, char *a); + +/* + * register us for the mod_log_config function registering phase + * to establish %{...}c and to be able to expand %{...}x variables. + */ +static void ssl_ext_mlc_register(void) +{ + ap_hook_register("ap::mod_log_config::log_c", + ssl_ext_mlc_log_c, AP_HOOK_NOCTX); + ap_hook_register("ap::mod_log_config::log_x", + ssl_ext_mlc_log_x, AP_HOOK_NOCTX); + return; +} + +static void ssl_ext_mlc_unregister(void) +{ + ap_hook_unregister("ap::mod_log_config::log_c", + ssl_ext_mlc_log_c); + ap_hook_unregister("ap::mod_log_config::log_x", + ssl_ext_mlc_log_x); + return; +} + +/* + * implement the %{..}c log function + * (we are the only function) + */ +static char *ssl_ext_mlc_log_c(request_rec *r, char *a) +{ + char *result; + + if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL) + return NULL; + result = NULL; + if (strEQ(a, "version")) + result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_PROTOCOL"); + else if (strEQ(a, "cipher")) + result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER"); + else if (strEQ(a, "subjectdn") || strEQ(a, "clientcert")) + result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_S_DN"); + else if (strEQ(a, "issuerdn") || strEQ(a, "cacert")) + result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_I_DN"); + else if (strEQ(a, "errcode")) + result = "-"; + else if (strEQ(a, "errstr")) + result = ap_ctx_get(r->connection->client->ctx, "ssl::verify::error"); + if (result != NULL && result[0] == NUL) + result = NULL; + return result; +} + +/* + * extend the implementation of the %{..}x log function + * (there can be more functions) + */ +static char *ssl_ext_mlc_log_x(request_rec *r, char *a) +{ + char *result; + + result = NULL; + if (ap_ctx_get(r->connection->client->ctx, "ssl") != NULL) + result = ssl_var_lookup(r->pool, r->server, r->connection, r, a); + if (result != NULL && result[0] == NUL) + result = NULL; + return result; +} + +/* _________________________________________________________________ +** +** SSL Extension to mod_rewrite +** _________________________________________________________________ +*/ + +static char *ssl_ext_mr_lookup_variable(request_rec *r, char *var); + +/* + * register us for the mod_rewrite lookup_variable() function + */ +static void ssl_ext_mr_register(void) +{ + ap_hook_register("ap::mod_rewrite::lookup_variable", + ssl_ext_mr_lookup_variable, AP_HOOK_NOCTX); + return; +} + +static void ssl_ext_mr_unregister(void) +{ + ap_hook_unregister("ap::mod_rewrite::lookup_variable", + ssl_ext_mr_lookup_variable); + return; +} + +static char *ssl_ext_mr_lookup_variable(request_rec *r, char *var) +{ + char *val; + + val = ssl_var_lookup(r->pool, r->server, r->connection, r, var); + if (val[0] == NUL) + val = NULL; + return val; +} + +/* _________________________________________________________________ +** +** SSL Extension to mod_proxy +** _________________________________________________________________ +*/ + +static int ssl_ext_mp_canon(request_rec *, char *); +static int ssl_ext_mp_handler(request_rec *, void *, char *, char *, int, char *); +static int ssl_ext_mp_set_destport(request_rec *); +static char *ssl_ext_mp_new_connection(request_rec *, BUFF *, char *); +static void ssl_ext_mp_close_connection(void *); +static int ssl_ext_mp_write_host_header(request_rec *, BUFF *, char *, int, char *); +#ifdef SSL_EXPERIMENTAL_PROXY +static void ssl_ext_mp_init(server_rec *, pool *); +static int ssl_ext_mp_verify_cb(int, X509_STORE_CTX *); +static int ssl_ext_mp_clientcert_cb(SSL *, X509 **, EVP_PKEY **); +#endif + +/* + * register us ... + */ +static void ssl_ext_mp_register(void) +{ +#ifdef SSL_EXPERIMENTAL_PROXY + ap_hook_register("ap::mod_proxy::init", + ssl_ext_mp_init, AP_HOOK_NOCTX); +#endif + ap_hook_register("ap::mod_proxy::canon", + ssl_ext_mp_canon, AP_HOOK_NOCTX); + ap_hook_register("ap::mod_proxy::handler", + ssl_ext_mp_handler, AP_HOOK_NOCTX); + ap_hook_register("ap::mod_proxy::http::handler::set_destport", + ssl_ext_mp_set_destport, AP_HOOK_NOCTX); + ap_hook_register("ap::mod_proxy::http::handler::new_connection", + ssl_ext_mp_new_connection, AP_HOOK_NOCTX); + ap_hook_register("ap::mod_proxy::http::handler::write_host_header", + ssl_ext_mp_write_host_header, AP_HOOK_NOCTX); + return; +} + +static void ssl_ext_mp_unregister(void) +{ +#ifdef SSL_EXPERIMENTAL_PROXY + ap_hook_unregister("ap::mod_proxy::init", ssl_ext_mp_init); +#endif + ap_hook_unregister("ap::mod_proxy::canon", ssl_ext_mp_canon); + ap_hook_unregister("ap::mod_proxy::handler", ssl_ext_mp_handler); + ap_hook_unregister("ap::mod_proxy::http::handler::set_destport", + ssl_ext_mp_set_destport); + ap_hook_unregister("ap::mod_proxy::http::handler::new_connection", + ssl_ext_mp_new_connection); + ap_hook_unregister("ap::mod_proxy::http::handler::write_host_header", + ssl_ext_mp_write_host_header); + return; +} + +/* + * SSL proxy initialization + */ +#ifdef SSL_EXPERIMENTAL_PROXY +static void ssl_ext_mp_init(server_rec *s, pool *p) +{ + SSLSrvConfigRec *sc; + char *cpVHostID; + int nVerify; + SSL_CTX *ctx; + char *cp; + STACK_OF(X509_INFO) *sk; + + /* + * Initialize each virtual server + */ + ERR_clear_error(); + for (; s != NULL; s = s->next) { + sc = mySrvConfig(s); + cpVHostID = ssl_util_vhostid(p, s); + + if (sc->bProxyVerify == UNSET) + sc->bProxyVerify = FALSE; + + /* + * Create new SSL context and configure callbacks + */ + if (sc->nProxyProtocol == SSL_PROTOCOL_NONE) { + ssl_log(s, SSL_LOG_ERROR, + "Init: (%s) No Proxy SSL protocols available [hint: SSLProxyProtocol]", + cpVHostID); + ssl_die(); + } + cp = ap_pstrcat(p, (sc->nProxyProtocol & SSL_PROTOCOL_SSLV2 ? "SSLv2, " : ""), + (sc->nProxyProtocol & SSL_PROTOCOL_SSLV3 ? "SSLv3, " : ""), + (sc->nProxyProtocol & SSL_PROTOCOL_TLSV1 ? "TLSv1, " : ""), NULL); + cp[strlen(cp)-2] = NUL; + ssl_log(s, SSL_LOG_TRACE, + "Init: (%s) Creating new proxy SSL context (protocols: %s)", + cpVHostID, cp); + if (sc->nProxyProtocol == SSL_PROTOCOL_SSLV2) + ctx = SSL_CTX_new(SSLv2_client_method()); /* only SSLv2 is left */ + else + ctx = SSL_CTX_new(SSLv23_client_method()); /* be more flexible */ + if (ctx == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to create SSL Proxy context", cpVHostID); + ssl_die(); + } + sc->pSSLProxyCtx = ctx; + SSL_CTX_set_options(ctx, SSL_OP_ALL); + if (!(sc->nProxyProtocol & SSL_PROTOCOL_SSLV2)) + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2); + if (!(sc->nProxyProtocol & SSL_PROTOCOL_SSLV3)) + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3); + if (!(sc->nProxyProtocol & SSL_PROTOCOL_TLSV1)) + SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1); + + if (sc->szProxyClientCertificateFile || sc->szProxyClientCertificatePath) { + sk = sk_X509_INFO_new_null(); + if (sc->szProxyClientCertificateFile) + SSL_load_CrtAndKeyInfo_file(p, sk, sc->szProxyClientCertificateFile); + if (sc->szProxyClientCertificatePath) + SSL_load_CrtAndKeyInfo_path(p, sk, sc->szProxyClientCertificatePath); + ssl_log(s, SSL_LOG_TRACE, "Init: (%s) loaded %d client certs for SSL proxy", + cpVHostID, sk_X509_INFO_num(sk)); + if (sk_X509_INFO_num(sk) > 0) { + SSL_CTX_set_client_cert_cb(ctx, ssl_ext_mp_clientcert_cb); + sc->skProxyClientCerts = sk; + } + } + + /* + * Calculate OpenSSL verify type for verifying the remote server + * certificate. We either verify it against our list of CA's, or don't + * bother at all. + */ + nVerify = SSL_VERIFY_NONE; + if (sc->bProxyVerify) + nVerify |= SSL_VERIFY_PEER; + if ( nVerify & SSL_VERIFY_PEER + && sc->szProxyCACertificateFile == NULL + && sc->szProxyCACertificatePath == NULL) { + ssl_log(s, SSL_LOG_ERROR, + "Init: (%s) SSLProxyVerify set to On but no CA certificates configured", + cpVHostID); + ssl_die(); + } + if ( nVerify & SSL_VERIFY_NONE + && ( sc->szProxyCACertificateFile != NULL + || sc->szProxyCACertificatePath != NULL)) { + ssl_log(s, SSL_LOG_WARN, + "init: (%s) CA certificates configured but ignored because SSLProxyVerify is Off", + cpVHostID); + } + SSL_CTX_set_verify(ctx, nVerify, ssl_ext_mp_verify_cb); + + /* + * Enable session caching. We can safely use the same cache + * as used for communicating with the other clients. + */ + SSL_CTX_sess_set_new_cb(sc->pSSLProxyCtx, ssl_callback_NewSessionCacheEntry); + SSL_CTX_sess_set_get_cb(sc->pSSLProxyCtx, ssl_callback_GetSessionCacheEntry); + SSL_CTX_sess_set_remove_cb(sc->pSSLProxyCtx, ssl_callback_DelSessionCacheEntry); + + /* + * Configure SSL Cipher Suite + */ + ssl_log(s, SSL_LOG_TRACE, + "Init: (%s) Configuring permitted SSL ciphers for SSL proxy", cpVHostID); + if (sc->szProxyCipherSuite != NULL) { + if (!SSL_CTX_set_cipher_list(sc->pSSLProxyCtx, sc->szProxyCipherSuite)) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to configure permitted SSL ciphers for SSL Proxy", + cpVHostID); + ssl_die(); + } + } + + /* + * Configure Client Authentication details + */ + if (sc->szProxyCACertificateFile != NULL || sc->szProxyCACertificatePath != NULL) { + ssl_log(s, SSL_LOG_DEBUG, + "Init: (%s) Configuring client verification locations for SSL Proxy", + cpVHostID); + if (!SSL_CTX_load_verify_locations(sc->pSSLProxyCtx, + sc->szProxyCACertificateFile, + sc->szProxyCACertificatePath)) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to configure SSL verify locations for SSL proxy", + cpVHostID); + ssl_die(); + } + } + } + return; +} +#endif /* SSL_EXPERIMENTAL_PROXY */ + +static int ssl_ext_mp_canon(request_rec *r, char *url) +{ + int rc; + + if (strcEQn(url, "https:", 6)) { + rc = OK; + ap_hook_call("ap::mod_proxy::http::canon", + &rc, r, url+6, "https", DEFAULT_HTTPS_PORT); + return rc; + } + return DECLINED; +} + +static int ssl_ext_mp_handler( + request_rec *r, void *cr, char *url, char *proxyhost, int proxyport, char *protocol) +{ + int rc; + + if (strcEQ(protocol, "https")) { + ap_ctx_set(r->ctx, "ssl::proxy::enabled", PTRUE); + ap_hook_call("ap::mod_proxy::http::handler", + &rc, r, cr, url, proxyhost, proxyport); + return rc; + } + else { + ap_ctx_set(r->ctx, "ssl::proxy::enabled", PFALSE); + } + return DECLINED; +} + +static int ssl_ext_mp_set_destport(request_rec *r) +{ + if (ap_ctx_get(r->ctx, "ssl::proxy::enabled") == PTRUE) + return DEFAULT_HTTPS_PORT; + else + return DEFAULT_HTTP_PORT; +} + +static char *ssl_ext_mp_new_connection(request_rec *r, BUFF *fb, char *peer) +{ +#ifndef SSL_EXPERIMENTAL_PROXY + SSL_CTX *ssl_ctx; +#endif + SSL *ssl; + char *errmsg; + int rc; + char *cpVHostID; + char *cpVHostMD5; +#ifdef SSL_EXPERIMENTAL_PROXY + SSLSrvConfigRec *sc; + char *cp; +#endif + + if (ap_ctx_get(r->ctx, "ssl::proxy::enabled") == PFALSE) + return NULL; + + /* + * Find context + */ +#ifdef SSL_EXPERIMENTAL_PROXY + sc = mySrvConfig(r->server); +#endif + cpVHostID = ssl_util_vhostid(r->pool, r->server); + + /* + * Create a SSL context and handle + */ +#ifdef SSL_EXPERIMENTAL_PROXY + ssl = SSL_new(sc->pSSLProxyCtx); +#else + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + ssl = SSL_new(ssl_ctx); +#endif + if (ssl == NULL) { + errmsg = ap_psprintf(r->pool, "SSL proxy new failed (%s): peer %s: %s", + cpVHostID, peer, ERR_reason_error_string(ERR_get_error())); + ap_ctx_set(fb->ctx, "ssl", NULL); + return errmsg; + } + SSL_clear(ssl); + cpVHostMD5 = ap_md5(r->pool, (unsigned char *)cpVHostID); + if (!SSL_set_session_id_context(ssl, (unsigned char *)cpVHostMD5, strlen(cpVHostMD5))) { + errmsg = ap_psprintf(r->pool, "Unable to set session id context to `%s': peer %s: %s", + cpVHostMD5, peer, ERR_reason_error_string(ERR_get_error())); + ap_ctx_set(fb->ctx, "ssl", NULL); + return errmsg; + } + SSL_set_fd(ssl, fb->fd); +#ifdef SSL_EXPERIMENTAL_PROXY + SSL_set_app_data(ssl, fb->ctx); +#endif + ap_ctx_set(fb->ctx, "ssl", ssl); +#ifdef SSL_EXPERIMENTAL_PROXY + ap_ctx_set(fb->ctx, "ssl::proxy::server_rec", r->server); + ap_ctx_set(fb->ctx, "ssl::proxy::peer", peer); + ap_ctx_set(fb->ctx, "ssl::proxy::servername", cpVHostID); + ap_ctx_set(fb->ctx, "ssl::proxy::verifyerror", NULL); +#endif + + /* + * Give us a chance to gracefully close the connection + */ + ap_register_cleanup(r->pool, (void *)fb, + ssl_ext_mp_close_connection, ssl_ext_mp_close_connection); + + /* + * Establish the SSL connection + */ + if ((rc = SSL_connect(ssl)) <= 0) { +#ifdef SSL_EXPERIMENTAL_PROXY + if ((cp = (char *)ap_ctx_get(fb->ctx, "ssl::proxy::verifyerror")) != NULL) { + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + SSL_smart_shutdown(ssl); + SSL_free(ssl); + ap_ctx_set(fb->ctx, "ssl", NULL); + ap_bsetflag(fb, B_EOF|B_EOUT, 1); + return NULL; + } +#endif + errmsg = ap_psprintf(r->pool, "SSL proxy connect failed (%s): peer %s: %s", + cpVHostID, peer, ERR_reason_error_string(ERR_get_error())); + ssl_log(r->server, SSL_LOG_ERROR, errmsg); + SSL_free(ssl); + ap_ctx_set(fb->ctx, "ssl", NULL); + return errmsg; + } + + return NULL; +} + +static void ssl_ext_mp_close_connection(void *_fb) +{ + BUFF *fb = _fb; + SSL *ssl; +#ifndef SSL_EXPERIMENTAL_PROXY + SSL_CTX *ctx; +#endif + + ssl = ap_ctx_get(fb->ctx, "ssl"); + if (ssl != NULL) { +#ifndef SSL_EXPERIMENTAL_PROXY + ctx = SSL_get_SSL_CTX(ssl); +#endif + SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); + SSL_smart_shutdown(ssl); + SSL_free(ssl); + ap_ctx_set(fb->ctx, "ssl", NULL); +#ifndef SSL_EXPERIMENTAL_PROXY + if (ctx != NULL) + SSL_CTX_free(ctx); +#endif + } + return; +} + +static int ssl_ext_mp_write_host_header( + request_rec *r, BUFF *fb, char *host, int port, char *portstr) +{ + if (ap_ctx_get(r->ctx, "ssl::proxy::enabled") == PFALSE) + return DECLINED; + + if (portstr != NULL && port != DEFAULT_HTTPS_PORT) { + ap_bvputs(fb, "Host: ", host, ":", portstr, "\r\n", NULL); + return OK; + } + return DECLINED; +} + +#ifdef SSL_EXPERIMENTAL_PROXY + +/* + * Callback for client certificate stuff. + * If the remote site sent us a SSLv3 list of acceptable CA's then trawl the + * table of client certs and send the first one that matches. + */ +static int ssl_ext_mp_clientcert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey) +{ + SSLSrvConfigRec *sc; + X509_NAME *xnx; + X509_NAME *issuer; + X509_INFO *xi; + char *peer; + char *servername; + server_rec *s; + ap_ctx *pCtx; + STACK_OF(X509_NAME) *sk; + STACK_OF(X509_INFO) *pcerts; + char *cp; + int i, j; + + pCtx = (ap_ctx *)SSL_get_app_data(ssl); + s = ap_ctx_get(pCtx, "ssl::proxy::server_rec"); + peer = ap_ctx_get(pCtx, "ssl::proxy::peer"); + servername = ap_ctx_get(pCtx, "ssl::proxy::servername"); + + sc = mySrvConfig(s); + pcerts = sc->skProxyClientCerts; + + ssl_log(s, SSL_LOG_DEBUG, + "Proxy client certificate callback: (%s) entered", servername); + + if ((pcerts == NULL) || (sk_X509_INFO_num(pcerts) <= 0)) { + ssl_log(s, SSL_LOG_TRACE, + "Proxy client certificate callback: (%s) " + "site wanted client certificate but none available", + servername); + return 0; + } + + sk = SSL_get_client_CA_list(ssl); + + if ((sk == NULL) || (sk_X509_NAME_num(sk) <= 0)) { + /* + * remote site didn't send us a list of acceptable CA certs, + * so lets send the first one we came across + */ + xi = sk_X509_INFO_value(pcerts, 0); + cp = X509_NAME_oneline(X509_get_subject_name(xi->x509), NULL, 0); + ssl_log(s, SSL_LOG_DEBUG, + "SSL Proxy: (%s) no acceptable CA list, sending %s", + servername, cp != NULL ? cp : "-unknown-"); + free(cp); + /* export structures to the caller */ + *x509 = xi->x509; + *pkey = xi->x_pkey->dec_pkey; + /* prevent OpenSSL freeing these structures */ + CRYPTO_add(&((*x509)->references), +1, CRYPTO_LOCK_X509_PKEY); + CRYPTO_add(&((*pkey)->references), +1, CRYPTO_LOCK_X509_PKEY); + return 1; + } + + for (i = 0; i < sk_X509_NAME_num(sk); i++) { + xnx = sk_X509_NAME_value(sk, i); + for (j = 0; j < sk_X509_INFO_num(pcerts); j++) { + xi = sk_X509_INFO_value(pcerts,j); + issuer = X509_get_issuer_name(xi->x509); + if (X509_NAME_cmp(issuer, xnx) == 0) { + cp = X509_NAME_oneline(X509_get_subject_name(xi->x509), NULL, 0); + ssl_log(s, SSL_LOG_DEBUG, "SSL Proxy: (%s) sending %s", + servername, cp != NULL ? cp : "-unknown-"); + free(cp); + /* export structures to the caller */ + *x509 = xi->x509; + *pkey = xi->x_pkey->dec_pkey; + /* prevent OpenSSL freeing these structures */ + CRYPTO_add(&((*x509)->references), +1, CRYPTO_LOCK_X509_PKEY); + CRYPTO_add(&((*pkey)->references), +1, CRYPTO_LOCK_X509_PKEY); + return 1; + } + } + } + ssl_log(s, SSL_LOG_TRACE, + "Proxy client certificate callback: (%s) " + "no client certificate found!?", servername); + return 0; +} + +/* + * This is the verify callback when we are connecting to a remote SSL server + * from the proxy. Information is passed in via the SSL "ctx" app_data + * mechanism. We pass in an Apache context in this field, which contains + * server_rec of the server making the proxy connection from the + * "ssl::proxy::server_rec" context. + * + * The result of the verification is passed back out to SSLERR via the return + * value. We also store the error message in the "proxyverifyfailed" context, + * so the caller of SSL_connect() can log a detailed error message. + */ +static int ssl_ext_mp_verify_cb(int ok, X509_STORE_CTX *ctx) +{ + SSLSrvConfigRec *sc; + X509 *xs; + int errnum; + int errdepth; + char *cp, *cp2; + ap_ctx *pCtx; + server_rec *s; + SSL *ssl; + char *peer; + char *servername; + + ssl = (SSL *)X509_STORE_CTX_get_app_data(ctx); + pCtx = (ap_ctx *)SSL_get_app_data(ssl); + s = ap_ctx_get(pCtx, "ssl::proxy::server_rec"); + peer = ap_ctx_get(pCtx, "ssl::proxy::peer"); + servername = ap_ctx_get(pCtx, "ssl::proxy::servername"); + sc = mySrvConfig(s); + + /* + * Unless stated otherwise by the configuration, we really don't + * care if the verification was okay or not, so lets return now + * before we do anything involving memory or time. + */ + if (sc->bProxyVerify == FALSE) + return ok; + + /* + * Get verify ingredients + */ + xs = X509_STORE_CTX_get_current_cert(ctx); + errnum = X509_STORE_CTX_get_error(ctx); + errdepth = X509_STORE_CTX_get_error_depth(ctx); + + /* + * Log verification information + */ + cp = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0); + cp2 = X509_NAME_oneline(X509_get_issuer_name(xs), NULL, 0); + ssl_log(s, SSL_LOG_DEBUG, + "SSL Proxy: (%s) Certificate Verification for remote server %s: " + "depth: %d, subject: %s, issuer: %s", + servername, peer != NULL ? peer : "-unknown-", + errdepth, cp != NULL ? cp : "-unknown-", + cp2 != NULL ? cp2 : "-unknown"); + free(cp); + free(cp2); + + /* + * If we already know it's not ok, log the real reason + */ + if (!ok) { + ssl_log(s, SSL_LOG_ERROR, + "SSL Proxy: (%s) Certificate Verification failed for %s: " + "Error (%d): %s", servername, + peer != NULL ? peer : "-unknown-", + errnum, X509_verify_cert_error_string(errnum)); + ap_ctx_set(pCtx, "ssl::proxy::verifyerror", + (void *)X509_verify_cert_error_string(errnum)); + return ok; + } + + /* + * Check the depth of the certificate chain + */ + if (sc->nProxyVerifyDepth > 0) { + if (errdepth > sc->nProxyVerifyDepth) { + ssl_log(s, SSL_LOG_ERROR, + "SSL Proxy: (%s) Certificate Verification failed for %s: " + "Certificate Chain too long " + "(chain has %d certificates, but maximum allowed are only %d)", + servername, peer, errdepth, sc->nProxyVerifyDepth); + ap_ctx_set(pCtx, "ssl::proxy::verifyerror", + (void *)X509_verify_cert_error_string(X509_V_ERR_CERT_CHAIN_TOO_LONG)); + ok = FALSE; + } + } + + /* + * And finally signal OpenSSL the (perhaps changed) state + */ + return (ok); +} + +#endif /* SSL_EXPERIMENTAL_PROXY */ + +/* _________________________________________________________________ +** +** SSL Extension to mod_status +** _________________________________________________________________ +*/ + +static void ssl_ext_ms_display(request_rec *, int, int); + +static void ssl_ext_ms_register(void) +{ + ap_hook_register("ap::mod_status::display", ssl_ext_ms_display, AP_HOOK_NOCTX); + return; +} + +static void ssl_ext_ms_unregister(void) +{ + ap_hook_unregister("ap::mod_status::display", ssl_ext_ms_display); + return; +} + +static void ssl_ext_ms_display_cb(char *str, void *_r) +{ + request_rec *r = (request_rec *)_r; + if (str != NULL) + ap_rputs(str, r); + return; +} + +static void ssl_ext_ms_display(request_rec *r, int no_table_report, int short_report) +{ + SSLSrvConfigRec *sc = mySrvConfig(r->server); + + if (sc == NULL) + return; + if (short_report) + return; + ap_rputs("<hr>\n", r); + ap_rputs("<table cellspacing=0 cellpadding=0>\n", r); + ap_rputs("<tr><td bgcolor=\"#000000\">\n", r); + ap_rputs("<b><font color=\"#ffffff\" face=\"Arial,Helvetica\">SSL/TLS Session Cache Status:</font></b>\r", r); + ap_rputs("</td></tr>\n", r); + ap_rputs("<tr><td bgcolor=\"#ffffff\">\n", r); + ssl_scache_status(r->server, r->pool, ssl_ext_ms_display_cb, r); + ap_rputs("</td></tr>\n", r); + ap_rputs("</table>\n", r); + return; +} + diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c new file mode 100644 index 0000000000..6a27c4af4d --- /dev/null +++ b/modules/ssl/ssl_engine_init.c @@ -0,0 +1,1090 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_init.c +** Initialization of Servers +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +/* ==================================================================== + * Copyright (c) 1995-1999 Ben Laurie. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by Ben Laurie + * for use in the Apache-SSL HTTP server project." + * + * 4. The name "Apache-SSL Server" must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 5. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Ben Laurie + * for use in the Apache-SSL HTTP server project." + * + * THIS SOFTWARE IS PROVIDED BY BEN LAURIE ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BEN LAURIE OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``Recursive, adj.; + see Recursive.'' + -- Unknown */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Module Initialization +** _________________________________________________________________ +*/ + +/* + * Per-module initialization + */ +void ssl_init_Module(server_rec *s, pool *p) +{ + SSLModConfigRec *mc = myModConfig(); + SSLSrvConfigRec *sc; + server_rec *s2; + char *cp; + + mc->nInitCount++; + + /* + * Let us cleanup on restarts and exists + */ + ap_register_cleanup(p, s, ssl_init_ModuleKill, ssl_init_ChildKill); + + /* + * Any init round fixes the global config + */ + ssl_config_global_create(); /* just to avoid problems */ + ssl_config_global_fix(); + + /* + * try to fix the configuration and open the dedicated SSL + * logfile as early as possible + */ + for (s2 = s; s2 != NULL; s2 = s2->next) { + sc = mySrvConfig(s2); + + /* Fix up stuff that may not have been set */ + if (sc->bEnabled == UNSET) + sc->bEnabled = FALSE; + if (sc->nVerifyClient == SSL_CVERIFY_UNSET) + sc->nVerifyClient = SSL_CVERIFY_NONE; + if (sc->nVerifyDepth == UNSET) + sc->nVerifyDepth = 1; +#ifdef SSL_EXPERIMENTAL_PROXY + if (sc->nProxyVerifyDepth == UNSET) + sc->nProxyVerifyDepth = 1; +#endif + if (sc->nSessionCacheTimeout == UNSET) + sc->nSessionCacheTimeout = SSL_SESSION_CACHE_TIMEOUT; + if (sc->nPassPhraseDialogType == SSL_PPTYPE_UNSET) + sc->nPassPhraseDialogType = SSL_PPTYPE_BUILTIN; + + /* Open the dedicated SSL logfile */ + ssl_log_open(s, s2, p); + } + + /* + * Identification + */ + if (mc->nInitCount == 1) { + ssl_log(s, SSL_LOG_INFO, "Server: %s, Interface: %s, Library: %s", + SERVER_BASEVERSION, + ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_INTERFACE"), + ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_LIBRARY")); +#ifdef WIN32 + ssl_log(s, SSL_LOG_WARN, "You are using mod_ssl under Win32. " + "This combination is *NOT* officially supported. " + "Use it at your own risk!"); +#endif + } + + /* + * Initialization round information + */ + if (mc->nInitCount == 1) + ssl_log(s, SSL_LOG_INFO, "Init: 1st startup round (still not detached)"); + else if (mc->nInitCount == 2) + ssl_log(s, SSL_LOG_INFO, "Init: 2nd startup round (already detached)"); + else + ssl_log(s, SSL_LOG_INFO, "Init: %d%s restart round (already detached)", + mc->nInitCount-2, (mc->nInitCount-2) == 1 ? "st" : "nd"); + +#ifdef SSL_VENDOR + ap_hook_use("ap::mod_ssl::vendor::init_module", + AP_HOOK_SIG3(void,ptr,ptr), AP_HOOK_ALL, s, p); +#endif + + /* + * The initialization phase inside the Apache API is totally bogus. + * We actually have three non-trivial problems: + * + * 1. Under Unix the API does a 2-round initialization of modules while + * under Win32 it doesn't. This means we have to make sure that at + * least the pass phrase dialog doesn't occur twice. We overcome this + * problem by using a counter (mc->nInitCount) which has to + * survive the init rounds. + * + * 2. Between the first and the second round Apache detaches from + * the terminal under Unix. This means that our pass phrase dialog + * _has_ to be done in the first round and _cannot_ be done in the + * second round. + * + * 3. When Dynamic Shared Object (DSO) mechanism is used under Unix the + * module segment (code & data) gets unloaded and re-loaded between + * the first and the second round. This means no global data survives + * between first and the second init round. We overcome this by using + * an entry ("ssl_module") inside the ap_global_ctx. + * + * The situation as a table: + * + * Unix/static Unix/DSO Win32 Action Required + * (-DSHARED_MODULE) (-DWIN32) + * ----------- ----------------- --------- ----------------------------------- + * - load module - - + * init init init SSL library init, Pass Phrase Dialog + * detach detach - - + * - reload module - - + * init init - SSL library init, mod_ssl init + * + * Ok, now try to solve this totally ugly situation... + */ + +#ifdef SHARED_MODULE + ssl_log(s, SSL_LOG_INFO, "Init: %snitializing %s library", + mc->nInitCount == 1 ? "I" : "Rei", SSL_LIBRARY_NAME); + ssl_init_SSLLibrary(); +#else + if (mc->nInitCount <= 2) { + ssl_log(s, SSL_LOG_INFO, "Init: %snitializing %s library", + mc->nInitCount == 1 ? "I" : "Rei", SSL_LIBRARY_NAME); + ssl_init_SSLLibrary(); + } +#endif + if (mc->nInitCount == 1) { + ssl_pphrase_Handle(s, p); + ssl_init_TmpKeysHandle(SSL_TKP_GEN, s, p); +#ifndef WIN32 + return; +#endif + } + + /* + * SSL external crypto device ("engine") support + */ +#ifdef SSL_EXPERIMENTAL_ENGINE + ssl_init_Engine(s, p); +#endif + + /* + * Warn the user that he should use the session cache. + * But we can operate without it, of course. + */ + if (mc->nSessionCacheMode == SSL_SCMODE_UNSET) { + ssl_log(s, SSL_LOG_WARN, + "Init: Session Cache is not configured [hint: SSLSessionCache]"); + mc->nSessionCacheMode = SSL_SCMODE_NONE; + } + + /* + * initialize the mutex handling and session caching + */ + ssl_mutex_init(s, p); + ssl_scache_init(s, p); + + /* + * Seed the Pseudo Random Number Generator (PRNG) + */ + ssl_rand_seed(s, p, SSL_RSCTX_STARTUP, "Init: "); + + /* + * allocate the temporary RSA keys and DH params + */ + ssl_init_TmpKeysHandle(SSL_TKP_ALLOC, s, p); + + /* + * initialize servers + */ + ssl_log(s, SSL_LOG_INFO, "Init: Initializing (virtual) servers for SSL"); + for (s2 = s; s2 != NULL; s2 = s2->next) { + sc = mySrvConfig(s2); + /* + * Either now skip this server when SSL is disabled for + * it or give out some information about what we're + * configuring. + */ + if (!sc->bEnabled) + continue; + ssl_log(s2, SSL_LOG_INFO, + "Init: Configuring server %s for SSL protocol", + ssl_util_vhostid(p, s2)); + + /* + * Read the server certificate and key + */ + ssl_init_ConfigureServer(s2, p, sc); + } + + /* + * Configuration consistency checks + */ + ssl_init_CheckServers(s, p); + + /* + * Announce mod_ssl and SSL library in HTTP Server field + * as ``mod_ssl/X.X.X OpenSSL/X.X.X'' + */ + if ((cp = ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_PRODUCT")) != NULL && cp[0] != NUL) + ap_add_version_component(cp); + ap_add_version_component(ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_INTERFACE")); + ap_add_version_component(ssl_var_lookup(p, NULL, NULL, NULL, "SSL_VERSION_LIBRARY")); + + return; +} + +/* + * Initialize SSL library (also already needed for the pass phrase dialog) + */ +void ssl_init_SSLLibrary(void) +{ +#ifdef WIN32 + CRYPTO_malloc_init(); +#endif + SSL_load_error_strings(); + SSL_library_init(); + ssl_util_thread_setup(); + X509V3_add_standard_extensions(); + return; +} + +/* + * Support for external a Crypto Device ("engine"), usually + * a hardware accellerator card for crypto operations. + */ +#ifdef SSL_EXPERIMENTAL_ENGINE +void ssl_init_Engine(server_rec *s, pool *p) +{ + SSLModConfigRec *mc = myModConfig(); + ENGINE *e; + + if (mc->szCryptoDevice != NULL) { + if ((e = ENGINE_by_id(mc->szCryptoDevice)) == NULL) { + ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load Crypto Device API `%s'", + mc->szCryptoDevice); + ssl_die(); + } + if (strEQ(mc->szCryptoDevice, "chil")) + ENGINE_ctrl(e, ENGINE_CTRL_CHIL_SET_FORKCHECK, 1, 0, 0); + if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) { + ssl_log(s, SSL_LOG_ERROR, "Init: Failed to enable Crypto Device API `%s'", + mc->szCryptoDevice); + ssl_die(); + } + ENGINE_free(e); + } + return; +} +#endif + +/* + * Handle the Temporary RSA Keys and DH Params + */ +void ssl_init_TmpKeysHandle(int action, server_rec *s, pool *p) +{ + SSLModConfigRec *mc = myModConfig(); + ssl_asn1_t *asn1; + unsigned char *ucp; + RSA *rsa; + DH *dh; + + /* Generate Keys and Params */ + if (action == SSL_TKP_GEN) { + + /* seed PRNG */ + ssl_rand_seed(s, p, SSL_RSCTX_STARTUP, "Init: "); + + /* generate 512 bit RSA key */ + ssl_log(s, SSL_LOG_INFO, "Init: Generating temporary RSA private keys (512/1024 bits)"); + if ((rsa = RSA_generate_key(512, RSA_F4, NULL, NULL)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: Failed to generate temporary 512 bit RSA private key"); + ssl_die(); + } + asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "RSA:512"); + asn1->nData = i2d_RSAPrivateKey(rsa, NULL); + asn1->cpData = ap_palloc(mc->pPool, asn1->nData); + ucp = asn1->cpData; i2d_RSAPrivateKey(rsa, &ucp); /* 2nd arg increments */ + RSA_free(rsa); + + /* generate 1024 bit RSA key */ + if ((rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: Failed to generate temporary 1024 bit RSA private key"); + ssl_die(); + } + asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "RSA:1024"); + asn1->nData = i2d_RSAPrivateKey(rsa, NULL); + asn1->cpData = ap_palloc(mc->pPool, asn1->nData); + ucp = asn1->cpData; i2d_RSAPrivateKey(rsa, &ucp); /* 2nd arg increments */ + RSA_free(rsa); + + ssl_log(s, SSL_LOG_INFO, "Init: Configuring temporary DH parameters (512/1024 bits)"); + + /* import 512 bit DH param */ + if ((dh = ssl_dh_GetTmpParam(512)) == NULL) { + ssl_log(s, SSL_LOG_ERROR, "Init: Failed to import temporary 512 bit DH parameters"); + ssl_die(); + } + asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "DH:512"); + asn1->nData = i2d_DHparams(dh, NULL); + asn1->cpData = ap_palloc(mc->pPool, asn1->nData); + ucp = asn1->cpData; i2d_DHparams(dh, &ucp); /* 2nd arg increments */ + /* no need to free dh, it's static */ + + /* import 1024 bit DH param */ + if ((dh = ssl_dh_GetTmpParam(1024)) == NULL) { + ssl_log(s, SSL_LOG_ERROR, "Init: Failed to import temporary 1024 bit DH parameters"); + ssl_die(); + } + asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tTmpKeys, "DH:1024"); + asn1->nData = i2d_DHparams(dh, NULL); + asn1->cpData = ap_palloc(mc->pPool, asn1->nData); + ucp = asn1->cpData; i2d_DHparams(dh, &ucp); /* 2nd arg increments */ + /* no need to free dh, it's static */ + } + + /* Allocate Keys and Params */ + else if (action == SSL_TKP_ALLOC) { + + ssl_log(s, SSL_LOG_INFO, "Init: Configuring temporary RSA private keys (512/1024 bits)"); + + /* allocate 512 bit RSA key */ + if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "RSA:512")) != NULL) { + ucp = asn1->cpData; + if ((mc->pTmpKeys[SSL_TKPIDX_RSA512] = +#if SSL_LIBRARY_VERSION >= 0x00907000 + (void *)d2i_RSAPrivateKey(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) { +#else + (void *)d2i_RSAPrivateKey(NULL, &ucp, asn1->nData)) == NULL) { +#endif + ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 512 bit RSA private key"); + ssl_die(); + } + } + + /* allocate 1024 bit RSA key */ + if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "RSA:1024")) != NULL) { + ucp = asn1->cpData; + if ((mc->pTmpKeys[SSL_TKPIDX_RSA1024] = +#if SSL_LIBRARY_VERSION >= 0x00907000 + (void *)d2i_RSAPrivateKey(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) { +#else + (void *)d2i_RSAPrivateKey(NULL, &ucp, asn1->nData)) == NULL) { +#endif + ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 1024 bit RSA private key"); + ssl_die(); + } + } + + ssl_log(s, SSL_LOG_INFO, "Init: Configuring temporary DH parameters (512/1024 bits)"); + + /* allocate 512 bit DH param */ + if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "DH:512")) != NULL) { + ucp = asn1->cpData; + if ((mc->pTmpKeys[SSL_TKPIDX_DH512] = +#if SSL_LIBRARY_VERSION >= 0x00907000 + (void *)d2i_DHparams(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) { +#else + (void *)d2i_DHparams(NULL, &ucp, asn1->nData)) == NULL) { +#endif + ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 512 bit DH parameters"); + ssl_die(); + } + } + + /* allocate 1024 bit DH param */ + if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tTmpKeys, "DH:1024")) != NULL) { + ucp = asn1->cpData; + if ((mc->pTmpKeys[SSL_TKPIDX_DH1024] = +#if SSL_LIBRARY_VERSION >= 0x00907000 + (void *)d2i_DHparams(NULL, (const unsigned char **)&ucp, asn1->nData)) == NULL) { +#else + (void *)d2i_DHparams(NULL, &ucp, asn1->nData)) == NULL) { +#endif + ssl_log(s, SSL_LOG_ERROR, "Init: Failed to load temporary 1024 bit DH parameters"); + ssl_die(); + } + } + } + + /* Free Keys and Params */ + else if (action == SSL_TKP_FREE) { + if (mc->pTmpKeys[SSL_TKPIDX_RSA512] != NULL) { + RSA_free((RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA512]); + mc->pTmpKeys[SSL_TKPIDX_RSA512] = NULL; + } + if (mc->pTmpKeys[SSL_TKPIDX_RSA1024] != NULL) { + RSA_free((RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024]); + mc->pTmpKeys[SSL_TKPIDX_RSA1024] = NULL; + } + if (mc->pTmpKeys[SSL_TKPIDX_DH512] != NULL) { + DH_free((DH *)mc->pTmpKeys[SSL_TKPIDX_DH512]); + mc->pTmpKeys[SSL_TKPIDX_DH512] = NULL; + } + if (mc->pTmpKeys[SSL_TKPIDX_DH1024] != NULL) { + DH_free((DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024]); + mc->pTmpKeys[SSL_TKPIDX_DH1024] = NULL; + } + } + return; +} + +/* + * Configure a particular server + */ +void ssl_init_ConfigureServer(server_rec *s, pool *p, SSLSrvConfigRec *sc) +{ + SSLModConfigRec *mc = myModConfig(); + int nVerify; + char *cpVHostID; + EVP_PKEY *pKey; + SSL_CTX *ctx; + STACK_OF(X509_NAME) *skCAList; + ssl_asn1_t *asn1; + unsigned char *ucp; + char *cp; + BOOL ok; + BOOL bSkipFirst; + int isca, pathlen; + int i, n; + + /* + * Create the server host:port string because we need it a lot + */ + cpVHostID = ssl_util_vhostid(p, s); + + /* + * Now check for important parameters and the + * possibility that the user forgot to set them. + */ + if (sc->szPublicCertFile[0] == NULL) { + ssl_log(s, SSL_LOG_ERROR, + "Init: (%s) No SSL Certificate set [hint: SSLCertificateFile]", + cpVHostID); + ssl_die(); + } + + /* + * Check for problematic re-initializations + */ + if (sc->pPublicCert[SSL_AIDX_RSA] != NULL || + sc->pPublicCert[SSL_AIDX_DSA] != NULL ) { + ssl_log(s, SSL_LOG_ERROR, + "Init: (%s) Illegal attempt to re-initialise SSL for server " + "(theoretically shouldn't happen!)", cpVHostID); + ssl_die(); + } + + /* + * Create the new per-server SSL context + */ + if (sc->nProtocol == SSL_PROTOCOL_NONE) { + ssl_log(s, SSL_LOG_ERROR, + "Init: (%s) No SSL protocols available [hint: SSLProtocol]", + cpVHostID); + ssl_die(); + } + cp = ap_pstrcat(p, (sc->nProtocol & SSL_PROTOCOL_SSLV2 ? "SSLv2, " : ""), + (sc->nProtocol & SSL_PROTOCOL_SSLV3 ? "SSLv3, " : ""), + (sc->nProtocol & SSL_PROTOCOL_TLSV1 ? "TLSv1, " : ""), NULL); + cp[strlen(cp)-2] = NUL; + ssl_log(s, SSL_LOG_TRACE, + "Init: (%s) Creating new SSL context (protocols: %s)", cpVHostID, cp); + if (sc->nProtocol == SSL_PROTOCOL_SSLV2) + ctx = SSL_CTX_new(SSLv2_server_method()); /* only SSLv2 is left */ + else + ctx = SSL_CTX_new(SSLv23_server_method()); /* be more flexible */ + SSL_CTX_set_options(ctx, SSL_OP_ALL); + if (!(sc->nProtocol & SSL_PROTOCOL_SSLV2)) + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2); + if (!(sc->nProtocol & SSL_PROTOCOL_SSLV3)) + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3); + if (!(sc->nProtocol & SSL_PROTOCOL_TLSV1)) + SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1); + SSL_CTX_set_app_data(ctx, s); + sc->pSSLCtx = ctx; + + /* + * Configure additional context ingredients + */ + SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); + if (mc->nSessionCacheMode == SSL_SCMODE_NONE) + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); + else + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER); + + /* + * Configure callbacks for SSL context + */ + nVerify = SSL_VERIFY_NONE; + if (sc->nVerifyClient == SSL_CVERIFY_REQUIRE) + nVerify |= SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + if ( (sc->nVerifyClient == SSL_CVERIFY_OPTIONAL) + || (sc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) ) + nVerify |= SSL_VERIFY_PEER; + SSL_CTX_set_verify(ctx, nVerify, ssl_callback_SSLVerify); + SSL_CTX_sess_set_new_cb(ctx, ssl_callback_NewSessionCacheEntry); + SSL_CTX_sess_set_get_cb(ctx, ssl_callback_GetSessionCacheEntry); + SSL_CTX_sess_set_remove_cb(ctx, ssl_callback_DelSessionCacheEntry); + SSL_CTX_set_tmp_rsa_callback(ctx, ssl_callback_TmpRSA); + SSL_CTX_set_tmp_dh_callback(ctx, ssl_callback_TmpDH); + SSL_CTX_set_info_callback(ctx, ssl_callback_LogTracingState); + + /* + * Configure SSL Cipher Suite + */ + if (sc->szCipherSuite != NULL) { + ssl_log(s, SSL_LOG_TRACE, + "Init: (%s) Configuring permitted SSL ciphers [%s]", + cpVHostID, sc->szCipherSuite); + if (!SSL_CTX_set_cipher_list(ctx, sc->szCipherSuite)) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to configure permitted SSL ciphers", + cpVHostID); + ssl_die(); + } + } + + /* + * Configure Client Authentication details + */ + if (sc->szCACertificateFile != NULL || sc->szCACertificatePath != NULL) { + ssl_log(s, SSL_LOG_TRACE, + "Init: (%s) Configuring client authentication", cpVHostID); + if (!SSL_CTX_load_verify_locations(ctx, + sc->szCACertificateFile, + sc->szCACertificatePath)) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to configure verify locations " + "for client authentication", cpVHostID); + ssl_die(); + } + if ((skCAList = ssl_init_FindCAList(s, p, sc->szCACertificateFile, + sc->szCACertificatePath)) == NULL) { + ssl_log(s, SSL_LOG_ERROR, + "Init: (%s) Unable to determine list of available " + "CA certificates for client authentication", cpVHostID); + ssl_die(); + } + SSL_CTX_set_client_CA_list(sc->pSSLCtx, skCAList); + } + + /* + * Configure Certificate Revocation List (CRL) Details + */ + if (sc->szCARevocationFile != NULL || sc->szCARevocationPath != NULL) { + ssl_log(s, SSL_LOG_TRACE, + "Init: (%s) Configuring certificate revocation facility", cpVHostID); + if ((sc->pRevocationStore = + SSL_X509_STORE_create(sc->szCARevocationFile, + sc->szCARevocationPath)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to configure X.509 CRL storage " + "for certificate revocation", cpVHostID); + ssl_die(); + } + } + + /* + * Give a warning when no CAs were configured but client authentication + * should take place. This cannot work. + */ + if (sc->nVerifyClient == SSL_CVERIFY_REQUIRE) { + skCAList = SSL_CTX_get_client_CA_list(ctx); + if (sk_X509_NAME_num(skCAList) == 0) + ssl_log(s, SSL_LOG_WARN, + "Init: Ops, you want to request client authentication, " + "but no CAs are known for verification!? " + "[Hint: SSLCACertificate*]"); + } + + /* + * Configure server certificate(s) + */ + ok = FALSE; + cp = ap_psprintf(p, "%s:RSA", cpVHostID); + if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPublicCert, cp)) != NULL) { + ssl_log(s, SSL_LOG_TRACE, + "Init: (%s) Configuring RSA server certificate", cpVHostID); + ucp = asn1->cpData; + if ((sc->pPublicCert[SSL_AIDX_RSA] = d2i_X509(NULL, &ucp, asn1->nData)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to import RSA server certificate", + cpVHostID); + ssl_die(); + } + if (SSL_CTX_use_certificate(ctx, sc->pPublicCert[SSL_AIDX_RSA]) <= 0) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to configure RSA server certificate", + cpVHostID); + ssl_die(); + } + ok = TRUE; + } + cp = ap_psprintf(p, "%s:DSA", cpVHostID); + if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPublicCert, cp)) != NULL) { + ssl_log(s, SSL_LOG_TRACE, + "Init: (%s) Configuring DSA server certificate", cpVHostID); + ucp = asn1->cpData; + if ((sc->pPublicCert[SSL_AIDX_DSA] = d2i_X509(NULL, &ucp, asn1->nData)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to import DSA server certificate", + cpVHostID); + ssl_die(); + } + if (SSL_CTX_use_certificate(ctx, sc->pPublicCert[SSL_AIDX_DSA]) <= 0) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to configure DSA server certificate", + cpVHostID); + ssl_die(); + } + ok = TRUE; + } + if (!ok) { + ssl_log(s, SSL_LOG_ERROR, + "Init: (%s) Ops, no RSA or DSA server certificate found?!", cpVHostID); + ssl_log(s, SSL_LOG_ERROR, + "Init: (%s) You have to perform a *full* server restart when you added or removed a certificate and/or key file", cpVHostID); + ssl_die(); + } + + /* + * Some information about the certificate(s) + */ + for (i = 0; i < SSL_AIDX_MAX; i++) { + if (sc->pPublicCert[i] != NULL) { + if (SSL_X509_isSGC(sc->pPublicCert[i])) { + ssl_log(s, SSL_LOG_INFO, + "Init: (%s) %s server certificate enables " + "Server Gated Cryptography (SGC)", + cpVHostID, (i == SSL_AIDX_RSA ? "RSA" : "DSA")); + } + if (SSL_X509_getBC(sc->pPublicCert[i], &isca, &pathlen)) { + if (isca) + ssl_log(s, SSL_LOG_WARN, + "Init: (%s) %s server certificate is a CA certificate " + "(BasicConstraints: CA == TRUE !?)", + cpVHostID, (i == SSL_AIDX_RSA ? "RSA" : "DSA")); + if (pathlen > 0) + ssl_log(s, SSL_LOG_WARN, + "Init: (%s) %s server certificate is not a leaf certificate " + "(BasicConstraints: pathlen == %d > 0 !?)", + cpVHostID, (i == SSL_AIDX_RSA ? "RSA" : "DSA"), pathlen); + } + if (SSL_X509_getCN(p, sc->pPublicCert[i], &cp)) { + if (ap_is_fnmatch(cp) && + !ap_fnmatch(cp, s->server_hostname, FNM_PERIOD|FNM_CASE_BLIND)) { + ssl_log(s, SSL_LOG_WARN, + "Init: (%s) %s server certificate wildcard CommonName (CN) `%s' " + "does NOT match server name!?", cpVHostID, + (i == SSL_AIDX_RSA ? "RSA" : "DSA"), cp); + } + else if (strNE(s->server_hostname, cp)) { + ssl_log(s, SSL_LOG_WARN, + "Init: (%s) %s server certificate CommonName (CN) `%s' " + "does NOT match server name!?", cpVHostID, + (i == SSL_AIDX_RSA ? "RSA" : "DSA"), cp); + } + } + } + } + + /* + * Configure server private key(s) + */ + ok = FALSE; + cp = ap_psprintf(p, "%s:RSA", cpVHostID); + if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPrivateKey, cp)) != NULL) { + ssl_log(s, SSL_LOG_TRACE, + "Init: (%s) Configuring RSA server private key", cpVHostID); + ucp = asn1->cpData; + if ((sc->pPrivateKey[SSL_AIDX_RSA] = + d2i_PrivateKey(EVP_PKEY_RSA, NULL, &ucp, asn1->nData)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to import RSA server private key", + cpVHostID); + ssl_die(); + } + if (SSL_CTX_use_PrivateKey(ctx, sc->pPrivateKey[SSL_AIDX_RSA]) <= 0) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to configure RSA server private key", + cpVHostID); + ssl_die(); + } + ok = TRUE; + } + cp = ap_psprintf(p, "%s:DSA", cpVHostID); + if ((asn1 = (ssl_asn1_t *)ssl_ds_table_get(mc->tPrivateKey, cp)) != NULL) { + ssl_log(s, SSL_LOG_TRACE, + "Init: (%s) Configuring DSA server private key", cpVHostID); + ucp = asn1->cpData; + if ((sc->pPrivateKey[SSL_AIDX_DSA] = + d2i_PrivateKey(EVP_PKEY_DSA, NULL, &ucp, asn1->nData)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to import DSA server private key", + cpVHostID); + ssl_die(); + } + if (SSL_CTX_use_PrivateKey(ctx, sc->pPrivateKey[SSL_AIDX_DSA]) <= 0) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: (%s) Unable to configure DSA server private key", + cpVHostID); + ssl_die(); + } + ok = TRUE; + } + if (!ok) { + ssl_log(s, SSL_LOG_ERROR, + "Init: (%s) Ops, no RSA or DSA server private key found?!", cpVHostID); + ssl_die(); + } + + /* + * Optionally copy DSA parameters for certificate from private key + * (see http://www.psy.uq.edu.au/~ftp/Crypto/ssleay/TODO.html) + */ + if ( sc->pPublicCert[SSL_AIDX_DSA] != NULL + && sc->pPrivateKey[SSL_AIDX_DSA] != NULL) { + pKey = X509_get_pubkey(sc->pPublicCert[SSL_AIDX_DSA]); + if ( pKey != NULL + && EVP_PKEY_type(pKey->type) == EVP_PKEY_DSA + && EVP_PKEY_missing_parameters(pKey)) + EVP_PKEY_copy_parameters(pKey, sc->pPrivateKey[SSL_AIDX_DSA]); + } + + /* + * Optionally configure extra server certificate chain certificates. + * This is usually done by OpenSSL automatically when one of the + * server cert issuers are found under SSLCACertificatePath or in + * SSLCACertificateFile. But because these are intended for client + * authentication it can conflict. For instance when you use a + * Global ID server certificate you've to send out the intermediate + * CA certificate, too. When you would just configure this with + * SSLCACertificateFile and also use client authentication mod_ssl + * would accept all clients also issued by this CA. Obviously this + * isn't what we want in this situation. So this feature here exists + * to allow one to explicity configure CA certificates which are + * used only for the server certificate chain. + */ + if (sc->szCertificateChain != NULL) { + bSkipFirst = FALSE; + for (i = 0; i < SSL_AIDX_MAX && sc->szPublicCertFile[i] != NULL; i++) { + if (strEQ(sc->szPublicCertFile[i], sc->szCertificateChain)) { + bSkipFirst = TRUE; + break; + } + } + if ((n = SSL_CTX_use_certificate_chain(ctx, sc->szCertificateChain, + bSkipFirst, NULL)) < 0) { + ssl_log(s, SSL_LOG_ERROR, + "Init: (%s) Failed to configure CA certificate chain!", cpVHostID); + ssl_die(); + } + ssl_log(s, SSL_LOG_TRACE, "Init: (%s) Configuring " + "server certificate chain (%d CA certificate%s)", cpVHostID, + n, n == 1 ? "" : "s"); + } + +#ifdef SSL_VENDOR + ap_hook_use("ap::mod_ssl::vendor::configure_server", + AP_HOOK_SIG4(void,ptr,ptr,ptr), AP_HOOK_ALL, + s, p, sc); +#endif + + return; +} + +void ssl_init_CheckServers(server_rec *sm, pool *p) +{ + server_rec *s; + server_rec **ps; + SSLSrvConfigRec *sc; + ssl_ds_table *t; + pool *sp; + char *key; + BOOL bConflict; + + /* + * Give out warnings when a server has HTTPS configured + * for the HTTP port or vice versa + */ + for (s = sm; s != NULL; s = s->next) { + sc = mySrvConfig(s); + if (sc->bEnabled && s->port == DEFAULT_HTTP_PORT) + ssl_log(sm, SSL_LOG_WARN, + "Init: (%s) You configured HTTPS(%d) on the standard HTTP(%d) port!", + ssl_util_vhostid(p, s), DEFAULT_HTTPS_PORT, DEFAULT_HTTP_PORT); + if (!sc->bEnabled && s->port == DEFAULT_HTTPS_PORT) + ssl_log(sm, SSL_LOG_WARN, + "Init: (%s) You configured HTTP(%d) on the standard HTTPS(%d) port!", + ssl_util_vhostid(p, s), DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); + } + + /* + * Give out warnings when more than one SSL-aware virtual server uses the + * same IP:port. This doesn't work because mod_ssl then will always use + * just the certificate/keys of one virtual host (which one cannot be said + * easily - but that doesn't matter here). + */ + sp = ap_make_sub_pool(p); + t = ssl_ds_table_make(sp, sizeof(server_rec *)); + bConflict = FALSE; + for (s = sm; s != NULL; s = s->next) { + sc = mySrvConfig(s); + if (!sc->bEnabled) + continue; + key = ap_psprintf(sp, "%pA:%u", &s->addrs->host_addr, s->addrs->host_port); + ps = ssl_ds_table_get(t, key); + if (ps != NULL) { + ssl_log(sm, SSL_LOG_WARN, + "Init: SSL server IP/port conflict: %s (%s:%d) vs. %s (%s:%d)", + ssl_util_vhostid(p, s), + (s->defn_name != NULL ? s->defn_name : "unknown"), + s->defn_line_number, + ssl_util_vhostid(p, *ps), + ((*ps)->defn_name != NULL ? (*ps)->defn_name : "unknown"), + (*ps)->defn_line_number); + bConflict = TRUE; + continue; + } + ps = ssl_ds_table_push(t, key); + *ps = s; + } + ssl_ds_table_kill(t); + ap_destroy_pool(sp); + if (bConflict) + ssl_log(sm, SSL_LOG_WARN, + "Init: You should not use name-based virtual hosts in conjunction with SSL!!"); + + return; +} + +static int ssl_init_FindCAList_X509NameCmp(X509_NAME **a, X509_NAME **b) +{ + return(X509_NAME_cmp(*a, *b)); +} + +STACK_OF(X509_NAME) *ssl_init_FindCAList(server_rec *s, pool *pp, char *cpCAfile, char *cpCApath) +{ + STACK_OF(X509_NAME) *skCAList; + STACK_OF(X509_NAME) *sk; + DIR *dir; + struct DIR_TYPE *direntry; + char *cp; + pool *p; + int n; + + /* + * Use a subpool so we don't bloat up the server pool which + * is remains in memory for the complete operation time of + * the server. + */ + p = ap_make_sub_pool(pp); + + /* + * Start with a empty stack/list where new + * entries get added in sorted order. + */ + skCAList = sk_X509_NAME_new(ssl_init_FindCAList_X509NameCmp); + + /* + * Process CA certificate bundle file + */ + if (cpCAfile != NULL) { + sk = SSL_load_client_CA_file(cpCAfile); + for(n = 0; sk != NULL && n < sk_X509_NAME_num(sk); n++) { + ssl_log(s, SSL_LOG_TRACE, + "CA certificate: %s", + X509_NAME_oneline(sk_X509_NAME_value(sk, n), NULL, 0)); + if (sk_X509_NAME_find(skCAList, sk_X509_NAME_value(sk, n)) < 0) + sk_X509_NAME_push(skCAList, sk_X509_NAME_value(sk, n)); + } + } + + /* + * Process CA certificate path files + */ + if (cpCApath != NULL) { + dir = ap_popendir(p, cpCApath); + while ((direntry = readdir(dir)) != NULL) { + cp = ap_pstrcat(p, cpCApath, "/", direntry->d_name, NULL); + sk = SSL_load_client_CA_file(cp); + for(n = 0; sk != NULL && n < sk_X509_NAME_num(sk); n++) { + ssl_log(s, SSL_LOG_TRACE, + "CA certificate: %s", + X509_NAME_oneline(sk_X509_NAME_value(sk, n), NULL, 0)); + if (sk_X509_NAME_find(skCAList, sk_X509_NAME_value(sk, n)) < 0) + sk_X509_NAME_push(skCAList, sk_X509_NAME_value(sk, n)); + } + } + ap_pclosedir(p, dir); + } + + /* + * Cleanup + */ + sk_X509_NAME_set_cmp_func(skCAList, NULL); + ap_destroy_pool(p); + + return skCAList; +} + +void ssl_init_Child(server_rec *s, pool *p) +{ + /* open the mutex lockfile */ + ssl_mutex_reinit(s, p); + return; +} + +void ssl_init_ChildKill(void *data) +{ + /* currently nothing to do */ + return; +} + +void ssl_init_ModuleKill(void *data) +{ + SSLSrvConfigRec *sc; + server_rec *s = (server_rec *)data; + + /* + * Drop the session cache and mutex + */ + ssl_scache_kill(s); + ssl_mutex_kill(s); + + /* + * Destroy the temporary keys and params + */ + ssl_init_TmpKeysHandle(SSL_TKP_FREE, s, NULL); + + /* + * Free the non-pool allocated structures + * in the per-server configurations + */ + for (; s != NULL; s = s->next) { + sc = mySrvConfig(s); + if (sc->pPublicCert[SSL_AIDX_RSA] != NULL) { + X509_free(sc->pPublicCert[SSL_AIDX_RSA]); + sc->pPublicCert[SSL_AIDX_RSA] = NULL; + } + if (sc->pPublicCert[SSL_AIDX_DSA] != NULL) { + X509_free(sc->pPublicCert[SSL_AIDX_DSA]); + sc->pPublicCert[SSL_AIDX_DSA] = NULL; + } + if (sc->pPrivateKey[SSL_AIDX_RSA] != NULL) { + EVP_PKEY_free(sc->pPrivateKey[SSL_AIDX_RSA]); + sc->pPrivateKey[SSL_AIDX_RSA] = NULL; + } + if (sc->pPrivateKey[SSL_AIDX_DSA] != NULL) { + EVP_PKEY_free(sc->pPrivateKey[SSL_AIDX_DSA]); + sc->pPrivateKey[SSL_AIDX_DSA] = NULL; + } + if (sc->pSSLCtx != NULL) { + SSL_CTX_free(sc->pSSLCtx); + sc->pSSLCtx = NULL; + } + } + + /* + * Try to kill the internals of the SSL library. + */ +#ifdef SHARED_MODULE + ERR_free_strings(); + ERR_remove_state(0); + EVP_cleanup(); +#endif + + return; +} + diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c new file mode 100644 index 0000000000..4ba1574ca8 --- /dev/null +++ b/modules/ssl/ssl_engine_io.c @@ -0,0 +1,728 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_io.c +** I/O Functions +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``MY HACK: This universe. + Just one little problem: + core keeps dumping.'' + -- Unknown */ +#include "mod_ssl.h" + +/* _________________________________________________________________ +** +** I/O Request Body Sucking and Re-Injection +** _________________________________________________________________ +*/ + +#ifndef SSL_CONSERVATIVE + +/* + * Background: + * + * 1. When the client sends a HTTP/HTTPS request, Apache's core code + * reads only the request line ("METHOD /path HTTP/x.y") and the + * attached MIME headers ("Foo: bar") up to the terminating line ("CR + * LF"). An attached request body (for instance the data of a POST + * method) is _NOT_ read. Instead it is read by mod_cgi's content + * handler and directly passed to the CGI script. + * + * 2. mod_ssl supports per-directory re-configuration of SSL parameters. + * This is implemented by performing an SSL renegotiation of the + * re-configured parameters after the request is read, but before the + * response is sent. In more detail: the renegotiation happens after the + * request line and MIME headers were read, but _before_ the attached + * request body is read. The reason simply is that in the HTTP protocol + * usually there is no acknowledgment step between the headers and the + * body (there is the 100-continue feature and the chunking facility + * only), so Apache has no API hook for this step. + * + * 3. the problem now occurs when the client sends a POST request for + * URL /foo via HTTPS the server and the server has SSL parameters + * re-configured on a per-URL basis for /foo. Then mod_ssl has to + * perform an SSL renegotiation after the request was read and before + * the response is sent. But the problem is the pending POST body data + * in the receive buffer of SSL (which Apache still has not read - it's + * pending until mod_cgi sucks it in). When mod_ssl now tries to perform + * the renegotiation the pending data leads to an I/O error. + * + * Solution Idea: + * + * There are only two solutions: Either to simply state that POST + * requests to URLs with SSL re-configurations are not allowed, or to + * renegotiate really after the _complete_ request (i.e. including + * the POST body) was read. Obviously the latter would be preferred, + * but it cannot be done easily inside Apache, because as already + * mentioned, there is no API step between the body reading and the body + * processing. And even when we mod_ssl would hook directly into the + * loop of mod_cgi, we wouldn't solve the problem for other handlers, of + * course. So the only general solution is to suck in the pending data + * of the request body from the OpenSSL BIO into the Apache BUFF. Then + * the renegotiation can be done and after this step Apache can proceed + * processing the request as before. + * + * Solution Implementation: + * + * We cannot simply suck in the data via an SSL_read-based loop because of + * HTTP chunking. Instead we _have_ to use the Apache API for this step which + * is aware of HTTP chunking. So the trick is to suck in the pending request + * data via the Apache API (which uses Apache's BUFF code and in the + * background mod_ssl's I/O glue code) and re-inject it later into the Apache + * BUFF code again. This way the data flows twice through the Apache BUFF, of + * course. But this way the solution doesn't depend on any Apache specifics + * and is fully transparent to Apache modules. + */ + +struct ssl_io_suck_st { + BOOL active; + char *bufptr; + int buflen; + char *pendptr; + int pendlen; +}; + +/* prepare request_rec structure for input sucking */ +static void ssl_io_suck_start(request_rec *r) +{ + struct ssl_io_suck_st *ss; + + ss = ap_ctx_get(r->ctx, "ssl::io::suck"); + if (ss == NULL) { + ss = ap_palloc(r->pool, sizeof(struct ssl_io_suck_st)); + ap_ctx_set(r->ctx, "ssl::io::suck", ss); + ss->buflen = 8192; + ss->bufptr = ap_palloc(r->pool, ss->buflen); + } + ss->pendptr = ss->bufptr; + ss->pendlen = 0; + ss->active = FALSE; + return; +} + +/* record a sucked input chunk */ +static void ssl_io_suck_record(request_rec *r, char *buf, int len) +{ + struct ssl_io_suck_st *ss; + + if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL) + return; + if (((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) < len) { + /* "expand" buffer: actually we cannot really expand the buffer + here, because Apache's pool system doesn't support expanding chunks + of memory. Instead we have to either reuse processed data or + allocate a new chunk of memory in advance if we really need more + memory. */ + int newlen; + char *newptr; + + if (( (ss->pendptr - ss->bufptr) + + ((ss->bufptr + ss->buflen) - (ss->pendptr + ss->pendlen)) ) >= len) { + /* make memory available by reusing already processed data */ + memmove(ss->bufptr, ss->pendptr, ss->pendlen); + ss->pendptr = ss->bufptr; + } + else { + /* too bad, we have to allocate a new larger buffer */ + newlen = (ss->buflen * 2) + len; + newptr = ap_palloc(r->pool, newlen); + ss->bufptr = newptr; + ss->buflen = newlen; + memcpy(ss->bufptr, ss->pendptr, ss->pendlen); + ss->pendptr = ss->bufptr; + } + } + memcpy(ss->pendptr+ss->pendlen, buf, len); + ss->pendlen += len; + return; +} + +/* finish request_rec after input sucking */ +static void ssl_io_suck_end(request_rec *r) +{ + struct ssl_io_suck_st *ss; + + if ((ss = ap_ctx_get(r->ctx, "ssl::io::suck")) == NULL) + return; + ss->active = TRUE; + r->read_body = REQUEST_NO_BODY; + r->read_length = 0; + r->read_chunked = 0; + r->remaining = 0; + ap_bsetflag(r->connection->client, B_CHUNK, 0); + return; +} + +void ssl_io_suck(request_rec *r, SSL *ssl) +{ + int rc; + int len; + char *buf; + int buflen; + char c; + int sucked; + + if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) == OK) { + if (ap_should_client_block(r)) { + + /* read client request block through Apache API */ + buflen = HUGE_STRING_LEN; + buf = ap_palloc(r->pool, buflen); + ap_hard_timeout("SSL I/O request body pre-sucking", r); + sucked = 0; + ssl_io_suck_start(r); + while ((len = ap_get_client_block(r, buf, buflen)) > 0) { + ssl_io_suck_record(r, buf, len); + sucked += len; + } + ssl_io_suck_end(r); + ap_kill_timeout(r); + + /* suck trailing data (usually CR LF) which + is still in the Apache BUFF layer */ + while (ap_bpeekc(r->connection->client) != EOF) { + c = ap_bgetc(r->connection->client); + ssl_io_suck_record(r, &c, 1); + sucked++; + } + + ssl_log(r->server, SSL_LOG_TRACE, + "I/O: sucked %d bytes of input data from SSL/TLS I/O layer " + "for delayed injection into Apache I/O layer", sucked); + } + } + return; +} + +/* the SSL_read replacement routine which knows about the suck buffer */ +static int ssl_io_suck_read(SSL *ssl, char *buf, int len) +{ + ap_ctx *actx; + struct ssl_io_suck_st *ss; + request_rec *r = NULL; + int rv; + + actx = (ap_ctx *)SSL_get_app_data2(ssl); + if (actx != NULL) + r = (request_rec *)ap_ctx_get(actx, "ssl::request_rec"); + + rv = -1; + if (r != NULL) { + ss = ap_ctx_get(r->ctx, "ssl::io::suck"); + if (ss != NULL) { + if (ss->active && ss->pendlen > 0) { + /* ok, there is pre-sucked data */ + len = (ss->pendlen > len ? len : ss->pendlen); + memcpy(buf, ss->pendptr, len); + ss->pendptr += len; + ss->pendlen -= len; + ssl_log(r->server, SSL_LOG_TRACE, + "I/O: injecting %d bytes of pre-sucked data " + "into Apache I/O layer", len); + rv = len; + } + } + } + if (rv == -1) + rv = SSL_read(ssl, buf, len); + return rv; +} + +/* override SSL_read in the following code... */ +#define SSL_read ssl_io_suck_read + +#endif /* !SSL_CONSERVATIVE */ + +/* _________________________________________________________________ +** +** I/O Hooks +** _________________________________________________________________ +*/ + +#ifndef NO_WRITEV +#include <sys/types.h> +#include <sys/uio.h> +#endif + +static int ssl_io_hook_read(BUFF *fb, char *buf, int len); +static int ssl_io_hook_write(BUFF *fb, char *buf, int len); +#ifndef NO_WRITEV +static int ssl_io_hook_writev(BUFF *fb, const struct iovec *iov, int iovcnt); +#endif +#ifdef WIN32 +static int ssl_io_hook_recvwithtimeout(BUFF *fb, char *buf, int len); +static int ssl_io_hook_sendwithtimeout(BUFF *fb, const char *buf, int len); +#endif /* WIN32 */ + +void ssl_io_register(void) +{ + ap_hook_register("ap::buff::read", ssl_io_hook_read, AP_HOOK_NOCTX); + ap_hook_register("ap::buff::write", ssl_io_hook_write, AP_HOOK_NOCTX); +#ifndef NO_WRITEV + ap_hook_register("ap::buff::writev", ssl_io_hook_writev, AP_HOOK_NOCTX); +#endif +#ifdef WIN32 + ap_hook_register("ap::buff::recvwithtimeout", + ssl_io_hook_recvwithtimeout, AP_HOOK_NOCTX); + ap_hook_register("ap::buff::sendwithtimeout", + ssl_io_hook_sendwithtimeout, AP_HOOK_NOCTX); +#endif + return; +} + +void ssl_io_unregister(void) +{ + ap_hook_unregister("ap::buff::read", ssl_io_hook_read); + ap_hook_unregister("ap::buff::write", ssl_io_hook_write); +#ifndef NO_WRITEV + ap_hook_unregister("ap::buff::writev", ssl_io_hook_writev); +#endif +#ifdef WIN32 + ap_hook_unregister("ap::buff::recvwithtimeout", ssl_io_hook_recvwithtimeout); + ap_hook_unregister("ap::buff::sendwithtimeout", ssl_io_hook_sendwithtimeout); +#endif + return; +} + +static int ssl_io_hook_read(BUFF *fb, char *buf, int len) +{ + SSL *ssl; + conn_rec *c; + int rc; + + if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) { + rc = SSL_read(ssl, buf, len); + /* + * Simulate an EINTR in case OpenSSL wants to read more. + * (This is usually the case when the client forces an SSL + * renegotation which is handled implicitly by OpenSSL.) + */ + if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_READ) + errno = EINTR; + /* + * Log SSL errors + */ + if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) { + c = (conn_rec *)SSL_get_app_data(ssl); + ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "SSL error on reading data"); + } + /* + * read(2) returns only the generic error number -1 + */ + if (rc < 0) + rc = -1; + } + else + rc = read(fb->fd_in, buf, len); + return rc; +} + +static int ssl_io_hook_write(BUFF *fb, char *buf, int len) +{ + SSL *ssl; + conn_rec *c; + int rc; + + if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) { + rc = SSL_write(ssl, buf, len); + /* + * Simulate an EINTR in case OpenSSL wants to write more. + */ + if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_WRITE) + errno = EINTR; + /* + * Log SSL errors + */ + if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) { + c = (conn_rec *)SSL_get_app_data(ssl); + ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "SSL error on writing data"); + } + /* + * write(2) returns only the generic error number -1 + */ + if (rc < 0) + rc = -1; + } + else + rc = write(fb->fd, buf, len); + return rc; +} + +#ifndef NO_WRITEV +/* the prototype for our own SSL_writev() */ +static int SSL_writev(SSL *, const struct iovec *, int); + +static int ssl_io_hook_writev(BUFF *fb, const struct iovec *iov, int iovcnt) +{ + SSL *ssl; + conn_rec *c; + int rc; + + if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) { + rc = SSL_writev(ssl, iov, iovcnt); + /* + * Simulate an EINTR in case OpenSSL wants to write more. + */ + if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_WANT_WRITE) + errno = EINTR; + /* + * Log SSL errors + */ + if (rc < 0 && SSL_get_error(ssl, rc) == SSL_ERROR_SSL) { + c = (conn_rec *)SSL_get_app_data(ssl); + ssl_log(c->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "SSL error on writing data"); + } + /* + * writev(2) returns only the generic error number -1 + */ + if (rc < 0) + rc = -1; + } + else + rc = writev(fb->fd, iov, iovcnt); + return rc; +} +#endif + +#ifdef WIN32 + +/* these two functions are exported from buff.c under WIN32 */ +API_EXPORT(int) sendwithtimeout(int sock, const char *buf, int len, int flags); +API_EXPORT(int) recvwithtimeout(int sock, char *buf, int len, int flags); + +/* and the prototypes for our SSL_xxx variants */ +static int SSL_sendwithtimeout(BUFF *fb, const char *buf, int len); +static int SSL_recvwithtimeout(BUFF *fb, char *buf, int len); + +static int ssl_io_hook_recvwithtimeout(BUFF *fb, char *buf, int len) +{ + SSL *ssl; + int rc; + + if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) + rc = SSL_recvwithtimeout(fb, buf, len); + else + rc = recvwithtimeout(fb->fd, buf, len, 0); + return rc; +} + +static int ssl_io_hook_sendwithtimeout(BUFF *fb, const char *buf, int len) +{ + SSL *ssl; + int rc; + + if ((ssl = ap_ctx_get(fb->ctx, "ssl")) != NULL) + rc = SSL_sendwithtimeout(fb, buf, len); + else + rc = sendwithtimeout(fb->fd, buf, len, 0); + return rc; +} + +#endif /* WIN32 */ + +/* _________________________________________________________________ +** +** Special Functions for OpenSSL +** _________________________________________________________________ +*/ + +#ifdef WIN32 + +static int SSL_sendwithtimeout(BUFF *fb, const char *buf, int len) +{ + int iostate = 1; + fd_set fdset; + struct timeval tv; + int err = WSAEWOULDBLOCK; + int rv; + int retry; + int sock = fb->fd; + SSL *ssl; + + ssl = ap_ctx_get(fb->ctx, "ssl"); + + if (!(tv.tv_sec = ap_check_alarm())) + return (SSL_write(ssl, (char*)buf, len)); + + rv = ioctlsocket(sock, FIONBIO, &iostate); + iostate = 0; + if (rv) { + err = WSAGetLastError(); + ap_assert(0); + } + rv = SSL_write(ssl, (char*)buf, len); + if (rv <= 0) { + if (BIO_sock_should_retry(rv)) { + do { + retry = 0; + FD_ZERO(&fdset); + FD_SET((unsigned int)sock, &fdset); + tv.tv_usec = 0; + rv = select(FD_SETSIZE, NULL, &fdset, NULL, &tv); + if (rv == SOCKET_ERROR) + err = WSAGetLastError(); + else if (rv == 0) { + ioctlsocket(sock, FIONBIO, &iostate); + if(ap_check_alarm() < 0) { + WSASetLastError(EINTR); /* Simulate an alarm() */ + return (SOCKET_ERROR); + } + } + else { + rv = SSL_write(ssl, (char*)buf, len); + if (BIO_sock_should_retry(rv)) { + ap_log_error(APLOG_MARK,APLOG_DEBUG, NULL, + "select claimed we could write, " + "but in fact we couldn't. " + "This is a bug in Windows."); + retry = 1; + Sleep(100); + } + } + } while(retry); + } + } + ioctlsocket(sock, FIONBIO, &iostate); + if (rv == SOCKET_ERROR) + WSASetLastError(err); + return (rv); +} + +static int SSL_recvwithtimeout(BUFF *fb, char *buf, int len) +{ + int iostate = 1; + fd_set fdset; + struct timeval tv; + int err = WSAEWOULDBLOCK; + int rv; + int sock = fb->fd_in; + SSL *ssl; + int retry; + + ssl = ap_ctx_get(fb->ctx, "ssl"); + + if (!(tv.tv_sec = ap_check_alarm())) + return (SSL_read(ssl, buf, len)); + + rv = ioctlsocket(sock, FIONBIO, &iostate); + iostate = 0; + ap_assert(!rv); + rv = SSL_read(ssl, buf, len); + if (rv <= 0) { + if (BIO_sock_should_retry(rv)) { + do { + retry = 0; + FD_ZERO(&fdset); + FD_SET((unsigned int)sock, &fdset); + tv.tv_usec = 0; + rv = select(FD_SETSIZE, &fdset, NULL, NULL, &tv); + if (rv == SOCKET_ERROR) + err = WSAGetLastError(); + else if (rv == 0) { + ioctlsocket(sock, FIONBIO, &iostate); + ap_check_alarm(); + WSASetLastError(WSAEWOULDBLOCK); + return (SOCKET_ERROR); + } + else { + rv = SSL_read(ssl, buf, len); + if (rv == SOCKET_ERROR) { + if (BIO_sock_should_retry(rv)) { + ap_log_error(APLOG_MARK,APLOG_DEBUG, NULL, + "select claimed we could read, " + "but in fact we couldn't. " + "This is a bug in Windows."); + retry = 1; + Sleep(100); + } + else { + err = WSAGetLastError(); + } + } + } + } while(retry); + } + } + ioctlsocket(sock, FIONBIO, &iostate); + if (rv == SOCKET_ERROR) + WSASetLastError(err); + return (rv); +} + +#endif /*WIN32*/ + +/* + * There is no SSL_writev() provided by OpenSSL. The reason is mainly because + * OpenSSL has to fragment the data itself again for the SSL record layer, so a + * writev() like interface makes not much sense. What we do is to emulate it + * to at least being able to use the write() like interface. But keep in mind + * that the network I/O performance is not write() like, of course. + */ +#ifndef NO_WRITEV +static int SSL_writev(SSL *ssl, const struct iovec *iov, int iovcnt) +{ + int i; + int n; + int rc; + + rc = 0; + for (i = 0; i < iovcnt; i++) { + if ((n = SSL_write(ssl, iov[i].iov_base, iov[i].iov_len)) == -1) { + rc = -1; + break; + } + rc += n; + } + return rc; +} +#endif + +/* _________________________________________________________________ +** +** I/O Data Debugging +** _________________________________________________________________ +*/ + +#define DUMP_WIDTH 16 + +static void ssl_io_data_dump(server_rec *srvr, const char *s, long len) +{ + char buf[256]; + char tmp[64]; + int i, j, rows, trunc; + unsigned char ch; + + trunc = 0; + for(; (len > 0) && ((s[len-1] == ' ') || (s[len-1] == '\0')); len--) + trunc++; + rows = (len / DUMP_WIDTH); + if ((rows * DUMP_WIDTH) < len) + rows++; + ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID, + "+-------------------------------------------------------------------------+"); + for(i = 0 ; i< rows; i++) { + ap_snprintf(tmp, sizeof(tmp), "| %04x: ", i * DUMP_WIDTH); + ap_cpystrn(buf, tmp, sizeof(buf)); + for (j = 0; j < DUMP_WIDTH; j++) { + if (((i * DUMP_WIDTH) + j) >= len) + ap_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf)); + else { + ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff; + ap_snprintf(tmp, sizeof(tmp), "%02x%c", ch , j==7 ? '-' : ' '); + ap_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf)); + } + } + ap_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf)); + for (j = 0; j < DUMP_WIDTH; j++) { + if (((i * DUMP_WIDTH) + j) >= len) + ap_cpystrn(buf+strlen(buf), " ", sizeof(buf)-strlen(buf)); + else { + ch = ((unsigned char)*((char *)(s) + i * DUMP_WIDTH + j)) & 0xff; + ap_snprintf(tmp, sizeof(tmp), "%c", ((ch >= ' ') && (ch <= '~')) ? ch : '.'); + ap_cpystrn(buf+strlen(buf), tmp, sizeof(buf)-strlen(buf)); + } + } + ap_cpystrn(buf+strlen(buf), " |", sizeof(buf)-strlen(buf)); + ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID, "%s", buf); + } + if (trunc > 0) + ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID, + "| %04x - <SPACES/NULS>", len + trunc); + ssl_log(srvr, SSL_LOG_DEBUG|SSL_NO_TIMESTAMP|SSL_NO_LEVELID, + "+-------------------------------------------------------------------------+"); + return; +} + +long ssl_io_data_cb(BIO *bio, int cmd, const char *argp, int argi, long argl, long rc) +{ + SSL *ssl; + conn_rec *c; + server_rec *s; + + if ((ssl = (SSL *)BIO_get_callback_arg(bio)) == NULL) + return rc; + if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL) + return rc; + s = c->server; + + if ( cmd == (BIO_CB_WRITE|BIO_CB_RETURN) + || cmd == (BIO_CB_READ |BIO_CB_RETURN) ) { + if (rc >= 0) { + ssl_log(s, SSL_LOG_DEBUG, + "%s: %s %ld/%d bytes %s BIO#%08X [mem: %08lX] %s", + SSL_LIBRARY_NAME, + (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"), + rc, argi, (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "to" : "from"), + bio, argp, + (argp != NULL ? "(BIO dump follows)" : "(Ops, no memory buffer?)")); + if (argp != NULL) + ssl_io_data_dump(s, argp, rc); + } + else { + ssl_log(s, SSL_LOG_DEBUG, + "%s: I/O error, %d bytes expected to %s on BIO#%08X [mem: %08lX]", + SSL_LIBRARY_NAME, argi, + (cmd == (BIO_CB_WRITE|BIO_CB_RETURN) ? "write" : "read"), + bio, argp); + } + } + return rc; +} + diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c new file mode 100644 index 0000000000..ca1b3f0a55 --- /dev/null +++ b/modules/ssl/ssl_engine_kernel.c @@ -0,0 +1,1905 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_kernel.c +** The SSL engine kernel +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +/* ==================================================================== + * Copyright (c) 1995-1999 Ben Laurie. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by Ben Laurie + * for use in the Apache-SSL HTTP server project." + * + * 4. The name "Apache-SSL Server" must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 5. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Ben Laurie + * for use in the Apache-SSL HTTP server project." + * + * THIS SOFTWARE IS PROVIDED BY BEN LAURIE ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BEN LAURIE OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``It took me fifteen years to discover + I had no talent for programming, but + I couldn't give it up because by that + time I was too famous.'' + -- Unknown */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** SSL Engine Kernel +** _________________________________________________________________ +*/ + +/* + * Connect Handler: + * Connect SSL to the accepted socket + * + * Usually we would need an Apache API hook which is triggered right after + * the socket is accepted for handling a new request. But Apache 1.3 doesn't + * provide such a hook, so we have to patch http_main.c and call this + * function directly. + */ +void ssl_hook_NewConnection(conn_rec *conn) +{ + server_rec *srvr; + BUFF *fb; + SSLSrvConfigRec *sc; + ap_ctx *apctx; + SSL *ssl; + char *cp; + char *cpVHostID; + char *cpVHostMD5; + X509 *xs; + int rc; + + /* + * Get context + */ + srvr = conn->server; + fb = conn->client; + sc = mySrvConfig(srvr); + + /* + * Create SSL context + */ + ap_ctx_set(fb->ctx, "ssl", NULL); + + /* + * Immediately stop processing if SSL + * is disabled for this connection + */ + if (sc == NULL || !sc->bEnabled) + return; + + /* + * Remember the connection information for + * later access inside callback functions + */ + cpVHostID = ssl_util_vhostid(conn->pool, srvr); + ssl_log(srvr, SSL_LOG_INFO, "Connection to child %d established " + "(server %s, client %s)", conn->child_num, cpVHostID, + conn->remote_ip != NULL ? conn->remote_ip : "unknown"); + + /* + * Seed the Pseudo Random Number Generator (PRNG) + */ + ssl_rand_seed(srvr, conn->pool, SSL_RSCTX_CONNECT, ""); + + /* + * Create a new SSL connection with the configured server SSL context and + * attach this to the socket. Additionally we register this attachment + * so we can detach later. + */ + if ((ssl = SSL_new(sc->pSSLCtx)) == NULL) { + ssl_log(conn->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Unable to create a new SSL connection from the SSL context"); + ap_ctx_set(fb->ctx, "ssl", NULL); + ap_bsetflag(fb, B_EOF|B_EOUT, 1); + conn->aborted = 1; + return; + } + SSL_clear(ssl); + cpVHostMD5 = ap_md5(conn->pool, (unsigned char *)cpVHostID); + if (!SSL_set_session_id_context(ssl, (unsigned char *)cpVHostMD5, strlen(cpVHostMD5))) { + ssl_log(conn->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Unable to set session id context to `%s'", cpVHostMD5); + ap_ctx_set(fb->ctx, "ssl", NULL); + ap_bsetflag(fb, B_EOF|B_EOUT, 1); + conn->aborted = 1; + return; + } + SSL_set_app_data(ssl, conn); + apctx = ap_ctx_new(conn->pool); + ap_ctx_set(apctx, "ssl::request_rec", NULL); + ap_ctx_set(apctx, "ssl::verify::depth", AP_CTX_NUM2PTR(0)); + SSL_set_app_data2(ssl, apctx); + SSL_set_fd(ssl, fb->fd); + ap_ctx_set(fb->ctx, "ssl", ssl); + + /* + * Configure callbacks for SSL connection + */ + SSL_set_tmp_rsa_callback(ssl, ssl_callback_TmpRSA); + SSL_set_tmp_dh_callback(ssl, ssl_callback_TmpDH); + if (sc->nLogLevel >= SSL_LOG_DEBUG) { + BIO_set_callback(SSL_get_rbio(ssl), ssl_io_data_cb); + BIO_set_callback_arg(SSL_get_rbio(ssl), ssl); + } + + /* + * Predefine some client verification results + */ + ap_ctx_set(fb->ctx, "ssl::client::dn", NULL); + ap_ctx_set(fb->ctx, "ssl::verify::error", NULL); + ap_ctx_set(fb->ctx, "ssl::verify::info", NULL); + SSL_set_verify_result(ssl, X509_V_OK); + + /* + * We have to manage a I/O timeout ourself, because Apache + * does it the first time when reading the request, but we're + * working some time before this happens. + */ + ap_ctx_set(ap_global_ctx, "ssl::handshake::timeout", (void *)FALSE); + ap_set_callback_and_alarm(ssl_hook_TimeoutConnection, srvr->timeout); + + /* + * Now enter the SSL Handshake Phase + */ + while (!SSL_is_init_finished(ssl)) { + + if ((rc = SSL_accept(ssl)) <= 0) { + + if (SSL_get_error(ssl, rc) == SSL_ERROR_ZERO_RETURN) { + /* + * The case where the connection was closed before any data + * was transferred. That's not a real error and can occur + * sporadically with some clients. + */ + ssl_log(srvr, SSL_LOG_INFO, + "SSL handshake stopped: connection was closed"); + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + SSL_smart_shutdown(ssl); + SSL_free(ssl); + ap_ctx_set(fb->ctx, "ssl", NULL); + ap_bsetflag(fb, B_EOF|B_EOUT, 1); + conn->aborted = 1; + return; + } + else if (ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) { + /* + * The case where OpenSSL has recognized a HTTP request: + * This means the client speaks plain HTTP on our HTTPS + * port. Hmmmm... At least for this error we can be more friendly + * and try to provide him with a HTML error page. We have only one + * problem: OpenSSL has already read some bytes from the HTTP + * request. So we have to skip the request line manually and + * instead provide a faked one in order to continue the internal + * Apache processing. + * + */ + char ca[2]; + int rv; + + /* log the situation */ + ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "SSL handshake failed: HTTP spoken on HTTPS port; " + "trying to send HTML error page"); + + /* first: skip the remaining bytes of the request line */ + do { + do { + rv = read(fb->fd, ca, 1); + } while (rv == -1 && errno == EINTR); + } while (rv > 0 && ca[0] != '\012' /*LF*/); + + /* second: fake the request line */ + fb->inbase = ap_palloc(fb->pool, fb->bufsiz); + ap_cpystrn((char *)fb->inbase, "GET /mod_ssl:error:HTTP-request HTTP/1.0\r\n", + fb->bufsiz); + fb->inptr = fb->inbase; + fb->incnt = strlen((char *)fb->inptr); + + /* third: kick away the SSL stuff */ + SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); + SSL_smart_shutdown(ssl); + SSL_free(ssl); + ap_ctx_set(fb->ctx, "ssl", NULL); + + /* finally: let Apache go on with processing */ + return; + } + else if (ap_ctx_get(ap_global_ctx, "ssl::handshake::timeout") == (void *)TRUE) { + ssl_log(srvr, SSL_LOG_ERROR, + "SSL handshake timed out (client %s, server %s)", + conn->remote_ip != NULL ? conn->remote_ip : "unknown", cpVHostID); + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + SSL_smart_shutdown(ssl); + SSL_free(ssl); + ap_ctx_set(fb->ctx, "ssl", NULL); + ap_bsetflag(fb, B_EOF|B_EOUT, 1); + conn->aborted = 1; + return; + } + else if (SSL_get_error(ssl, rc) == SSL_ERROR_SYSCALL) { + if (errno == EINTR) + continue; + if (errno > 0) + ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR|SSL_ADD_ERRNO, + "SSL handshake interrupted by system " + "[Hint: Stop button pressed in browser?!]"); + else + ssl_log(srvr, SSL_LOG_INFO|SSL_ADD_SSLERR|SSL_ADD_ERRNO, + "Spurious SSL handshake interrupt" + "[Hint: Usually just one of those OpenSSL confusions!?]"); + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + SSL_smart_shutdown(ssl); + SSL_free(ssl); + ap_ctx_set(fb->ctx, "ssl", NULL); + ap_bsetflag(fb, B_EOF|B_EOUT, 1); + conn->aborted = 1; + return; + } + else { + /* + * Ok, anything else is a fatal error + */ + ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR|SSL_ADD_ERRNO, + "SSL handshake failed (server %s, client %s)", cpVHostID, + conn->remote_ip != NULL ? conn->remote_ip : "unknown"); + + /* + * try to gracefully shutdown the connection: + * - send an own shutdown message (be gracefully) + * - don't wait for peer's shutdown message (deadloop) + * - kick away the SSL stuff immediately + * - block the socket, so Apache cannot operate any more + */ + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + SSL_smart_shutdown(ssl); + SSL_free(ssl); + ap_ctx_set(fb->ctx, "ssl", NULL); + ap_bsetflag(fb, B_EOF|B_EOUT, 1); + conn->aborted = 1; + return; + } + } + + /* + * Check for failed client authentication + */ + if ( SSL_get_verify_result(ssl) != X509_V_OK + || ap_ctx_get(fb->ctx, "ssl::verify::error") != NULL) { + cp = (char *)ap_ctx_get(fb->ctx, "ssl::verify::error"); + ssl_log(srvr, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "SSL client authentication failed: %s", + cp != NULL ? cp : "unknown reason"); + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + SSL_smart_shutdown(ssl); + SSL_free(ssl); + ap_ctx_set(fb->ctx, "ssl", NULL); + ap_bsetflag(fb, B_EOF|B_EOUT, 1); + conn->aborted = 1; + return; + } + + /* + * Remember the peer certificate's DN + */ + if ((xs = SSL_get_peer_certificate(ssl)) != NULL) { + cp = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0); + ap_ctx_set(fb->ctx, "ssl::client::dn", ap_pstrdup(conn->pool, cp)); + free(cp); + } + + /* + * Make really sure that when a peer certificate + * is required we really got one... (be paranoid) + */ + if ( sc->nVerifyClient == SSL_CVERIFY_REQUIRE + && ap_ctx_get(fb->ctx, "ssl::client::dn") == NULL) { + ssl_log(srvr, SSL_LOG_ERROR, + "No acceptable peer certificate available"); + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + SSL_smart_shutdown(ssl); + SSL_free(ssl); + ap_ctx_set(fb->ctx, "ssl", NULL); + ap_bsetflag(fb, B_EOF|B_EOUT, 1); + conn->aborted = 1; + return; + } + } + + /* + * Remove the timeout handling + */ + ap_set_callback_and_alarm(NULL, 0); + ap_ctx_set(ap_global_ctx, "ssl::handshake::timeout", (void *)FALSE); + + /* + * Improve I/O throughput by using + * OpenSSL's read-ahead functionality + * (don't used under Win32, because + * there we use select()) + */ +#ifndef WIN32 + SSL_set_read_ahead(ssl, TRUE); +#endif + +#ifdef SSL_VENDOR + /* Allow vendors to do more things on connection time... */ + ap_hook_use("ap::mod_ssl::vendor::new_connection", + AP_HOOK_SIG2(void,ptr), AP_HOOK_ALL, conn); +#endif + + return; +} + +/* + * Signal handler function for the SSL handshake phase + */ +void ssl_hook_TimeoutConnection(int sig) +{ + /* we just set a flag for the handshake processing loop */ + ap_ctx_set(ap_global_ctx, "ssl::handshake::timeout", (void *)TRUE); + return; +} + +/* + * Close the SSL part of the socket connection + * (called immediately _before_ the socket is closed) + */ +void ssl_hook_CloseConnection(conn_rec *conn) +{ + SSL *ssl; + char *cpType; + + ssl = ap_ctx_get(conn->client->ctx, "ssl"); + if (ssl == NULL) + return; + + /* + * First make sure that no more data is pending in Apache's BUFF, + * because when it's (implicitly) flushed later by the ap_bclose() + * calls of Apache it would lead to an I/O error in the browser due + * to the fact that the SSL layer was already removed by us. + */ + ap_bflush(conn->client); + + /* + * Now close the SSL layer of the connection. We've to take + * the TLSv1 standard into account here: + * + * | 7.2.1. Closure alerts + * | + * | The client and the server must share knowledge that the connection is + * | ending in order to avoid a truncation attack. Either party may + * | initiate the exchange of closing messages. + * | + * | close_notify + * | This message notifies the recipient that the sender will not send + * | any more messages on this connection. The session becomes + * | unresumable if any connection is terminated without proper + * | close_notify messages with level equal to warning. + * | + * | Either party may initiate a close by sending a close_notify alert. + * | Any data received after a closure alert is ignored. + * | + * | Each party is required to send a close_notify alert before closing + * | the write side of the connection. It is required that the other party + * | respond with a close_notify alert of its own and close down the + * | connection immediately, discarding any pending writes. It is not + * | required for the initiator of the close to wait for the responding + * | close_notify alert before closing the read side of the connection. + * + * This means we've to send a close notify message, but haven't to wait + * for the close notify of the client. Actually we cannot wait for the + * close notify of the client because some clients (including Netscape + * 4.x) don't send one, so we would hang. + */ + + /* + * exchange close notify messages, but allow the user + * to force the type of handshake via SetEnvIf directive + */ + if (ap_ctx_get(conn->client->ctx, "ssl::flag::unclean-shutdown") == PTRUE) { + /* perform no close notify handshake at all + (violates the SSL/TLS standard!) */ + SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); + cpType = "unclean"; + } + else if (ap_ctx_get(conn->client->ctx, "ssl::flag::accurate-shutdown") == PTRUE) { + /* send close notify and wait for clients close notify + (standard compliant, but usually causes connection hangs) */ + SSL_set_shutdown(ssl, 0); + cpType = "accurate"; + } + else { + /* send close notify, but don't wait for clients close notify + (standard compliant and safe, so it's the DEFAULT!) */ + SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); + cpType = "standard"; + } + SSL_smart_shutdown(ssl); + + /* deallocate the SSL connection */ + SSL_free(ssl); + ap_ctx_set(conn->client->ctx, "ssl", NULL); + + /* and finally log the fact that we've closed the connection */ + ssl_log(conn->server, SSL_LOG_INFO, + "Connection to child %d closed with %s shutdown (server %s, client %s)", + conn->child_num, cpType, ssl_util_vhostid(conn->pool, conn->server), + conn->remote_ip != NULL ? conn->remote_ip : "unknown"); + return; +} + +/* + * Post Read Request Handler + */ +int ssl_hook_ReadReq(request_rec *r) +{ + SSL *ssl; + ap_ctx *apctx; + + /* + * Get the SSL connection structure and perform the + * delayed interlinking from SSL back to request_rec + */ + ssl = ap_ctx_get(r->connection->client->ctx, "ssl"); + if (ssl != NULL) { + apctx = SSL_get_app_data2(ssl); + ap_ctx_set(apctx, "ssl::request_rec", r); + } + + /* + * Force the mod_ssl content handler when URL indicates this + */ + if (strEQn(r->uri, "/mod_ssl:", 9)) + r->handler = "mod_ssl:content-handler"; + if (ssl != NULL) { + ap_ctx_set(r->ctx, "ap::http::method", "https"); + ap_ctx_set(r->ctx, "ap::default::port", "443"); + } + else { + ap_ctx_set(r->ctx, "ap::http::method", NULL); + ap_ctx_set(r->ctx, "ap::default::port", NULL); + } + return DECLINED; +} + +/* + * URL Translation Handler + */ +int ssl_hook_Translate(request_rec *r) +{ + if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL) + return DECLINED; + + /* + * Log information about incoming HTTPS requests + */ + if (ap_is_initial_req(r)) + ssl_log(r->server, SSL_LOG_INFO, + "%s HTTPS request received for child %d (server %s)", + r->connection->keepalives <= 0 ? + "Initial (No.1)" : + ap_psprintf(r->pool, "Subsequent (No.%d)", + r->connection->keepalives+1), + r->connection->child_num, + ssl_util_vhostid(r->pool, r->server)); + + /* + * Move SetEnvIf information from request_rec to conn_rec/BUFF + * to allow the close connection handler to use them. + */ + if (ap_table_get(r->subprocess_env, "ssl-unclean-shutdown") != NULL) + ap_ctx_set(r->connection->client->ctx, "ssl::flag::unclean-shutdown", PTRUE); + else + ap_ctx_set(r->connection->client->ctx, "ssl::flag::unclean-shutdown", PFALSE); + if (ap_table_get(r->subprocess_env, "ssl-accurate-shutdown") != NULL) + ap_ctx_set(r->connection->client->ctx, "ssl::flag::accurate-shutdown", PTRUE); + else + ap_ctx_set(r->connection->client->ctx, "ssl::flag::accurate-shutdown", PFALSE); + + return DECLINED; +} + +/* + * Content Handler + */ +int ssl_hook_Handler(request_rec *r) +{ + int port; + char *thisport; + char *thisurl; + + if (strNEn(r->uri, "/mod_ssl:", 9)) + return DECLINED; + + if (strEQ(r->uri, "/mod_ssl:error:HTTP-request")) { + thisport = ""; + port = ap_get_server_port(r); + if (!ap_is_default_port(port, r)) + thisport = ap_psprintf(r->pool, ":%u", port); + thisurl = ap_psprintf(r->pool, "https://%s%s/", + ap_get_server_name(r), thisport); + + ap_table_setn(r->notes, "error-notes", ap_psprintf(r->pool, + "Reason: You're speaking plain HTTP to an SSL-enabled server port.<BR>\n" + "Instead use the HTTPS scheme to access this URL, please.<BR>\n" + "<BLOCKQUOTE>Hint: <A HREF=\"%s\"><B>%s</B></A></BLOCKQUOTE>", + thisurl, thisurl)); + } + + return HTTP_BAD_REQUEST; +} + +/* + * Access Handler + */ +int ssl_hook_Access(request_rec *r) +{ + SSLDirConfigRec *dc; + SSLSrvConfigRec *sc; + SSL *ssl; + SSL_CTX *ctx = NULL; + array_header *apRequirement; + ssl_require_t *pRequirements; + ssl_require_t *pRequirement; + char *cp; + int ok; + int i; + BOOL renegotiate; + BOOL renegotiate_quick; +#ifdef SSL_EXPERIMENTAL_PERDIRCA + BOOL reconfigured_locations; + STACK_OF(X509_NAME) *skCAList; + char *cpCAPath; + char *cpCAFile; +#endif + X509 *cert; + STACK_OF(X509) *certstack; + X509_STORE *certstore; + X509_STORE_CTX certstorectx; + int depth; + STACK_OF(SSL_CIPHER) *skCipherOld; + STACK_OF(SSL_CIPHER) *skCipher; + SSL_CIPHER *pCipher; + ap_ctx *apctx; + int nVerifyOld; + int nVerify; + int n; + void *vp; + int rc; + + dc = myDirConfig(r); + sc = mySrvConfig(r->server); + ssl = ap_ctx_get(r->connection->client->ctx, "ssl"); + if (ssl != NULL) + ctx = SSL_get_SSL_CTX(ssl); + + /* + * Support for SSLRequireSSL directive + */ + if (dc->bSSLRequired && ssl == NULL) { + ap_log_reason("SSL connection required", r->filename, r); + /* remember forbidden access for strict require option */ + ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1); + return FORBIDDEN; + } + + /* + * Check to see if SSL protocol is on + */ + if (!sc->bEnabled) + return DECLINED; + if (ssl == NULL) + return DECLINED; + + /* + * Support for per-directory reconfigured SSL connection parameters. + * + * This is implemented by forcing an SSL renegotiation with the + * reconfigured parameter suite. But Apache's internal API processing + * makes our life very hard here, because when internal sub-requests occur + * we nevertheless should avoid multiple unnecessary SSL handshakes (they + * require extra network I/O and especially time to perform). + * + * But the optimization for filtering out the unnecessary handshakes isn't + * obvious and trivial. Especially because while Apache is in its + * sub-request processing the client could force additional handshakes, + * too. And these take place perhaps without our notice. So the only + * possibility is to explicitly _ask_ OpenSSL whether the renegotiation + * has to be performed or not. It has to performed when some parameters + * which were previously known (by us) are not those we've now + * reconfigured (as known by OpenSSL) or (in optimized way) at least when + * the reconfigured parameter suite is stronger (more restrictions) than + * the currently active one. + */ + renegotiate = FALSE; + renegotiate_quick = FALSE; +#ifdef SSL_EXPERIMENTAL_PERDIRCA + reconfigured_locations = FALSE; +#endif + + /* + * Override of SSLCipherSuite + * + * We provide two options here: + * + * o The paranoid and default approach where we force a renegotiation when + * the cipher suite changed in _any_ way (which is straight-forward but + * often forces renegotiations too often and is perhaps not what the + * user actually wanted). + * + * o The optimized and still secure way where we force a renegotiation + * only if the currently active cipher is no longer contained in the + * reconfigured/new cipher suite. Any other changes are not important + * because it's the servers choice to select a cipher from the ones the + * client supports. So as long as the current cipher is still in the new + * cipher suite we're happy. Because we can assume we would have + * selected it again even when other (better) ciphers exists now in the + * new cipher suite. This approach is fine because the user explicitly + * has to enable this via ``SSLOptions +OptRenegotiate''. So we do no + * implicit optimizations. + */ + if (dc->szCipherSuite != NULL) { + /* remember old state */ + pCipher = NULL; + skCipherOld = NULL; + if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) + pCipher = SSL_get_current_cipher(ssl); + else { + skCipherOld = SSL_get_ciphers(ssl); + if (skCipherOld != NULL) + skCipherOld = sk_SSL_CIPHER_dup(skCipherOld); + } + /* configure new state */ + if (!SSL_set_cipher_list(ssl, dc->szCipherSuite)) { + ssl_log(r->server, SSL_LOG_WARN|SSL_ADD_SSLERR, + "Unable to reconfigure (per-directory) permitted SSL ciphers"); + if (skCipherOld != NULL) + sk_SSL_CIPHER_free(skCipherOld); + return FORBIDDEN; + } + /* determine whether a renegotiation has to be forced */ + skCipher = SSL_get_ciphers(ssl); + if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) { + /* optimized way */ + if ((pCipher == NULL && skCipher != NULL) || + (pCipher != NULL && skCipher == NULL) ) + renegotiate = TRUE; + else if (pCipher != NULL && skCipher != NULL + && sk_SSL_CIPHER_find(skCipher, pCipher) < 0) { + renegotiate = TRUE; + } + } + else { + /* paranoid way */ + if ((skCipherOld == NULL && skCipher != NULL) || + (skCipherOld != NULL && skCipher == NULL) ) + renegotiate = TRUE; + else if (skCipherOld != NULL && skCipher != NULL) { + for (n = 0; !renegotiate && n < sk_SSL_CIPHER_num(skCipher); n++) { + if (sk_SSL_CIPHER_find(skCipherOld, sk_SSL_CIPHER_value(skCipher, n)) < 0) + renegotiate = TRUE; + } + for (n = 0; !renegotiate && n < sk_SSL_CIPHER_num(skCipherOld); n++) { + if (sk_SSL_CIPHER_find(skCipher, sk_SSL_CIPHER_value(skCipherOld, n)) < 0) + renegotiate = TRUE; + } + } + } + /* cleanup */ + if (skCipherOld != NULL) + sk_SSL_CIPHER_free(skCipherOld); + /* tracing */ + if (renegotiate) + ssl_log(r->server, SSL_LOG_TRACE, + "Reconfigured cipher suite will force renegotiation"); + } + + /* + * override of SSLVerifyDepth + * + * The depth checks are handled by us manually inside the verify callback + * function and not by OpenSSL internally (and our function is aware of + * both the per-server and per-directory contexts). So we cannot ask + * OpenSSL about the currently verify depth. Instead we remember it in our + * ap_ctx attached to the SSL* of OpenSSL. We've to force the + * renegotiation if the reconfigured/new verify depth is less than the + * currently active/remembered verify depth (because this means more + * restriction on the certificate chain). + */ + if (dc->nVerifyDepth != UNSET) { + apctx = SSL_get_app_data2(ssl); + if ((vp = ap_ctx_get(apctx, "ssl::verify::depth")) != NULL) + n = (int)AP_CTX_PTR2NUM(vp); + else + n = sc->nVerifyDepth; + ap_ctx_set(apctx, "ssl::verify::depth", + AP_CTX_NUM2PTR(dc->nVerifyDepth)); + /* determine whether a renegotiation has to be forced */ + if (dc->nVerifyDepth < n) { + renegotiate = TRUE; + ssl_log(r->server, SSL_LOG_TRACE, + "Reduced client verification depth will force renegotiation"); + } + } + + /* + * override of SSLVerifyClient + * + * We force a renegotiation if the reconfigured/new verify type is + * stronger than the currently active verify type. + * + * The order is: none << optional_no_ca << optional << require + * + * Additionally the following optimization is possible here: When the + * currently active verify type is "none" but a client certificate is + * already known/present, it's enough to manually force a client + * verification but at least skip the I/O-intensive renegotation + * handshake. + */ + if (dc->nVerifyClient != SSL_CVERIFY_UNSET) { + /* remember old state */ + nVerifyOld = SSL_get_verify_mode(ssl); + /* configure new state */ + nVerify = SSL_VERIFY_NONE; + if (dc->nVerifyClient == SSL_CVERIFY_REQUIRE) + nVerify |= SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + if ( (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL) + || (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) ) + nVerify |= SSL_VERIFY_PEER; + SSL_set_verify(ssl, nVerify, ssl_callback_SSLVerify); + SSL_set_verify_result(ssl, X509_V_OK); + /* determine whether we've to force a renegotiation */ + if (nVerify != nVerifyOld) { + if ( ( (nVerifyOld == SSL_VERIFY_NONE) + && (nVerify != SSL_VERIFY_NONE)) + || ( !(nVerifyOld & SSL_VERIFY_PEER) + && (nVerify & SSL_VERIFY_PEER)) + || ( !(nVerifyOld & (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) + && (nVerify & (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)))) { + renegotiate = TRUE; + /* optimization */ + if ( dc->nOptions & SSL_OPT_OPTRENEGOTIATE + && nVerifyOld == SSL_VERIFY_NONE + && SSL_get_peer_certificate(ssl) != NULL) + renegotiate_quick = TRUE; + ssl_log(r->server, SSL_LOG_TRACE, + "Changed client verification type will force %srenegotiation", + renegotiate_quick ? "quick " : ""); + } + } + } + + /* + * override SSLCACertificateFile & SSLCACertificatePath + * This is tagged experimental because it has to use an ugly kludge: We + * have to change the locations inside the SSL_CTX* (per-server global) + * instead inside SSL* (per-connection local) and reconfigure it to the + * old values later. That's problematic at least for the threaded process + * model of Apache under Win32 or when an error occurs. But unless + * OpenSSL provides a SSL_load_verify_locations() function we've no other + * chance to provide this functionality... + */ +#ifdef SSL_EXPERIMENTAL_PERDIRCA + if ( ( dc->szCACertificateFile != NULL + && ( sc->szCACertificateFile == NULL + || ( sc->szCACertificateFile != NULL + && strNE(dc->szCACertificateFile, sc->szCACertificateFile)))) + || ( dc->szCACertificatePath != NULL + && ( sc->szCACertificatePath == NULL + || ( sc->szCACertificatePath != NULL + && strNE(dc->szCACertificatePath, sc->szCACertificatePath)))) ) { + cpCAFile = dc->szCACertificateFile != NULL ? + dc->szCACertificateFile : sc->szCACertificateFile; + cpCAPath = dc->szCACertificatePath != NULL ? + dc->szCACertificatePath : sc->szCACertificatePath; + /* + FIXME: This should be... + if (!SSL_load_verify_locations(ssl, cpCAFile, cpCAPath)) { + ...but OpenSSL still doesn't provide this! + */ + if (!SSL_CTX_load_verify_locations(ctx, cpCAFile, cpCAPath)) { + ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Unable to reconfigure verify locations " + "for client authentication"); + return FORBIDDEN; + } + if ((skCAList = ssl_init_FindCAList(r->server, r->pool, + cpCAFile, cpCAPath)) == NULL) { + ssl_log(r->server, SSL_LOG_ERROR, + "Unable to determine list of available " + "CA certificates for client authentication"); + return FORBIDDEN; + } + SSL_set_client_CA_list(ssl, skCAList); + renegotiate = TRUE; + reconfigured_locations = TRUE; + ssl_log(r->server, SSL_LOG_TRACE, + "Changed client verification locations will force renegotiation"); + } +#endif /* SSL_EXPERIMENTAL_PERDIRCA */ + +#ifdef SSL_CONSERVATIVE + /* + * SSL renegotiations in conjunction with HTTP + * requests using the POST method are not supported. + */ + if (renegotiate && r->method_number == M_POST) { + ssl_log(r->server, SSL_LOG_ERROR, + "SSL Re-negotiation in conjunction with POST method not supported!"); + ssl_log(r->server, SSL_LOG_INFO, + "You have to compile without -DSSL_CONSERVATIVE to enabled support for this."); + return METHOD_NOT_ALLOWED; + } +#endif /* SSL_CONSERVATIVE */ + + /* + * now do the renegotiation if anything was actually reconfigured + */ + if (renegotiate) { + /* + * Now we force the SSL renegotation by sending the Hello Request + * message to the client. Here we have to do a workaround: Actually + * OpenSSL returns immediately after sending the Hello Request (the + * intent AFAIK is because the SSL/TLS protocol says it's not a must + * that the client replies to a Hello Request). But because we insist + * on a reply (anything else is an error for us) we have to go to the + * ACCEPT state manually. Using SSL_set_accept_state() doesn't work + * here because it resets too much of the connection. So we set the + * state explicitly and continue the handshake manually. + */ + ssl_log(r->server, SSL_LOG_INFO, "Requesting connection re-negotiation"); + if (renegotiate_quick) { + /* perform just a manual re-verification of the peer */ + ssl_log(r->server, SSL_LOG_TRACE, + "Performing quick renegotiation: just re-verifying the peer"); + certstore = SSL_CTX_get_cert_store(ctx); + if (certstore == NULL) { + ssl_log(r->server, SSL_LOG_ERROR, "Cannot find certificate storage"); + return FORBIDDEN; + } + certstack = SSL_get_peer_cert_chain(ssl); + if (certstack == NULL || sk_X509_num(certstack) == 0) { + ssl_log(r->server, SSL_LOG_ERROR, "Cannot find peer certificate chain"); + return FORBIDDEN; + } + cert = sk_X509_value(certstack, 0); + X509_STORE_CTX_init(&certstorectx, certstore, cert, certstack); + depth = SSL_get_verify_depth(ssl); + if (depth >= 0) + X509_STORE_CTX_set_depth(&certstorectx, depth); + X509_STORE_CTX_set_ex_data(&certstorectx, + SSL_get_ex_data_X509_STORE_CTX_idx(), (char *)ssl); + if (!X509_verify_cert(&certstorectx)) + ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Re-negotiation verification step failed"); + SSL_set_verify_result(ssl, certstorectx.error); + X509_STORE_CTX_cleanup(&certstorectx); + } + else { + /* do a full renegotiation */ + ssl_log(r->server, SSL_LOG_TRACE, + "Performing full renegotiation: complete handshake protocol"); + if (r->main != NULL) + SSL_set_session_id_context(ssl, (unsigned char *)&(r->main), sizeof(r->main)); + else + SSL_set_session_id_context(ssl, (unsigned char *)&r, sizeof(r)); +#ifndef SSL_CONSERVATIVE + ssl_io_suck(r, ssl); +#endif + SSL_renegotiate(ssl); + SSL_do_handshake(ssl); + if (SSL_get_state(ssl) != SSL_ST_OK) { + ssl_log(r->server, SSL_LOG_ERROR, "Re-negotiation request failed"); + return FORBIDDEN; + } + ssl_log(r->server, SSL_LOG_INFO, "Awaiting re-negotiation handshake"); + SSL_set_state(ssl, SSL_ST_ACCEPT); + SSL_do_handshake(ssl); + if (SSL_get_state(ssl) != SSL_ST_OK) { + ssl_log(r->server, SSL_LOG_ERROR, + "Re-negotiation handshake failed: Not accepted by client!?"); + return FORBIDDEN; + } + } + + /* + * Remember the peer certificate's DN + */ + if ((cert = SSL_get_peer_certificate(ssl)) != NULL) { + cp = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); + ap_ctx_set(r->connection->client->ctx, "ssl::client::dn", + ap_pstrdup(r->connection->pool, cp)); + free(cp); + } + + /* + * Finally check for acceptable renegotiation results + */ + if (dc->nVerifyClient != SSL_CVERIFY_NONE) { + if ( dc->nVerifyClient == SSL_CVERIFY_REQUIRE + && SSL_get_verify_result(ssl) != X509_V_OK ) { + ssl_log(r->server, SSL_LOG_ERROR, + "Re-negotiation handshake failed: Client verification failed"); + return FORBIDDEN; + } + if ( dc->nVerifyClient == SSL_CVERIFY_REQUIRE + && SSL_get_peer_certificate(ssl) == NULL ) { + ssl_log(r->server, SSL_LOG_ERROR, + "Re-negotiation handshake failed: Client certificate missing"); + return FORBIDDEN; + } + } + } + + /* + * Under old OpenSSL we had to change the X509_STORE inside the + * SSL_CTX instead inside the SSL structure, so we have to reconfigure it + * to the old values. This should be changed with forthcoming OpenSSL + * versions when better functionality is avaiable. + */ +#ifdef SSL_EXPERIMENTAL_PERDIRCA + if (renegotiate && reconfigured_locations) { + if (!SSL_CTX_load_verify_locations(ctx, + sc->szCACertificateFile, sc->szCACertificatePath)) { + ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Unable to reconfigure verify locations " + "to per-server configuration parameters"); + return FORBIDDEN; + } + } +#endif /* SSL_EXPERIMENTAL_PERDIRCA */ + + /* + * Check SSLRequire boolean expressions + */ + apRequirement = dc->aRequirement; + pRequirements = (ssl_require_t *)apRequirement->elts; + for (i = 0; i < apRequirement->nelts; i++) { + pRequirement = &pRequirements[i]; + ok = ssl_expr_exec(r, pRequirement->mpExpr); + if (ok < 0) { + cp = ap_psprintf(r->pool, "Failed to execute SSL requirement expression: %s", + ssl_expr_get_error()); + ap_log_reason(cp, r->filename, r); + /* remember forbidden access for strict require option */ + ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1); + return FORBIDDEN; + } + if (ok != 1) { + ssl_log(r->server, SSL_LOG_INFO, + "Access to %s denied for %s (requirement expression not fulfilled)", + r->filename, r->connection->remote_ip); + ssl_log(r->server, SSL_LOG_INFO, + "Failed expression: %s", pRequirement->cpExpr); + ap_log_reason("SSL requirement expression not fulfilled " + "(see SSL logfile for more details)", r->filename, r); + /* remember forbidden access for strict require option */ + ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1); + return FORBIDDEN; + } + } + + /* + * Else access is granted from our point of view (except vendor + * handlers override). But we have to return DECLINED here instead + * of OK, because mod_auth and other modules still might want to + * deny access. + */ + rc = DECLINED; +#ifdef SSL_VENDOR + ap_hook_use("ap::mod_ssl::vendor::access_handler", + AP_HOOK_SIG2(int,ptr), AP_HOOK_DECLINE(DECLINED), + &rc, r); +#endif + return rc; +} + +/* + * Auth Handler: + * Fake a Basic authentication from the X509 client certificate. + * + * This must be run fairly early on to prevent a real authentication from + * occuring, in particular it must be run before anything else that + * authenticates a user. This means that the Module statement for this + * module should be LAST in the Configuration file. + */ +int ssl_hook_Auth(request_rec *r) +{ + SSLSrvConfigRec *sc = mySrvConfig(r->server); + SSLDirConfigRec *dc = myDirConfig(r); + char b1[MAX_STRING_LEN], b2[MAX_STRING_LEN]; + char *clientdn; + const char *cpAL; + const char *cpUN; + const char *cpPW; + + /* + * Additionally forbid access (again) + * when strict require option is used. + */ + if ( (dc->nOptions & SSL_OPT_STRICTREQUIRE) + && (ap_table_get(r->notes, "ssl-access-forbidden") != NULL)) + return FORBIDDEN; + + /* + * Make sure the user is not able to fake the client certificate + * based authentication by just entering an X.509 Subject DN + * ("/XX=YYY/XX=YYY/..") as the username and "password" as the + * password. + */ + if ((cpAL = ap_table_get(r->headers_in, "Authorization")) != NULL) { + if (strcEQ(ap_getword(r->pool, &cpAL, ' '), "Basic")) { + while (*cpAL == ' ' || *cpAL == '\t') + cpAL++; + cpAL = ap_pbase64decode(r->pool, cpAL); + cpUN = ap_getword_nulls(r->pool, &cpAL, ':'); + cpPW = cpAL; + if (cpUN[0] == '/' && strEQ(cpPW, "password")) + return FORBIDDEN; + } + } + + /* + * We decline operation in various situations... + */ + if (!sc->bEnabled) + return DECLINED; + if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL) + return DECLINED; + if (!(dc->nOptions & SSL_OPT_FAKEBASICAUTH)) + return DECLINED; + if (r->connection->user) + return DECLINED; + if ((clientdn = (char *)ap_ctx_get(r->connection->client->ctx, "ssl::client::dn")) == NULL) + return DECLINED; + + /* + * Fake a password - which one would be immaterial, as, it seems, an empty + * password in the users file would match ALL incoming passwords, if only + * we were using the standard crypt library routine. Unfortunately, OpenSSL + * "fixes" a "bug" in crypt and thus prevents blank passwords from + * working. (IMHO what they really fix is a bug in the users of the code + * - failing to program correctly for shadow passwords). We need, + * therefore, to provide a password. This password can be matched by + * adding the string "xxj31ZMTZzkVA" as the password in the user file. + * This is just the crypted variant of the word "password" ;-) + */ + ap_snprintf(b1, sizeof(b1), "%s:password", clientdn); + ssl_util_uuencode(b2, b1, FALSE); + ap_snprintf(b1, sizeof(b1), "Basic %s", b2); + ap_table_set(r->headers_in, "Authorization", b1); + ssl_log(r->server, SSL_LOG_INFO, + "Faking HTTP Basic Auth header: \"Authorization: %s\"", b1); + + return DECLINED; +} + +int ssl_hook_UserCheck(request_rec *r) +{ + SSLDirConfigRec *dc = myDirConfig(r); + + /* + * Additionally forbid access (again) + * when strict require option is used. + */ + if ( (dc->nOptions & SSL_OPT_STRICTREQUIRE) + && (ap_table_get(r->notes, "ssl-access-forbidden") != NULL)) + return FORBIDDEN; + + return DECLINED; +} + +/* + * Fixup Handler + */ + +static const char *ssl_hook_Fixup_vars[] = { + "SSL_VERSION_INTERFACE", + "SSL_VERSION_LIBRARY", + "SSL_PROTOCOL", + "SSL_CIPHER", + "SSL_CIPHER_EXPORT", + "SSL_CIPHER_USEKEYSIZE", + "SSL_CIPHER_ALGKEYSIZE", + "SSL_CLIENT_VERIFY", + "SSL_CLIENT_M_VERSION", + "SSL_CLIENT_M_SERIAL", + "SSL_CLIENT_V_START", + "SSL_CLIENT_V_END", + "SSL_CLIENT_S_DN", + "SSL_CLIENT_S_DN_C", + "SSL_CLIENT_S_DN_ST", + "SSL_CLIENT_S_DN_L", + "SSL_CLIENT_S_DN_O", + "SSL_CLIENT_S_DN_OU", + "SSL_CLIENT_S_DN_CN", + "SSL_CLIENT_S_DN_T", + "SSL_CLIENT_S_DN_I", + "SSL_CLIENT_S_DN_G", + "SSL_CLIENT_S_DN_S", + "SSL_CLIENT_S_DN_D", + "SSL_CLIENT_S_DN_UID", + "SSL_CLIENT_S_DN_Email", + "SSL_CLIENT_I_DN", + "SSL_CLIENT_I_DN_C", + "SSL_CLIENT_I_DN_ST", + "SSL_CLIENT_I_DN_L", + "SSL_CLIENT_I_DN_O", + "SSL_CLIENT_I_DN_OU", + "SSL_CLIENT_I_DN_CN", + "SSL_CLIENT_I_DN_T", + "SSL_CLIENT_I_DN_I", + "SSL_CLIENT_I_DN_G", + "SSL_CLIENT_I_DN_S", + "SSL_CLIENT_I_DN_D", + "SSL_CLIENT_I_DN_UID", + "SSL_CLIENT_I_DN_Email", + "SSL_CLIENT_A_KEY", + "SSL_CLIENT_A_SIG", + "SSL_SERVER_M_VERSION", + "SSL_SERVER_M_SERIAL", + "SSL_SERVER_V_START", + "SSL_SERVER_V_END", + "SSL_SERVER_S_DN", + "SSL_SERVER_S_DN_C", + "SSL_SERVER_S_DN_ST", + "SSL_SERVER_S_DN_L", + "SSL_SERVER_S_DN_O", + "SSL_SERVER_S_DN_OU", + "SSL_SERVER_S_DN_CN", + "SSL_SERVER_S_DN_T", + "SSL_SERVER_S_DN_I", + "SSL_SERVER_S_DN_G", + "SSL_SERVER_S_DN_S", + "SSL_SERVER_S_DN_D", + "SSL_SERVER_S_DN_UID", + "SSL_SERVER_S_DN_Email", + "SSL_SERVER_I_DN", + "SSL_SERVER_I_DN_C", + "SSL_SERVER_I_DN_ST", + "SSL_SERVER_I_DN_L", + "SSL_SERVER_I_DN_O", + "SSL_SERVER_I_DN_OU", + "SSL_SERVER_I_DN_CN", + "SSL_SERVER_I_DN_T", + "SSL_SERVER_I_DN_I", + "SSL_SERVER_I_DN_G", + "SSL_SERVER_I_DN_S", + "SSL_SERVER_I_DN_D", + "SSL_SERVER_I_DN_UID", + "SSL_SERVER_I_DN_Email", + "SSL_SERVER_A_KEY", + "SSL_SERVER_A_SIG", + "SSL_SESSION_ID", + NULL +}; + +int ssl_hook_Fixup(request_rec *r) +{ + SSLSrvConfigRec *sc = mySrvConfig(r->server); + SSLDirConfigRec *dc = myDirConfig(r); + table *e = r->subprocess_env; + char *var; + char *val; + STACK_OF(X509) *sk; + SSL *ssl; + int i; + + /* + * Check to see if SSL is on + */ + if (!sc->bEnabled) + return DECLINED; + if ((ssl = ap_ctx_get(r->connection->client->ctx, "ssl")) == NULL) + return DECLINED; + + /* + * Annotate the SSI/CGI environment with standard SSL information + */ + /* the always present HTTPS (=HTTP over SSL) flag! */ + ap_table_set(e, "HTTPS", "on"); + /* standard SSL environment variables */ + if (dc->nOptions & SSL_OPT_STDENVVARS) { + for (i = 0; ssl_hook_Fixup_vars[i] != NULL; i++) { + var = (char *)ssl_hook_Fixup_vars[i]; + val = ssl_var_lookup(r->pool, r->server, r->connection, r, var); + if (!strIsEmpty(val)) + ap_table_set(e, var, val); + } + } + + /* + * On-demand bloat up the SSI/CGI environment with certificate data + */ + if (dc->nOptions & SSL_OPT_EXPORTCERTDATA) { + val = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_SERVER_CERT"); + ap_table_set(e, "SSL_SERVER_CERT", val); + val = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_CERT"); + ap_table_set(e, "SSL_CLIENT_CERT", val); + if ((sk = SSL_get_peer_cert_chain(ssl)) != NULL) { + for (i = 0; i < sk_X509_num(sk); i++) { + var = ap_psprintf(r->pool, "SSL_CLIENT_CERT_CHAIN_%d", i); + val = ssl_var_lookup(r->pool, r->server, r->connection, r, var); + if (val != NULL) + ap_table_set(e, var, val); + } + } + } + + /* + * On-demand bloat up the SSI/CGI environment with compat variables + */ +#ifdef SSL_COMPAT + if (dc->nOptions & SSL_OPT_COMPATENVVARS) + ssl_compat_variables(r); +#endif + + return DECLINED; +} + +/* _________________________________________________________________ +** +** OpenSSL Callback Functions +** _________________________________________________________________ +*/ + +/* + * Handle out temporary RSA private keys on demand + * + * The background of this as the TLSv1 standard explains it: + * + * | D.1. Temporary RSA keys + * | + * | US Export restrictions limit RSA keys used for encryption to 512 + * | bits, but do not place any limit on lengths of RSA keys used for + * | signing operations. Certificates often need to be larger than 512 + * | bits, since 512-bit RSA keys are not secure enough for high-value + * | transactions or for applications requiring long-term security. Some + * | certificates are also designated signing-only, in which case they + * | cannot be used for key exchange. + * | + * | When the public key in the certificate cannot be used for encryption, + * | the server signs a temporary RSA key, which is then exchanged. In + * | exportable applications, the temporary RSA key should be the maximum + * | allowable length (i.e., 512 bits). Because 512-bit RSA keys are + * | relatively insecure, they should be changed often. For typical + * | electronic commerce applications, it is suggested that keys be + * | changed daily or every 500 transactions, and more often if possible. + * | Note that while it is acceptable to use the same temporary key for + * | multiple transactions, it must be signed each time it is used. + * | + * | RSA key generation is a time-consuming process. In many cases, a + * | low-priority process can be assigned the task of key generation. + * | Whenever a new key is completed, the existing temporary key can be + * | replaced with the new one. + * + * So we generated 512 and 1024 bit temporary keys on startup + * which we now just handle out on demand.... + */ +RSA *ssl_callback_TmpRSA(SSL *pSSL, int nExport, int nKeyLen) +{ + SSLModConfigRec *mc = myModConfig(); + RSA *rsa; + + rsa = NULL; + if (nExport) { + /* It's because an export cipher is used */ + if (nKeyLen == 512) + rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA512]; + else if (nKeyLen == 1024) + rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024]; + else + /* it's too expensive to generate on-the-fly, so keep 1024bit */ + rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024]; + } + else { + /* It's because a sign-only certificate situation exists */ + rsa = (RSA *)mc->pTmpKeys[SSL_TKPIDX_RSA1024]; + } + return rsa; +} + +/* + * Handle out the already generated DH parameters... + */ +DH *ssl_callback_TmpDH(SSL *pSSL, int nExport, int nKeyLen) +{ + SSLModConfigRec *mc = myModConfig(); + DH *dh; + + dh = NULL; + if (nExport) { + /* It's because an export cipher is used */ + if (nKeyLen == 512) + dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH512]; + else if (nKeyLen == 1024) + dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024]; + else + /* it's too expensive to generate on-the-fly, so keep 1024bit */ + dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024]; + } + else { + /* It's because a sign-only certificate situation exists */ + dh = (DH *)mc->pTmpKeys[SSL_TKPIDX_DH1024]; + } + return dh; +} + +/* + * This OpenSSL callback function is called when OpenSSL + * does client authentication and verifies the certificate chain. + */ +int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx) +{ + SSL *ssl; + conn_rec *conn; + server_rec *s; + request_rec *r; + SSLSrvConfigRec *sc; + SSLDirConfigRec *dc; + ap_ctx *actx; + X509 *xs; + int errnum; + int errdepth; + char *cp; + char *cp2; + int depth; + int verify; + + /* + * Get Apache context back through OpenSSL context + */ + ssl = (SSL *)X509_STORE_CTX_get_app_data(ctx); + conn = (conn_rec *)SSL_get_app_data(ssl); + actx = (ap_ctx *)SSL_get_app_data2(ssl); + r = (request_rec *)ap_ctx_get(actx, "ssl::request_rec"); + s = conn->server; + sc = mySrvConfig(s); + dc = (r != NULL ? myDirConfig(r) : NULL); + + /* + * Get verify ingredients + */ + xs = X509_STORE_CTX_get_current_cert(ctx); + errnum = X509_STORE_CTX_get_error(ctx); + errdepth = X509_STORE_CTX_get_error_depth(ctx); + + /* + * Log verification information + */ + cp = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0); + cp2 = X509_NAME_oneline(X509_get_issuer_name(xs), NULL, 0); + ssl_log(s, SSL_LOG_TRACE, + "Certificate Verification: depth: %d, subject: %s, issuer: %s", + errdepth, cp != NULL ? cp : "-unknown-", + cp2 != NULL ? cp2 : "-unknown"); + if (cp) + free(cp); + if (cp2) + free(cp2); + + /* + * Check for optionally acceptable non-verifiable issuer situation + */ + if (dc != NULL && dc->nVerifyClient != SSL_CVERIFY_UNSET) + verify = dc->nVerifyClient; + else + verify = sc->nVerifyClient; + if ( ( errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT + || errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN + || errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY +#if SSL_LIBRARY_VERSION >= 0x00905000 + || errnum == X509_V_ERR_CERT_UNTRUSTED +#endif + || errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE ) + && verify == SSL_CVERIFY_OPTIONAL_NO_CA ) { + ssl_log(s, SSL_LOG_TRACE, + "Certificate Verification: Verifiable Issuer is configured as " + "optional, therefore we're accepting the certificate"); + ap_ctx_set(conn->client->ctx, "ssl::verify::info", "GENEROUS"); + ok = TRUE; + } + + /* + * Additionally perform CRL-based revocation checks + */ + if (ok) { + ok = ssl_callback_SSLVerify_CRL(ok, ctx, s); + if (!ok) + errnum = X509_STORE_CTX_get_error(ctx); + } + + /* + * If we already know it's not ok, log the real reason + */ + if (!ok) { + ssl_log(s, SSL_LOG_ERROR, "Certificate Verification: Error (%d): %s", + errnum, X509_verify_cert_error_string(errnum)); + ap_ctx_set(conn->client->ctx, "ssl::client::dn", NULL); + ap_ctx_set(conn->client->ctx, "ssl::verify::error", + (void *)X509_verify_cert_error_string(errnum)); + } + + /* + * Finally check the depth of the certificate verification + */ + if (dc != NULL && dc->nVerifyDepth != UNSET) + depth = dc->nVerifyDepth; + else + depth = sc->nVerifyDepth; + if (errdepth > depth) { + ssl_log(s, SSL_LOG_ERROR, + "Certificate Verification: Certificate Chain too long " + "(chain has %d certificates, but maximum allowed are only %d)", + errdepth, depth); + ap_ctx_set(conn->client->ctx, "ssl::verify::error", + (void *)X509_verify_cert_error_string(X509_V_ERR_CERT_CHAIN_TOO_LONG)); + ok = FALSE; + } + + /* + * And finally signal OpenSSL the (perhaps changed) state + */ + return (ok); +} + +int ssl_callback_SSLVerify_CRL( + int ok, X509_STORE_CTX *ctx, server_rec *s) +{ + SSLSrvConfigRec *sc; + X509_OBJECT obj; + X509_NAME *subject; + X509_NAME *issuer; + X509 *xs; + X509_CRL *crl; + X509_REVOKED *revoked; + long serial; + BIO *bio; + int i, n, rc; + char *cp; + char *cp2; + + /* + * Unless a revocation store for CRLs was created we + * cannot do any CRL-based verification, of course. + */ + sc = mySrvConfig(s); + if (sc->pRevocationStore == NULL) + return ok; + + /* + * Determine certificate ingredients in advance + */ + xs = X509_STORE_CTX_get_current_cert(ctx); + subject = X509_get_subject_name(xs); + issuer = X509_get_issuer_name(xs); + + /* + * OpenSSL provides the general mechanism to deal with CRLs but does not + * use them automatically when verifying certificates, so we do it + * explicitly here. We will check the CRL for the currently checked + * certificate, if there is such a CRL in the store. + * + * We come through this procedure for each certificate in the certificate + * chain, starting with the root-CA's certificate. At each step we've to + * both verify the signature on the CRL (to make sure it's a valid CRL) + * and it's revocation list (to make sure the current certificate isn't + * revoked). But because to check the signature on the CRL we need the + * public key of the issuing CA certificate (which was already processed + * one round before), we've a little problem. But we can both solve it and + * at the same time optimize the processing by using the following + * verification scheme (idea and code snippets borrowed from the GLOBUS + * project): + * + * 1. We'll check the signature of a CRL in each step when we find a CRL + * through the _subject_ name of the current certificate. This CRL + * itself will be needed the first time in the next round, of course. + * But we do the signature processing one round before this where the + * public key of the CA is available. + * + * 2. We'll check the revocation list of a CRL in each step when + * we find a CRL through the _issuer_ name of the current certificate. + * This CRLs signature was then already verified one round before. + * + * This verification scheme allows a CA to revoke its own certificate as + * well, of course. + */ + + /* + * Try to retrieve a CRL corresponding to the _subject_ of + * the current certificate in order to verify it's integrity. + */ + memset((char *)&obj, 0, sizeof(obj)); + rc = SSL_X509_STORE_lookup(sc->pRevocationStore, X509_LU_CRL, subject, &obj); + crl = obj.data.crl; + if (rc > 0 && crl != NULL) { + /* + * Log information about CRL + * (A little bit complicated because of ASN.1 and BIOs...) + */ + if (ssl_log_applies(s, SSL_LOG_TRACE)) { + bio = BIO_new(BIO_s_mem()); + BIO_printf(bio, "lastUpdate: "); + ASN1_UTCTIME_print(bio, X509_CRL_get_lastUpdate(crl)); + BIO_printf(bio, ", nextUpdate: "); + ASN1_UTCTIME_print(bio, X509_CRL_get_nextUpdate(crl)); + n = BIO_pending(bio); + cp = malloc(n+1); + n = BIO_read(bio, cp, n); + cp[n] = NUL; + BIO_free(bio); + cp2 = X509_NAME_oneline(subject, NULL, 0); + ssl_log(s, SSL_LOG_TRACE, "CA CRL: Issuer: %s, %s", cp2, cp); + free(cp2); + free(cp); + } + + /* + * Verify the signature on this CRL + */ + if (X509_CRL_verify(crl, X509_get_pubkey(xs)) <= 0) { + ssl_log(s, SSL_LOG_WARN, "Invalid signature on CRL"); + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); + X509_OBJECT_free_contents(&obj); + return FALSE; + } + + /* + * Check date of CRL to make sure it's not expired + */ + i = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl)); + if (i == 0) { + ssl_log(s, SSL_LOG_WARN, "Found CRL has invalid nextUpdate field"); + X509_STORE_CTX_set_error(ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); + X509_OBJECT_free_contents(&obj); + return FALSE; + } + if (i < 0) { + ssl_log(s, SSL_LOG_WARN, + "Found CRL is expired - " + "revoking all certificates until you get updated CRL"); + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED); + X509_OBJECT_free_contents(&obj); + return FALSE; + } + X509_OBJECT_free_contents(&obj); + } + + /* + * Try to retrieve a CRL corresponding to the _issuer_ of + * the current certificate in order to check for revocation. + */ + memset((char *)&obj, 0, sizeof(obj)); + rc = SSL_X509_STORE_lookup(sc->pRevocationStore, X509_LU_CRL, issuer, &obj); + crl = obj.data.crl; + if (rc > 0 && crl != NULL) { + /* + * Check if the current certificate is revoked by this CRL + */ +#if SSL_LIBRARY_VERSION < 0x00904000 + n = sk_num(X509_CRL_get_REVOKED(crl)); +#else + n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); +#endif + for (i = 0; i < n; i++) { +#if SSL_LIBRARY_VERSION < 0x00904000 + revoked = (X509_REVOKED *)sk_value(X509_CRL_get_REVOKED(crl), i); +#else + revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); +#endif + if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(xs)) == 0) { + + serial = ASN1_INTEGER_get(revoked->serialNumber); + cp = X509_NAME_oneline(issuer, NULL, 0); + ssl_log(s, SSL_LOG_INFO, + "Certificate with serial %ld (0x%lX) " + "revoked per CRL from issuer %s", + serial, serial, cp); + free(cp); + + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); + X509_OBJECT_free_contents(&obj); + return FALSE; + } + } + X509_OBJECT_free_contents(&obj); + } + return ok; +} + +/* + * This callback function is executed by OpenSSL whenever a new SSL_SESSION is + * added to the internal OpenSSL session cache. We use this hook to spread the + * SSL_SESSION also to the inter-process disk-cache to make share it with our + * other Apache pre-forked server processes. + */ +int ssl_callback_NewSessionCacheEntry(SSL *ssl, SSL_SESSION *pNew) +{ + conn_rec *conn; + server_rec *s; + SSLSrvConfigRec *sc; + long t; + BOOL rc; + + /* + * Get Apache context back through OpenSSL context + */ + conn = (conn_rec *)SSL_get_app_data(ssl); + s = conn->server; + sc = mySrvConfig(s); + + /* + * Set the timeout also for the internal OpenSSL cache, because this way + * our inter-process cache is consulted only when it's really necessary. + */ + t = sc->nSessionCacheTimeout; + SSL_set_timeout(pNew, t); + + /* + * Store the SSL_SESSION in the inter-process cache with the + * same expire time, so it expires automatically there, too. + */ + t = (SSL_get_time(pNew) + sc->nSessionCacheTimeout); + rc = ssl_scache_store(s, pNew->session_id, pNew->session_id_length, t, pNew); + + /* + * Log this cache operation + */ + ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: " + "request=SET status=%s id=%s timeout=%ds (session caching)", + rc == TRUE ? "OK" : "BAD", + SSL_SESSION_id2sz(pNew->session_id, pNew->session_id_length), + t-time(NULL)); + + /* + * return 0 which means to OpenSSL that the pNew is still + * valid and was not freed by us with SSL_SESSION_free(). + */ + return 0; +} + +/* + * This callback function is executed by OpenSSL whenever a + * SSL_SESSION is looked up in the internal OpenSSL cache and it + * was not found. We use this to lookup the SSL_SESSION in the + * inter-process disk-cache where it was perhaps stored by one + * of our other Apache pre-forked server processes. + */ +SSL_SESSION *ssl_callback_GetSessionCacheEntry( + SSL *ssl, unsigned char *id, int idlen, int *pCopy) +{ + conn_rec *conn; + server_rec *s; + SSL_SESSION *pSession; + + /* + * Get Apache context back through OpenSSL context + */ + conn = (conn_rec *)SSL_get_app_data(ssl); + s = conn->server; + + /* + * Try to retrieve the SSL_SESSION from the inter-process cache + */ + pSession = ssl_scache_retrieve(s, id, idlen); + + /* + * Log this cache operation + */ + if (pSession != NULL) + ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: " + "request=GET status=FOUND id=%s (session reuse)", + SSL_SESSION_id2sz(id, idlen)); + else + ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: " + "request=GET status=MISSED id=%s (session renewal)", + SSL_SESSION_id2sz(id, idlen)); + + /* + * Return NULL or the retrieved SSL_SESSION. But indicate (by + * setting pCopy to 0) that the reference count on the + * SSL_SESSION should not be incremented by the SSL library, + * because we will no longer hold a reference to it ourself. + */ + *pCopy = 0; + return pSession; +} + +/* + * This callback function is executed by OpenSSL whenever a + * SSL_SESSION is removed from the the internal OpenSSL cache. + * We use this to remove the SSL_SESSION in the inter-process + * disk-cache, too. + */ +void ssl_callback_DelSessionCacheEntry( + SSL_CTX *ctx, SSL_SESSION *pSession) +{ + server_rec *s; + + /* + * Get Apache context back through OpenSSL context + */ + s = (server_rec *)SSL_CTX_get_app_data(ctx); + if (s == NULL) /* on server shutdown Apache is already gone */ + return; + + /* + * Remove the SSL_SESSION from the inter-process cache + */ + ssl_scache_remove(s, pSession->session_id, pSession->session_id_length); + + /* + * Log this cache operation + */ + ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache: " + "request=REM status=OK id=%s (session dead)", + SSL_SESSION_id2sz(pSession->session_id, + pSession->session_id_length)); + + return; +} + +/* + * This callback function is executed while OpenSSL processes the + * SSL handshake and does SSL record layer stuff. We use it to + * trace OpenSSL's processing in out SSL logfile. + */ +void ssl_callback_LogTracingState(SSL *ssl, int where, int rc) +{ + conn_rec *c; + server_rec *s; + SSLSrvConfigRec *sc; + char *str; + + /* + * find corresponding server + */ + if ((c = (conn_rec *)SSL_get_app_data(ssl)) == NULL) + return; + s = c->server; + if ((sc = mySrvConfig(s)) == NULL) + return; + + /* + * create the various trace messages + */ + if (sc->nLogLevel >= SSL_LOG_TRACE) { + if (where & SSL_CB_HANDSHAKE_START) + ssl_log(s, SSL_LOG_TRACE, "%s: Handshake: start", SSL_LIBRARY_NAME); + else if (where & SSL_CB_HANDSHAKE_DONE) + ssl_log(s, SSL_LOG_TRACE, "%s: Handshake: done", SSL_LIBRARY_NAME); + else if (where & SSL_CB_LOOP) + ssl_log(s, SSL_LOG_TRACE, "%s: Loop: %s", + SSL_LIBRARY_NAME, SSL_state_string_long(ssl)); + else if (where & SSL_CB_READ) + ssl_log(s, SSL_LOG_TRACE, "%s: Read: %s", + SSL_LIBRARY_NAME, SSL_state_string_long(ssl)); + else if (where & SSL_CB_WRITE) + ssl_log(s, SSL_LOG_TRACE, "%s: Write: %s", + SSL_LIBRARY_NAME, SSL_state_string_long(ssl)); + else if (where & SSL_CB_ALERT) { + str = (where & SSL_CB_READ) ? "read" : "write"; + ssl_log(s, SSL_LOG_TRACE, "%s: Alert: %s:%s:%s\n", + SSL_LIBRARY_NAME, str, + SSL_alert_type_string_long(rc), + SSL_alert_desc_string_long(rc)); + } + else if (where & SSL_CB_EXIT) { + if (rc == 0) + ssl_log(s, SSL_LOG_TRACE, "%s: Exit: failed in %s", + SSL_LIBRARY_NAME, SSL_state_string_long(ssl)); + else if (rc < 0) + ssl_log(s, SSL_LOG_TRACE, "%s: Exit: error in %s", + SSL_LIBRARY_NAME, SSL_state_string_long(ssl)); + } + } + + /* + * Because SSL renegotations can happen at any time (not only after + * SSL_accept()), the best way to log the current connection details is + * right after a finished handshake. + */ + if (where & SSL_CB_HANDSHAKE_DONE) { + ssl_log(s, SSL_LOG_INFO, + "Connection: Client IP: %s, Protocol: %s, Cipher: %s (%s/%s bits)", + ssl_var_lookup(NULL, s, c, NULL, "REMOTE_ADDR"), + ssl_var_lookup(NULL, s, c, NULL, "SSL_PROTOCOL"), + ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER"), + ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_USEKEYSIZE"), + ssl_var_lookup(NULL, s, c, NULL, "SSL_CIPHER_ALGKEYSIZE")); + } + + return; +} + diff --git a/modules/ssl/ssl_engine_log.c b/modules/ssl/ssl_engine_log.c new file mode 100644 index 0000000000..0e1c53a852 --- /dev/null +++ b/modules/ssl/ssl_engine_log.c @@ -0,0 +1,326 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_log.c +** Logging Facility +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``The difference between a computer + industry job and open-source software + hacking is about 30 hours a week.'' + -- Ralf S. Engelschall */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Logfile Support +** _________________________________________________________________ +*/ + +/* + * Open the SSL logfile + */ +void ssl_log_open(server_rec *s_main, server_rec *s, pool *p) +{ + char *szLogFile; + SSLSrvConfigRec *sc_main = mySrvConfig(s_main); + SSLSrvConfigRec *sc = mySrvConfig(s); + piped_log *pl; + + /* + * Short-circuit for inherited logfiles in order to save + * filedescriptors in mass-vhost situation. Be careful, this works + * fine because the close happens implicitly by the pool facility. + */ + if ( s != s_main + && sc_main->fileLogFile != NULL + && ( (sc->szLogFile == NULL) + || ( sc->szLogFile != NULL + && sc_main->szLogFile != NULL + && strEQ(sc->szLogFile, sc_main->szLogFile)))) { + sc->fileLogFile = sc_main->fileLogFile; + } + else if (sc->szLogFile != NULL) { + if (strEQ(sc->szLogFile, "/dev/null")) + return; + else if (sc->szLogFile[0] == '|') { + szLogFile = ssl_util_server_root_relative(p, "log", sc->szLogFile+1); + if ((pl = ap_open_piped_log(p, szLogFile)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Cannot open reliable pipe to SSL logfile filter %s", szLogFile); + ssl_die(); + } + sc->fileLogFile = ap_pfdopen(p, ap_piped_log_write_fd(pl), "a"); + setbuf(sc->fileLogFile, NULL); + } + else { + szLogFile = ssl_util_server_root_relative(p, "log", sc->szLogFile); + if ((sc->fileLogFile = ap_pfopen(p, szLogFile, "a")) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Cannot open SSL logfile %s", szLogFile); + ssl_die(); + } + setbuf(sc->fileLogFile, NULL); + } + } + return; +} + +static struct { + int nLevel; + char *szLevel; +} ssl_log_level2string[] = { + { SSL_LOG_ERROR, "error" }, + { SSL_LOG_WARN, "warn" }, + { SSL_LOG_INFO, "info" }, + { SSL_LOG_TRACE, "trace" }, + { SSL_LOG_DEBUG, "debug" }, + { 0, NULL } +}; + +static struct { + char *cpPattern; + char *cpAnnotation; +} ssl_log_annotate[] = { + { "*envelope*bad*decrypt*", "wrong pass phrase!?" }, + { "*CLIENT_HELLO*unknown*protocol*", "speaking not SSL to HTTPS port!?" }, + { "*CLIENT_HELLO*http*request*", "speaking HTTP to HTTPS port!?" }, + { "*SSL3_READ_BYTES:sslv3*alert*bad*certificate*", "Subject CN in certificate not server name or identical to CA!?" }, + { "*self signed certificate in certificate chain*", "Client certificate signed by CA not known to server?" }, + { "*peer did not return a certificate*", "No CAs known to server for verification?" }, + { "*no shared cipher*", "Too restrictive SSLCipherSuite or using DSA server certificate?" }, + { "*no start line*", "Bad file contents or format - or even just a forgotten SSLCertificateKeyFile?" }, + { "*bad password read*", "You entered an incorrect pass phrase!?" }, + { "*bad mac decode*", "Browser still remembered details of a re-created server certificate?" }, + { NULL, NULL } +}; + +static char *ssl_log_annotation(char *error) +{ + char *errstr; + int i; + + errstr = NULL; + for (i = 0; ssl_log_annotate[i].cpPattern != NULL; i++) { + if (ap_strcmp_match(error, ssl_log_annotate[i].cpPattern) == 0) { + errstr = ssl_log_annotate[i].cpAnnotation; + break; + } + } + return errstr; +} + +BOOL ssl_log_applies(server_rec *s, int level) +{ + SSLSrvConfigRec *sc; + + sc = mySrvConfig(s); + if ( sc->fileLogFile == NULL + && !(level & SSL_LOG_ERROR)) + return FALSE; + if ( level > sc->nLogLevel + && !(level & SSL_LOG_ERROR)) + return FALSE; + return TRUE; +} + +void ssl_log(server_rec *s, int level, const char *msg, ...) +{ + char tstr[80]; + char lstr[20]; + char vstr[1024]; + char str[1024]; + char nstr[2]; + int timz; + struct tm *t; + va_list ap; + int add; + int i; + char *astr; + int safe_errno; + unsigned long e; + SSLSrvConfigRec *sc; + char *cpE; + char *cpA; + + /* initialization */ + va_start(ap, msg); + safe_errno = errno; + sc = mySrvConfig(s); + + /* strip out additional flags */ + add = (level & ~SSL_LOG_MASK); + level = (level & SSL_LOG_MASK); + + /* reduce flags when not reasonable in context */ + if (add & SSL_ADD_ERRNO && errno == 0) + add &= ~SSL_ADD_ERRNO; + if (add & SSL_ADD_SSLERR && ERR_peek_error() == 0) + add &= ~SSL_ADD_SSLERR; + + /* we log only levels below, except for errors */ + if ( sc->fileLogFile == NULL + && !(level & SSL_LOG_ERROR)) + return; + if ( level > sc->nLogLevel + && !(level & SSL_LOG_ERROR)) + return; + + /* determine the time entry string */ + if (add & SSL_NO_TIMESTAMP) + tstr[0] = NUL; + else { + t = ap_get_gmtoff(&timz); + strftime(tstr, 80, "[%d/%b/%Y %H:%M:%S", t); + i = strlen(tstr); + ap_snprintf(tstr+i, 80-i, " %05d] ", (unsigned int)getpid()); + } + + /* determine whether newline should be written */ + if (add & SSL_NO_NEWLINE) + nstr[0] = NUL; + else { + nstr[0] = '\n'; + nstr[1] = NUL; + } + + /* determine level name */ + lstr[0] = NUL; + if (!(add & SSL_NO_LEVELID)) { + for (i = 0; ssl_log_level2string[i].nLevel != 0; i++) { + if (ssl_log_level2string[i].nLevel == level) { + ap_snprintf(lstr, sizeof(lstr), "[%s]", ssl_log_level2string[i].szLevel); + break; + } + } + for (i = strlen(lstr); i <= 7; i++) + lstr[i] = ' '; + lstr[i] = NUL; + } + + /* create custom message */ + ap_vsnprintf(vstr, sizeof(vstr), msg, ap); + + /* write out SSLog message */ + if ((add & SSL_ADD_ERRNO) && (add & SSL_ADD_SSLERR)) + astr = " (System and " SSL_LIBRARY_NAME " library errors follow)"; + else if (add & SSL_ADD_ERRNO) + astr = " (System error follows)"; + else if (add & SSL_ADD_SSLERR) + astr = " (" SSL_LIBRARY_NAME " library error follows)"; + else + astr = ""; + if (level <= sc->nLogLevel && sc->fileLogFile != NULL) { + ap_snprintf(str, sizeof(str), "%s%s%s%s%s", tstr, lstr, vstr, astr, nstr); + fprintf(sc->fileLogFile, "%s", str); + } + if (level & SSL_LOG_ERROR) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s, + "mod_ssl: %s%s", vstr, astr); + + /* write out additional attachment messages */ + if (add & SSL_ADD_ERRNO) { + if (level <= sc->nLogLevel && sc->fileLogFile != NULL) { + ap_snprintf(str, sizeof(str), "%s%sSystem: %s (errno: %d)%s", + tstr, lstr, strerror(safe_errno), safe_errno, nstr); + fprintf(sc->fileLogFile, "%s", str); + } + if (level & SSL_LOG_ERROR) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s, + "System: %s (errno: %d)", + strerror(safe_errno), safe_errno); + } + if (add & SSL_ADD_SSLERR) { + while ((e = ERR_get_error())) { + cpE = ERR_error_string(e, NULL); + cpA = ssl_log_annotation(cpE); + if (level <= sc->nLogLevel && sc->fileLogFile != NULL) { + ap_snprintf(str, sizeof(str), "%s%s%s: %s%s%s%s%s", + tstr, lstr, SSL_LIBRARY_NAME, cpE, + cpA != NULL ? " [Hint: " : "", + cpA != NULL ? cpA : "", cpA != NULL ? "]" : "", + nstr); + fprintf(sc->fileLogFile, "%s", str); + } + if (level & SSL_LOG_ERROR) + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, s, + "%s: %s%s%s%s", SSL_LIBRARY_NAME, cpE, + cpA != NULL ? " [Hint: " : "", + cpA != NULL ? cpA : "", cpA != NULL ? "]" : ""); + } + } + /* make sure the next log starts from a clean base */ + /* ERR_clear_error(); */ + + /* cleanup and return */ + if (sc->fileLogFile != NULL) + fflush(sc->fileLogFile); + errno = safe_errno; + va_end(ap); + return; +} + +void ssl_die(void) +{ + /* + * This is used for fatal errors and here + * it is common module practice to really + * exit from the complete program. + */ + exit(1); +} + diff --git a/modules/ssl/ssl_engine_mutex.c b/modules/ssl/ssl_engine_mutex.c new file mode 100644 index 0000000000..146f9ce4d9 --- /dev/null +++ b/modules/ssl/ssl_engine_mutex.c @@ -0,0 +1,397 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_mutex.c +** Semaphore for Mutual Exclusion +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``Real programmers confuse + Christmas and Halloween + because DEC 25 = OCT 31.'' + -- Unknown */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Mutex Support (Common) +** _________________________________________________________________ +*/ + +void ssl_mutex_init(server_rec *s, pool *p) +{ + SSLModConfigRec *mc = myModConfig(); + + if (mc->nMutexMode == SSL_MUTEXMODE_FILE) + ssl_mutex_file_create(s, p); + else if (mc->nMutexMode == SSL_MUTEXMODE_SEM) + ssl_mutex_sem_create(s, p); + return; +} + +void ssl_mutex_reinit(server_rec *s, pool *p) +{ + SSLModConfigRec *mc = myModConfig(); + + if (mc->nMutexMode == SSL_MUTEXMODE_FILE) + ssl_mutex_file_open(s, p); + else if (mc->nMutexMode == SSL_MUTEXMODE_SEM) + ssl_mutex_sem_open(s, p); + return; +} + +void ssl_mutex_on(server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(); + BOOL ok = TRUE; + + if (mc->nMutexMode == SSL_MUTEXMODE_FILE) + ok = ssl_mutex_file_acquire(); + else if (mc->nMutexMode == SSL_MUTEXMODE_SEM) + ok = ssl_mutex_sem_acquire(); + if (!ok) + ssl_log(s, SSL_LOG_WARN, "Failed to acquire global mutex lock"); + return; +} + +void ssl_mutex_off(server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(); + BOOL ok = TRUE; + + if (mc->nMutexMode == SSL_MUTEXMODE_FILE) + ok = ssl_mutex_file_release(); + else if (mc->nMutexMode == SSL_MUTEXMODE_SEM) + ok = ssl_mutex_sem_release(); + if (!ok) + ssl_log(s, SSL_LOG_WARN, "Failed to release global mutex lock"); + return; +} + +void ssl_mutex_kill(server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(); + + if (mc->nMutexMode == SSL_MUTEXMODE_FILE) + ssl_mutex_file_remove(s); + else if (mc->nMutexMode == SSL_MUTEXMODE_SEM) + ssl_mutex_sem_remove(s); + return; +} + + +/* _________________________________________________________________ +** +** Mutex Support (Lockfile) +** _________________________________________________________________ +*/ + +void ssl_mutex_file_create(server_rec *s, pool *p) +{ +#ifndef WIN32 + SSLModConfigRec *mc = myModConfig(); + + /* create the lockfile */ + unlink(mc->szMutexFile); + if ((mc->nMutexFD = ap_popenf(p, mc->szMutexFile, + O_WRONLY|O_CREAT, SSL_MUTEX_LOCK_MODE)) < 0) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Parent process could not create SSLMutex lockfile %s", + mc->szMutexFile); + ssl_die(); + } + ap_pclosef(p, mc->nMutexFD); + + /* make sure the childs have access to this file */ +#ifndef OS2 + if (geteuid() == 0 /* is superuser */) + chown(mc->szMutexFile, ap_user_id, -1 /* no gid change */); +#endif + + /* open the lockfile for real */ + if ((mc->nMutexFD = ap_popenf(p, mc->szMutexFile, + O_WRONLY, SSL_MUTEX_LOCK_MODE)) < 0) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Parent could not open SSLMutex lockfile %s", + mc->szMutexFile); + ssl_die(); + } +#endif + return; +} + +void ssl_mutex_file_open(server_rec *s, pool *p) +{ +#ifndef WIN32 + SSLModConfigRec *mc = myModConfig(); + + /* open the lockfile (once per child) to get a unique fd */ + if ((mc->nMutexFD = ap_popenf(p, mc->szMutexFile, + O_WRONLY, SSL_MUTEX_LOCK_MODE)) < 0) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Child could not open SSLMutex lockfile %s", + mc->szMutexFile); + ssl_die(); + } +#endif + return; +} + +void ssl_mutex_file_remove(void *data) +{ +#ifndef WIN32 + SSLModConfigRec *mc = myModConfig(); + + /* remove the mutex lockfile */ + unlink(mc->szMutexFile); +#endif + return; +} + +#ifndef WIN32 +#ifdef SSL_USE_FCNTL +static struct flock lock_it; +static struct flock unlock_it; +#endif +#endif + +BOOL ssl_mutex_file_acquire(void) +{ + int rc = -1; +#ifndef WIN32 + SSLModConfigRec *mc = myModConfig(); + +#ifdef SSL_USE_FCNTL + lock_it.l_whence = SEEK_SET; /* from current point */ + lock_it.l_start = 0; /* -"- */ + lock_it.l_len = 0; /* until end of file */ + lock_it.l_type = F_WRLCK; /* set exclusive/write lock */ + lock_it.l_pid = 0; /* pid not actually interesting */ + + while ( ((rc = fcntl(mc->nMutexFD, F_SETLKW, &lock_it)) < 0) + && (errno == EINTR) ) + ; +#endif +#ifdef SSL_USE_FLOCK + while ( ((rc = flock(mc->nMutexFD, LOCK_EX)) < 0) + && (errno == EINTR) ) + ; +#endif +#endif + + if (rc < 0) + return FALSE; + else + return TRUE; +} + +BOOL ssl_mutex_file_release(void) +{ + int rc = -1; +#ifndef WIN32 + SSLModConfigRec *mc = myModConfig(); + +#ifdef SSL_USE_FCNTL + unlock_it.l_whence = SEEK_SET; /* from current point */ + unlock_it.l_start = 0; /* -"- */ + unlock_it.l_len = 0; /* until end of file */ + unlock_it.l_type = F_UNLCK; /* unlock */ + unlock_it.l_pid = 0; /* pid not actually interesting */ + + while ( (rc = fcntl(mc->nMutexFD, F_SETLKW, &unlock_it)) < 0 + && (errno == EINTR) ) + ; +#endif +#ifdef SSL_USE_FLOCK + while ( (rc = flock(mc->nMutexFD, LOCK_UN)) < 0 + && (errno == EINTR) ) + ; +#endif +#endif + + if (rc < 0) + return FALSE; + else + return TRUE; +} + +/* _________________________________________________________________ +** +** Mutex Support (Process Semaphore) +** _________________________________________________________________ +*/ + +void ssl_mutex_sem_create(server_rec *s, pool *p) +{ +#ifdef SSL_CAN_USE_SEM + int semid; + SSLModConfigRec *mc = myModConfig(); +#ifdef SSL_HAVE_IPCSEM + union ssl_ipc_semun semctlarg; + struct semid_ds semctlbuf; +#endif + +#ifdef SSL_HAVE_IPCSEM + semid = semget(IPC_PRIVATE, 1, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR); + if (semid == -1 && errno == EEXIST) + semid = semget(IPC_PRIVATE, 1, IPC_EXCL|S_IRUSR|S_IWUSR); + if (semid == -1) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Parent process could not create private SSLMutex semaphore"); + ssl_die(); + } + semctlarg.val = 0; + if (semctl(semid, 0, SETVAL, semctlarg) < 0) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Parent process could not initialize SSLMutex semaphore value"); + ssl_die(); + } + semctlbuf.sem_perm.uid = ap_user_id; + semctlbuf.sem_perm.gid = ap_group_id; + semctlbuf.sem_perm.mode = 0660; + semctlarg.buf = &semctlbuf; + if (semctl(semid, 0, IPC_SET, semctlarg) < 0) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Parent process could not set permissions for SSLMutex semaphore"); + ssl_die(); + } +#endif +#ifdef SSL_HAVE_W32SEM + semid = (int)ap_create_mutex("mod_ssl_mutex"); +#endif + mc->nMutexSEMID = semid; +#endif + return; +} + +void ssl_mutex_sem_open(server_rec *s, pool *p) +{ +#ifdef SSL_CAN_USE_SEM +#ifdef SSL_HAVE_W32SEM + SSLModConfigRec *mc = myModConfig(); + + mc->nMutexSEMID = (int)ap_open_mutex("mod_ssl_mutex"); +#endif +#endif + return; +} + +void ssl_mutex_sem_remove(void *data) +{ +#ifdef SSL_CAN_USE_SEM + SSLModConfigRec *mc = myModConfig(); + +#ifdef SSL_HAVE_IPCSEM + semctl(mc->nMutexSEMID, 0, IPC_RMID, 0); +#endif +#ifdef SSL_HAVE_W32SEM + ap_destroy_mutex((mutex *)mc->nMutexSEMID); +#endif +#endif + return; +} + +BOOL ssl_mutex_sem_acquire(void) +{ + int rc = 0; +#ifdef SSL_CAN_USE_SEM + SSLModConfigRec *mc = myModConfig(); + +#ifdef SSL_HAVE_IPCSEM + struct sembuf sb[] = { + { 0, 0, 0 }, /* wait for semaphore */ + { 0, 1, SEM_UNDO } /* increment semaphore */ + }; + + while ( (rc = semop(mc->nMutexSEMID, sb, 2)) < 0 + && (errno == EINTR) ) + ; +#endif +#ifdef SSL_HAVE_W32SEM + rc = ap_acquire_mutex((mutex *)mc->nMutexSEMID); +#endif +#endif + if (rc != 0) + return FALSE; + else + return TRUE; +} + +BOOL ssl_mutex_sem_release(void) +{ + int rc = 0; +#ifdef SSL_CAN_USE_SEM + SSLModConfigRec *mc = myModConfig(); + +#ifdef SSL_HAVE_IPCSEM + struct sembuf sb[] = { + { 0, -1, SEM_UNDO } /* decrements semaphore */ + }; + + while ( (rc = semop(mc->nMutexSEMID, sb, 1)) < 0 + && (errno == EINTR) ) + ; +#endif +#ifdef SSL_HAVE_W32SEM + rc = ap_release_mutex((mutex *)mc->nMutexSEMID); +#endif +#endif + if (rc != 0) + return FALSE; + else + return TRUE; +} + diff --git a/modules/ssl/ssl_engine_pphrase.c b/modules/ssl/ssl_engine_pphrase.c new file mode 100644 index 0000000000..2cef4e309e --- /dev/null +++ b/modules/ssl/ssl_engine_pphrase.c @@ -0,0 +1,545 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_pphrase.c +** Pass Phrase Dialog +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``Treat your password like your + toothbrush. Don't let anybody + else use it, and get a new one + every six months.'' + -- Clifford Stoll */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Pass Phrase and Private Key Handling +** _________________________________________________________________ +*/ + +#define STDERR_FILENO_STORE 50 +#define BUILTIN_DIALOG_BACKOFF 2 +#define BUILTIN_DIALOG_RETRIES 5 + +void ssl_pphrase_Handle(server_rec *s, pool *p) +{ + SSLModConfigRec *mc = myModConfig(); + SSLSrvConfigRec *sc; + server_rec *pServ; + char *cpVHostID; + char szPath[MAX_STRING_LEN]; + EVP_PKEY *pPrivateKey; + ssl_asn1_t *asn1; + unsigned char *ucp; + X509 *pX509Cert; + FILE *fp; + BOOL bReadable; + ssl_ds_array *aPassPhrase; + int nPassPhrase; + int nPassPhraseCur; + char *cpPassPhraseCur; + int nPassPhraseRetry; + int nPassPhraseDialog; + int nPassPhraseDialogCur; + BOOL bPassPhraseDialogOnce; + char **cpp; + int i, j; + ssl_algo_t algoCert, algoKey, at; + char *an; + char *cp; + + /* + * Start with a fresh pass phrase array + */ + aPassPhrase = ssl_ds_array_make(p, sizeof(char *)); + nPassPhrase = 0; + nPassPhraseDialog = 0; + + /* + * Walk through all configured servers + */ + for (pServ = s; pServ != NULL; pServ = pServ->next) { + sc = mySrvConfig(pServ); + + if (!sc->bEnabled) + continue; + + cpVHostID = ssl_util_vhostid(p, pServ); + ssl_log(pServ, SSL_LOG_INFO, + "Init: Loading certificate & private key of SSL-aware server %s", + cpVHostID); + + /* + * Read in server certificate(s): This is the easy part + * because this file isn't encrypted in any way. + */ + if (sc->szPublicCertFile[0] == NULL) { + ssl_log(pServ, SSL_LOG_ERROR, + "Init: Server %s should be SSL-aware but has no certificate configured " + "[Hint: SSLCertificateFile]", cpVHostID); + ssl_die(); + } + algoCert = SSL_ALGO_UNKNOWN; + algoKey = SSL_ALGO_UNKNOWN; + for (i = 0, j = 0; i < SSL_AIDX_MAX && sc->szPublicCertFile[i] != NULL; i++) { + + ap_cpystrn(szPath, sc->szPublicCertFile[i], sizeof(szPath)); + if ((fp = ap_pfopen(p, szPath, "r")) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Init: Can't open server certificate file %s", szPath); + ssl_die(); + } + if ((pX509Cert = SSL_read_X509(fp, NULL, NULL)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: Unable to read server certificate from file %s", szPath); + ssl_die(); + } + ap_pfclose(p, fp); + + /* + * check algorithm type of certificate and make + * sure only one certificate per type is used. + */ + at = ssl_util_algotypeof(pX509Cert, NULL); + an = ssl_util_algotypestr(at); + if (algoCert & at) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: Multiple %s server certificates not allowed", an); + ssl_die(); + } + algoCert |= at; + + /* + * Insert the certificate into global module configuration to let it + * survive the processing between the 1st Apache API init round (where + * we operate here) and the 2nd Apache init round (where the + * certificate is actually used to configure mod_ssl's per-server + * configuration structures). + */ + cp = ap_psprintf(mc->pPool, "%s:%s", cpVHostID, an); + asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tPublicCert, cp); + asn1->nData = i2d_X509(pX509Cert, NULL); + asn1->cpData = ap_palloc(mc->pPool, asn1->nData); + ucp = asn1->cpData; i2d_X509(pX509Cert, &ucp); /* 2nd arg increments */ + + /* + * Free the X509 structure + */ + X509_free(pX509Cert); + + /* + * Read in the private key: This is the non-trivial part, because the + * key is typically encrypted, so a pass phrase dialog has to be used + * to request it from the user (or it has to be alternatively gathered + * from a dialog program). The important point here is that ISPs + * usually have hundrets of virtual servers configured and a lot of + * them use SSL, so really we have to minimize the pass phrase + * dialogs. + * + * The idea is this: When N virtual hosts are configured and all of + * them use encrypted private keys with different pass phrases, we + * have no chance and have to pop up N pass phrase dialogs. But + * usually the admin is clever enough and uses the same pass phrase + * for more private key files (typically he even uses one single pass + * phrase for all). When this is the case we can minimize the dialogs + * by trying to re-use already known/entered pass phrases. + */ + if (sc->szPrivateKeyFile[j] != NULL) + ap_cpystrn(szPath, sc->szPrivateKeyFile[j++], sizeof(szPath)); + + /* + * Try to read the private key file with the help of + * the callback function which serves the pass + * phrases to OpenSSL + */ + myCtxVarSet(mc, 1, pServ); + myCtxVarSet(mc, 2, p); + myCtxVarSet(mc, 3, aPassPhrase); + myCtxVarSet(mc, 4, &nPassPhraseCur); + myCtxVarSet(mc, 5, &cpPassPhraseCur); + myCtxVarSet(mc, 6, cpVHostID); + myCtxVarSet(mc, 7, an); + myCtxVarSet(mc, 8, &nPassPhraseDialog); + myCtxVarSet(mc, 9, &nPassPhraseDialogCur); + myCtxVarSet(mc, 10, &bPassPhraseDialogOnce); + + nPassPhraseCur = 0; + nPassPhraseRetry = 0; + nPassPhraseDialogCur = 0; + bPassPhraseDialogOnce = TRUE; + + pPrivateKey = NULL; + + for (;;) { + /* + * Try to read the private key file with the help of + * the callback function which serves the pass + * phrases to OpenSSL + */ + if ((fp = ap_pfopen(p, szPath, "r")) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Init: Can't open server private key file %s", szPath); + ssl_die(); + } + cpPassPhraseCur = NULL; + bReadable = ((pPrivateKey = SSL_read_PrivateKey(fp, NULL, + ssl_pphrase_Handle_CB)) != NULL ? TRUE : FALSE); + ap_pfclose(p, fp); + + /* + * when the private key file now was readable, + * it's fine and we go out of the loop + */ + if (bReadable) + break; + + /* + * when we have more remembered pass phrases + * try to reuse these first. + */ + if (nPassPhraseCur < nPassPhrase) { + nPassPhraseCur++; + continue; + } + + /* + * else it's not readable and we have no more + * remembered pass phrases. Then this has to mean + * that the callback function popped up the dialog + * but a wrong pass phrase was entered. We give the + * user (but not the dialog program) a few more + * chances... + */ + if ( sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN + && cpPassPhraseCur != NULL + && nPassPhraseRetry < BUILTIN_DIALOG_RETRIES ) { + fprintf(stdout, "Apache:mod_ssl:Error: Pass phrase incorrect " + "(%d more retr%s permitted).\n", + (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry), + (BUILTIN_DIALOG_RETRIES-nPassPhraseRetry) == 1 ? "y" : "ies"); + nPassPhraseRetry++; + if (nPassPhraseRetry > BUILTIN_DIALOG_BACKOFF) + sleep((nPassPhraseRetry-BUILTIN_DIALOG_BACKOFF)*5); + continue; + } + + /* + * Ok, anything else now means a fatal error. + */ + if (cpPassPhraseCur == NULL) + ssl_log(pServ, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Init: Private key not found"); + if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) { + fprintf(stdout, "Apache:mod_ssl:Error: Private key not found.\n"); + fprintf(stdout, "**Stopped\n"); + } + else { + ssl_log(pServ, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Init: Pass phrase incorrect"); + if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) { + fprintf(stdout, "Apache:mod_ssl:Error: Pass phrase incorrect.\n"); + fprintf(stdout, "**Stopped\n"); + } + } + ssl_die(); + } + + if (pPrivateKey == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: Unable to read server private key from file %s", szPath); + ssl_die(); + } + + /* + * check algorithm type of private key and make + * sure only one private key per type is used. + */ + at = ssl_util_algotypeof(NULL, pPrivateKey); + an = ssl_util_algotypestr(at); + if (algoKey & at) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_SSLERR, + "Init: Multiple %s server private keys not allowed", an); + ssl_die(); + } + algoKey |= at; + + /* + * Log the type of reading + */ + if (nPassPhraseDialogCur == 0) + ssl_log(pServ, SSL_LOG_TRACE, + "Init: (%s) unencrypted %s private key - pass phrase not required", + cpVHostID, an); + else { + if (cpPassPhraseCur != NULL) + ssl_log(pServ, SSL_LOG_TRACE, + "Init: (%s) encrypted %s private key - pass phrase requested", + cpVHostID, an); + else + ssl_log(pServ, SSL_LOG_TRACE, + "Init: (%s) encrypted %s private key - pass phrase reused", + cpVHostID, an); + } + + /* + * Ok, when we have one more pass phrase store it + */ + if (cpPassPhraseCur != NULL) { + cpp = (char **)ssl_ds_array_push(aPassPhrase); + *cpp = cpPassPhraseCur; + nPassPhrase++; + } + + /* + * Insert private key into the global module configuration + * (we convert it to a stand-alone DER byte sequence + * because the SSL library uses static variables inside a + * RSA structure which do not survive DSO reloads!) + */ + cp = ap_psprintf(mc->pPool, "%s:%s", cpVHostID, an); + asn1 = (ssl_asn1_t *)ssl_ds_table_push(mc->tPrivateKey, cp); + asn1->nData = i2d_PrivateKey(pPrivateKey, NULL); + asn1->cpData = ap_palloc(mc->pPool, asn1->nData); + ucp = asn1->cpData; i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */ + + /* + * Free the private key structure + */ + EVP_PKEY_free(pPrivateKey); + } + } + + /* + * Let the user know when we're successful. + */ + if (nPassPhraseDialog > 0) { + sc = mySrvConfig(s); + if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) { + fprintf(stdout, "\n"); + fprintf(stdout, "Ok: Pass Phrase Dialog successful.\n"); + } + } + + /* + * Wipe out the used memory from the + * pass phrase array and then deallocate it + */ + if (!ssl_ds_array_isempty(aPassPhrase)) { + ssl_ds_array_wipeout(aPassPhrase); + ssl_ds_array_kill(aPassPhrase); + ssl_log(s, SSL_LOG_INFO, "Init: Wiped out the queried pass phrases from memory"); + } + + return; +} + +int ssl_pphrase_Handle_CB(char *buf, int bufsize, int verify) +{ + SSLModConfigRec *mc = myModConfig(); + server_rec *s; + pool *p; + ssl_ds_array *aPassPhrase; + SSLSrvConfigRec *sc; + int *pnPassPhraseCur; + char **cppPassPhraseCur; + char *cpVHostID; + char *cpAlgoType; + int *pnPassPhraseDialog; + int *pnPassPhraseDialogCur; + BOOL *pbPassPhraseDialogOnce; + int stderr_store; + char **cpp; + int len = -1; + + /* + * Reconnect to the context of ssl_phrase_Handle() + */ + s = myCtxVarGet(mc, 1, server_rec *); + p = myCtxVarGet(mc, 2, pool *); + aPassPhrase = myCtxVarGet(mc, 3, ssl_ds_array *); + pnPassPhraseCur = myCtxVarGet(mc, 4, int *); + cppPassPhraseCur = myCtxVarGet(mc, 5, char **); + cpVHostID = myCtxVarGet(mc, 6, char *); + cpAlgoType = myCtxVarGet(mc, 7, char *); + pnPassPhraseDialog = myCtxVarGet(mc, 8, int *); + pnPassPhraseDialogCur = myCtxVarGet(mc, 9, int *); + pbPassPhraseDialogOnce = myCtxVarGet(mc, 10, BOOL *); + sc = mySrvConfig(s); + + (*pnPassPhraseDialog)++; + (*pnPassPhraseDialogCur)++; + + /* + * When remembered pass phrases are available use them... + */ + if ((cpp = (char **)ssl_ds_array_get(aPassPhrase, *pnPassPhraseCur)) != NULL) { + ap_cpystrn(buf, *cpp, bufsize); + len = strlen(buf); + return len; + } + + /* + * Builtin dialog + */ + if (sc->nPassPhraseDialogType == SSL_PPTYPE_BUILTIN) { + char *prompt; + int i; +#ifdef WIN32 + FILE *con; +#endif + + ssl_log(s, SSL_LOG_INFO, + "Init: Requesting pass phrase via builtin terminal dialog"); + + /* + * Reconnect STDERR to terminal (here STDOUT) because + * at our init stage Apache already connected STDERR + * to the general error logfile. + */ +#ifdef WIN32 + stderr_store = STDERR_FILENO_STORE; +#else + if ((stderr_store = open("/dev/null", O_WRONLY)) == -1) + stderr_store = STDERR_FILENO_STORE; +#endif + dup2(STDERR_FILENO, stderr_store); +#ifdef WIN32 + if ((con = fopen("con", "w")) != NULL) + dup2(fileno(con), STDERR_FILENO); + else + dup2(STDOUT_FILENO, STDERR_FILENO); +#else + dup2(STDOUT_FILENO, STDERR_FILENO); +#endif + + /* + * The first time display a header to inform the user about what + * program he actually speaks to, which module is responsible for + * this terminal dialog and why to the hell he has to enter + * something... + */ + if (*pnPassPhraseDialog == 1) { + fprintf(stderr, "%s mod_ssl/%s (Pass Phrase Dialog)\n", + SERVER_BASEVERSION, MOD_SSL_VERSION); + fprintf(stderr, "Some of your private key files are encrypted for security reasons.\n"); + fprintf(stderr, "In order to read them you have to provide us with the pass phrases.\n"); + } + if (*pbPassPhraseDialogOnce) { + *pbPassPhraseDialogOnce = FALSE; + fprintf(stderr, "\n"); + fprintf(stderr, "Server %s (%s)\n", cpVHostID, cpAlgoType); + } + + /* + * Emulate the OpenSSL internal pass phrase dialog + * (see crypto/pem/pem_lib.c:def_callback() for details) + */ + prompt = "Enter pass phrase:"; + for (;;) { + if ((i = EVP_read_pw_string(buf, bufsize, prompt, FALSE)) != 0) { + PEMerr(PEM_F_DEF_CALLBACK,PEM_R_PROBLEMS_GETTING_PASSWORD); + memset(buf, 0, (unsigned int)bufsize); + return (-1); + } + len = strlen(buf); + if (len < 1) + fprintf(stderr, "Apache:mod_ssl:Error: Pass phrase empty (needs to be at least 1 character).\n"); + else + break; + } + + /* + * Restore STDERR to Apache error logfile + */ + dup2(stderr_store, STDERR_FILENO); + close(stderr_store); +#ifdef WIN32 + if (con != NULL) + fclose(con); +#endif + } + + /* + * Filter program + */ + else if (sc->nPassPhraseDialogType == SSL_PPTYPE_FILTER) { + char *cmd; + char *result; + + ssl_log(s, SSL_LOG_INFO, + "Init: Requesting pass phrase from dialog filter program (%s)", + sc->szPassPhraseDialogPath); + + if (strchr(sc->szPassPhraseDialogPath, ' ') != NULL) + cmd = ap_psprintf(p, "\"%s\" %s %s", sc->szPassPhraseDialogPath, cpVHostID, cpAlgoType); + else + cmd = ap_psprintf(p, "%s %s %s", sc->szPassPhraseDialogPath, cpVHostID, cpAlgoType); + result = ssl_util_readfilter(s, p, cmd); + ap_cpystrn(buf, result, bufsize); + len = strlen(buf); + } + + /* + * Ok, we now have the pass phrase, so give it back + */ + *cppPassPhraseCur = ap_pstrdup(p, buf); + + /* + * And return it's length to OpenSSL... + */ + return (len); +} + diff --git a/modules/ssl/ssl_engine_rand.c b/modules/ssl/ssl_engine_rand.c new file mode 100644 index 0000000000..afb49b4f5c --- /dev/null +++ b/modules/ssl/ssl_engine_rand.c @@ -0,0 +1,215 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_rand.c +** Random Number Generator Seeding +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``The generation of random + numbers is too important + to be left to chance.'' */ + +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Support for better seeding of SSL library's RNG +** _________________________________________________________________ +*/ + +static int ssl_rand_choosenum(int, int); +static int ssl_rand_feedfp(pool *, FILE *, int); + +int ssl_rand_seed(server_rec *s, pool *p, ssl_rsctx_t nCtx, char *prefix) +{ + SSLModConfigRec *mc; + array_header *apRandSeed; + ssl_randseed_t *pRandSeeds; + ssl_randseed_t *pRandSeed; + unsigned char stackdata[256]; + int nReq, nDone; + FILE *fp; + int i, n, l; + time_t t; + pid_t pid; + + mc = myModConfig(); + nReq = 0; + nDone = 0; + apRandSeed = mc->aRandSeed; + pRandSeeds = (ssl_randseed_t *)apRandSeed->elts; + for (i = 0; i < apRandSeed->nelts; i++) { + pRandSeed = &pRandSeeds[i]; + if (pRandSeed->nCtx == nCtx) { + nReq += pRandSeed->nBytes; + if (pRandSeed->nSrc == SSL_RSSRC_FILE) { + /* + * seed in contents of an external file + */ + if ((fp = ap_pfopen(p, pRandSeed->cpPath, "r")) == NULL) + continue; + nDone += ssl_rand_feedfp(p, fp, pRandSeed->nBytes); + ap_pfclose(p, fp); + } + else if (pRandSeed->nSrc == SSL_RSSRC_EXEC) { + /* + * seed in contents generated by an external program + */ + if ((fp = ssl_util_ppopen(s, p, ap_psprintf(p, "%s %d", + pRandSeed->cpPath, pRandSeed->nBytes))) == NULL) + continue; + nDone += ssl_rand_feedfp(p, fp, pRandSeed->nBytes); + ssl_util_ppclose(s, p, fp); + } +#if SSL_LIBRARY_VERSION >= 0x00905100 + else if (pRandSeed->nSrc == SSL_RSSRC_EGD) { + /* + * seed in contents provided by the external + * Entropy Gathering Daemon (EGD) + */ + if ((n = RAND_egd(pRandSeed->cpPath)) == -1) + continue; + nDone += n; + } +#endif + else if (pRandSeed->nSrc == SSL_RSSRC_BUILTIN) { + /* + * seed in the current time (usually just 4 bytes) + */ + t = time(NULL); + l = sizeof(time_t); + RAND_seed((unsigned char *)&t, l); + nDone += l; + + /* + * seed in the current process id (usually just 4 bytes) + */ + pid = getpid(); + l = sizeof(pid_t); + RAND_seed((unsigned char *)&pid, l); + nDone += l; + + /* + * seed in some current state of the run-time stack (128 bytes) + */ + n = ssl_rand_choosenum(0, sizeof(stackdata)-128-1); + RAND_seed(stackdata+n, 128); + nDone += 128; + + /* + * seed in an 1KB extract of the current scoreboard + */ + if (ap_scoreboard_image != NULL) { + n = ssl_rand_choosenum(0, SCOREBOARD_SIZE-1024-1); + RAND_seed((unsigned char *)ap_scoreboard_image+n, 1024); + nDone += 1024; + } + } + } + } + ssl_log(s, SSL_LOG_INFO, "%sSeeding PRNG with %d bytes of entropy", prefix, nDone); + +#if SSL_LIBRARY_VERSION >= 0x00905100 + if (RAND_status() == 0) + ssl_log(s, SSL_LOG_WARN, "%sPRNG still contains not sufficient entropy!", prefix); +#endif + return nDone; +} + +#define BUFSIZE 8192 + +static int ssl_rand_feedfp(pool *p, FILE *fp, int nReq) +{ + int nDone; + unsigned char caBuf[BUFSIZE]; + int nBuf; + int nRead; + int nTodo; + + nDone = 0; + nRead = BUFSIZE; + nTodo = nReq; + while (1) { + if (nReq > 0) + nRead = (nTodo < BUFSIZE ? nTodo : BUFSIZE); + if ((nBuf = (int)fread(caBuf, 1, nRead, fp)) <= 0) + break; + RAND_seed(caBuf, nBuf); + nDone += nBuf; + if (nReq > 0) { + nTodo -= nBuf; + if (nTodo <= 0) + break; + } + } + return nDone; +} + +static int ssl_rand_choosenum(int l, int h) +{ + int i; + char buf[50]; + + srand((unsigned int)time(NULL)); + ap_snprintf(buf, sizeof(buf), "%.0f", + (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l))); + i = atoi(buf)+1; + if (i < l) i = l; + if (i > h) i = h; + return i; +} + diff --git a/modules/ssl/ssl_engine_vars.c b/modules/ssl/ssl_engine_vars.c new file mode 100644 index 0000000000..c0755a5ec8 --- /dev/null +++ b/modules/ssl/ssl_engine_vars.c @@ -0,0 +1,615 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_engine_vars.c +** Variable Lookup Facility +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``Those of you who think they + know everything are very annoying + to those of us who do.'' + -- Unknown */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Variable Lookup +** _________________________________________________________________ +*/ + +static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name); +static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var); +static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var); +static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var); +static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm); +static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs); +static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var); +static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs); +static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c); +static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var); +static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize); +static char *ssl_var_lookup_ssl_version(pool *p, char *var); + +void ssl_var_register(void) +{ + ap_hook_configure("ap::mod_ssl::var_lookup", + AP_HOOK_SIG6(ptr,ptr,ptr,ptr,ptr,ptr), AP_HOOK_DECLINE(NULL)); + ap_hook_register("ap::mod_ssl::var_lookup", + ssl_var_lookup, AP_HOOK_NOCTX); + return; +} + +void ssl_var_unregister(void) +{ + ap_hook_unregister("ap::mod_ssl::var_lookup", ssl_var_lookup); + return; +} + +char *ssl_var_lookup(pool *p, server_rec *s, conn_rec *c, request_rec *r, char *var) +{ + SSLModConfigRec *mc = myModConfig(); + char *result; + BOOL resdup; + time_t tc; + struct tm *tm; + + result = NULL; + resdup = TRUE; + + /* + * When no pool is given try to find one + */ + if (p == NULL) { + if (r != NULL) + p = r->pool; + else if (c != NULL) + p = c->pool; + else + p = mc->pPool; + } + + /* + * Request dependent stuff + */ + if (r != NULL) { + if (strcEQ(var, "HTTP_USER_AGENT")) + result = ssl_var_lookup_header(p, r, "User-Agent"); + else if (strcEQ(var, "HTTP_REFERER")) + result = ssl_var_lookup_header(p, r, "Referer"); + else if (strcEQ(var, "HTTP_COOKIE")) + result = ssl_var_lookup_header(p, r, "Cookie"); + else if (strcEQ(var, "HTTP_FORWARDED")) + result = ssl_var_lookup_header(p, r, "Forwarded"); + else if (strcEQ(var, "HTTP_HOST")) + result = ssl_var_lookup_header(p, r, "Host"); + else if (strcEQ(var, "HTTP_PROXY_CONNECTION")) + result = ssl_var_lookup_header(p, r, "Proxy-Connection"); + else if (strcEQ(var, "HTTP_ACCEPT")) + result = ssl_var_lookup_header(p, r, "Accept"); + else if (strlen(var) > 5 && strcEQn(var, "HTTP:", 5)) + /* all other headers from which we are still not know about */ + result = ssl_var_lookup_header(p, r, var+5); + else if (strcEQ(var, "THE_REQUEST")) + result = r->the_request; + else if (strcEQ(var, "REQUEST_METHOD")) + result = (char *)(r->method); + else if (strcEQ(var, "REQUEST_SCHEME")) + result = ap_http_method(r); + else if (strcEQ(var, "REQUEST_URI")) + result = r->uri; + else if (strcEQ(var, "SCRIPT_FILENAME") || + strcEQ(var, "REQUEST_FILENAME")) + result = r->filename; + else if (strcEQ(var, "PATH_INFO")) + result = r->path_info; + else if (strcEQ(var, "QUERY_STRING")) + result = r->args; + else if (strcEQ(var, "REMOTE_HOST")) + result = (char *)ap_get_remote_host(r->connection, + r->per_dir_config, REMOTE_NAME); + else if (strcEQ(var, "REMOTE_IDENT")) + result = (char *)ap_get_remote_logname(r); + else if (strcEQ(var, "IS_SUBREQ")) + result = (r->main != NULL ? "true" : "false"); + else if (strcEQ(var, "DOCUMENT_ROOT")) + result = (char *)ap_document_root(r); + else if (strcEQ(var, "SERVER_ADMIN")) + result = r->server->server_admin; + else if (strcEQ(var, "SERVER_NAME")) + result = (char *)ap_get_server_name(r); + else if (strcEQ(var, "SERVER_PORT")) + result = ap_psprintf(p, "%u", ap_get_server_port(r)); + else if (strcEQ(var, "SERVER_PROTOCOL")) + result = r->protocol; + } + + /* + * Connection stuff + */ + if (result == NULL && c != NULL) { + if (strcEQ(var, "REMOTE_ADDR")) + result = c->remote_ip; + else if (strcEQ(var, "REMOTE_USER")) + result = c->user; + else if (strcEQ(var, "AUTH_TYPE")) + result = c->ap_auth_type; + else if (strlen(var) > 4 && strcEQn(var, "SSL_", 4)) + result = ssl_var_lookup_ssl(p, c, var+4); + else if (strcEQ(var, "HTTPS")) { + if (ap_ctx_get(c->client->ctx, "ssl") != NULL) + result = "on"; + else + result = "off"; + } + } + + /* + * Totally independent stuff + */ + if (result == NULL) { + if (strlen(var) > 12 && strcEQn(var, "SSL_VERSION_", 12)) + result = ssl_var_lookup_ssl_version(p, var+12); + else if (strcEQ(var, "SERVER_SOFTWARE")) + result = (char *)ap_get_server_version(); + else if (strcEQ(var, "API_VERSION")) { + result = ap_psprintf(p, "%d", MODULE_MAGIC_NUMBER); + resdup = FALSE; + } + else if (strcEQ(var, "TIME_YEAR")) { + tc = time(NULL); + tm = localtime(&tc); + result = ap_psprintf(p, "%02d%02d", + (tm->tm_year / 100) + 19, tm->tm_year % 100); + resdup = FALSE; + } +#define MKTIMESTR(format, tmfield) \ + tc = time(NULL); \ + tm = localtime(&tc); \ + result = ap_psprintf(p, format, tm->tmfield); \ + resdup = FALSE; + else if (strcEQ(var, "TIME_MON")) { + MKTIMESTR("%02d", tm_mon+1) + } + else if (strcEQ(var, "TIME_DAY")) { + MKTIMESTR("%02d", tm_mday) + } + else if (strcEQ(var, "TIME_HOUR")) { + MKTIMESTR("%02d", tm_hour) + } + else if (strcEQ(var, "TIME_MIN")) { + MKTIMESTR("%02d", tm_min) + } + else if (strcEQ(var, "TIME_SEC")) { + MKTIMESTR("%02d", tm_sec) + } + else if (strcEQ(var, "TIME_WDAY")) { + MKTIMESTR("%d", tm_wday) + } + else if (strcEQ(var, "TIME")) { + tc = time(NULL); + tm = localtime(&tc); + result = ap_psprintf(p, + "%02d%02d%02d%02d%02d%02d%02d", (tm->tm_year / 100) + 19, + (tm->tm_year % 100), tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + resdup = FALSE; + } + /* all other env-variables from the parent Apache process */ + else if (strlen(var) > 4 && strcEQn(var, "ENV:", 4)) { + result = (char *)ap_table_get(r->notes, var+4); + if (result == NULL) + result = (char *)ap_table_get(r->subprocess_env, var+4); + if (result == NULL) + result = getenv(var+4); + } + } + + if (result != NULL && resdup) + result = ap_pstrdup(p, result); + if (result == NULL) + result = ""; + return result; +} + +static char *ssl_var_lookup_header(pool *p, request_rec *r, const char *name) +{ + array_header *hdrs_arr; + table_entry *hdrs; + int i; + + hdrs_arr = ap_table_elts(r->headers_in); + hdrs = (table_entry *)hdrs_arr->elts; + for (i = 0; i < hdrs_arr->nelts; ++i) { + if (hdrs[i].key == NULL) + continue; + if (strcEQ(hdrs[i].key, name)) + return ap_pstrdup(p, hdrs[i].val); + } + return NULL; +} + +static char *ssl_var_lookup_ssl(pool *p, conn_rec *c, char *var) +{ + char *result; + X509 *xs; + STACK_OF(X509) *sk; + SSL *ssl; + + result = NULL; + + ssl = ap_ctx_get(c->client->ctx, "ssl"); + if (strlen(var) > 8 && strcEQn(var, "VERSION_", 8)) { + result = ssl_var_lookup_ssl_version(p, var+8); + } + else if (ssl != NULL && strcEQ(var, "PROTOCOL")) { + result = (char *)SSL_get_version(ssl); + } + else if (ssl != NULL && strcEQ(var, "SESSION_ID")) { + SSL_SESSION *pSession = SSL_get_session(ssl); + result = ap_pstrdup(p, SSL_SESSION_id2sz(pSession->session_id, + pSession->session_id_length)); + } + else if (ssl != NULL && strlen(var) >= 6 && strcEQn(var, "CIPHER", 6)) { + result = ssl_var_lookup_ssl_cipher(p, c, var+6); + } + else if (ssl != NULL && strlen(var) > 18 && strcEQn(var, "CLIENT_CERT_CHAIN_", 18)) { + sk = SSL_get_peer_cert_chain(ssl); + result = ssl_var_lookup_ssl_cert_chain(p, sk, var+17); + } + else if (ssl != NULL && strcEQ(var, "CLIENT_VERIFY")) { + result = ssl_var_lookup_ssl_cert_verify(p, c); + } + else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "CLIENT_", 7)) { + if ((xs = SSL_get_peer_certificate(ssl)) != NULL) + result = ssl_var_lookup_ssl_cert(p, xs, var+7); + } + else if (ssl != NULL && strlen(var) > 7 && strcEQn(var, "SERVER_", 7)) { + if ((xs = SSL_get_certificate(ssl)) != NULL) + result = ssl_var_lookup_ssl_cert(p, xs, var+7); + } + return result; +} + +static char *ssl_var_lookup_ssl_cert(pool *p, X509 *xs, char *var) +{ + char *result; + BOOL resdup; + X509_NAME *xsname; + int nid; + char *cp; + + result = NULL; + resdup = TRUE; + + if (strcEQ(var, "M_VERSION")) { + result = ap_psprintf(p, "%lu", X509_get_version(xs)+1); + resdup = FALSE; + } + else if (strcEQ(var, "M_SERIAL")) { + result = ssl_var_lookup_ssl_cert_serial(p, xs); + } + else if (strcEQ(var, "V_START")) { + result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notBefore(xs)); + } + else if (strcEQ(var, "V_END")) { + result = ssl_var_lookup_ssl_cert_valid(p, X509_get_notAfter(xs)); + } + else if (strcEQ(var, "S_DN")) { + xsname = X509_get_subject_name(xs); + cp = X509_NAME_oneline(xsname, NULL, 0); + result = ap_pstrdup(p, cp); + free(cp); + resdup = FALSE; + } + else if (strlen(var) > 5 && strcEQn(var, "S_DN_", 5)) { + xsname = X509_get_subject_name(xs); + result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5); + resdup = FALSE; + } + else if (strcEQ(var, "I_DN")) { + xsname = X509_get_issuer_name(xs); + cp = X509_NAME_oneline(xsname, NULL, 0); + result = ap_pstrdup(p, cp); + free(cp); + resdup = FALSE; + } + else if (strlen(var) > 5 && strcEQn(var, "I_DN_", 5)) { + xsname = X509_get_issuer_name(xs); + result = ssl_var_lookup_ssl_cert_dn(p, xsname, var+5); + resdup = FALSE; + } + else if (strcEQ(var, "A_SIG")) { + nid = OBJ_obj2nid(xs->cert_info->signature->algorithm); + result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid)); + resdup = FALSE; + } + else if (strcEQ(var, "A_KEY")) { + nid = OBJ_obj2nid(xs->cert_info->key->algor->algorithm); + result = ap_pstrdup(p, (nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(nid)); + resdup = FALSE; + } + else if (strcEQ(var, "CERT")) { + result = ssl_var_lookup_ssl_cert_PEM(p, xs); + } + + if (result != NULL && resdup) + result = ap_pstrdup(p, result); + return result; +} + +static const struct { + char *name; + int nid; +} ssl_var_lookup_ssl_cert_dn_rec[] = { + { "C", NID_countryName }, + { "ST", NID_stateOrProvinceName }, /* officially (RFC2156) */ + { "SP", NID_stateOrProvinceName }, /* compatibility (SSLeay) */ + { "L", NID_localityName }, + { "O", NID_organizationName }, + { "OU", NID_organizationalUnitName }, + { "CN", NID_commonName }, + { "T", NID_title }, + { "I", NID_initials }, + { "G", NID_givenName }, + { "S", NID_surname }, + { "D", NID_description }, + { "UID", NID_uniqueIdentifier }, + { "Email", NID_pkcs9_emailAddress }, + { NULL, 0 } +}; + +static char *ssl_var_lookup_ssl_cert_dn(pool *p, X509_NAME *xsname, char *var) +{ + char *result; + X509_NAME_ENTRY *xsne; + int i, j, n; + + result = NULL; + + for (i = 0; ssl_var_lookup_ssl_cert_dn_rec[i].name != NULL; i++) { + if (strEQ(var, ssl_var_lookup_ssl_cert_dn_rec[i].name)) { + for (j = 0; j < sk_X509_NAME_ENTRY_num(xsname->entries); j++) { + xsne = sk_X509_NAME_ENTRY_value(xsname->entries, j); + n = OBJ_obj2nid(xsne->object); + if (n == ssl_var_lookup_ssl_cert_dn_rec[i].nid) { + result = ap_palloc(p, xsne->value->length+1); + ap_cpystrn(result, (char *)xsne->value->data, xsne->value->length+1); +#ifdef CHARSET_EBCDIC + ascii2ebcdic(result, result, xsne->value->length); +#endif /* CHARSET_EBCDIC */ + result[xsne->value->length] = NUL; + break; + } + } + break; + } + } + return result; +} + +static char *ssl_var_lookup_ssl_cert_valid(pool *p, ASN1_UTCTIME *tm) +{ + char *result; + BIO* bio; + int n; + + if ((bio = BIO_new(BIO_s_mem())) == NULL) + return NULL; + ASN1_UTCTIME_print(bio, tm); + n = BIO_pending(bio); + result = ap_pcalloc(p, n+1); + n = BIO_read(bio, result, n); + result[n] = NUL; + BIO_free(bio); + return result; +} + +static char *ssl_var_lookup_ssl_cert_serial(pool *p, X509 *xs) +{ + char *result; + BIO *bio; + int n; + + if ((bio = BIO_new(BIO_s_mem())) == NULL) + return NULL; + i2a_ASN1_INTEGER(bio, X509_get_serialNumber(xs)); + n = BIO_pending(bio); + result = ap_pcalloc(p, n+1); + n = BIO_read(bio, result, n); + result[n] = NUL; + BIO_free(bio); + return result; +} + +static char *ssl_var_lookup_ssl_cert_chain(pool *p, STACK_OF(X509) *sk, char *var) +{ + char *result; + X509 *xs; + int n; + + result = NULL; + + if (strspn(var, "0123456789") == strlen(var)) { + n = atoi(var); + if (n < sk_X509_num(sk)) { + xs = sk_X509_value(sk, n); + result = ssl_var_lookup_ssl_cert_PEM(p, xs); + } + } + + return result; +} + +static char *ssl_var_lookup_ssl_cert_PEM(pool *p, X509 *xs) +{ + char *result; + BIO *bio; + int n; + + if ((bio = BIO_new(BIO_s_mem())) == NULL) + return NULL; + PEM_write_bio_X509(bio, xs); + n = BIO_pending(bio); + result = ap_pcalloc(p, n+1); + n = BIO_read(bio, result, n); + result[n] = NUL; + BIO_free(bio); + return result; +} + +static char *ssl_var_lookup_ssl_cert_verify(pool *p, conn_rec *c) +{ + char *result; + long vrc; + char *verr; + char *vinfo; + SSL *ssl; + X509 *xs; + + result = NULL; + ssl = ap_ctx_get(c->client->ctx, "ssl"); + verr = ap_ctx_get(c->client->ctx, "ssl::verify::error"); + vinfo = ap_ctx_get(c->client->ctx, "ssl::verify::info"); + vrc = SSL_get_verify_result(ssl); + xs = SSL_get_peer_certificate(ssl); + + if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs == NULL) + /* no client verification done at all */ + result = "NONE"; + else if (vrc == X509_V_OK && verr == NULL && vinfo == NULL && xs != NULL) + /* client verification done successful */ + result = "SUCCESS"; + else if (vrc == X509_V_OK && vinfo != NULL && strEQ(vinfo, "GENEROUS")) + /* client verification done in generous way */ + result = "GENEROUS"; + else + /* client verification failed */ + result = ap_psprintf(p, "FAILED:%s", verr); + return result; +} + +static char *ssl_var_lookup_ssl_cipher(pool *p, conn_rec *c, char *var) +{ + char *result; + BOOL resdup; + int usekeysize, algkeysize; + SSL *ssl; + + result = NULL; + resdup = TRUE; + + ssl = ap_ctx_get(c->client->ctx, "ssl"); + ssl_var_lookup_ssl_cipher_bits(ssl, &usekeysize, &algkeysize); + + if (strEQ(var, "")) + result = (ssl != NULL ? (char *)SSL_get_cipher_name(ssl) : NULL); + else if (strcEQ(var, "_EXPORT")) + result = (usekeysize < 56 ? "true" : "false"); + else if (strcEQ(var, "_USEKEYSIZE")) { + result = ap_psprintf(p, "%d", usekeysize); + resdup = FALSE; + } + else if (strcEQ(var, "_ALGKEYSIZE")) { + result = ap_psprintf(p, "%d", algkeysize); + resdup = FALSE; + } + + if (result != NULL && resdup) + result = ap_pstrdup(p, result); + return result; +} + +static void ssl_var_lookup_ssl_cipher_bits(SSL *ssl, int *usekeysize, int *algkeysize) +{ + SSL_CIPHER *cipher; + + *usekeysize = 0; + *algkeysize = 0; + if (ssl != NULL) + if ((cipher = SSL_get_current_cipher(ssl)) != NULL) + *usekeysize = SSL_CIPHER_get_bits(cipher, algkeysize); + return; +} + +static char *ssl_var_lookup_ssl_version(pool *p, char *var) +{ + char *result; + char *cp, *cp2; + + result = NULL; + + if (strEQ(var, "PRODUCT")) { +#if defined(SSL_PRODUCT_NAME) && defined(SSL_PRODUCT_VERSION) + result = ap_psprintf(p, "%s/%s", SSL_PRODUCT_NAME, SSL_PRODUCT_VERSION); +#else + result = NULL; +#endif + } + else if (strEQ(var, "INTERFACE")) { + result = ap_psprintf(p, "mod_ssl/%s", MOD_SSL_VERSION); + } + else if (strEQ(var, "LIBRARY")) { + result = ap_pstrdup(p, SSL_LIBRARY_TEXT); + if ((cp = strchr(result, ' ')) != NULL) { + *cp = '/'; + if ((cp2 = strchr(cp, ' ')) != NULL) + *cp2 = NUL; + } + } + return result; +} + diff --git a/modules/ssl/ssl_expr.c b/modules/ssl/ssl_expr.c new file mode 100644 index 0000000000..49ab873ded --- /dev/null +++ b/modules/ssl/ssl_expr.c @@ -0,0 +1,119 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_expr.c +** Expression Handling +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``It is hard to fly with + the eagles when you work + with the turkeys.'' + -- Unknown */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Expression Handling +** _________________________________________________________________ +*/ + +ssl_expr_info_type ssl_expr_info; +char *ssl_expr_error; + +ssl_expr *ssl_expr_comp(pool *p, char *expr) +{ + ssl_expr_info.pool = p; + ssl_expr_info.inputbuf = expr; + ssl_expr_info.inputlen = strlen(expr); + ssl_expr_info.inputptr = ssl_expr_info.inputbuf; + ssl_expr_info.expr = FALSE; + + ssl_expr_error = NULL; + if (ssl_expr_yyparse()) + return NULL; + return ssl_expr_info.expr; +} + +char *ssl_expr_get_error(void) +{ + if (ssl_expr_error == NULL) + return ""; + return ssl_expr_error; +} + +ssl_expr *ssl_expr_make(ssl_expr_node_op op, void *a1, void *a2) +{ + ssl_expr *node; + + node = (ssl_expr *)ap_palloc(ssl_expr_info.pool, sizeof(ssl_expr)); + node->node_op = op; + node->node_arg1 = (char *)a1; + node->node_arg2 = (char *)a2; + return node; +} + +int ssl_expr_exec(request_rec *r, ssl_expr *expr) +{ + BOOL rc; + + rc = ssl_expr_eval(r, expr); + if (ssl_expr_error != NULL) + return (-1); + else + return (rc ? 1 : 0); +} + diff --git a/modules/ssl/ssl_expr.h b/modules/ssl/ssl_expr.h new file mode 100644 index 0000000000..419bb02192 --- /dev/null +++ b/modules/ssl/ssl_expr.h @@ -0,0 +1,139 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_expr.h +** Expression Handling (Header) +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + + /* ``May all your PUSHes be POPed.'' */ + +#ifndef SSL_EXPR_H +#define SSL_EXPR_H + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE !FALSE +#endif + +#ifndef YY_NULL +#define YY_NULL 0 +#endif + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef BOOL +#define BOOL unsigned int +#endif + +#ifndef NULL +#define NULL (void *)0 +#endif + +#ifndef NUL +#define NUL '\0' +#endif + +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif + +typedef enum { + op_NOP, op_ListElement, + op_True, op_False, op_Not, op_Or, op_And, op_Comp, + op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE, op_IN, op_REG, op_NRE, + op_Digit, op_String, op_Regex, op_Var, op_Func +} ssl_expr_node_op; + +typedef struct { + ssl_expr_node_op node_op; + void *node_arg1; + void *node_arg2; +} ssl_expr_node; + +typedef ssl_expr_node ssl_expr; + +typedef struct { + pool *pool; + char *inputbuf; + int inputlen; + char *inputptr; + ssl_expr *expr; +} ssl_expr_info_type; + +extern ssl_expr_info_type ssl_expr_info; +extern char *ssl_expr_error; + +#define yylval ssl_expr_yylval +#define yyerror ssl_expr_yyerror +#define yyinput ssl_expr_yyinput + +extern int ssl_expr_yyparse(void); +extern int ssl_expr_yyerror(char *); +extern int ssl_expr_yylex(void); + +extern ssl_expr *ssl_expr_comp(pool *, char *); +extern int ssl_expr_exec(request_rec *, ssl_expr *); +extern char *ssl_expr_get_error(void); +extern ssl_expr *ssl_expr_make(ssl_expr_node_op, void *, void *); +extern BOOL ssl_expr_eval(request_rec *, ssl_expr *); + +#endif /* SSL_EXPR_H */ diff --git a/modules/ssl/ssl_expr_eval.c b/modules/ssl/ssl_expr_eval.c new file mode 100644 index 0000000000..d8c5ea5f9e --- /dev/null +++ b/modules/ssl/ssl_expr_eval.c @@ -0,0 +1,282 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_expr_eval.c +** Expression Evaluation +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``Make love, + not software!'' + -- Unknown */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Expression Evaluation +** _________________________________________________________________ +*/ + +static BOOL ssl_expr_eval_comp(request_rec *, ssl_expr *); +static char *ssl_expr_eval_word(request_rec *, ssl_expr *); +static char *ssl_expr_eval_func_file(request_rec *, char *); +static int ssl_expr_eval_strcmplex(char *, char *); + +BOOL ssl_expr_eval(request_rec *r, ssl_expr *node) +{ + switch (node->node_op) { + case op_True: { + return TRUE; + } + case op_False: { + return FALSE; + } + case op_Not: { + ssl_expr *e = (ssl_expr *)node->node_arg1; + return (!ssl_expr_eval(r, e)); + } + case op_Or: { + ssl_expr *e1 = (ssl_expr *)node->node_arg1; + ssl_expr *e2 = (ssl_expr *)node->node_arg2; + return (ssl_expr_eval(r, e1) || ssl_expr_eval(r, e2)); + } + case op_And: { + ssl_expr *e1 = (ssl_expr *)node->node_arg1; + ssl_expr *e2 = (ssl_expr *)node->node_arg2; + return (ssl_expr_eval(r, e1) && ssl_expr_eval(r, e2)); + } + case op_Comp: { + ssl_expr *e = (ssl_expr *)node->node_arg1; + return ssl_expr_eval_comp(r, e); + } + default: { + ssl_expr_error = "Internal evaluation error: Unknown expression node"; + return FALSE; + } + } +} + +static BOOL ssl_expr_eval_comp(request_rec *r, ssl_expr *node) +{ + switch (node->node_op) { + case op_EQ: { + ssl_expr *e1 = (ssl_expr *)node->node_arg1; + ssl_expr *e2 = (ssl_expr *)node->node_arg2; + return (strcmp(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) == 0); + } + case op_NE: { + ssl_expr *e1 = (ssl_expr *)node->node_arg1; + ssl_expr *e2 = (ssl_expr *)node->node_arg2; + return (strcmp(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) != 0); + } + case op_LT: { + ssl_expr *e1 = (ssl_expr *)node->node_arg1; + ssl_expr *e2 = (ssl_expr *)node->node_arg2; + return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) < 0); + } + case op_LE: { + ssl_expr *e1 = (ssl_expr *)node->node_arg1; + ssl_expr *e2 = (ssl_expr *)node->node_arg2; + return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) <= 0); + } + case op_GT: { + ssl_expr *e1 = (ssl_expr *)node->node_arg1; + ssl_expr *e2 = (ssl_expr *)node->node_arg2; + return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) > 0); + } + case op_GE: { + ssl_expr *e1 = (ssl_expr *)node->node_arg1; + ssl_expr *e2 = (ssl_expr *)node->node_arg2; + return (ssl_expr_eval_strcmplex(ssl_expr_eval_word(r, e1), ssl_expr_eval_word(r, e2)) >= 0); + } + case op_IN: { + ssl_expr *e1 = (ssl_expr *)node->node_arg1; + ssl_expr *e2 = (ssl_expr *)node->node_arg2; + ssl_expr *e3; + char *w1 = ssl_expr_eval_word(r, e1); + BOOL found = FALSE; + do { + e3 = (ssl_expr *)e2->node_arg1; + e2 = (ssl_expr *)e2->node_arg2; + if (strcmp(w1, ssl_expr_eval_word(r, e3)) == 0) { + found = TRUE; + break; + } + } while (e2 != NULL); + return found; + } + case op_REG: { + ssl_expr *e1; + ssl_expr *e2; + char *word; + regex_t *regex; + + e1 = (ssl_expr *)node->node_arg1; + e2 = (ssl_expr *)node->node_arg2; + word = ssl_expr_eval_word(r, e1); + regex = (regex_t *)(e2->node_arg1); + return (regexec(regex, word, 0, NULL, 0) == 0); + } + case op_NRE: { + ssl_expr *e1; + ssl_expr *e2; + char *word; + regex_t *regex; + + e1 = (ssl_expr *)node->node_arg1; + e2 = (ssl_expr *)node->node_arg2; + word = ssl_expr_eval_word(r, e1); + regex = (regex_t *)(e2->node_arg1); + return !(regexec(regex, word, 0, NULL, 0) == 0); + } + default: { + ssl_expr_error = "Internal evaluation error: Unknown expression node"; + return FALSE; + } + } +} + +static char *ssl_expr_eval_word(request_rec *r, ssl_expr *node) +{ + switch (node->node_op) { + case op_Digit: { + char *string = (char *)node->node_arg1; + return string; + } + case op_String: { + char *string = (char *)node->node_arg1; + return string; + } + case op_Var: { + char *var = (char *)node->node_arg1; + char *val = ssl_var_lookup(r->pool, r->server, r->connection, r, var); + return (val == NULL ? "" : val); + } + case op_Func: { + char *name = (char *)node->node_arg1; + ssl_expr *args = (ssl_expr *)node->node_arg2; + if (strEQ(name, "file")) + return ssl_expr_eval_func_file(r, (char *)(args->node_arg1)); + else { + ssl_expr_error = "Internal evaluation error: Unknown function name"; + return ""; + } + } + default: { + ssl_expr_error = "Internal evaluation error: Unknown expression node"; + return FALSE; + } + } +} + +static char *ssl_expr_eval_func_file(request_rec *r, char *filename) +{ + FILE *fp; + char *buf; + int len; + + if ((fp = ap_pfopen(r->pool, filename, "r")) == NULL) { + ssl_expr_error = "Cannot open file"; + return ""; + } + fseek(fp, 0, SEEK_END); + len = ftell(fp); + if (len == 0) { + buf = (char *)ap_palloc(r->pool, sizeof(char) * 1); + *buf = NUL; + } + else { + if ((buf = (char *)ap_palloc(r->pool, sizeof(char) * len+1)) == NULL) { + ssl_expr_error = "Cannot allocate memory"; + ap_pfclose(r->pool, fp); + return ""; + } + fseek(fp, 0, SEEK_SET); + if (fread(buf, len, 1, fp) == 0) { + ssl_expr_error = "Cannot read from file"; + fclose(fp); + return (""); + } + buf[len] = NUL; + } + ap_pfclose(r->pool, fp); + return buf; +} + +/* a variant of strcmp(3) which works correctly also for number strings */ +static int ssl_expr_eval_strcmplex(char *cpNum1, char *cpNum2) +{ + int i, n1, n2; + + if (cpNum1 == NULL) + return -1; + if (cpNum2 == NULL) + return +1; + n1 = strlen(cpNum1); + n2 = strlen(cpNum2); + if (n1 > n2) + return 1; + if (n1 < n2) + return -1; + for (i = 0; i < n1; i++) { + if (cpNum1[i] > cpNum2[i]) + return 1; + if (cpNum1[i] < cpNum2[i]) + return -1; + } + return 0; +} + diff --git a/modules/ssl/ssl_expr_parse.c b/modules/ssl/ssl_expr_parse.c new file mode 100644 index 0000000000..8e35553a4e --- /dev/null +++ b/modules/ssl/ssl_expr_parse.c @@ -0,0 +1,605 @@ +#ifndef lint +static char const +ssl_expr_yyrcsid[] = "$FreeBSD: src/usr.bin/yacc/skeleton.c,v 1.28 2000/01/17 02:04:06 bde Exp $"; +#endif +#include <stdlib.h> +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define YYLEX ssl_expr_yylex() +#define YYEMPTY -1 +#define ssl_expr_yyclearin (ssl_expr_yychar=(YYEMPTY)) +#define ssl_expr_yyerrok (ssl_expr_yyerrflag=0) +#define YYRECOVERING() (ssl_expr_yyerrflag!=0) +static int ssl_expr_yygrowstack(); +#define YYPREFIX "ssl_expr_yy" +#line 72 "ssl_expr_parse.y" +#include "mod_ssl.h" +#line 75 "ssl_expr_parse.y" +typedef union { + char *cpVal; + ssl_expr *exVal; +} YYSTYPE; +#line 24 "y.tab.c" +#define YYERRCODE 256 +#define T_TRUE 257 +#define T_FALSE 258 +#define T_DIGIT 259 +#define T_ID 260 +#define T_STRING 261 +#define T_REGEX 262 +#define T_REGEX_I 263 +#define T_FUNC_FILE 264 +#define T_OP_EQ 265 +#define T_OP_NE 266 +#define T_OP_LT 267 +#define T_OP_LE 268 +#define T_OP_GT 269 +#define T_OP_GE 270 +#define T_OP_REG 271 +#define T_OP_NRE 272 +#define T_OP_IN 273 +#define T_OP_OR 274 +#define T_OP_AND 275 +#define T_OP_NOT 276 +const short ssl_expr_yylhs[] = { -1, + 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 5, 5, 6, + 6, 6, 6, 4, 4, 3, +}; +const short ssl_expr_yylen[] = { 2, + 1, 1, 1, 2, 3, 3, 1, 3, 3, 3, + 3, 3, 3, 3, 5, 3, 3, 1, 3, 1, + 1, 4, 1, 1, 1, 4, +}; +const short ssl_expr_yydefred[] = { 0, + 2, 3, 20, 21, 0, 0, 0, 0, 0, 0, + 7, 23, 0, 0, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, + 0, 0, 6, 9, 10, 11, 12, 13, 14, 24, + 25, 16, 17, 0, 26, 22, 0, 18, 15, 0, + 19, +}; +const short ssl_expr_yydgoto[] = { 9, + 10, 11, 12, 42, 47, 13, +}; +const short ssl_expr_yysindex[] = { -37, + 0, 0, 0, 0, -35, -37, -37, -99, 0, -247, + 0, 0, -250, -229, 0, -39, -227, -37, -37, -33, + -33, -33, -33, -33, -33, -233, -233, -89, -6, 0, + -87, -239, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -33, 0, 0, -38, 0, 0, -33, + 0, +}; +const short ssl_expr_yyrindex[] = { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; +const short ssl_expr_yygindex[] = { 0, + 7, 0, 0, 13, 0, -13, +}; +#define YYTABLESIZE 275 +const short ssl_expr_yytable[] = { 8, + 5, 30, 7, 8, 14, 50, 34, 35, 36, 37, + 38, 39, 15, 16, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 17, 32, 33, 18, 19, 40, 41, + 48, 29, 31, 44, 45, 19, 51, 46, 1, 43, + 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 2, 3, 0, 4, 0, 3, 5, 4, 0, 0, + 5, 0, 0, 0, 18, 19, 0, 0, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, +}; +const short ssl_expr_yycheck[] = { 37, + 0, 41, 40, 37, 40, 44, 20, 21, 22, 23, + 24, 25, 6, 7, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 123, 18, 19, 274, 275, 262, 263, + 44, 261, 260, 123, 41, 275, 50, 125, 0, 27, + -1, 41, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 125, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 257, + 258, 259, -1, 261, -1, 259, 264, 261, -1, -1, + 264, -1, -1, -1, 274, 275, -1, -1, 276, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 274, +}; +#define YYFINAL 9 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 276 +#if YYDEBUG +const char * const ssl_expr_yyname[] = { +"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,"'%'",0,0,"'('","')'",0,0,"','",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'{'",0,"'}'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"T_TRUE", +"T_FALSE","T_DIGIT","T_ID","T_STRING","T_REGEX","T_REGEX_I","T_FUNC_FILE", +"T_OP_EQ","T_OP_NE","T_OP_LT","T_OP_LE","T_OP_GT","T_OP_GE","T_OP_REG", +"T_OP_NRE","T_OP_IN","T_OP_OR","T_OP_AND","T_OP_NOT", +}; +const char * const ssl_expr_yyrule[] = { +"$accept : root", +"root : expr", +"expr : T_TRUE", +"expr : T_FALSE", +"expr : T_OP_NOT expr", +"expr : expr T_OP_OR expr", +"expr : expr T_OP_AND expr", +"expr : comparison", +"expr : '(' expr ')'", +"comparison : word T_OP_EQ word", +"comparison : word T_OP_NE word", +"comparison : word T_OP_LT word", +"comparison : word T_OP_LE word", +"comparison : word T_OP_GT word", +"comparison : word T_OP_GE word", +"comparison : word T_OP_IN '{' words '}'", +"comparison : word T_OP_REG regex", +"comparison : word T_OP_NRE regex", +"words : word", +"words : words ',' word", +"word : T_DIGIT", +"word : T_STRING", +"word : '%' '{' T_ID '}'", +"word : funccall", +"regex : T_REGEX", +"regex : T_REGEX_I", +"funccall : T_FUNC_FILE '(' T_STRING ')'", +}; +#endif +#if YYDEBUG +#include <stdio.h> +#endif +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 10000 +#define YYMAXDEPTH 10000 +#endif +#endif +#define YYINITSTACKSIZE 200 +int ssl_expr_yydebug; +int ssl_expr_yynerrs; +int ssl_expr_yyerrflag; +int ssl_expr_yychar; +short *ssl_expr_yyssp; +YYSTYPE *ssl_expr_yyvsp; +YYSTYPE ssl_expr_yyval; +YYSTYPE ssl_expr_yylval; +short *ssl_expr_yyss; +short *ssl_expr_yysslim; +YYSTYPE *ssl_expr_yyvs; +int ssl_expr_yystacksize; +#line 180 "ssl_expr_parse.y" + +int ssl_expr_yyerror(char *s) +{ + ssl_expr_error = s; + return 2; +} + +#line 230 "y.tab.c" +/* allocate initial stack or double stack size, up to YYMAXDEPTH */ +static int ssl_expr_yygrowstack() +{ + int newsize, i; + short *newss; + YYSTYPE *newvs; + + if ((newsize = ssl_expr_yystacksize) == 0) + newsize = YYINITSTACKSIZE; + else if (newsize >= YYMAXDEPTH) + return -1; + else if ((newsize *= 2) > YYMAXDEPTH) + newsize = YYMAXDEPTH; + i = ssl_expr_yyssp - ssl_expr_yyss; + newss = ssl_expr_yyss ? (short *)realloc(ssl_expr_yyss, newsize * sizeof *newss) : + (short *)malloc(newsize * sizeof *newss); + if (newss == NULL) + return -1; + ssl_expr_yyss = newss; + ssl_expr_yyssp = newss + i; + newvs = ssl_expr_yyvs ? (YYSTYPE *)realloc(ssl_expr_yyvs, newsize * sizeof *newvs) : + (YYSTYPE *)malloc(newsize * sizeof *newvs); + if (newvs == NULL) + return -1; + ssl_expr_yyvs = newvs; + ssl_expr_yyvsp = newvs + i; + ssl_expr_yystacksize = newsize; + ssl_expr_yysslim = ssl_expr_yyss + newsize - 1; + return 0; +} + +#define YYABORT goto ssl_expr_yyabort +#define YYREJECT goto ssl_expr_yyabort +#define YYACCEPT goto ssl_expr_yyaccept +#define YYERROR goto ssl_expr_yyerrlab + +#ifndef YYPARSE_PARAM +#if defined(__cplusplus) || __STDC__ +#define YYPARSE_PARAM_ARG void +#define YYPARSE_PARAM_DECL +#else /* ! ANSI-C/C++ */ +#define YYPARSE_PARAM_ARG +#define YYPARSE_PARAM_DECL +#endif /* ANSI-C/C++ */ +#else /* YYPARSE_PARAM */ +#ifndef YYPARSE_PARAM_TYPE +#define YYPARSE_PARAM_TYPE void * +#endif +#if defined(__cplusplus) || __STDC__ +#define YYPARSE_PARAM_ARG YYPARSE_PARAM_TYPE YYPARSE_PARAM +#define YYPARSE_PARAM_DECL +#else /* ! ANSI-C/C++ */ +#define YYPARSE_PARAM_ARG YYPARSE_PARAM +#define YYPARSE_PARAM_DECL YYPARSE_PARAM_TYPE YYPARSE_PARAM; +#endif /* ANSI-C/C++ */ +#endif /* ! YYPARSE_PARAM */ + +int +ssl_expr_yyparse (YYPARSE_PARAM_ARG) + YYPARSE_PARAM_DECL +{ + register int ssl_expr_yym, ssl_expr_yyn, ssl_expr_yystate; +#if YYDEBUG + register const char *ssl_expr_yys; + + if ((ssl_expr_yys = getenv("YYDEBUG"))) + { + ssl_expr_yyn = *ssl_expr_yys; + if (ssl_expr_yyn >= '0' && ssl_expr_yyn <= '9') + ssl_expr_yydebug = ssl_expr_yyn - '0'; + } +#endif + + ssl_expr_yynerrs = 0; + ssl_expr_yyerrflag = 0; + ssl_expr_yychar = (-1); + + if (ssl_expr_yyss == NULL && ssl_expr_yygrowstack()) goto ssl_expr_yyoverflow; + ssl_expr_yyssp = ssl_expr_yyss; + ssl_expr_yyvsp = ssl_expr_yyvs; + *ssl_expr_yyssp = ssl_expr_yystate = 0; + +ssl_expr_yyloop: + if ((ssl_expr_yyn = ssl_expr_yydefred[ssl_expr_yystate])) goto ssl_expr_yyreduce; + if (ssl_expr_yychar < 0) + { + if ((ssl_expr_yychar = ssl_expr_yylex()) < 0) ssl_expr_yychar = 0; +#if YYDEBUG + if (ssl_expr_yydebug) + { + ssl_expr_yys = 0; + if (ssl_expr_yychar <= YYMAXTOKEN) ssl_expr_yys = ssl_expr_yyname[ssl_expr_yychar]; + if (!ssl_expr_yys) ssl_expr_yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, ssl_expr_yystate, ssl_expr_yychar, ssl_expr_yys); + } +#endif + } + if ((ssl_expr_yyn = ssl_expr_yysindex[ssl_expr_yystate]) && (ssl_expr_yyn += ssl_expr_yychar) >= 0 && + ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == ssl_expr_yychar) + { +#if YYDEBUG + if (ssl_expr_yydebug) + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, ssl_expr_yystate, ssl_expr_yytable[ssl_expr_yyn]); +#endif + if (ssl_expr_yyssp >= ssl_expr_yysslim && ssl_expr_yygrowstack()) + { + goto ssl_expr_yyoverflow; + } + *++ssl_expr_yyssp = ssl_expr_yystate = ssl_expr_yytable[ssl_expr_yyn]; + *++ssl_expr_yyvsp = ssl_expr_yylval; + ssl_expr_yychar = (-1); + if (ssl_expr_yyerrflag > 0) --ssl_expr_yyerrflag; + goto ssl_expr_yyloop; + } + if ((ssl_expr_yyn = ssl_expr_yyrindex[ssl_expr_yystate]) && (ssl_expr_yyn += ssl_expr_yychar) >= 0 && + ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == ssl_expr_yychar) + { + ssl_expr_yyn = ssl_expr_yytable[ssl_expr_yyn]; + goto ssl_expr_yyreduce; + } + if (ssl_expr_yyerrflag) goto ssl_expr_yyinrecovery; +#if defined(lint) || defined(__GNUC__) + goto ssl_expr_yynewerror; +#endif +ssl_expr_yynewerror: + ssl_expr_yyerror("syntax error"); +#if defined(lint) || defined(__GNUC__) + goto ssl_expr_yyerrlab; +#endif +ssl_expr_yyerrlab: + ++ssl_expr_yynerrs; +ssl_expr_yyinrecovery: + if (ssl_expr_yyerrflag < 3) + { + ssl_expr_yyerrflag = 3; + for (;;) + { + if ((ssl_expr_yyn = ssl_expr_yysindex[*ssl_expr_yyssp]) && (ssl_expr_yyn += YYERRCODE) >= 0 && + ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == YYERRCODE) + { +#if YYDEBUG + if (ssl_expr_yydebug) + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *ssl_expr_yyssp, ssl_expr_yytable[ssl_expr_yyn]); +#endif + if (ssl_expr_yyssp >= ssl_expr_yysslim && ssl_expr_yygrowstack()) + { + goto ssl_expr_yyoverflow; + } + *++ssl_expr_yyssp = ssl_expr_yystate = ssl_expr_yytable[ssl_expr_yyn]; + *++ssl_expr_yyvsp = ssl_expr_yylval; + goto ssl_expr_yyloop; + } + else + { +#if YYDEBUG + if (ssl_expr_yydebug) + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *ssl_expr_yyssp); +#endif + if (ssl_expr_yyssp <= ssl_expr_yyss) goto ssl_expr_yyabort; + --ssl_expr_yyssp; + --ssl_expr_yyvsp; + } + } + } + else + { + if (ssl_expr_yychar == 0) goto ssl_expr_yyabort; +#if YYDEBUG + if (ssl_expr_yydebug) + { + ssl_expr_yys = 0; + if (ssl_expr_yychar <= YYMAXTOKEN) ssl_expr_yys = ssl_expr_yyname[ssl_expr_yychar]; + if (!ssl_expr_yys) ssl_expr_yys = "illegal-symbol"; + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, ssl_expr_yystate, ssl_expr_yychar, ssl_expr_yys); + } +#endif + ssl_expr_yychar = (-1); + goto ssl_expr_yyloop; + } +ssl_expr_yyreduce: +#if YYDEBUG + if (ssl_expr_yydebug) + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, ssl_expr_yystate, ssl_expr_yyn, ssl_expr_yyrule[ssl_expr_yyn]); +#endif + ssl_expr_yym = ssl_expr_yylen[ssl_expr_yyn]; + ssl_expr_yyval = ssl_expr_yyvsp[1-ssl_expr_yym]; + switch (ssl_expr_yyn) + { +case 1: +#line 118 "ssl_expr_parse.y" +{ ssl_expr_info.expr = ssl_expr_yyvsp[0].exVal; } +break; +case 2: +#line 121 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_True, NULL, NULL); } +break; +case 3: +#line 122 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_False, NULL, NULL); } +break; +case 4: +#line 123 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_Not, ssl_expr_yyvsp[0].exVal, NULL); } +break; +case 5: +#line 124 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_Or, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); } +break; +case 6: +#line 125 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_And, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); } +break; +case 7: +#line 126 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_Comp, ssl_expr_yyvsp[0].exVal, NULL); } +break; +case 8: +#line 127 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_yyvsp[-1].exVal; } +break; +case 9: +#line 130 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_EQ, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); } +break; +case 10: +#line 131 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_NE, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); } +break; +case 11: +#line 132 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_LT, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); } +break; +case 12: +#line 133 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_LE, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); } +break; +case 13: +#line 134 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_GT, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); } +break; +case 14: +#line 135 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_GE, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); } +break; +case 15: +#line 136 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_IN, ssl_expr_yyvsp[-4].exVal, ssl_expr_yyvsp[-1].exVal); } +break; +case 16: +#line 137 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_REG, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); } +break; +case 17: +#line 138 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_NRE, ssl_expr_yyvsp[-2].exVal, ssl_expr_yyvsp[0].exVal); } +break; +case 18: +#line 141 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_ListElement, ssl_expr_yyvsp[0].exVal, NULL); } +break; +case 19: +#line 142 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_ListElement, ssl_expr_yyvsp[0].exVal, ssl_expr_yyvsp[-2].exVal); } +break; +case 20: +#line 145 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_Digit, ssl_expr_yyvsp[0].cpVal, NULL); } +break; +case 21: +#line 146 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_String, ssl_expr_yyvsp[0].cpVal, NULL); } +break; +case 22: +#line 147 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_make(op_Var, ssl_expr_yyvsp[-1].cpVal, NULL); } +break; +case 23: +#line 148 "ssl_expr_parse.y" +{ ssl_expr_yyval.exVal = ssl_expr_yyvsp[0].exVal; } +break; +case 24: +#line 151 "ssl_expr_parse.y" +{ + regex_t *regex; + if ((regex = ap_pregcomp(ssl_expr_info.pool, ssl_expr_yyvsp[0].cpVal, + REG_EXTENDED|REG_NOSUB)) == NULL) { + ssl_expr_error = "Failed to compile regular expression"; + YYERROR; + regex = NULL; + } + ssl_expr_yyval.exVal = ssl_expr_make(op_Regex, regex, NULL); + } +break; +case 25: +#line 161 "ssl_expr_parse.y" +{ + regex_t *regex; + if ((regex = ap_pregcomp(ssl_expr_info.pool, ssl_expr_yyvsp[0].cpVal, + REG_EXTENDED|REG_NOSUB|REG_ICASE)) == NULL) { + ssl_expr_error = "Failed to compile regular expression"; + YYERROR; + regex = NULL; + } + ssl_expr_yyval.exVal = ssl_expr_make(op_Regex, regex, NULL); + } +break; +case 26: +#line 173 "ssl_expr_parse.y" +{ + ssl_expr *args = ssl_expr_make(op_ListElement, ssl_expr_yyvsp[-1].cpVal, NULL); + ssl_expr_yyval.exVal = ssl_expr_make(op_Func, "file", args); + } +break; +#line 550 "y.tab.c" + } + ssl_expr_yyssp -= ssl_expr_yym; + ssl_expr_yystate = *ssl_expr_yyssp; + ssl_expr_yyvsp -= ssl_expr_yym; + ssl_expr_yym = ssl_expr_yylhs[ssl_expr_yyn]; + if (ssl_expr_yystate == 0 && ssl_expr_yym == 0) + { +#if YYDEBUG + if (ssl_expr_yydebug) + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); +#endif + ssl_expr_yystate = YYFINAL; + *++ssl_expr_yyssp = YYFINAL; + *++ssl_expr_yyvsp = ssl_expr_yyval; + if (ssl_expr_yychar < 0) + { + if ((ssl_expr_yychar = ssl_expr_yylex()) < 0) ssl_expr_yychar = 0; +#if YYDEBUG + if (ssl_expr_yydebug) + { + ssl_expr_yys = 0; + if (ssl_expr_yychar <= YYMAXTOKEN) ssl_expr_yys = ssl_expr_yyname[ssl_expr_yychar]; + if (!ssl_expr_yys) ssl_expr_yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, ssl_expr_yychar, ssl_expr_yys); + } +#endif + } + if (ssl_expr_yychar == 0) goto ssl_expr_yyaccept; + goto ssl_expr_yyloop; + } + if ((ssl_expr_yyn = ssl_expr_yygindex[ssl_expr_yym]) && (ssl_expr_yyn += ssl_expr_yystate) >= 0 && + ssl_expr_yyn <= YYTABLESIZE && ssl_expr_yycheck[ssl_expr_yyn] == ssl_expr_yystate) + ssl_expr_yystate = ssl_expr_yytable[ssl_expr_yyn]; + else + ssl_expr_yystate = ssl_expr_yydgoto[ssl_expr_yym]; +#if YYDEBUG + if (ssl_expr_yydebug) + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *ssl_expr_yyssp, ssl_expr_yystate); +#endif + if (ssl_expr_yyssp >= ssl_expr_yysslim && ssl_expr_yygrowstack()) + { + goto ssl_expr_yyoverflow; + } + *++ssl_expr_yyssp = ssl_expr_yystate; + *++ssl_expr_yyvsp = ssl_expr_yyval; + goto ssl_expr_yyloop; +ssl_expr_yyoverflow: + ssl_expr_yyerror("yacc stack overflow"); +ssl_expr_yyabort: + return (1); +ssl_expr_yyaccept: + return (0); +} diff --git a/modules/ssl/ssl_expr_parse.h b/modules/ssl/ssl_expr_parse.h new file mode 100644 index 0000000000..618cacbe3b --- /dev/null +++ b/modules/ssl/ssl_expr_parse.h @@ -0,0 +1,29 @@ +#ifndef YYERRCODE +#define YYERRCODE 256 +#endif + +#define T_TRUE 257 +#define T_FALSE 258 +#define T_DIGIT 259 +#define T_ID 260 +#define T_STRING 261 +#define T_REGEX 262 +#define T_REGEX_I 263 +#define T_FUNC_FILE 264 +#define T_OP_EQ 265 +#define T_OP_NE 266 +#define T_OP_LT 267 +#define T_OP_LE 268 +#define T_OP_GT 269 +#define T_OP_GE 270 +#define T_OP_REG 271 +#define T_OP_NRE 272 +#define T_OP_IN 273 +#define T_OP_OR 274 +#define T_OP_AND 275 +#define T_OP_NOT 276 +typedef union { + char *cpVal; + ssl_expr *exVal; +} YYSTYPE; +extern YYSTYPE ssl_expr_yylval; diff --git a/modules/ssl/ssl_expr_parse.y b/modules/ssl/ssl_expr_parse.y new file mode 100644 index 0000000000..1e3ad6e513 --- /dev/null +++ b/modules/ssl/ssl_expr_parse.y @@ -0,0 +1,186 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | +** | '_ ` _ \ / _ \ / _` | / __/ __| | +** | | | | | | (_) | (_| | \__ \__ \ | mod_ssl - Apache Interface to OpenSSL +** |_| |_| |_|\___/ \__,_|___|___/___/_| http://www.modssl.org/ +** |_____| +** ssl_expr_parse.y +** Expression LR(1) Parser +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + + /* ``What you see is all you get.'' + -- Brian Kernighan */ + +/* _________________________________________________________________ +** +** Expression Parser +** _________________________________________________________________ +*/ + +%{ +#include "mod_ssl.h" +%} + +%union { + char *cpVal; + ssl_expr *exVal; +} + +%token T_TRUE +%token T_FALSE + +%token <cpVal> T_DIGIT +%token <cpVal> T_ID +%token <cpVal> T_STRING +%token <cpVal> T_REGEX +%token <cpVal> T_REGEX_I + +%token T_FUNC_FILE + +%token T_OP_EQ +%token T_OP_NE +%token T_OP_LT +%token T_OP_LE +%token T_OP_GT +%token T_OP_GE +%token T_OP_REG +%token T_OP_NRE +%token T_OP_IN + +%token T_OP_OR +%token T_OP_AND +%token T_OP_NOT + +%left T_OP_OR +%left T_OP_AND +%left T_OP_NOT + +%type <exVal> expr +%type <exVal> comparison +%type <exVal> funccall +%type <exVal> regex +%type <exVal> words +%type <exVal> word + +%% + +root : expr { ssl_expr_info.expr = $1; } + ; + +expr : T_TRUE { $$ = ssl_expr_make(op_True, NULL, NULL); } + | T_FALSE { $$ = ssl_expr_make(op_False, NULL, NULL); } + | T_OP_NOT expr { $$ = ssl_expr_make(op_Not, $2, NULL); } + | expr T_OP_OR expr { $$ = ssl_expr_make(op_Or, $1, $3); } + | expr T_OP_AND expr { $$ = ssl_expr_make(op_And, $1, $3); } + | comparison { $$ = ssl_expr_make(op_Comp, $1, NULL); } + | '(' expr ')' { $$ = $2; } + ; + +comparison: word T_OP_EQ word { $$ = ssl_expr_make(op_EQ, $1, $3); } + | word T_OP_NE word { $$ = ssl_expr_make(op_NE, $1, $3); } + | word T_OP_LT word { $$ = ssl_expr_make(op_LT, $1, $3); } + | word T_OP_LE word { $$ = ssl_expr_make(op_LE, $1, $3); } + | word T_OP_GT word { $$ = ssl_expr_make(op_GT, $1, $3); } + | word T_OP_GE word { $$ = ssl_expr_make(op_GE, $1, $3); } + | word T_OP_IN '{' words '}' { $$ = ssl_expr_make(op_IN, $1, $4); } + | word T_OP_REG regex { $$ = ssl_expr_make(op_REG, $1, $3); } + | word T_OP_NRE regex { $$ = ssl_expr_make(op_NRE, $1, $3); } + ; + +words : word { $$ = ssl_expr_make(op_ListElement, $1, NULL); } + | words ',' word { $$ = ssl_expr_make(op_ListElement, $3, $1); } + ; + +word : T_DIGIT { $$ = ssl_expr_make(op_Digit, $1, NULL); } + | T_STRING { $$ = ssl_expr_make(op_String, $1, NULL); } + | '%' '{' T_ID '}' { $$ = ssl_expr_make(op_Var, $3, NULL); } + | funccall { $$ = $1; } + ; + +regex : T_REGEX { + regex_t *regex; + if ((regex = ap_pregcomp(ssl_expr_info.pool, $1, + REG_EXTENDED|REG_NOSUB)) == NULL) { + ssl_expr_error = "Failed to compile regular expression"; + YYERROR; + regex = NULL; + } + $$ = ssl_expr_make(op_Regex, regex, NULL); + } + | T_REGEX_I { + regex_t *regex; + if ((regex = ap_pregcomp(ssl_expr_info.pool, $1, + REG_EXTENDED|REG_NOSUB|REG_ICASE)) == NULL) { + ssl_expr_error = "Failed to compile regular expression"; + YYERROR; + regex = NULL; + } + $$ = ssl_expr_make(op_Regex, regex, NULL); + } + ; + +funccall : T_FUNC_FILE '(' T_STRING ')' { + ssl_expr *args = ssl_expr_make(op_ListElement, $3, NULL); + $$ = ssl_expr_make(op_Func, "file", args); + } + ; + +%% + +int yyerror(char *s) +{ + ssl_expr_error = s; + return 2; +} + diff --git a/modules/ssl/ssl_expr_scan.c b/modules/ssl/ssl_expr_scan.c new file mode 100644 index 0000000000..f3e9f9d1f5 --- /dev/null +++ b/modules/ssl/ssl_expr_scan.c @@ -0,0 +1,2002 @@ +#define yy_create_buffer ssl_expr_yy_create_buffer +#define yy_delete_buffer ssl_expr_yy_delete_buffer +#define yy_scan_buffer ssl_expr_yy_scan_buffer +#define yy_scan_string ssl_expr_yy_scan_string +#define yy_scan_bytes ssl_expr_yy_scan_bytes +#define yy_flex_debug ssl_expr_yy_flex_debug +#define yy_init_buffer ssl_expr_yy_init_buffer +#define yy_flush_buffer ssl_expr_yy_flush_buffer +#define yy_load_buffer_state ssl_expr_yy_load_buffer_state +#define yy_switch_to_buffer ssl_expr_yy_switch_to_buffer +#define yyin ssl_expr_yyin +#define yyleng ssl_expr_yyleng +#define yylex ssl_expr_yylex +#define yyout ssl_expr_yyout +#define yyrestart ssl_expr_yyrestart +#define yytext ssl_expr_yytext + +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include <stdio.h> + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include <stdlib.h> +#include <unistd.h> + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include <io.h> +#include <stdlib.h> +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart YY_PROTO(( FILE *input_file )); + +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); +void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); +#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) + +YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); +YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str )); +YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); + +static void *yy_flex_alloc YY_PROTO(( yy_size_t )); +static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); +static void yy_flex_free YY_PROTO(( void * )); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + + +#define yywrap() 1 +#define YY_SKIP_YYWRAP +typedef unsigned char YY_CHAR; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +typedef int yy_state_type; +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 46 +#define YY_END_OF_BUFFER 47 +static yyconst short int yy_accept[86] = + { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 47, 45, + 1, 38, 2, 45, 43, 24, 45, 28, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, + 13, 4, 3, 14, 16, 18, 17, 1, 22, 32, + 34, 43, 26, 20, 31, 30, 44, 44, 19, 44, + 44, 29, 27, 39, 25, 23, 15, 15, 21, 44, + 35, 44, 36, 13, 12, 5, 6, 10, 11, 7, + 8, 9, 33, 44, 44, 37, 44, 5, 6, 44, + 40, 41, 5, 42, 0 + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 1, 1, 1, 6, 1, 1, + 1, 1, 1, 1, 7, 1, 1, 8, 8, 8, + 8, 8, 8, 8, 8, 9, 9, 7, 1, 10, + 11, 12, 1, 1, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 1, 14, 1, 1, 7, 1, 15, 16, 13, 17, + + 18, 19, 20, 13, 21, 13, 13, 22, 23, 24, + 25, 13, 26, 27, 28, 29, 30, 13, 13, 13, + 13, 13, 1, 31, 1, 32, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[33] = + { 0, + 1, 1, 2, 1, 3, 1, 4, 4, 4, 1, + 1, 1, 4, 3, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 1, 1 + } ; + +static yyconst short int yy_base[93] = + { 0, + 0, 0, 30, 31, 0, 0, 82, 81, 101, 142, + 35, 28, 142, 94, 32, 88, 31, 87, 0, 69, + 66, 28, 28, 67, 29, 63, 30, 63, 62, 57, + 0, 142, 142, 88, 142, 142, 142, 48, 142, 142, + 142, 44, 142, 142, 142, 142, 0, 70, 0, 64, + 63, 0, 0, 0, 0, 0, 142, 0, 0, 55, + 0, 46, 142, 0, 142, 53, 62, 142, 142, 142, + 142, 142, 0, 44, 48, 0, 41, 70, 72, 38, + 0, 0, 74, 0, 142, 117, 121, 125, 50, 129, + 133, 137 + + } ; + +static yyconst short int yy_def[93] = + { 0, + 85, 1, 86, 86, 87, 87, 88, 88, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 89, 89, + 89, 89, 89, 89, 89, 90, 89, 89, 89, 85, + 91, 85, 85, 92, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 85, 89, 89, 89, + 89, 89, 85, 91, 85, 85, 85, 85, 85, 85, + 85, 85, 89, 89, 89, 89, 89, 85, 85, 89, + 89, 89, 85, 89, 0, 85, 85, 85, 85, 85, + 85, 85 + + } ; + +static yyconst short int yy_nxt[175] = + { 0, + 10, 11, 11, 12, 13, 14, 10, 15, 15, 16, + 17, 18, 19, 10, 20, 19, 19, 21, 22, 23, + 24, 25, 26, 27, 28, 19, 19, 19, 29, 19, + 30, 10, 32, 32, 33, 33, 38, 38, 39, 42, + 42, 44, 50, 34, 34, 52, 55, 59, 51, 38, + 38, 42, 42, 47, 60, 84, 53, 56, 82, 40, + 78, 79, 45, 57, 57, 81, 57, 57, 57, 79, + 79, 80, 57, 57, 57, 77, 57, 83, 79, 79, + 79, 79, 79, 76, 75, 74, 73, 63, 62, 61, + 54, 49, 48, 57, 57, 66, 67, 46, 43, 41, + + 85, 37, 37, 68, 85, 85, 69, 85, 85, 85, + 85, 70, 85, 85, 71, 85, 72, 31, 31, 31, + 31, 35, 35, 35, 35, 36, 36, 36, 36, 58, + 85, 58, 58, 64, 85, 85, 64, 65, 65, 65, + 65, 9, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85 + } ; + +static yyconst short int yy_chk[175] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 4, 3, 4, 11, 11, 12, 15, + 15, 17, 22, 3, 4, 23, 25, 27, 22, 38, + 38, 42, 42, 89, 27, 80, 23, 25, 77, 12, + 66, 66, 17, 26, 26, 75, 26, 26, 26, 67, + 67, 74, 26, 26, 26, 62, 26, 78, 78, 79, + 79, 83, 83, 60, 51, 50, 48, 30, 29, 28, + 24, 21, 20, 26, 26, 34, 34, 18, 16, 14, + + 9, 8, 7, 34, 0, 0, 34, 0, 0, 0, + 0, 34, 0, 0, 34, 0, 34, 86, 86, 86, + 86, 87, 87, 87, 87, 88, 88, 88, 88, 90, + 0, 90, 90, 91, 0, 0, 91, 92, 92, 92, + 92, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 85, 85, 85 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "ssl_expr_scan.l" +#define INITIAL 0 +/* _ _ +** _ __ ___ ___ __| | ___ ___| | +** | '_ ` _ \ / _ \ / _` | / __/ __| | +** | | | | | | (_) | (_| | \__ \__ \ | mod_ssl - Apache Interface to OpenSSL +** |_| |_| |_|\___/ \__,_|___|___/___/_| http://www.modssl.org/ +** |_____| +** ssl_expr_scan.l +** Expression Scanner +*/ +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ +/* ``Killing for peace is +like fucking for virginity.'' +-- Unknown */ +/* _________________________________________________________________ +** +** Expression Scanner +** _________________________________________________________________ +*/ +#line 73 "ssl_expr_scan.l" +#include "mod_ssl.h" + +#include "ssl_expr_parse.h" + +#define YY_NO_UNPUT 1 +int yyinput(char *buf, int max_size); + +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + (result = yyinput(buf, max_size)) + +#define MAX_STR_LEN 2048 +/* %option stack */ +#define YY_NEVER_INTERACTIVE 1 +#define str 1 + +#define regex 2 +#define regex_flags 3 + +#line 537 "lex.ssl_expr_yy.c" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap YY_PROTO(( void )); +#else +extern int yywrap YY_PROTO(( void )); +#endif +#endif + +#ifndef YY_NO_UNPUT +static void yyunput YY_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen YY_PROTO(( yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif +#endif + +#if YY_STACK_USED +static int yy_start_stack_ptr = 0; +static int yy_start_stack_depth = 0; +static int *yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include <stdlib.h> +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \ + && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +YY_DECL + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 94 "ssl_expr_scan.l" + + + char caStr[MAX_STR_LEN]; + char *cpStr = NULL; + char caRegex[MAX_STR_LEN]; + char *cpRegex = NULL; + char cRegexDel = NUL; + + /* + * Whitespaces + */ +#line 700 "lex.ssl_expr_yy.c" + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 86 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_current_state != 85 ); + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 105 "ssl_expr_scan.l" +{ + /* NOP */ +} + YY_BREAK +/* + * C-style strings ("...") + */ +case 2: +YY_RULE_SETUP +#line 112 "ssl_expr_scan.l" +{ + cpStr = caStr; + BEGIN(str); +} + YY_BREAK +case 3: +YY_RULE_SETUP +#line 116 "ssl_expr_scan.l" +{ + BEGIN(INITIAL); + *cpStr = NUL; + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caStr); + return T_STRING; +} + YY_BREAK +case 4: +YY_RULE_SETUP +#line 122 "ssl_expr_scan.l" +{ + yyerror("Unterminated string"); +} + YY_BREAK +case 5: +YY_RULE_SETUP +#line 125 "ssl_expr_scan.l" +{ + int result; + + (void)sscanf(yytext+1, "%o", &result); + if (result > 0xff) + yyerror("Escape sequence out of bound"); + else + *cpStr++ = result; +} + YY_BREAK +case 6: +YY_RULE_SETUP +#line 134 "ssl_expr_scan.l" +{ + yyerror("Bad escape sequence"); +} + YY_BREAK +case 7: +YY_RULE_SETUP +#line 137 "ssl_expr_scan.l" +{ *cpStr++ = '\n'; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 138 "ssl_expr_scan.l" +{ *cpStr++ = '\r'; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 139 "ssl_expr_scan.l" +{ *cpStr++ = '\t'; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 140 "ssl_expr_scan.l" +{ *cpStr++ = '\b'; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 141 "ssl_expr_scan.l" +{ *cpStr++ = '\f'; } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 142 "ssl_expr_scan.l" +{ + *cpStr++ = yytext[1]; +} + YY_BREAK +case 13: +YY_RULE_SETUP +#line 145 "ssl_expr_scan.l" +{ + char *cp = yytext; + while (*cp != NUL) + *cpStr++ = *cp++; +} + YY_BREAK +case 14: +YY_RULE_SETUP +#line 150 "ssl_expr_scan.l" +{ + *cpStr++ = yytext[1]; +} + YY_BREAK +/* + * Regular Expression + */ +case 15: +YY_RULE_SETUP +#line 157 "ssl_expr_scan.l" +{ + cRegexDel = yytext[1]; + cpRegex = caRegex; + BEGIN(regex); +} + YY_BREAK +case 16: +YY_RULE_SETUP +#line 162 "ssl_expr_scan.l" +{ + if (yytext[0] == cRegexDel) { + *cpRegex = NUL; + BEGIN(regex_flags); + } + else { + *cpRegex++ = yytext[0]; + } +} + YY_BREAK +case 17: +YY_RULE_SETUP +#line 171 "ssl_expr_scan.l" +{ + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex); + BEGIN(INITIAL); + return T_REGEX_I; +} + YY_BREAK +case 18: +YY_RULE_SETUP +#line 176 "ssl_expr_scan.l" +{ + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex); + yyless(0); + BEGIN(INITIAL); + return T_REGEX; +} + YY_BREAK +case YY_STATE_EOF(regex_flags): +#line 182 "ssl_expr_scan.l" +{ + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex); + BEGIN(INITIAL); + return T_REGEX; +} + YY_BREAK +/* + * Operators + */ +case 19: +YY_RULE_SETUP +#line 191 "ssl_expr_scan.l" +{ return T_OP_EQ; } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 192 "ssl_expr_scan.l" +{ return T_OP_EQ; } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 193 "ssl_expr_scan.l" +{ return T_OP_NE; } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 194 "ssl_expr_scan.l" +{ return T_OP_NE; } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 195 "ssl_expr_scan.l" +{ return T_OP_LT; } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 196 "ssl_expr_scan.l" +{ return T_OP_LT; } + YY_BREAK +case 25: +YY_RULE_SETUP +#line 197 "ssl_expr_scan.l" +{ return T_OP_LE; } + YY_BREAK +case 26: +YY_RULE_SETUP +#line 198 "ssl_expr_scan.l" +{ return T_OP_LE; } + YY_BREAK +case 27: +YY_RULE_SETUP +#line 199 "ssl_expr_scan.l" +{ return T_OP_GT; } + YY_BREAK +case 28: +YY_RULE_SETUP +#line 200 "ssl_expr_scan.l" +{ return T_OP_GT; } + YY_BREAK +case 29: +YY_RULE_SETUP +#line 201 "ssl_expr_scan.l" +{ return T_OP_GE; } + YY_BREAK +case 30: +YY_RULE_SETUP +#line 202 "ssl_expr_scan.l" +{ return T_OP_GE; } + YY_BREAK +case 31: +YY_RULE_SETUP +#line 203 "ssl_expr_scan.l" +{ return T_OP_REG; } + YY_BREAK +case 32: +YY_RULE_SETUP +#line 204 "ssl_expr_scan.l" +{ return T_OP_NRE; } + YY_BREAK +case 33: +YY_RULE_SETUP +#line 205 "ssl_expr_scan.l" +{ return T_OP_AND; } + YY_BREAK +case 34: +YY_RULE_SETUP +#line 206 "ssl_expr_scan.l" +{ return T_OP_AND; } + YY_BREAK +case 35: +YY_RULE_SETUP +#line 207 "ssl_expr_scan.l" +{ return T_OP_OR; } + YY_BREAK +case 36: +YY_RULE_SETUP +#line 208 "ssl_expr_scan.l" +{ return T_OP_OR; } + YY_BREAK +case 37: +YY_RULE_SETUP +#line 209 "ssl_expr_scan.l" +{ return T_OP_NOT; } + YY_BREAK +case 38: +YY_RULE_SETUP +#line 210 "ssl_expr_scan.l" +{ return T_OP_NOT; } + YY_BREAK +case 39: +YY_RULE_SETUP +#line 211 "ssl_expr_scan.l" +{ return T_OP_IN; } + YY_BREAK +/* + * Functions + */ +case 40: +YY_RULE_SETUP +#line 216 "ssl_expr_scan.l" +{ return T_FUNC_FILE; } + YY_BREAK +/* + * Specials + */ +case 41: +YY_RULE_SETUP +#line 221 "ssl_expr_scan.l" +{ return T_TRUE; } + YY_BREAK +case 42: +YY_RULE_SETUP +#line 222 "ssl_expr_scan.l" +{ return T_FALSE; } + YY_BREAK +/* + * Digits + */ +case 43: +YY_RULE_SETUP +#line 227 "ssl_expr_scan.l" +{ + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext); + return T_DIGIT; +} + YY_BREAK +/* + * Identifiers + */ +case 44: +YY_RULE_SETUP +#line 235 "ssl_expr_scan.l" +{ + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext); + return T_ID; +} + YY_BREAK +/* + * Anything else is returned as is... + */ +case 45: +YY_RULE_SETUP +#line 243 "ssl_expr_scan.l" +{ + return yytext[0]; +} + YY_BREAK +case 46: +YY_RULE_SETUP +#line 247 "ssl_expr_scan.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 1100 "lex.ssl_expr_yy.c" +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(str): +case YY_STATE_EOF(regex): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_current_buffer->yy_n_chars = yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc( (void *) b->yy_ch_buf, + b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 86 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; +#endif + { + register int yy_is_jam; + register char *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 86 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 85); + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifndef YY_NO_UNPUT +#ifdef YY_USE_PROTOS +static void yyunput( int c, register char *yy_bp ) +#else +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; +#endif + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_current_buffer->yy_n_chars = + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yy_c_buf_p - yytext_ptr; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + return EOF; + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + + return c; + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (void *) b->yy_ch_buf ); + + yy_flex_free( (void *) b ); + } + + +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int isatty YY_PROTO(( int )); +#endif +#endif + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + +#if YY_ALWAYS_INTERACTIVE + b->yy_is_interactive = 1; +#else +#if YY_NEVER_INTERACTIVE + b->yy_is_interactive = 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef YY_USE_PROTOS +void yy_flush_buffer( YY_BUFFER_STATE b ) +#else +void yy_flush_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) +#else +YY_BUFFER_STATE yy_scan_buffer( base, size ) +char *base; +yy_size_t size; +#endif + { + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef YY_NO_SCAN_STRING +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str ) +#else +YY_BUFFER_STATE yy_scan_string( yy_str ) +yyconst char *yy_str; +#endif + { + int len; + for ( len = 0; yy_str[len]; ++len ) + ; + + return yy_scan_bytes( yy_str, len ); + } +#endif + + +#ifndef YY_NO_SCAN_BYTES +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) +#else +YY_BUFFER_STATE yy_scan_bytes( bytes, len ) +yyconst char *bytes; +int len; +#endif + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) yy_flex_alloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef YY_NO_PUSH_STATE +#ifdef YY_USE_PROTOS +static void yy_push_state( int new_state ) +#else +static void yy_push_state( new_state ) +int new_state; +#endif + { + if ( yy_start_stack_ptr >= yy_start_stack_depth ) + { + yy_size_t new_size; + + yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yy_start_stack_depth * sizeof( int ); + + if ( ! yy_start_stack ) + yy_start_stack = (int *) yy_flex_alloc( new_size ); + + else + yy_start_stack = (int *) yy_flex_realloc( + (void *) yy_start_stack, new_size ); + + if ( ! yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + yy_start_stack[yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +static void yy_pop_state() + { + if ( --yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yy_start_stack[yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +static int yy_top_state() + { + return yy_start_stack[yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +#ifdef YY_USE_PROTOS +static void yy_fatal_error( yyconst char msg[] ) +#else +static void yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +#ifdef YY_USE_PROTOS +static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) +#else +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int yy_flex_strlen( yyconst char *s ) +#else +static int yy_flex_strlen( s ) +yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *yy_flex_alloc( yy_size_t size ) +#else +static void *yy_flex_alloc( size ) +yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *yy_flex_realloc( void *ptr, yy_size_t size ) +#else +static void *yy_flex_realloc( ptr, size ) +void *ptr; +yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void yy_flex_free( void *ptr ) +#else +static void yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + yylex(); + return 0; + } +#endif +#line 247 "ssl_expr_scan.l" + + +int yyinput(char *buf, int max_size) +{ + int n; + + if ((n = MIN(max_size, ssl_expr_info.inputbuf + + ssl_expr_info.inputlen + - ssl_expr_info.inputptr)) <= 0) + return YY_NULL; + memcpy(buf, ssl_expr_info.inputptr, n); + ssl_expr_info.inputptr += n; + return n; +} + diff --git a/modules/ssl/ssl_expr_scan.l b/modules/ssl/ssl_expr_scan.l new file mode 100644 index 0000000000..a0db7cccde --- /dev/null +++ b/modules/ssl/ssl_expr_scan.l @@ -0,0 +1,261 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | +** | '_ ` _ \ / _ \ / _` | / __/ __| | +** | | | | | | (_) | (_| | \__ \__ \ | mod_ssl - Apache Interface to OpenSSL +** |_| |_| |_|\___/ \__,_|___|___/___/_| http://www.modssl.org/ +** |_____| +** ssl_expr_scan.l +** Expression Scanner +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + + /* ``Killing for peace is + like fucking for virginity.'' + -- Unknown */ + +/* _________________________________________________________________ +** +** Expression Scanner +** _________________________________________________________________ +*/ + +%{ +#include "mod_ssl.h" + +#include "ssl_expr_parse.h" + +#define YY_NO_UNPUT 1 +int yyinput(char *buf, int max_size); + +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + (result = yyinput(buf, max_size)) + +#define MAX_STR_LEN 2048 +%} + +%pointer +/* %option stack */ +%option never-interactive +%option noyywrap +%x str +%x regex regex_flags + +%% + + char caStr[MAX_STR_LEN]; + char *cpStr = NULL; + char caRegex[MAX_STR_LEN]; + char *cpRegex = NULL; + char cRegexDel = NUL; + + /* + * Whitespaces + */ +[ \t\n]+ { + /* NOP */ +} + + /* + * C-style strings ("...") + */ +\" { + cpStr = caStr; + BEGIN(str); +} +<str>\" { + BEGIN(INITIAL); + *cpStr = NUL; + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caStr); + return T_STRING; +} +<str>\n { + yyerror("Unterminated string"); +} +<str>\\[0-7]{1,3} { + int result; + + (void)sscanf(yytext+1, "%o", &result); + if (result > 0xff) + yyerror("Escape sequence out of bound"); + else + *cpStr++ = result; +} +<str>\\[0-9]+ { + yyerror("Bad escape sequence"); +} +<str>\\n { *cpStr++ = '\n'; } +<str>\\r { *cpStr++ = '\r'; } +<str>\\t { *cpStr++ = '\t'; } +<str>\\b { *cpStr++ = '\b'; } +<str>\\f { *cpStr++ = '\f'; } +<str>\\(.|\n) { + *cpStr++ = yytext[1]; +} +<str>[^\\\n\"]+ { + char *cp = yytext; + while (*cp != NUL) + *cpStr++ = *cp++; +} +<str>. { + *cpStr++ = yytext[1]; +} + + /* + * Regular Expression + */ +"m". { + cRegexDel = yytext[1]; + cpRegex = caRegex; + BEGIN(regex); +} +<regex>.|\n { + if (yytext[0] == cRegexDel) { + *cpRegex = NUL; + BEGIN(regex_flags); + } + else { + *cpRegex++ = yytext[0]; + } +} +<regex_flags>i { + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex); + BEGIN(INITIAL); + return T_REGEX_I; +} +<regex_flags>.|\n { + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex); + yyless(0); + BEGIN(INITIAL); + return T_REGEX; +} +<regex_flags><<EOF>> { + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, caRegex); + BEGIN(INITIAL); + return T_REGEX; +} + + /* + * Operators + */ +"eq" { return T_OP_EQ; } +"==" { return T_OP_EQ; } +"ne" { return T_OP_NE; } +"!=" { return T_OP_NE; } +"lt" { return T_OP_LT; } +"<" { return T_OP_LT; } +"le" { return T_OP_LE; } +"<=" { return T_OP_LE; } +"gt" { return T_OP_GT; } +">" { return T_OP_GT; } +"ge" { return T_OP_GE; } +">=" { return T_OP_GE; } +"=~" { return T_OP_REG; } +"!~" { return T_OP_NRE; } +"and" { return T_OP_AND; } +"&&" { return T_OP_AND; } +"or" { return T_OP_OR; } +"||" { return T_OP_OR; } +"not" { return T_OP_NOT; } +"!" { return T_OP_NOT; } +"in" { return T_OP_IN; } + + /* + * Functions + */ +"file" { return T_FUNC_FILE; } + + /* + * Specials + */ +"true" { return T_TRUE; } +"false" { return T_FALSE; } + + /* + * Digits + */ +[0-9]+ { + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext); + return T_DIGIT; +} + + /* + * Identifiers + */ +[a-zA-Z][a-zA-Z0-9_:-]* { + yylval.cpVal = ap_pstrdup(ssl_expr_info.pool, yytext); + return T_ID; +} + + /* + * Anything else is returned as is... + */ +.|\n { + return yytext[0]; +} + +%% + +int yyinput(char *buf, int max_size) +{ + int n; + + if ((n = MIN(max_size, ssl_expr_info.inputbuf + + ssl_expr_info.inputlen + - ssl_expr_info.inputptr)) <= 0) + return YY_NULL; + memcpy(buf, ssl_expr_info.inputptr, n); + ssl_expr_info.inputptr += n; + return n; +} + diff --git a/modules/ssl/ssl_scache.c b/modules/ssl/ssl_scache.c new file mode 100644 index 0000000000..139c7865fe --- /dev/null +++ b/modules/ssl/ssl_scache.c @@ -0,0 +1,204 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_scache.c +** Session Cache Abstraction +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``Open-Source Software: generous + programmers from around the world all + join forces to help you shoot + yourself in the foot for free.'' + -- Unknown */ +#include "mod_ssl.h" + +/* _________________________________________________________________ +** +** Session Cache: Common Abstraction Layer +** _________________________________________________________________ +*/ + +void ssl_scache_init(server_rec *s, pool *p) +{ + SSLModConfigRec *mc = myModConfig(); + + if (mc->nSessionCacheMode == SSL_SCMODE_DBM) + ssl_scache_dbm_init(s, p); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT) + ssl_scache_shmht_init(s, p); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB) + ssl_scache_shmcb_init(s, p); +#ifdef SSL_VENDOR + else + ap_hook_use("ap::mod_ssl::vendor::scache_init", + AP_HOOK_SIG3(void,ptr,ptr), AP_HOOK_ALL, s, p); +#endif + return; +} + +void ssl_scache_kill(server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(); + + if (mc->nSessionCacheMode == SSL_SCMODE_DBM) + ssl_scache_dbm_kill(s); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT) + ssl_scache_shmht_kill(s); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB) + ssl_scache_shmcb_kill(s); +#ifdef SSL_VENDOR + else + ap_hook_use("ap::mod_ssl::vendor::scache_kill", + AP_HOOK_SIG2(void,ptr), AP_HOOK_ALL, s); +#endif + return; +} + +BOOL ssl_scache_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess) +{ + SSLModConfigRec *mc = myModConfig(); + BOOL rv = FALSE; + + if (mc->nSessionCacheMode == SSL_SCMODE_DBM) + rv = ssl_scache_dbm_store(s, id, idlen, expiry, sess); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT) + rv = ssl_scache_shmht_store(s, id, idlen, expiry, sess); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB) + rv = ssl_scache_shmcb_store(s, id, idlen, expiry, sess); +#ifdef SSL_VENDOR + else + ap_hook_use("ap::mod_ssl::vendor::scache_store", + AP_HOOK_SIG6(int,ptr,ptr,int,int,ptr), AP_HOOK_ALL, + (int *)&rv, s, id, idlen, (int)expiry, sess); +#endif + return rv; +} + +SSL_SESSION *ssl_scache_retrieve(server_rec *s, UCHAR *id, int idlen) +{ + SSLModConfigRec *mc = myModConfig(); + SSL_SESSION *sess = NULL; + + if (mc->nSessionCacheMode == SSL_SCMODE_DBM) + sess = ssl_scache_dbm_retrieve(s, id, idlen); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT) + sess = ssl_scache_shmht_retrieve(s, id, idlen); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB) + sess = ssl_scache_shmcb_retrieve(s, id, idlen); +#ifdef SSL_VENDOR + else + ap_hook_use("ap::mod_ssl::vendor::scache_retrieve", + AP_HOOK_SIG4(ptr,ptr,ptr,int), AP_HOOK_ALL, + &sess, s, id, idlen); +#endif + return sess; +} + +void ssl_scache_remove(server_rec *s, UCHAR *id, int idlen) +{ + SSLModConfigRec *mc = myModConfig(); + + if (mc->nSessionCacheMode == SSL_SCMODE_DBM) + ssl_scache_dbm_remove(s, id, idlen); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT) + ssl_scache_shmht_remove(s, id, idlen); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB) + ssl_scache_shmcb_remove(s, id, idlen); +#ifdef SSL_VENDOR + else + ap_hook_use("ap::mod_ssl::vendor::scache_remove", + AP_HOOK_SIG4(void,ptr,ptr,int), AP_HOOK_ALL, s, id, idlen); +#endif + return; +} + +void ssl_scache_status(server_rec *s, pool *p, void (*func)(char *, void *), void *arg) +{ + SSLModConfigRec *mc = myModConfig(); + + if (mc->nSessionCacheMode == SSL_SCMODE_DBM) + ssl_scache_dbm_status(s, p, func, arg); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT) + ssl_scache_shmht_status(s, p, func, arg); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB) + ssl_scache_shmcb_status(s, p, func, arg); +#ifdef SSL_VENDOR + else + ap_hook_use("ap::mod_ssl::vendor::scache_status", + AP_HOOK_SIG5(void,ptr,ptr,ptr,ptr), AP_HOOK_ALL, + s, p, func, arg); +#endif + return; +} + +void ssl_scache_expire(server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(); + + if (mc->nSessionCacheMode == SSL_SCMODE_DBM) + ssl_scache_dbm_expire(s); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMHT) + ssl_scache_shmht_expire(s); + else if (mc->nSessionCacheMode == SSL_SCMODE_SHMCB) + ssl_scache_shmcb_expire(s); +#ifdef SSL_VENDOR + else + ap_hook_use("ap::mod_ssl::vendor::scache_expire", + AP_HOOK_SIG2(void,ptr), AP_HOOK_ALL, s); +#endif + return; +} + diff --git a/modules/ssl/ssl_scache_dbm.c b/modules/ssl/ssl_scache_dbm.c new file mode 100644 index 0000000000..323c612991 --- /dev/null +++ b/modules/ssl/ssl_scache_dbm.c @@ -0,0 +1,440 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_scache_dbm.c +** Session Cache via DBM +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include "mod_ssl.h" + +void ssl_scache_dbm_init(server_rec *s, pool *p) +{ + SSLModConfigRec *mc = myModConfig(); + DBM *dbm; + + /* for the DBM we need the data file */ + if (mc->szSessionCacheDataFile == NULL) { + ssl_log(s, SSL_LOG_ERROR, "SSLSessionCache required"); + ssl_die(); + } + + /* open it once to create it and to make sure it _can_ be created */ + ssl_mutex_on(s); + if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile, + O_RDWR|O_CREAT, SSL_DBM_FILE_MODE)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Cannot create SSLSessionCache DBM file `%s'", + mc->szSessionCacheDataFile); + ssl_mutex_off(s); + return; + } + ssl_dbm_close(dbm); + +#if !defined(OS2) && !defined(WIN32) + /* + * We have to make sure the Apache child processes have access to + * the DBM file. But because there are brain-dead platforms where we + * cannot exactly determine the suffixes we try all possibilities. + */ + if (geteuid() == 0 /* is superuser */) { + chown(mc->szSessionCacheDataFile, ap_user_id, -1 /* no gid change */); + if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_DIR, NULL), + ap_user_id, -1) == -1) { + if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL), + ap_user_id, -1) == -1) + chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".dir", NULL), + ap_user_id, -1); + } + if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_PAG, NULL), + ap_user_id, -1) == -1) { + if (chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL), + ap_user_id, -1) == -1) + chown(ap_pstrcat(p, mc->szSessionCacheDataFile, ".pag", NULL), + ap_user_id, -1); + } + } +#endif + ssl_mutex_off(s); + ssl_scache_dbm_expire(s); + return; +} + +void ssl_scache_dbm_kill(server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(); + pool *p; + + if ((p = ap_make_sub_pool(NULL)) != NULL) { + /* the correct way */ + unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_DIR, NULL)); + unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, SSL_DBM_FILE_SUFFIX_PAG, NULL)); + /* the additional ways to be sure */ + unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, ".dir", NULL)); + unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, ".pag", NULL)); + unlink(ap_pstrcat(p, mc->szSessionCacheDataFile, ".db", NULL)); + unlink(mc->szSessionCacheDataFile); + ap_destroy_pool(p); + } + return; +} + +BOOL ssl_scache_dbm_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess) +{ + SSLModConfigRec *mc = myModConfig(); + DBM *dbm; + datum dbmkey; + datum dbmval; + UCHAR ucaData[SSL_SESSION_MAX_DER]; + int nData; + UCHAR *ucp; + + /* streamline session data */ + ucp = ucaData; + nData = i2d_SSL_SESSION(sess, &ucp); + + /* be careful: do not try to store too much bytes in a DBM file! */ +#ifdef SSL_USE_SDBM + if ((idlen + nData) >= PAIRMAX) + return FALSE; +#else + if ((idlen + nData) >= 950 /* at least less than approx. 1KB */) + return FALSE; +#endif + + /* create DBM key */ + dbmkey.dptr = (char *)id; + dbmkey.dsize = idlen; + + /* create DBM value */ + dbmval.dsize = sizeof(time_t) + nData; + dbmval.dptr = (char *)malloc(dbmval.dsize); + if (dbmval.dptr == NULL) + return FALSE; + memcpy((char *)dbmval.dptr, &expiry, sizeof(time_t)); + memcpy((char *)dbmval.dptr+sizeof(time_t), ucaData, nData); + + /* and store it to the DBM file */ + ssl_mutex_on(s); + if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile, + O_RDWR, SSL_DBM_FILE_MODE)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Cannot open SSLSessionCache DBM file `%s' for writing (store)", + mc->szSessionCacheDataFile); + ssl_mutex_off(s); + free(dbmval.dptr); + return FALSE; + } + if (ssl_dbm_store(dbm, dbmkey, dbmval, DBM_INSERT) < 0) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Cannot store SSL session to DBM file `%s'", + mc->szSessionCacheDataFile); + ssl_dbm_close(dbm); + ssl_mutex_off(s); + free(dbmval.dptr); + return FALSE; + } + ssl_dbm_close(dbm); + ssl_mutex_off(s); + + /* free temporary buffers */ + free(dbmval.dptr); + + /* allow the regular expiring to occur */ + ssl_scache_dbm_expire(s); + + return TRUE; +} + +SSL_SESSION *ssl_scache_dbm_retrieve(server_rec *s, UCHAR *id, int idlen) +{ + SSLModConfigRec *mc = myModConfig(); + DBM *dbm; + datum dbmkey; + datum dbmval; + SSL_SESSION *sess = NULL; + UCHAR *ucpData; + int nData; + time_t expiry; + time_t now; + + /* allow the regular expiring to occur */ + ssl_scache_dbm_expire(s); + + /* create DBM key and values */ + dbmkey.dptr = (char *)id; + dbmkey.dsize = idlen; + + /* and fetch it from the DBM file */ + ssl_mutex_on(s); + if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile, + O_RDONLY, SSL_DBM_FILE_MODE)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Cannot open SSLSessionCache DBM file `%s' for reading (fetch)", + mc->szSessionCacheDataFile); + ssl_mutex_off(s); + return NULL; + } + dbmval = ssl_dbm_fetch(dbm, dbmkey); + ssl_dbm_close(dbm); + ssl_mutex_off(s); + + /* immediately return if not found */ + if (dbmval.dptr == NULL || dbmval.dsize <= sizeof(time_t)) + return NULL; + + /* parse resulting data */ + nData = dbmval.dsize-sizeof(time_t); + ucpData = (UCHAR *)malloc(nData); + if (ucpData == NULL) + return NULL; + memcpy(ucpData, (char *)dbmval.dptr+sizeof(time_t), nData); + memcpy(&expiry, dbmval.dptr, sizeof(time_t)); + + /* make sure the stuff is still not expired */ + now = time(NULL); + if (expiry <= now) { + ssl_scache_dbm_remove(s, id, idlen); + return NULL; + } + + /* unstreamed SSL_SESSION */ + sess = d2i_SSL_SESSION(NULL, &ucpData, nData); + + return sess; +} + +void ssl_scache_dbm_remove(server_rec *s, UCHAR *id, int idlen) +{ + SSLModConfigRec *mc = myModConfig(); + DBM *dbm; + datum dbmkey; + + /* create DBM key and values */ + dbmkey.dptr = (char *)id; + dbmkey.dsize = idlen; + + /* and delete it from the DBM file */ + ssl_mutex_on(s); + if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile, + O_RDWR, SSL_DBM_FILE_MODE)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Cannot open SSLSessionCache DBM file `%s' for writing (delete)", + mc->szSessionCacheDataFile); + ssl_mutex_off(s); + return; + } + ssl_dbm_delete(dbm, dbmkey); + ssl_dbm_close(dbm); + ssl_mutex_off(s); + + return; +} + +void ssl_scache_dbm_expire(server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(); + SSLSrvConfigRec *sc = mySrvConfig(s); + static time_t tLast = 0; + DBM *dbm; + datum dbmkey; + datum dbmval; + pool *p; + time_t tExpiresAt; + int nElements = 0; + int nDeleted = 0; + int bDelete; + datum *keylist; + int keyidx; + int i; + time_t tNow; + + /* + * make sure the expiration for still not-accessed session + * cache entries is done only from time to time + */ + tNow = time(NULL); + if (tNow < tLast+sc->nSessionCacheTimeout) + return; + tLast = tNow; + + /* + * Here we have to be very carefully: Not all DBM libraries are + * smart enough to allow one to iterate over the elements and at the + * same time delete expired ones. Some of them get totally crazy + * while others have no problems. So we have to do it the slower but + * more safe way: we first iterate over all elements and remember + * those which have to be expired. Then in a second pass we delete + * all those expired elements. Additionally we reopen the DBM file + * to be really safe in state. + */ + +#define KEYMAX 1024 + + ssl_mutex_on(s); + for (;;) { + /* allocate the key array in a memory sub pool */ + if ((p = ap_make_sub_pool(NULL)) == NULL) + break; + if ((keylist = ap_palloc(p, sizeof(dbmkey)*KEYMAX)) == NULL) { + ap_destroy_pool(p); + break; + } + + /* pass 1: scan DBM database */ + keyidx = 0; + if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile, + O_RDWR, SSL_DBM_FILE_MODE)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Cannot open SSLSessionCache DBM file `%s' for scanning", + mc->szSessionCacheDataFile); + ap_destroy_pool(p); + break; + } + dbmkey = ssl_dbm_firstkey(dbm); + while (dbmkey.dptr != NULL) { + nElements++; + bDelete = FALSE; + dbmval = ssl_dbm_fetch(dbm, dbmkey); + if (dbmval.dsize <= sizeof(time_t) || dbmval.dptr == NULL) + bDelete = TRUE; + else { + memcpy(&tExpiresAt, dbmval.dptr, sizeof(time_t)); + if (tExpiresAt <= tNow) + bDelete = TRUE; + } + if (bDelete) { + if ((keylist[keyidx].dptr = ap_palloc(p, dbmkey.dsize)) != NULL) { + memcpy(keylist[keyidx].dptr, dbmkey.dptr, dbmkey.dsize); + keylist[keyidx].dsize = dbmkey.dsize; + keyidx++; + if (keyidx == KEYMAX) + break; + } + } + dbmkey = ssl_dbm_nextkey(dbm); + } + ssl_dbm_close(dbm); + + /* pass 2: delete expired elements */ + if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile, + O_RDWR, SSL_DBM_FILE_MODE)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Cannot re-open SSLSessionCache DBM file `%s' for expiring", + mc->szSessionCacheDataFile); + ap_destroy_pool(p); + break; + } + for (i = 0; i < keyidx; i++) { + ssl_dbm_delete(dbm, keylist[i]); + nDeleted++; + } + ssl_dbm_close(dbm); + + /* destroy temporary pool */ + ap_destroy_pool(p); + + if (keyidx < KEYMAX) + break; + } + ssl_mutex_off(s); + + ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache (DBM) Expiry: " + "old: %d, new: %d, removed: %d", nElements, nElements-nDeleted, nDeleted); + return; +} + +void ssl_scache_dbm_status(server_rec *s, pool *p, void (*func)(char *, void *), void *arg) +{ + SSLModConfigRec *mc = myModConfig(); + DBM *dbm; + datum dbmkey; + datum dbmval; + int nElem; + int nSize; + int nAverage; + + nElem = 0; + nSize = 0; + ssl_mutex_on(s); + if ((dbm = ssl_dbm_open(mc->szSessionCacheDataFile, + O_RDONLY, SSL_DBM_FILE_MODE)) == NULL) { + ssl_log(s, SSL_LOG_ERROR|SSL_ADD_ERRNO, + "Cannot open SSLSessionCache DBM file `%s' for status retrival", + mc->szSessionCacheDataFile); + ssl_mutex_off(s); + return; + } + dbmkey = ssl_dbm_firstkey(dbm); + for ( ; dbmkey.dptr != NULL; dbmkey = ssl_dbm_nextkey(dbm)) { + dbmval = ssl_dbm_fetch(dbm, dbmkey); + if (dbmval.dptr == NULL) + continue; + nElem += 1; + nSize += dbmval.dsize; + } + ssl_dbm_close(dbm); + ssl_mutex_off(s); + if (nSize > 0 && nElem > 0) + nAverage = nSize / nElem; + else + nAverage = 0; + func(ap_psprintf(p, "cache type: <b>DBM</b>, maximum size: <b>unlimited</b><br>"), arg); + func(ap_psprintf(p, "current sessions: <b>%d</b>, current size: <b>%d</b> bytes<br>", nElem, nSize), arg); + func(ap_psprintf(p, "average session size: <b>%d</b> bytes<br>", nAverage), arg); + return; +} + diff --git a/modules/ssl/ssl_scache_shmcb.c b/modules/ssl/ssl_scache_shmcb.c new file mode 100644 index 0000000000..e588f0a5d1 --- /dev/null +++ b/modules/ssl/ssl_scache_shmcb.c @@ -0,0 +1,1343 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_scache_shmcb.c +** Session Cache via Shared Memory (Cyclic Buffer Variant) +*/ + +/* ==================================================================== + * Copyright (c) 2000-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include "mod_ssl.h" + +/* + * This shared memory based SSL session cache implementation was + * originally written by Geoff Thorpe <geoff@eu.c2.net> for C2Net Europe + * and as a contribution to Ralf Engelschall's mod_ssl project. + */ + +/* + * The shared-memory segment header can be cast to and from the + * SHMCBHeader type, all other structures need to be initialised by + * utility functions. + * + * The "header" looks like this; + * + * data applying to the overall structure: + * - division_offset (unsigned int): + * how far into the shared memory segment the first division is. + * - division_size (unsigned int): + * how many bytes each division occupies. + * (NB: This includes the queue and the cache) + * - division_mask (unsigned char): + * the "mask" in the next line. Add one to this, + * and that's the number of divisions. + * + * data applying to within each division: + * - queue_size (unsigned int): + * how big each "queue" is. NB: The queue is the first block in each + * division and is followed immediately by the cache itself so so + * there's no cache_offset value. + * + * data applying to within each queue: + * - index_num (unsigned char): + * how many indexes in each cache's queue + * - index_offset (unsigned char): + * how far into the queue the first index is. + * - index_size: + * how big each index is. + * + * data applying to within each cache: + * - cache_data_offset (unsigned int): + * how far into the cache the session-data array is stored. + * - cache_data_size (unsigned int): + * how big each cache's data block is. + * + * statistics data (this will eventually be per-division but right now + * there's only one mutex): + * - stores (unsigned long): + * how many stores have been performed in the cache. + * - expiries (unsigned long): + * how many session have been expired from the cache. + * - scrolled (unsigned long): + * how many sessions have been scrolled out of full cache during a + * "store" operation. This is different to the "removes" stats as + * they are requested by mod_ssl/Apache, these are done because of + * cache logistics. (NB: Also, this value should be deducible from + * the others if my code has no bugs, but I count it anyway - plus + * it helps debugging :-). + * - retrieves_hit (unsigned long): + * how many session-retrieves have succeeded. + * - retrieves_miss (unsigned long): + * how many session-retrieves have failed. + * - removes_hit (unsigned long): + * - removes_miss (unsigned long): + * + * Following immediately after the header is an array of "divisions". + * Each division is simply a "queue" immediately followed by its + * corresponding "cache". Each division handles some pre-defined band + * of sessions by using the "division_mask" in the header. Eg. if + * division_mask=0x1f then there are 32 divisions, the first of which + * will store sessions whose least-significant 5 bits are 0, the second + * stores session whose LS 5 bits equal 1, etc. A queue is an indexing + * structure referring to its corresponding cache. + * + * A "queue" looks like this; + * + * - first_pos (unsigned int): + * the location within the array of indexes where the virtual + * "left-hand-edge" of the cyclic buffer is. + * - pos_count (unsigned int): + * the number of indexes occupied from first_pos onwards. + * + * ...followed by an array of indexes, each of which can be + * memcpy'd to and from an SHMCBIndex, and look like this; + * + * - expires (time_t): + * the time() value at which this session expires. + * - offset (unsigned int): + * the offset within the cache data block where the corresponding + * session is stored. + * - s_id2 (unsigned char): + * the second byte of the session_id, stored as an optimisation to + * reduce the number of d2i_SSL_SESSION calls that are made when doing + * a lookup. + * - removed (unsigned char): + * a byte used to indicate whether a session has been "passively" + * removed. Ie. it is still in the cache but is to be disregarded by + * any "retrieve" operation. + * + * A "cache" looks like this; + * + * - first_pos (unsigned int): + * the location within the data block where the virtual + * "left-hand-edge" of the cyclic buffer is. + * - pos_count (unsigned int): + * the number of bytes used in the data block from first_pos onwards. + * + * ...followed by the data block in which actual DER-encoded SSL + * sessions are stored. + */ + +/* + * Header - can be memcpy'd to and from the front of the shared + * memory segment. NB: The first copy (commented out) has the + * elements in a meaningful order, but due to data-alignment + * braindeadness, the second (uncommented) copy has the types grouped + * so as to decrease "struct-bloat". sigh. + */ +typedef struct { +#if 0 + unsigned char division_mask; + unsigned int division_offset; + unsigned int division_size; + unsigned int queue_size; + unsigned char index_num; + unsigned char index_offset; + unsigned char index_size; + unsigned int cache_data_offset; + unsigned int cache_data_size; + unsigned long num_stores; + unsigned long num_expiries; + unsigned long num_scrolled; + unsigned long num_retrieves_hit; + unsigned long num_retrieves_miss; + unsigned long num_removes_hit; + unsigned long num_removes_miss; +#else + unsigned long num_stores; + unsigned long num_expiries; + unsigned long num_scrolled; + unsigned long num_retrieves_hit; + unsigned long num_retrieves_miss; + unsigned long num_removes_hit; + unsigned long num_removes_miss; + unsigned int division_offset; + unsigned int division_size; + unsigned int queue_size; + unsigned int cache_data_offset; + unsigned int cache_data_size; + unsigned char division_mask; + unsigned char index_num; + unsigned char index_offset; + unsigned char index_size; +#endif +} SHMCBHeader; + +/* + * Index - can be memcpy'd to and from an index inside each + * queue's index array. + */ +typedef struct { + time_t expires; + unsigned int offset; + unsigned char s_id2; + unsigned char removed; +} SHMCBIndex; + +/* + * Queue - must be populated by a call to shmcb_get_division + * and the structure's pointers are used for updating (ie. + * the structure doesn't need any "set" to update values). + */ +typedef struct { + SHMCBHeader *header; + unsigned int *first_pos; + unsigned int *pos_count; + SHMCBIndex *indexes; +} SHMCBQueue; + +/* + * Cache - same comment as for Queue. 'Queue's are in a 1-1 + * correspondance with 'Cache's and are usually carried round + * in a pair, they are only seperated for clarity. + */ +typedef struct { + SHMCBHeader *header; + unsigned int *first_pos; + unsigned int *pos_count; + unsigned char *data; +} SHMCBCache; + +/* + * Forward function prototypes. + */ + +/* Functions for working around data-alignment-picky systems (sparcs, + Irix, etc). These use "memcpy" as a way of foxing these systems into + treating the composite types as byte-arrays rather than higher-level + primitives that it prefers to have 4-(or 8-)byte aligned. I don't + envisage this being a performance issue as a couple of 2 or 4 byte + memcpys can hardly make a dent on the massive memmove operations this + cache technique avoids, nor the overheads of ASN en/decoding. */ +static unsigned int shmcb_get_safe_uint(unsigned int *); +static void shmcb_set_safe_uint(unsigned int *, unsigned int); +#if 0 /* Unused so far */ +static unsigned long shmcb_get_safe_ulong(unsigned long *); +static void shmcb_set_safe_ulong(unsigned long *, unsigned long); +#endif +static time_t shmcb_get_safe_time(time_t *); +static void shmcb_set_safe_time(time_t *, time_t); + +/* Underlying functions for session-caching */ +static BOOL shmcb_init_memory(server_rec *, void *, unsigned int); +static BOOL shmcb_store_session(server_rec *, void *, UCHAR *, int, SSL_SESSION *, time_t); +static SSL_SESSION *shmcb_retrieve_session(server_rec *, void *, UCHAR *, int); +static BOOL shmcb_remove_session(server_rec *, void *, UCHAR *, int); + +/* Utility functions for manipulating the structures */ +static void shmcb_get_header(void *, SHMCBHeader **); +static BOOL shmcb_get_division(SHMCBHeader *, SHMCBQueue *, SHMCBCache *, unsigned int); +static SHMCBIndex *shmcb_get_index(const SHMCBQueue *, unsigned int); +static unsigned int shmcb_expire_division(server_rec *, SHMCBQueue *, SHMCBCache *); +static BOOL shmcb_insert_encoded_session(server_rec *, SHMCBQueue *, SHMCBCache *, unsigned char *, unsigned int, unsigned char *, time_t); +static SSL_SESSION *shmcb_lookup_session_id(server_rec *, SHMCBQueue *, SHMCBCache *, UCHAR *, int); +static BOOL shmcb_remove_session_id(server_rec *, SHMCBQueue *, SHMCBCache *, UCHAR *, int); + +/* + * Data-alignment functions (a.k.a. avoidance tactics) + * + * NB: On HPUX (and possibly others) there is a *very* mischievous little + * "optimisation" in the compilers where it will convert the following; + * memcpy(dest_ptr, &source, sizeof(unsigned int)); + * (where dest_ptr is of type (unsigned int *) and source is (unsigned int)) + * into; + * *dest_ptr = source; (or *dest_ptr = *(&source), not sure). + * Either way, it completely destroys the whole point of these _safe_ + * functions, because the assignment operation will fall victim to the + * architecture's byte-alignment dictations, whereas the memcpy (as a + * byte-by-byte copy) should not. sigh. So, if you're wondering about the + * apparently unnecessary conversions to (unsigned char *) in these + * functions, you now have an explanation. Don't just revert them back and + * say "ooh look, it still works" - if you try it on HPUX (well, 32-bit + * HPUX 11.00 at least) you may find it fails with a SIGBUS. :-( + */ + +static unsigned int shmcb_get_safe_uint(unsigned int *ptr) +{ + unsigned char *from; + unsigned int ret; + + from = (unsigned char *)ptr; + memcpy(&ret, from, sizeof(unsigned int)); + return ret; +} + +static void shmcb_set_safe_uint(unsigned int *ptr, unsigned int val) +{ + unsigned char *to, *from; + + to = (unsigned char *)ptr; + from = (unsigned char *)(&val); + memcpy(to, from, sizeof(unsigned int)); +} + +#if 0 /* Unused so far */ +static unsigned long shmcb_get_safe_ulong(unsigned long *ptr) +{ + unsigned char *from; + unsigned long ret; + + from = (unsigned char *)ptr; + memcpy(&ret, from, sizeof(unsigned long)); + return ret; +} + +static void shmcb_set_safe_ulong(unsigned long *ptr, unsigned long val) +{ + unsigned char *to, *from; + + to = (unsigned char *)ptr; + from = (unsigned char *)(&val); + memcpy(to, from, sizeof(unsigned long)); +} +#endif + +static time_t shmcb_get_safe_time(time_t * ptr) +{ + unsigned char *from; + time_t ret; + + from = (unsigned char *)ptr; + memcpy(&ret, from, sizeof(time_t)); + return ret; +} + +static void shmcb_set_safe_time(time_t * ptr, time_t val) +{ + unsigned char *to, *from; + + to = (unsigned char *)ptr; + from = (unsigned char *)(&val); + memcpy(to, from, sizeof(time_t)); +} + +/* +** +** High-Level "handlers" as per ssl_scache.c +** +*/ + +static void *shmcb_malloc(size_t size) +{ + SSLModConfigRec *mc = myModConfig(); + return ap_mm_malloc(mc->pSessionCacheDataMM, size); +} + +void ssl_scache_shmcb_init(server_rec *s, pool *p) +{ + SSLModConfigRec *mc = myModConfig(); + AP_MM *mm; + void *shm_segment = NULL; + int avail, avail_orig; + + /* + * Create shared memory segment + */ + if (mc->szSessionCacheDataFile == NULL) { + ssl_log(s, SSL_LOG_ERROR, "SSLSessionCache required"); + ssl_die(); + } + if ((mm = ap_mm_create(mc->nSessionCacheDataSize, + mc->szSessionCacheDataFile)) == NULL) { + ssl_log(s, SSL_LOG_ERROR, + "Cannot allocate shared memory: %s", ap_mm_error()); + ssl_die(); + } + mc->pSessionCacheDataMM = mm; + + /* + * Make sure the child processes have access to the underlying files + */ + ap_mm_permission(mm, SSL_MM_FILE_MODE, ap_user_id, -1); + + /* + * Create cache inside the shared memory segment + */ + avail = avail_orig = ap_mm_available(mm); + ssl_log(s, SSL_LOG_TRACE, "Shared-memory segment has %u available", + avail); + + /* + * For some reason to do with MM's internal management, I can't + * allocate the full amount. Implement a reasonable form of trial + * and error and output trace information. + */ + while ((shm_segment == NULL) && ((avail_orig - avail) * 100 < avail_orig)) { + shm_segment = shmcb_malloc(avail); + if (shm_segment == NULL) { + ssl_log(s, SSL_LOG_TRACE, + "shmcb_malloc attempt for %u bytes failed", avail); + avail -= 2; + } + } + if (shm_segment == NULL) { + ssl_log(s, SSL_LOG_ERROR, + "Cannot allocate memory for the 'shmcb' session cache\n"); + ssl_die(); + } + ssl_log(s, SSL_LOG_TRACE, "shmcb_init allocated %u bytes of shared " + "memory", avail); + if (!shmcb_init_memory(s, shm_segment, avail)) { + ssl_log(s, SSL_LOG_ERROR, + "Failure initialising 'shmcb' shared memory"); + ssl_die(); + } + ssl_log(s, SSL_LOG_INFO, "Shared memory session cache initialised"); + + /* + * Success ... we hack the memory block into place by cheating for + * now and stealing a member variable the original shared memory + * cache was using. :-) + */ + mc->tSessionCacheDataTable = (table_t *) shm_segment; + return; +} + +void ssl_scache_shmcb_kill(server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(); + + if (mc->pSessionCacheDataMM != NULL) { + ap_mm_destroy(mc->pSessionCacheDataMM); + mc->pSessionCacheDataMM = NULL; + } + return; +} + +BOOL ssl_scache_shmcb_store(server_rec *s, UCHAR * id, int idlen, + time_t timeout, SSL_SESSION * pSession) +{ + SSLModConfigRec *mc = myModConfig(); + void *shm_segment; + BOOL to_return = FALSE; + + /* We've kludged our pointer into the other cache's member variable. */ + shm_segment = (void *) mc->tSessionCacheDataTable; + ssl_mutex_on(s); + if (!shmcb_store_session(s, shm_segment, id, idlen, pSession, timeout)) + /* in this cache engine, "stores" should never fail. */ + ssl_log(s, SSL_LOG_ERROR, "'shmcb' code was unable to store a " + "session in the cache."); + else { + ssl_log(s, SSL_LOG_TRACE, "shmcb_store successful"); + to_return = TRUE; + } + ssl_mutex_off(s); + return to_return; +} + +SSL_SESSION *ssl_scache_shmcb_retrieve(server_rec *s, UCHAR * id, int idlen) +{ + SSLModConfigRec *mc = myModConfig(); + void *shm_segment; + SSL_SESSION *pSession; + + /* We've kludged our pointer into the other cache's member variable. */ + shm_segment = (void *) mc->tSessionCacheDataTable; + ssl_mutex_on(s); + pSession = shmcb_retrieve_session(s, shm_segment, id, idlen); + ssl_mutex_off(s); + if (pSession) + ssl_log(s, SSL_LOG_TRACE, "shmcb_retrieve had a hit"); + else { + ssl_log(s, SSL_LOG_TRACE, "shmcb_retrieve had a miss"); + ssl_log(s, SSL_LOG_INFO, "Client requested a 'session-resume' but " + "we have no such session."); + } + return pSession; +} + +void ssl_scache_shmcb_remove(server_rec *s, UCHAR * id, int idlen) +{ + SSLModConfigRec *mc = myModConfig(); + void *shm_segment; + + /* We've kludged our pointer into the other cache's member variable. */ + shm_segment = (void *) mc->tSessionCacheDataTable; + shmcb_remove_session(s, shm_segment, id, idlen); +} + +void ssl_scache_shmcb_expire(server_rec *s) +{ + /* NOP */ + return; +} + +void ssl_scache_shmcb_status(server_rec *s, pool *p, + void (*func) (char *, void *), void *arg) +{ + SSLModConfigRec *mc = myModConfig(); + SHMCBHeader *header; + SHMCBQueue queue; + SHMCBCache cache; + SHMCBIndex *idx; + void *shm_segment; + unsigned int loop, total, cache_total, non_empty_divisions; + int index_pct, cache_pct; + double expiry_total; + time_t average_expiry, now, max_expiry, min_expiry, idxexpiry; + + ssl_log(s, SSL_LOG_TRACE, "inside ssl_scache_shmcb_status"); + + /* We've kludged our pointer into the other cache's member variable. */ + shm_segment = (void *) mc->tSessionCacheDataTable; + + /* Get the header structure. */ + shmcb_get_header(shm_segment, &header); + total = cache_total = non_empty_divisions = 0; + average_expiry = max_expiry = min_expiry = 0; + expiry_total = 0; + + /* It may seem strange to grab "now" at this point, but in theory + * we should never have a negative threshold but grabbing "now" after + * the loop (which performs expiries) could allow that chance. */ + now = time(NULL); + for (loop = 0; loop <= header->division_mask; loop++) { + if (shmcb_get_division(header, &queue, &cache, loop)) { + shmcb_expire_division(s, &queue, &cache); + total += shmcb_get_safe_uint(queue.pos_count); + cache_total += shmcb_get_safe_uint(cache.pos_count); + if (shmcb_get_safe_uint(queue.pos_count) > 0) { + idx = shmcb_get_index(&queue, + shmcb_get_safe_uint(queue.first_pos)); + non_empty_divisions++; + idxexpiry = shmcb_get_safe_time(&(idx->expires)); + expiry_total += (double) idxexpiry; + max_expiry = (idxexpiry > max_expiry ? idxexpiry : + max_expiry); + if (min_expiry == 0) + min_expiry = idxexpiry; + else + min_expiry = (idxexpiry < min_expiry ? idxexpiry : + min_expiry); + } + } + } + index_pct = (100 * total) / (header->index_num * (header->division_mask + 1)); + cache_pct = (100 * cache_total) / (header->cache_data_size * (header->division_mask + 1)); + func(ap_psprintf(p, "cache type: <b>SHMCB</b>, shared memory: <b>%d</b> " + "bytes, current sessions: <b>%d</b><br>", + mc->nSessionCacheDataSize, total), arg); + func(ap_psprintf(p, "sub-caches: <b>%d</b>, indexes per sub-cache: " + "<b>%d</b><br>", (int) header->division_mask + 1, + (int) header->index_num), arg); + if (non_empty_divisions != 0) { + average_expiry = (time_t)(expiry_total / (double)non_empty_divisions); + func(ap_psprintf(p, "time left on oldest entries' SSL sessions: "), arg); + if (now < average_expiry) + func(ap_psprintf(p, "avg: <b>%d</b> seconds, (range: %d...%d)<br>", + (int)(average_expiry - now), (int) (min_expiry - now), + (int)(max_expiry - now)), arg); + else + func(ap_psprintf(p, "expiry threshold: <b>Calculation Error!</b>" + "<br>"), arg); + + } + func(ap_psprintf(p, "index usage: <b>%d%%</b>, cache usage: <b>%d%%</b>" + "<br>", index_pct, cache_pct), arg); + func(ap_psprintf(p, "total sessions stored since starting: <b>%lu</b><br>", + header->num_stores), arg); + func(ap_psprintf(p, "total sessions expired since starting: <b>%lu</b><br>", + header->num_expiries), arg); + func(ap_psprintf(p, "total (pre-expiry) sessions scrolled out of the " + "cache: <b>%lu</b><br>", header->num_scrolled), arg); + func(ap_psprintf(p, "total retrieves since starting: <b>%lu</b> hit, " + "<b>%lu</b> miss<br>", header->num_retrieves_hit, + header->num_retrieves_miss), arg); + func(ap_psprintf(p, "total removes since starting: <b>%lu</b> hit, " + "<b>%lu</b> miss<br>", header->num_removes_hit, + header->num_removes_miss), arg); + ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_status"); + return; +} + +/* +** +** Memory manipulation and low-level cache operations +** +*/ + +static BOOL shmcb_init_memory( + server_rec *s, void *shm_mem, + unsigned int shm_mem_size) +{ + SHMCBHeader *header; + SHMCBQueue queue; + SHMCBCache cache; + unsigned int temp, loop, granularity; + + ssl_log(s, SSL_LOG_TRACE, "entered shmcb_init_memory()"); + + /* Calculate some sizes... */ + temp = sizeof(SHMCBHeader); + + /* If the segment is ridiculously too small, bail out */ + if (shm_mem_size < (2*temp)) { + ssl_log(s, SSL_LOG_ERROR, "shared memory segment too small"); + return FALSE; + } + + /* Make temp the amount of memory without the header */ + temp = shm_mem_size - temp; + + /* Work on the basis that you need 10 bytes index for each session + * (approx 150 bytes), which is to divide temp by 160 - and then + * make sure we err on having too index space to burn even when + * the cache is full, which is a lot less stupid than having + * having not enough index space to utilise the whole cache!. */ + temp /= 120; + ssl_log(s, SSL_LOG_TRACE, "for %u bytes, recommending %u indexes", + shm_mem_size, temp); + + /* We should divide these indexes evenly amongst the queues. Try + * to get it so that there are roughly half the number of divisions + * as there are indexes in each division. */ + granularity = 256; + while ((temp / granularity) < (2 * granularity)) + granularity /= 2; + + /* So we have 'granularity' divisions, set 'temp' equal to the + * number of indexes in each division. */ + temp /= granularity; + + /* Too small? Bail ... */ + if (temp < 5) { + ssl_log(s, SSL_LOG_ERROR, "shared memory segment too small"); + return FALSE; + } + + /* OK, we're sorted - from here on in, the return should be TRUE */ + header = (SHMCBHeader *)shm_mem; + header->division_mask = (unsigned char)(granularity - 1); + header->division_offset = sizeof(SHMCBHeader); + header->index_num = temp; + header->index_offset = (2 * sizeof(unsigned int)); + header->index_size = sizeof(SHMCBIndex); + header->queue_size = header->index_offset + + (header->index_num * header->index_size); + + /* Now calculate the space for each division */ + temp = shm_mem_size - header->division_offset; + header->division_size = temp / granularity; + + /* Calculate the space left in each division for the cache */ + temp -= header->queue_size; + header->cache_data_offset = (2 * sizeof(unsigned int)); + header->cache_data_size = header->division_size - + header->queue_size - header->cache_data_offset; + + /* Output trace info */ + ssl_log(s, SSL_LOG_TRACE, "shmcb_init_memory choices follow"); + ssl_log(s, SSL_LOG_TRACE, "division_mask = 0x%02X", header->division_mask); + ssl_log(s, SSL_LOG_TRACE, "division_offset = %u", header->division_offset); + ssl_log(s, SSL_LOG_TRACE, "division_size = %u", header->division_size); + ssl_log(s, SSL_LOG_TRACE, "queue_size = %u", header->queue_size); + ssl_log(s, SSL_LOG_TRACE, "index_num = %u", header->index_num); + ssl_log(s, SSL_LOG_TRACE, "index_offset = %u", header->index_offset); + ssl_log(s, SSL_LOG_TRACE, "index_size = %u", header->index_size); + ssl_log(s, SSL_LOG_TRACE, "cache_data_offset = %u", header->cache_data_offset); + ssl_log(s, SSL_LOG_TRACE, "cache_data_size = %u", header->cache_data_size); + + /* The header is done, make the caches empty */ + for (loop = 0; loop < granularity; loop++) { + if (!shmcb_get_division(header, &queue, &cache, loop)) + ssl_log(s, SSL_LOG_ERROR, "shmcb_init_memory, " "internal error"); + shmcb_set_safe_uint(cache.first_pos, 0); + shmcb_set_safe_uint(cache.pos_count, 0); + shmcb_set_safe_uint(queue.first_pos, 0); + shmcb_set_safe_uint(queue.pos_count, 0); + } + + ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_init_memory()"); + return TRUE; +} + +static BOOL shmcb_store_session( + server_rec *s, void *shm_segment, UCHAR * id, + int idlen, SSL_SESSION * pSession, + time_t timeout) +{ + SHMCBHeader *header; + SHMCBQueue queue; + SHMCBCache cache; + unsigned char masked_index; + unsigned char encoded[SSL_SESSION_MAX_DER]; + unsigned char *ptr_encoded; + unsigned int len_encoded; + time_t expiry_time; + + ssl_log(s, SSL_LOG_TRACE, "inside shmcb_store_session"); + + /* Get the header structure, which division this session will fall into etc. */ + shmcb_get_header(shm_segment, &header); + masked_index = pSession->session_id[0] & header->division_mask; + ssl_log(s, SSL_LOG_TRACE, "session_id[0]=%u, masked index=%u", + pSession->session_id[0], masked_index); + if (!shmcb_get_division(header, &queue, &cache, (unsigned int)masked_index)) { + ssl_log(s, SSL_LOG_ERROR, "shmcb_store_session, " "internal error"); + return FALSE; + } + + /* Serialise the session, work out how much we're dealing + * with. NB: This check could be removed if we're not paranoid + * or we find some assurance that it will never be necessary. */ + len_encoded = i2d_SSL_SESSION(pSession, NULL); + if (len_encoded > SSL_SESSION_MAX_DER) { + ssl_log(s, SSL_LOG_ERROR, "session is too big (%u bytes)", + len_encoded); + return FALSE; + } + ptr_encoded = encoded; + len_encoded = i2d_SSL_SESSION(pSession, &ptr_encoded); + expiry_time = timeout; + if (!shmcb_insert_encoded_session(s, &queue, &cache, encoded, + len_encoded, pSession->session_id, + expiry_time)) { + ssl_log(s, SSL_LOG_ERROR, "can't store a session!"); + return FALSE; + } + ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_store successfully"); + header->num_stores++; + return TRUE; +} + +static SSL_SESSION *shmcb_retrieve_session( + server_rec *s, void *shm_segment, + UCHAR * id, int idlen) +{ + SHMCBHeader *header; + SHMCBQueue queue; + SHMCBCache cache; + unsigned char masked_index; + SSL_SESSION *pSession; + + ssl_log(s, SSL_LOG_TRACE, "inside shmcb_retrieve_session"); + if (idlen < 2) { + ssl_log(s, SSL_LOG_ERROR, "unusably short session_id provided " + "(%u bytes)", idlen); + return FALSE; + } + + /* Get the header structure, which division this session lookup + * will come from etc. */ + shmcb_get_header(shm_segment, &header); + masked_index = id[0] & header->division_mask; + ssl_log(s, SSL_LOG_TRACE, "id[0]=%u, masked index=%u", id[0], + masked_index); + if (!shmcb_get_division(header, &queue, &cache, (unsigned int) masked_index)) { + ssl_log(s, SSL_LOG_ERROR, "shmcb_retrieve_session, " "internal error"); + header->num_retrieves_miss++; + return FALSE; + } + + /* Get the session corresponding to the session_id or NULL if it + * doesn't exist (or is flagged as "removed"). */ + pSession = shmcb_lookup_session_id(s, &queue, &cache, id, idlen); + if (pSession) + header->num_retrieves_hit++; + else + header->num_retrieves_miss++; + ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_retrieve_session"); + return pSession; +} + +static BOOL shmcb_remove_session( + server_rec *s, void *shm_segment, + UCHAR * id, int idlen) +{ + SHMCBHeader *header; + SHMCBQueue queue; + SHMCBCache cache; + unsigned char masked_index; + BOOL res; + + ssl_log(s, SSL_LOG_TRACE, "inside shmcb_remove_session"); + if (id == NULL) { + ssl_log(s, SSL_LOG_ERROR, "remove called with NULL session_id!"); + return FALSE; + } + + /* Get the header structure, which division this session remove + * will happen in etc. */ + shmcb_get_header(shm_segment, &header); + masked_index = id[0] & header->division_mask; + ssl_log(s, SSL_LOG_TRACE, "id[0]=%u, masked index=%u", + id[0], masked_index); + if (!shmcb_get_division(header, &queue, &cache, (unsigned int)masked_index)) { + ssl_log(s, SSL_LOG_ERROR, "shmcb_remove_session, internal error"); + header->num_removes_miss++; + return FALSE; + } + res = shmcb_remove_session_id(s, &queue, &cache, id, idlen); + if (res) + header->num_removes_hit++; + else + header->num_removes_miss++; + ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_remove_session"); + return res; +} + + +/* +** +** Weirdo cyclic buffer functions +** +*/ + +/* This gets used in the cyclic "index array" (in the 'Queue's) and + * in the cyclic 'Cache's too ... you provide the "width" of the + * cyclic store, the starting position and how far to move (with + * wrapping if necessary). Basically it's addition modulo buf_size. */ +static unsigned int shmcb_cyclic_increment( + unsigned int buf_size, + unsigned int start_pos, + unsigned int to_add) +{ + start_pos += to_add; + while (start_pos >= buf_size) + start_pos -= buf_size; + return start_pos; +} + +/* Given two positions in a cyclic buffer, calculate the "distance". + * This is to cover the case ("non-trivial") where the 'next' offset + * is to the left of the 'start' offset. NB: This calculates the + * space inclusive of one end-point but not the other. There is an + * ambiguous case (which is why we use the <start_pos,offset> + * coordinate system rather than <start_pos,end_pos> one) when 'start' + * is the same as 'next'. It could indicate the buffer is full or it + * can indicate the buffer is empty ... I choose the latter as it's + * easier and usually necessary to check if the buffer is full anyway + * before doing incremental logic (which is this useful for), but we + * definitely need the empty case handled - in fact it's our starting + * state!! */ +static unsigned int shmcb_cyclic_space( + unsigned int buf_size, + unsigned int start_offset, + unsigned int next_offset) +{ + /* Is it the trivial case? */ + if (start_offset <= next_offset) + return (next_offset - start_offset); /* yes */ + else + return ((buf_size - start_offset) + next_offset); /* no */ +} + +/* A "normal-to-cyclic" memcpy ... this takes a linear block of + * memory and copies it onto a cyclic buffer. The purpose and + * function of this is pretty obvious, you need to cover the case + * that the destination (cyclic) buffer has to wrap round. */ +static void shmcb_cyclic_ntoc_memcpy( + unsigned int buf_size, + unsigned char *data, + unsigned int dest_offset, + unsigned char *src, unsigned int src_len) +{ + /* Can it be copied all in one go? */ + if (dest_offset + src_len < buf_size) + /* yes */ + memcpy(data + dest_offset, src, src_len); + else { + /* no */ + memcpy(data + dest_offset, src, buf_size - dest_offset); + memcpy(data, src + buf_size - dest_offset, + src_len + dest_offset - buf_size); + } + return; +} + +/* A "cyclic-to-normal" memcpy ... given the last function, this + * one's purpose is clear, it copies out of a cyclic buffer handling + * wrapping. */ +static void shmcb_cyclic_cton_memcpy( + unsigned int buf_size, + unsigned char *dest, + unsigned char *data, + unsigned int src_offset, + unsigned int src_len) +{ + /* Can it be copied all in one go? */ + if (src_offset + src_len < buf_size) + /* yes */ + memcpy(dest, data + src_offset, src_len); + else { + /* no */ + memcpy(dest, data + src_offset, buf_size - src_offset); + memcpy(dest + buf_size - src_offset, data, + src_len + src_offset - buf_size); + } + return; +} + +/* Here's the cool hack that makes it all work ... by simply + * making the first collection of bytes *be* our header structure + * (casting it into the C structure), we have the perfect way to + * maintain state in a shared-memory session cache from one call + * (and process) to the next, use the shared memory itself! The + * original mod_ssl shared-memory session cache uses variables + * inside the context, but we simply use that for storing the + * pointer to the shared memory itself. And don't forget, after + * Apache's initialisation, this "header" is constant/read-only + * so we can read it outside any locking. + * <grin> - sometimes I just *love* coding y'know?! */ +static void shmcb_get_header(void *shm_mem, SHMCBHeader **header) +{ + *header = (SHMCBHeader *)shm_mem; + return; +} + +/* This is what populates our "interesting" structures. Given a + * pointer to the header, and an index into the appropriate + * division (this must have already been masked using the + * division_mask by the caller!), we can populate the provided + * SHMCBQueue and SHMCBCache structures with values and + * pointers to the underlying shared memory. Upon returning + * (if not FALSE), the caller can meddle with the pointer + * values and they will map into the shared-memory directly, + * as such there's no need to "free" or "set" the Queue or + * Cache values, they were themselves references to the *real* + * data. */ +static BOOL shmcb_get_division( + SHMCBHeader *header, SHMCBQueue *queue, + SHMCBCache *cache, unsigned int idx) +{ + unsigned char *pQueue; + unsigned char *pCache; + + /* bounds check */ + if (idx > (unsigned int) header->division_mask) + return FALSE; + + /* Locate the blocks of memory storing the corresponding data */ + pQueue = ((unsigned char *) header) + header->division_offset + + (idx * header->division_size); + pCache = pQueue + header->queue_size; + + /* Populate the structures with appropriate pointers */ + queue->first_pos = (unsigned int *) pQueue; + + /* Our structures stay packed, no matter what the system's + * data-alignment regime is. */ + queue->pos_count = (unsigned int *) (pQueue + sizeof(unsigned int)); + queue->indexes = (SHMCBIndex *) (pQueue + (2 * sizeof(unsigned int))); + cache->first_pos = (unsigned int *) pCache; + cache->pos_count = (unsigned int *) (pCache + sizeof(unsigned int)); + cache->data = (unsigned char *) (pCache + (2 * sizeof(unsigned int))); + queue->header = cache->header = header; + + return TRUE; +} + +/* This returns a pointer to the piece of shared memory containing + * a specified 'Index'. SHMCBIndex, like SHMCBHeader, is a fixed + * width non-referencing structure of primitive types that can be + * cast onto the corresponding block of shared memory. Thus, by + * returning a cast pointer to that section of shared memory, the + * caller can read and write values to and from the "structure" and + * they are actually reading and writing the underlying shared + * memory. */ +static SHMCBIndex *shmcb_get_index( + const SHMCBQueue *queue, unsigned int idx) +{ + /* bounds check */ + if (idx > (unsigned int) queue->header->index_num) + return NULL; + + /* Return a pointer to the index. NB: I am being horribly pendantic + * here so as to avoid any potential data-alignment assumptions being + * placed on the pointer arithmetic by the compiler (sigh). */ + return (SHMCBIndex *)(((unsigned char *) queue->indexes) + + (idx * sizeof(SHMCBIndex))); +} + +/* This functions rolls expired cache (and index) entries off the front + * of the cyclic buffers in a division. The function returns the number + * of expired sessions. */ +static unsigned int shmcb_expire_division( + server_rec *s, SHMCBQueue *queue, SHMCBCache *cache) +{ + SHMCBIndex *idx; + time_t now; + unsigned int loop, index_num, pos_count, new_pos; + SHMCBHeader *header; + + ssl_log(s, SSL_LOG_TRACE, "entering shmcb_expire_division"); + + /* We must calculate num and space ourselves based on expiry times. */ + now = time(NULL); + loop = 0; + new_pos = shmcb_get_safe_uint(queue->first_pos); + + /* Cache useful values */ + header = queue->header; + index_num = header->index_num; + pos_count = shmcb_get_safe_uint(queue->pos_count); + while (loop < pos_count) { + idx = shmcb_get_index(queue, new_pos); + if (shmcb_get_safe_time(&(idx->expires)) > now) + /* it hasn't expired yet, we're done iterating */ + break; + /* This one should be expired too. Shift to the next entry. */ + loop++; + new_pos = shmcb_cyclic_increment(index_num, new_pos, 1); + } + + /* Find the new_offset and make the expiries happen. */ + if (loop > 0) { + ssl_log(s, SSL_LOG_TRACE, "will be expiring %u sessions", loop); + /* We calculate the new_offset by "peeking" (or in the + * case it's the last entry, "sneaking" ;-). */ + if (loop == pos_count) { + /* We are expiring everything! This is easy to do... */ + shmcb_set_safe_uint(queue->pos_count, 0); + shmcb_set_safe_uint(cache->pos_count, 0); + } + else { + /* The Queue is easy to adjust */ + shmcb_set_safe_uint(queue->pos_count, + shmcb_get_safe_uint(queue->pos_count) - loop); + shmcb_set_safe_uint(queue->first_pos, new_pos); + /* peek to the start of the next session */ + idx = shmcb_get_index(queue, new_pos); + /* We can use shmcb_cyclic_space because we've guaranteed + * we don't fit the ambiguous full/empty case. */ + shmcb_set_safe_uint(cache->pos_count, + shmcb_get_safe_uint(cache->pos_count) - + shmcb_cyclic_space(header->cache_data_size, + shmcb_get_safe_uint(cache->first_pos), + shmcb_get_safe_uint(&(idx->offset)))); + shmcb_set_safe_uint(cache->first_pos, shmcb_get_safe_uint(&(idx->offset))); + } + ssl_log(s, SSL_LOG_TRACE, "we now have %u sessions", + shmcb_get_safe_uint(queue->pos_count)); + } + header->num_expiries += loop; + return loop; +} + +/* Inserts a new encoded session into a queue/cache pair - expiring + * (early or otherwise) any leading sessions as necessary to ensure + * there is room. An error return (FALSE) should only happen in the + * event of surreal values being passed on, or ridiculously small + * cache sizes. NB: For tracing purposes, this function is also given + * the server_rec to allow "ssl_log()". */ +static BOOL shmcb_insert_encoded_session( + server_rec *s, SHMCBQueue * queue, + SHMCBCache * cache, + unsigned char *encoded, + unsigned int encoded_len, + unsigned char *session_id, + time_t expiry_time) +{ + SHMCBHeader *header; + SHMCBIndex *idx = NULL; + unsigned int gap, new_pos, loop, new_offset; + int need; + + ssl_log(s, SSL_LOG_TRACE, "entering shmcb_insert_encoded_session, " + "*queue->pos_count = %u", shmcb_get_safe_uint(queue->pos_count)); + + /* If there's entries to expire, ditch them first thing. */ + shmcb_expire_division(s, queue, cache); + header = cache->header; + gap = header->cache_data_size - shmcb_get_safe_uint(cache->pos_count); + if (gap < encoded_len) { + new_pos = shmcb_get_safe_uint(queue->first_pos); + loop = 0; + need = (int) encoded_len - (int) gap; + while ((need > 0) && (loop + 1 < shmcb_get_safe_uint(queue->pos_count))) { + new_pos = shmcb_cyclic_increment(header->index_num, new_pos, 1); + loop += 1; + idx = shmcb_get_index(queue, new_pos); + need = (int) encoded_len - (int) gap - + shmcb_cyclic_space(header->cache_data_size, + shmcb_get_safe_uint(cache->first_pos), + shmcb_get_safe_uint(&(idx->offset))); + } + if (loop > 0) { + ssl_log(s, SSL_LOG_TRACE, "about to scroll %u sessions from %u", + loop, shmcb_get_safe_uint(queue->pos_count)); + /* We are removing "loop" items from the cache. */ + shmcb_set_safe_uint(cache->pos_count, + shmcb_get_safe_uint(cache->pos_count) - + shmcb_cyclic_space(header->cache_data_size, + shmcb_get_safe_uint(cache->first_pos), + shmcb_get_safe_uint(&(idx->offset)))); + shmcb_set_safe_uint(cache->first_pos, shmcb_get_safe_uint(&(idx->offset))); + shmcb_set_safe_uint(queue->pos_count, shmcb_get_safe_uint(queue->pos_count) - loop); + shmcb_set_safe_uint(queue->first_pos, new_pos); + ssl_log(s, SSL_LOG_TRACE, "now only have %u sessions", + shmcb_get_safe_uint(queue->pos_count)); + /* Update the stats!!! */ + header->num_scrolled += loop; + } + } + + /* probably unecessary checks, but I'll leave them until this code + * is verified. */ + if (shmcb_get_safe_uint(cache->pos_count) + encoded_len > + header->cache_data_size) { + ssl_log(s, SSL_LOG_ERROR, "shmcb_insert_encoded_session, " + "internal error"); + return FALSE; + } + if (shmcb_get_safe_uint(queue->pos_count) == header->index_num) { + ssl_log(s, SSL_LOG_ERROR, "shmcb_insert_encoded_session, " + "internal error"); + return FALSE; + } + ssl_log(s, SSL_LOG_TRACE, "we have %u bytes and %u indexes free - " + "enough", header->cache_data_size - + shmcb_get_safe_uint(cache->pos_count), header->index_num - + shmcb_get_safe_uint(queue->pos_count)); + + + /* HERE WE ASSUME THAT THE NEW SESSION SHOULD GO ON THE END! I'M NOT + * CHECKING WHETHER IT SHOULD BE GENUINELY "INSERTED" SOMEWHERE. + * + * We either fix that, or find out at a "higher" (read "mod_ssl") + * level whether it is possible to have distinct session caches for + * any attempted tomfoolery to do with different session timeouts. + * Knowing in advance that we can have a cache-wide constant timeout + * would make this stuff *MUCH* more efficient. Mind you, it's very + * efficient right now because I'm ignoring this problem!!! + */ + + /* Increment to the first unused byte */ + new_offset = shmcb_cyclic_increment(header->cache_data_size, + shmcb_get_safe_uint(cache->first_pos), + shmcb_get_safe_uint(cache->pos_count)); + /* Copy the DER-encoded session into place */ + shmcb_cyclic_ntoc_memcpy(header->cache_data_size, cache->data, + new_offset, encoded, encoded_len); + /* Get the new index that this session is stored in. */ + new_pos = shmcb_cyclic_increment(header->index_num, + shmcb_get_safe_uint(queue->first_pos), + shmcb_get_safe_uint(queue->pos_count)); + ssl_log(s, SSL_LOG_TRACE, "storing in index %u, at offset %u", new_pos, + new_offset); + idx = shmcb_get_index(queue, new_pos); + if (idx == NULL) { + ssl_log(s, SSL_LOG_ERROR, "shmcb_insert_encoded_session, " + "internal error"); + return FALSE; + } + memset(idx, 0, sizeof(SHMCBIndex)); + shmcb_set_safe_time(&(idx->expires), expiry_time); + shmcb_set_safe_uint(&(idx->offset), new_offset); + + /* idx->removed = (unsigned char)0; */ /* Not needed given the memset above. */ + idx->s_id2 = session_id[1]; + ssl_log(s, SSL_LOG_TRACE, "session_id[0]=%u, idx->s_id2=%u", + session_id[0], session_id[1]); + + /* All that remains is to adjust the cache's and queue's "pos_count"s. */ + shmcb_set_safe_uint(cache->pos_count, + shmcb_get_safe_uint(cache->pos_count) + encoded_len); + shmcb_set_safe_uint(queue->pos_count, + shmcb_get_safe_uint(queue->pos_count) + 1); + + /* And just for good debugging measure ... */ + ssl_log(s, SSL_LOG_TRACE, "leaving now with %u bytes in the cache and " + "%u indexes", shmcb_get_safe_uint(cache->pos_count), + shmcb_get_safe_uint(queue->pos_count)); + ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_insert_encoded_session"); + return TRUE; +} + +/* Performs a lookup into a queue/cache pair for a + * session_id. If found, the session is deserialised + * and returned, otherwise NULL. */ +static SSL_SESSION *shmcb_lookup_session_id( + server_rec *s, SHMCBQueue *queue, + SHMCBCache *cache, UCHAR *id, + int idlen) +{ + unsigned char tempasn[SSL_SESSION_MAX_DER]; + SHMCBIndex *idx; + SHMCBHeader *header; + SSL_SESSION *pSession = NULL; + unsigned int curr_pos, loop, count; + unsigned char *ptr; + time_t now; + + ssl_log(s, SSL_LOG_TRACE, "entering shmcb_lookup_session_id"); + + /* If there are entries to expire, ditch them first thing. */ + shmcb_expire_division(s, queue, cache); + now = time(NULL); + curr_pos = shmcb_get_safe_uint(queue->first_pos); + count = shmcb_get_safe_uint(queue->pos_count); + header = queue->header; + for (loop = 0; loop < count; loop++) { + ssl_log(s, SSL_LOG_TRACE, "loop=%u, count=%u, curr_pos=%u", + loop, count, curr_pos); + idx = shmcb_get_index(queue, curr_pos); + ssl_log(s, SSL_LOG_TRACE, "idx->s_id2=%u, id[1]=%u, offset=%u", + idx->s_id2, id[1], shmcb_get_safe_uint(&(idx->offset))); + /* Only look into the session further if; + * (a) the second byte of the session_id matches, + * (b) the "removed" flag isn't set, + * (c) the session hasn't expired yet. + * We do (c) like this so that it saves us having to + * do natural expiries ... naturally expired sessions + * scroll off the front anyway when the cache is full and + * "rotating", the only real issue that remains is the + * removal or disabling of forcibly killed sessions. */ + if ((idx->s_id2 == id[1]) && !idx->removed && + (shmcb_get_safe_time(&(idx->expires)) > now)) { + ssl_log(s, SSL_LOG_TRACE, "at index %u, found possible " + "session match", curr_pos); + shmcb_cyclic_cton_memcpy(header->cache_data_size, + tempasn, cache->data, + shmcb_get_safe_uint(&(idx->offset)), + SSL_SESSION_MAX_DER); + ptr = tempasn; + pSession = d2i_SSL_SESSION(NULL, &ptr, SSL_SESSION_MAX_DER); + if (pSession == NULL) { + ssl_log(s, SSL_LOG_ERROR, "scach2_lookup_" + "session_id, internal error"); + return NULL; + } + if ((pSession->session_id_length == idlen) && + (memcmp(pSession->session_id, id, idlen) == 0)) { + ssl_log(s, SSL_LOG_TRACE, "a match!"); + return pSession; + } + ssl_log(s, SSL_LOG_TRACE, "not a match"); + SSL_SESSION_free(pSession); + pSession = NULL; + } + curr_pos = shmcb_cyclic_increment(header->index_num, curr_pos, 1); + } + ssl_log(s, SSL_LOG_TRACE, "no matching sessions were found"); + return NULL; +} + +static BOOL shmcb_remove_session_id( + server_rec *s, SHMCBQueue *queue, + SHMCBCache *cache, UCHAR *id, int idlen) +{ + unsigned char tempasn[SSL_SESSION_MAX_DER]; + SSL_SESSION *pSession = NULL; + SHMCBIndex *idx; + SHMCBHeader *header; + unsigned int curr_pos, loop, count; + unsigned char *ptr; + BOOL to_return = FALSE; + + ssl_log(s, SSL_LOG_TRACE, "entering shmcb_remove_session_id"); + + /* If there's entries to expire, ditch them first thing. */ + /* shmcb_expire_division(s, queue, cache); */ + + /* Regarding the above ... hmmm ... I know my expiry code is slightly + * "faster" than all this remove stuff ... but if the higher level + * code calls a "remove" operation (and this *only* seems to happen + * when it has spotted an expired session before we had a chance to) + * then it should get credit for a remove (stats-wise). Also, in the + * off-chance that the server *requests* a renegotiate and wants to + * wipe the session clean we should give that priority over our own + * routine expiry handling. So I've moved the expiry check to *after* + * this general remove stuff. */ + curr_pos = shmcb_get_safe_uint(queue->first_pos); + count = shmcb_get_safe_uint(queue->pos_count); + header = cache->header; + for (loop = 0; loop < count; loop++) { + ssl_log(s, SSL_LOG_TRACE, "loop=%u, count=%u, curr_pos=%u", + loop, count, curr_pos); + idx = shmcb_get_index(queue, curr_pos); + ssl_log(s, SSL_LOG_TRACE, "idx->s_id2=%u, id[1]=%u", idx->s_id2, + id[1]); + /* Only look into the session further if the second byte of the + * session_id matches. */ + if (idx->s_id2 == id[1]) { + ssl_log(s, SSL_LOG_TRACE, "at index %u, found possible " + "session match", curr_pos); + shmcb_cyclic_cton_memcpy(header->cache_data_size, + tempasn, cache->data, + shmcb_get_safe_uint(&(idx->offset)), + SSL_SESSION_MAX_DER); + ptr = tempasn; + pSession = d2i_SSL_SESSION(NULL, &ptr, SSL_SESSION_MAX_DER); + if (pSession == NULL) { + ssl_log(s, SSL_LOG_ERROR, "shmcb_remove_session_id, " + "internal error"); + goto end; + } + if ((pSession->session_id_length == idlen) + && (memcmp(id, pSession->session_id, idlen) == 0)) { + ssl_log(s, SSL_LOG_TRACE, "a match!"); + /* Scrub out this session "quietly" */ + idx->removed = (unsigned char) 1; + SSL_SESSION_free(pSession); + to_return = TRUE; + goto end; + } + ssl_log(s, SSL_LOG_TRACE, "not a match"); + SSL_SESSION_free(pSession); + pSession = NULL; + } + curr_pos = shmcb_cyclic_increment(header->index_num, curr_pos, 1); + } + ssl_log(s, SSL_LOG_TRACE, "no matching sessions were found"); + + /* If there's entries to expire, ditch them now. */ + shmcb_expire_division(s, queue, cache); +end: + ssl_log(s, SSL_LOG_TRACE, "leaving shmcb_remove_session_id"); + return to_return; +} + diff --git a/modules/ssl/ssl_scache_shmht.c b/modules/ssl/ssl_scache_shmht.c new file mode 100644 index 0000000000..18e688af2c --- /dev/null +++ b/modules/ssl/ssl_scache_shmht.c @@ -0,0 +1,347 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_scache_shmht.c +** Session Cache via Shared Memory (Hash Table Variant) +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include "mod_ssl.h" + +/* + * Wrapper functions for table library which resemble malloc(3) & Co + * but use the variants from the MM shared memory library. + */ + +static void *ssl_scache_shmht_malloc(size_t size) +{ + SSLModConfigRec *mc = myModConfig(); + return ap_mm_malloc(mc->pSessionCacheDataMM, size); +} + +static void *ssl_scache_shmht_calloc(size_t number, size_t size) +{ + SSLModConfigRec *mc = myModConfig(); + return ap_mm_calloc(mc->pSessionCacheDataMM, number, size); +} + +static void *ssl_scache_shmht_realloc(void *ptr, size_t size) +{ + SSLModConfigRec *mc = myModConfig(); + return ap_mm_realloc(mc->pSessionCacheDataMM, ptr, size); +} + +static void ssl_scache_shmht_free(void *ptr) +{ + SSLModConfigRec *mc = myModConfig(); + ap_mm_free(mc->pSessionCacheDataMM, ptr); + return; +} + +/* + * Now the actual session cache implementation + * based on a hash table inside a shared memory segment. + */ + +void ssl_scache_shmht_init(server_rec *s, pool *p) +{ + SSLModConfigRec *mc = myModConfig(); + AP_MM *mm; + table_t *ta; + int ta_errno; + int avail; + int n; + + /* + * Create shared memory segment + */ + if (mc->szSessionCacheDataFile == NULL) { + ssl_log(s, SSL_LOG_ERROR, "SSLSessionCache required"); + ssl_die(); + } + if ((mm = ap_mm_create(mc->nSessionCacheDataSize, + mc->szSessionCacheDataFile)) == NULL) { + ssl_log(s, SSL_LOG_ERROR, + "Cannot allocate shared memory: %s", ap_mm_error()); + ssl_die(); + } + mc->pSessionCacheDataMM = mm; + + /* + * Make sure the childs have access to the underlaying files + */ + ap_mm_permission(mm, SSL_MM_FILE_MODE, ap_user_id, -1); + + /* + * Create hash table in shared memory segment + */ + avail = ap_mm_available(mm); + n = (avail/2) / 1024; + n = n < 10 ? 10 : n; + if ((ta = table_alloc(n, &ta_errno, + ssl_scache_shmht_malloc, + ssl_scache_shmht_calloc, + ssl_scache_shmht_realloc, + ssl_scache_shmht_free )) == NULL) { + ssl_log(s, SSL_LOG_ERROR, + "Cannot allocate hash table in shared memory: %s", + table_strerror(ta_errno)); + ssl_die(); + } + table_attr(ta, TABLE_FLAG_AUTO_ADJUST|TABLE_FLAG_ADJUST_DOWN); + table_set_data_alignment(ta, sizeof(char *)); + table_clear(ta); + mc->tSessionCacheDataTable = ta; + + /* + * Log the done work + */ + ssl_log(s, SSL_LOG_INFO, + "Init: Created hash-table (%d buckets) " + "in shared memory (%d bytes) for SSL session cache", n, avail); + return; +} + +void ssl_scache_shmht_kill(server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(); + + if (mc->pSessionCacheDataMM != NULL) { + ap_mm_destroy(mc->pSessionCacheDataMM); + mc->pSessionCacheDataMM = NULL; + } + return; +} + +BOOL ssl_scache_shmht_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess) +{ + SSLModConfigRec *mc = myModConfig(); + void *vp; + UCHAR ucaData[SSL_SESSION_MAX_DER]; + int nData; + UCHAR *ucp; + + /* streamline session data */ + ucp = ucaData; + nData = i2d_SSL_SESSION(sess, &ucp); + + ssl_mutex_on(s); + if (table_insert_kd(mc->tSessionCacheDataTable, + id, idlen, NULL, sizeof(time_t)+nData, + NULL, &vp, 1) != TABLE_ERROR_NONE) { + ssl_mutex_off(s); + return FALSE; + } + memcpy(vp, &expiry, sizeof(time_t)); + memcpy((char *)vp+sizeof(time_t), ucaData, nData); + ssl_mutex_off(s); + + /* allow the regular expiring to occur */ + ssl_scache_shmht_expire(s); + + return TRUE; +} + +SSL_SESSION *ssl_scache_shmht_retrieve(server_rec *s, UCHAR *id, int idlen) +{ + SSLModConfigRec *mc = myModConfig(); + void *vp; + SSL_SESSION *sess = NULL; + UCHAR *ucpData; + int nData; + time_t expiry; + time_t now; + int n; + + /* allow the regular expiring to occur */ + ssl_scache_shmht_expire(s); + + /* lookup key in table */ + ssl_mutex_on(s); + if (table_retrieve(mc->tSessionCacheDataTable, + id, idlen, &vp, &n) != TABLE_ERROR_NONE) { + ssl_mutex_off(s); + return NULL; + } + + /* copy over the information to the SCI */ + nData = n-sizeof(time_t); + ucpData = (UCHAR *)malloc(nData); + if (ucpData == NULL) { + ssl_mutex_off(s); + return NULL; + } + memcpy(&expiry, vp, sizeof(time_t)); + memcpy(ucpData, (char *)vp+sizeof(time_t), nData); + ssl_mutex_off(s); + + /* make sure the stuff is still not expired */ + now = time(NULL); + if (expiry <= now) { + ssl_scache_shmht_remove(s, id, idlen); + return NULL; + } + + /* unstreamed SSL_SESSION */ + sess = d2i_SSL_SESSION(NULL, &ucpData, nData); + + return sess; +} + +void ssl_scache_shmht_remove(server_rec *s, UCHAR *id, int idlen) +{ + SSLModConfigRec *mc = myModConfig(); + + /* remove value under key in table */ + ssl_mutex_on(s); + table_delete(mc->tSessionCacheDataTable, id, idlen, NULL, NULL); + ssl_mutex_off(s); + return; +} + +void ssl_scache_shmht_expire(server_rec *s) +{ + SSLModConfigRec *mc = myModConfig(); + SSLSrvConfigRec *sc = mySrvConfig(s); + static time_t tLast = 0; + table_linear_t iterator; + time_t tExpiresAt; + void *vpKey; + void *vpKeyThis; + void *vpData; + int nKey; + int nKeyThis; + int nData; + int nElements = 0; + int nDeleted = 0; + int bDelete; + int rc; + time_t tNow; + + /* + * make sure the expiration for still not-accessed session + * cache entries is done only from time to time + */ + tNow = time(NULL); + if (tNow < tLast+sc->nSessionCacheTimeout) + return; + tLast = tNow; + + ssl_mutex_on(s); + if (table_first_r(mc->tSessionCacheDataTable, &iterator, + &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) { + do { + bDelete = FALSE; + nElements++; + if (nData < sizeof(time_t) || vpData == NULL) + bDelete = TRUE; + else { + memcpy(&tExpiresAt, vpData, sizeof(time_t)); + if (tExpiresAt <= tNow) + bDelete = TRUE; + } + vpKeyThis = vpKey; + nKeyThis = nKey; + rc = table_next_r(mc->tSessionCacheDataTable, &iterator, + &vpKey, &nKey, &vpData, &nData); + if (bDelete) { + table_delete(mc->tSessionCacheDataTable, + vpKeyThis, nKeyThis, NULL, NULL); + nDeleted++; + } + } while (rc == TABLE_ERROR_NONE); + } + ssl_mutex_off(s); + ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache (SHMHT) Expiry: " + "old: %d, new: %d, removed: %d", nElements, nElements-nDeleted, nDeleted); + return; +} + +void ssl_scache_shmht_status(server_rec *s, pool *p, void (*func)(char *, void *), void *arg) +{ + SSLModConfigRec *mc = myModConfig(); + void *vpKey; + void *vpData; + int nKey; + int nData; + int nElem; + int nSize; + int nAverage; + + nElem = 0; + nSize = 0; + ssl_mutex_on(s); + if (table_first(mc->tSessionCacheDataTable, + &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) { + do { + if (vpKey == NULL || vpData == NULL) + continue; + nElem += 1; + nSize += nData; + } while (table_next(mc->tSessionCacheDataTable, + &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE); + } + ssl_mutex_off(s); + if (nSize > 0 && nElem > 0) + nAverage = nSize / nElem; + else + nAverage = 0; + func(ap_psprintf(p, "cache type: <b>SHMHT</b>, maximum size: <b>%d</b> bytes<br>", mc->nSessionCacheDataSize), arg); + func(ap_psprintf(p, "current sessions: <b>%d</b>, current size: <b>%d</b> bytes<br>", nElem, nSize), arg); + func(ap_psprintf(p, "average session size: <b>%d</b> bytes<br>", nAverage), arg); + return; +} + diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c new file mode 100644 index 0000000000..af4a9672f2 --- /dev/null +++ b/modules/ssl/ssl_util.c @@ -0,0 +1,437 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_util.c +** Utility Functions +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +/* ==================================================================== + * Copyright (c) 1995-1999 Ben Laurie. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by Ben Laurie + * for use in the Apache-SSL HTTP server project." + * + * 4. The name "Apache-SSL Server" must not be used to + * endorse or promote products derived from this software without + * prior written permission. + * + * 5. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by Ben Laurie + * for use in the Apache-SSL HTTP server project." + * + * THIS SOFTWARE IS PROVIDED BY BEN LAURIE ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BEN LAURIE OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + /* ``Every day of my life + I am forced to add another + name to the list of people + who piss me off!'' + -- Calvin */ +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Utility Functions +** _________________________________________________________________ +*/ + +char *ssl_util_server_root_relative(pool *p, char *what, char *arg) +{ + char *rv = NULL; + +#ifdef SSL_VENDOR + ap_hook_use("ap::mod_ssl::vendor::ssl_server_root_relative", + AP_HOOK_SIG4(ptr,ptr,ptr,ptr), AP_HOOK_ALL, &rv, p, what, arg); + if (rv != NULL) + return rv; +#endif + rv = ap_server_root_relative(p, arg); + return rv; +} + +char *ssl_util_vhostid(pool *p, server_rec *s) +{ + char *id; + SSLSrvConfigRec *sc; + char *host; + unsigned int port; + + host = s->server_hostname; + if (s->port != 0) + port = s->port; + else { + sc = mySrvConfig(s); + if (sc->bEnabled) + port = DEFAULT_HTTPS_PORT; + else + port = DEFAULT_HTTP_PORT; + } + id = ap_psprintf(p, "%s:%u", host, port); + return id; +} + +void ssl_util_strupper(char *s) +{ + for (; *s; ++s) + *s = toupper(*s); + return; +} + +static const char ssl_util_uuencode_six2pr[64+1] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +void ssl_util_uuencode(char *szTo, const char *szFrom, BOOL bPad) +{ + ssl_util_uuencode_binary((unsigned char *)szTo, + (const unsigned char *)szFrom, + strlen(szFrom), bPad); +} + +void ssl_util_uuencode_binary( + unsigned char *szTo, const unsigned char *szFrom, int nLength, BOOL bPad) +{ + const unsigned char *s; + int nPad = 0; + + for (s = szFrom; nLength > 0; s += 3) { + *szTo++ = ssl_util_uuencode_six2pr[s[0] >> 2]; + *szTo++ = ssl_util_uuencode_six2pr[(s[0] << 4 | s[1] >> 4) & 0x3f]; + if (--nLength == 0) { + nPad = 2; + break; + } + *szTo++ = ssl_util_uuencode_six2pr[(s[1] << 2 | s[2] >> 6) & 0x3f]; + if (--nLength == 0) { + nPad = 1; + break; + } + *szTo++ = ssl_util_uuencode_six2pr[s[2] & 0x3f]; + --nLength; + } + while(bPad && nPad--) + *szTo++ = NUL; + *szTo = NUL; + return; +} + +FILE *ssl_util_ppopen(server_rec *s, pool *p, char *cmd) +{ + FILE *fpout; + int rc; + + fpout = NULL; + rc = ap_spawn_child(p, ssl_util_ppopen_child, + (void *)cmd, kill_after_timeout, + NULL, &fpout, NULL); + if (rc == 0 || fpout == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, s, + "ssl_util_ppopen: could not run: %s", cmd); + return NULL; + } + return (fpout); +} + +int ssl_util_ppopen_child(void *cmd, child_info *pinfo) +{ + int child_pid = 1; + + /* + * Prepare for exec + */ + ap_cleanup_for_exec(); +#ifdef SIGHUP + signal(SIGHUP, SIG_IGN); +#endif + + /* + * Exec() the child program + */ +#if defined(WIN32) + /* MS Windows */ + { + char pCommand[MAX_STRING_LEN]; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ap_snprintf(pCommand, sizeof(pCommand), "%s /C %s", SHELL_PATH, cmd); + + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdInput = pinfo->hPipeInputRead; + si.hStdOutput = pinfo->hPipeOutputWrite; + si.hStdError = pinfo->hPipeErrorWrite; + + if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, 0, + environ, NULL, &si, &pi)) { + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + child_pid = pi.dwProcessId; + } + } +#elif defined(OS2) + /* IBM OS/2 */ + spawnl(P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL); +#else + /* Standard Unix */ + execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL); +#endif + return (child_pid); +} + +void ssl_util_ppclose(server_rec *s, pool *p, FILE *fp) +{ + ap_pfclose(p, fp); + return; +} + +/* + * Run a filter program and read the first line of its stdout output + */ +char *ssl_util_readfilter(server_rec *s, pool *p, char *cmd) +{ + static char buf[MAX_STRING_LEN]; + FILE *fp; + char c; + int k; + + if ((fp = ssl_util_ppopen(s, p, cmd)) == NULL) + return NULL; + for (k = 0; read(fileno(fp), &c, 1) == 1 + && (k < MAX_STRING_LEN-1) ; ) { + if (c == '\n' || c == '\r') + break; + buf[k++] = c; + } + buf[k] = NUL; + ssl_util_ppclose(s, p, fp); + + return buf; +} + +BOOL ssl_util_path_check(ssl_pathcheck_t pcm, char *path) +{ + struct stat sb; + + if (path == NULL) + return FALSE; + if (pcm & SSL_PCM_EXISTS && stat(path, &sb) != 0) + return FALSE; + if (pcm & SSL_PCM_ISREG && !S_ISREG(sb.st_mode)) + return FALSE; + if (pcm & SSL_PCM_ISDIR && !S_ISDIR(sb.st_mode)) + return FALSE; + if (pcm & SSL_PCM_ISNONZERO && sb.st_mode <= 0) + return FALSE; + return TRUE; +} + +ssl_algo_t ssl_util_algotypeof(X509 *pCert, EVP_PKEY *pKey) +{ + ssl_algo_t t; + + t = SSL_ALGO_UNKNOWN; + if (pCert != NULL) + pKey = X509_get_pubkey(pCert); + if (pKey != NULL) { + switch (EVP_PKEY_type(pKey->type)) { + case EVP_PKEY_RSA: + t = SSL_ALGO_RSA; + break; + case EVP_PKEY_DSA: + t = SSL_ALGO_DSA; + break; + default: + break; + } + } + return t; +} + +char *ssl_util_algotypestr(ssl_algo_t t) +{ + char *cp; + + cp = "UNKNOWN"; + switch (t) { + case SSL_ALGO_RSA: + cp = "RSA"; + break; + case SSL_ALGO_DSA: + cp = "DSA"; + break; + default: + break; + } + return cp; +} + +char *ssl_util_ptxtsub( + pool *p, const char *cpLine, const char *cpMatch, char *cpSubst) +{ +#define MAX_PTXTSUB 100 + char *cppMatch[MAX_PTXTSUB]; + char *cpResult; + int nResult; + int nLine; + int nSubst; + int nMatch; + char *cpI; + char *cpO; + char *cp; + int i; + + /* + * Pass 1: find substitution locations and calculate sizes + */ + nLine = strlen(cpLine); + nMatch = strlen(cpMatch); + nSubst = strlen(cpSubst); + for (cpI = (char *)cpLine, i = 0, nResult = 0; + cpI < cpLine+nLine && i < MAX_PTXTSUB; ) { + if ((cp = strstr(cpI, cpMatch)) != NULL) { + cppMatch[i++] = cp; + nResult += ((cp-cpI)+nSubst); + cpI = (cp+nMatch); + } + else { + nResult += strlen(cpI); + break; + } + } + cppMatch[i] = NULL; + if (i == 0) + return NULL; + + /* + * Pass 2: allocate memory and assemble result + */ + cpResult = ap_pcalloc(p, nResult+1); + for (cpI = (char *)cpLine, cpO = cpResult, i = 0; cppMatch[i] != NULL; i++) { + ap_cpystrn(cpO, cpI, cppMatch[i]-cpI+1); + cpO += (cppMatch[i]-cpI); + ap_cpystrn(cpO, cpSubst, nSubst+1); + cpO += nSubst; + cpI = (cppMatch[i]+nMatch); + } + ap_cpystrn(cpO, cpI, cpResult+nResult-cpO+1); + + return cpResult; +} + +/* _________________________________________________________________ +** +** Special Functions for Win32/OpenSSL +** _________________________________________________________________ +*/ + +#ifdef WIN32 +static HANDLE lock_cs[CRYPTO_NUM_LOCKS]; + +static void win32_locking_callback(int mode, int type, char* file, int line) +{ + if (mode & CRYPTO_LOCK) + WaitForSingleObject(lock_cs[type], INFINITE); + else + ReleaseMutex(lock_cs[type]); + return; +} +#endif /* WIN32 */ + +void ssl_util_thread_setup(void) +{ +#ifdef WIN32 + int i; + + for (i = 0; i < CRYPTO_NUM_LOCKS; i++) + lock_cs[i] = CreateMutex(NULL, FALSE, NULL); + CRYPTO_set_locking_callback((void(*)(int, int, const char *, int)) + win32_locking_callback); +#endif /* WIN32 */ + return; +} + diff --git a/modules/ssl/ssl_util_sdbm.c b/modules/ssl/ssl_util_sdbm.c new file mode 100644 index 0000000000..c728f90c86 --- /dev/null +++ b/modules/ssl/ssl_util_sdbm.c @@ -0,0 +1,926 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_util_sdbm.c +** Built-in Simple DBM +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. + * + * core routines + */ + +#include "mod_ssl.h" + +#ifdef SSL_USE_SDBM + +#include <stdio.h> +#include <stdlib.h> +#ifdef WIN32 +#include <io.h> +#include <errno.h> +#else +#include <unistd.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#ifdef __STDC__ +#include <stddef.h> +#endif + +#ifndef NULL +#define NULL (void *)0 +#endif + +/* + * externals + */ +#ifdef sun +extern int errno; +#endif + +/* + * forward + */ +static int getdbit proto((DBM *, long)); +static int setdbit proto((DBM *, long)); +static int getpage proto((DBM *, long)); +static datum getnext proto((DBM *)); +static int makroom proto((DBM *, long, int)); + +/* + * useful macros + */ +#define bad(x) ((x).dptr == NULL || (x).dsize <= 0) +#define exhash(item) sdbm_hash((item).dptr, (item).dsize) +#define ioerr(db) ((db)->flags |= DBM_IOERR) + +#define OFF_PAG(off) (long) (off) * PBLKSIZ +#define OFF_DIR(off) (long) (off) * DBLKSIZ + +static long masks[] = { + 000000000000, 000000000001, 000000000003, 000000000007, + 000000000017, 000000000037, 000000000077, 000000000177, + 000000000377, 000000000777, 000000001777, 000000003777, + 000000007777, 000000017777, 000000037777, 000000077777, + 000000177777, 000000377777, 000000777777, 000001777777, + 000003777777, 000007777777, 000017777777, 000037777777, + 000077777777, 000177777777, 000377777777, 000777777777, + 001777777777, 003777777777, 007777777777, 017777777777 +}; + +datum nullitem = {NULL, 0}; + +DBM * +sdbm_open(file, flags, mode) +register char *file; +register int flags; +register int mode; +{ + register DBM *db; + register char *dirname; + register char *pagname; + register int n; + + if (file == NULL || !*file) + return errno = EINVAL, (DBM *) NULL; +/* + * need space for two seperate filenames + */ + n = strlen(file) * 2 + strlen(DIRFEXT) + strlen(PAGFEXT) + 2; + + if ((dirname = malloc((unsigned) n)) == NULL) + return errno = ENOMEM, (DBM *) NULL; +/* + * build the file names + */ + dirname = strcat(strcpy(dirname, file), DIRFEXT); + pagname = strcpy(dirname + strlen(dirname) + 1, file); + pagname = strcat(pagname, PAGFEXT); + + db = sdbm_prep(dirname, pagname, flags, mode); + free((char *) dirname); + return db; +} + +DBM * +sdbm_prep(dirname, pagname, flags, mode) +char *dirname; +char *pagname; +int flags; +int mode; +{ + register DBM *db; + struct stat dstat; + + if ((db = (DBM *) malloc(sizeof(DBM))) == NULL) + return errno = ENOMEM, (DBM *) NULL; + + db->flags = 0; + db->hmask = 0; + db->blkptr = 0; + db->keyptr = 0; +/* + * adjust user flags so that WRONLY becomes RDWR, + * as required by this package. Also set our internal + * flag for RDONLY if needed. + */ + if (flags & O_WRONLY) + flags = (flags & ~O_WRONLY) | O_RDWR; + else if ((flags & 03) == O_RDONLY) + db->flags = DBM_RDONLY; +#if defined(OS2) || defined(MSDOS) || defined(WIN32) + flags |= O_BINARY; +#endif + +/* + * open the files in sequence, and stat the dirfile. + * If we fail anywhere, undo everything, return NULL. + */ + if ((db->pagf = open(pagname, flags, mode)) > -1) { + if ((db->dirf = open(dirname, flags, mode)) > -1) { +/* + * need the dirfile size to establish max bit number. + */ + if (fstat(db->dirf, &dstat) == 0) { +/* + * zero size: either a fresh database, or one with a single, + * unsplit data page: dirpage is all zeros. + */ + db->dirbno = (!dstat.st_size) ? 0 : -1; + db->pagbno = -1; + db->maxbno = dstat.st_size * BYTESIZ; + + (void) memset(db->pagbuf, 0, PBLKSIZ); + (void) memset(db->dirbuf, 0, DBLKSIZ); + /* + * success + */ + return db; + } + (void) close(db->dirf); + } + (void) close(db->pagf); + } + free((char *) db); + return (DBM *) NULL; +} + +void +sdbm_close(db) +register DBM *db; +{ + if (db == NULL) + errno = EINVAL; + else { + (void) close(db->dirf); + (void) close(db->pagf); + free((char *) db); + } +} + +datum +sdbm_fetch(db, key) +register DBM *db; +datum key; +{ + if (db == NULL || bad(key)) + return errno = EINVAL, nullitem; + + if (getpage(db, exhash(key))) + return getpair(db->pagbuf, key); + + return ioerr(db), nullitem; +} + +int +sdbm_delete(db, key) +register DBM *db; +datum key; +{ + if (db == NULL || bad(key)) + return errno = EINVAL, -1; + if (sdbm_rdonly(db)) + return errno = EPERM, -1; + + if (getpage(db, exhash(key))) { + if (!delpair(db->pagbuf, key)) + return -1; +/* + * update the page file + */ + if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0 + || write(db->pagf, db->pagbuf, PBLKSIZ) < 0) + return ioerr(db), -1; + + return 0; + } + + return ioerr(db), -1; +} + +int +sdbm_store(db, key, val, flags) +register DBM *db; +datum key; +datum val; +int flags; +{ + int need; + register long hash; + + if (db == NULL || bad(key)) + return errno = EINVAL, -1; + if (sdbm_rdonly(db)) + return errno = EPERM, -1; + + need = key.dsize + val.dsize; +/* + * is the pair too big (or too small) for this database ?? + */ + if (need < 0 || need > PAIRMAX) + return errno = EINVAL, -1; + + if (getpage(db, (hash = exhash(key)))) { +/* + * if we need to replace, delete the key/data pair + * first. If it is not there, ignore. + */ + if (flags == DBM_REPLACE) + (void) delpair(db->pagbuf, key); +#ifdef SEEDUPS + else if (duppair(db->pagbuf, key)) + return 1; +#endif +/* + * if we do not have enough room, we have to split. + */ + if (!fitpair(db->pagbuf, need)) + if (!makroom(db, hash, need)) + return ioerr(db), -1; +/* + * we have enough room or split is successful. insert the key, + * and update the page file. + */ + (void) putpair(db->pagbuf, key, val); + + if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0 + || write(db->pagf, db->pagbuf, PBLKSIZ) < 0) + return ioerr(db), -1; + /* + * success + */ + return 0; + } + + return ioerr(db), -1; +} + +/* + * makroom - make room by splitting the overfull page + * this routine will attempt to make room for SPLTMAX times before + * giving up. + */ +static int +makroom(db, hash, need) +register DBM *db; +long hash; +int need; +{ + long newp; + char twin[PBLKSIZ]; + char *pag = db->pagbuf; + char *new = twin; + register int smax = SPLTMAX; + + do { +/* + * split the current page + */ + (void) splpage(pag, new, db->hmask + 1); +/* + * address of the new page + */ + newp = (hash & db->hmask) | (db->hmask + 1); + +/* + * write delay, read avoidence/cache shuffle: + * select the page for incoming pair: if key is to go to the new page, + * write out the previous one, and copy the new one over, thus making + * it the current page. If not, simply write the new page, and we are + * still looking at the page of interest. current page is not updated + * here, as sdbm_store will do so, after it inserts the incoming pair. + */ + if (hash & (db->hmask + 1)) { + if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0 + || write(db->pagf, db->pagbuf, PBLKSIZ) < 0) + return 0; + db->pagbno = newp; + (void) memcpy(pag, new, PBLKSIZ); + } + else if (lseek(db->pagf, OFF_PAG(newp), SEEK_SET) < 0 + || write(db->pagf, new, PBLKSIZ) < 0) + return 0; + + if (!setdbit(db, db->curbit)) + return 0; +/* + * see if we have enough room now + */ + if (fitpair(pag, need)) + return 1; +/* + * try again... update curbit and hmask as getpage would have + * done. because of our update of the current page, we do not + * need to read in anything. BUT we have to write the current + * [deferred] page out, as the window of failure is too great. + */ + db->curbit = 2 * db->curbit + + ((hash & (db->hmask + 1)) ? 2 : 1); + db->hmask |= db->hmask + 1; + + if (lseek(db->pagf, OFF_PAG(db->pagbno), SEEK_SET) < 0 + || write(db->pagf, db->pagbuf, PBLKSIZ) < 0) + return 0; + + } while (--smax); +/* + * if we are here, this is real bad news. After SPLTMAX splits, + * we still cannot fit the key. say goodnight. + */ +#ifdef BADMESS + (void) write(2, "sdbm: cannot insert after SPLTMAX attempts.\n", 44); +#endif + return 0; + +} + +/* + * the following two routines will break if + * deletions aren't taken into account. (ndbm bug) + */ +datum +sdbm_firstkey(db) +register DBM *db; +{ + if (db == NULL) + return errno = EINVAL, nullitem; +/* + * start at page 0 + */ + if (lseek(db->pagf, OFF_PAG(0), SEEK_SET) < 0 + || read(db->pagf, db->pagbuf, PBLKSIZ) < 0) + return ioerr(db), nullitem; + db->pagbno = 0; + db->blkptr = 0; + db->keyptr = 0; + + return getnext(db); +} + +datum +sdbm_nextkey(db) +register DBM *db; +{ + if (db == NULL) + return errno = EINVAL, nullitem; + return getnext(db); +} + +/* + * all important binary trie traversal + */ +static int +getpage(db, hash) +register DBM *db; +register long hash; +{ + register int hbit; + register long dbit; + register long pagb; + + dbit = 0; + hbit = 0; + while (dbit < db->maxbno && getdbit(db, dbit)) + dbit = 2 * dbit + ((hash & (1 << hbit++)) ? 2 : 1); + + debug(("dbit: %d...", dbit)); + + db->curbit = dbit; + db->hmask = masks[hbit]; + + pagb = hash & db->hmask; +/* + * see if the block we need is already in memory. + * note: this lookaside cache has about 10% hit rate. + */ + if (pagb != db->pagbno) { +/* + * note: here, we assume a "hole" is read as 0s. + * if not, must zero pagbuf first. + */ + if (lseek(db->pagf, OFF_PAG(pagb), SEEK_SET) < 0 + || read(db->pagf, db->pagbuf, PBLKSIZ) < 0) + return 0; + if (!chkpage(db->pagbuf)) + return 0; + db->pagbno = pagb; + + debug(("pag read: %d\n", pagb)); + } + return 1; +} + +static int +getdbit(db, dbit) +register DBM *db; +register long dbit; +{ + register long c; + register long dirb; + + c = dbit / BYTESIZ; + dirb = c / DBLKSIZ; + + if (dirb != db->dirbno) { + if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0 + || read(db->dirf, db->dirbuf, DBLKSIZ) < 0) + return 0; + db->dirbno = dirb; + + debug(("dir read: %d\n", dirb)); + } + + return db->dirbuf[c % DBLKSIZ] & (1 << dbit % BYTESIZ); +} + +static int +setdbit(db, dbit) +register DBM *db; +register long dbit; +{ + register long c; + register long dirb; + + c = dbit / BYTESIZ; + dirb = c / DBLKSIZ; + + if (dirb != db->dirbno) { + if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0 + || read(db->dirf, db->dirbuf, DBLKSIZ) < 0) + return 0; + db->dirbno = dirb; + + debug(("dir read: %d\n", dirb)); + } + + db->dirbuf[c % DBLKSIZ] |= (1 << dbit % BYTESIZ); + + if (dbit >= db->maxbno) + db->maxbno += DBLKSIZ * BYTESIZ; + + if (lseek(db->dirf, OFF_DIR(dirb), SEEK_SET) < 0 + || write(db->dirf, db->dirbuf, DBLKSIZ) < 0) + return 0; + + return 1; +} + +/* + * getnext - get the next key in the page, and if done with + * the page, try the next page in sequence + */ +static datum +getnext(db) +register DBM *db; +{ + datum key; + + for (;;) { + db->keyptr++; + key = getnkey(db->pagbuf, db->keyptr); + if (key.dptr != NULL) + return key; +/* + * we either run out, or there is nothing on this page.. + * try the next one... If we lost our position on the + * file, we will have to seek. + */ + db->keyptr = 0; + if (db->pagbno != db->blkptr++) + if (lseek(db->pagf, OFF_PAG(db->blkptr), SEEK_SET) < 0) + break; + db->pagbno = db->blkptr; + if (read(db->pagf, db->pagbuf, PBLKSIZ) <= 0) + break; + if (!chkpage(db->pagbuf)) + break; + } + + return ioerr(db), nullitem; +} + +/* ************************* */ + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. keep it that way. + * + * hashing routine + */ + +/* + * polynomial conversion ignoring overflows + * [this seems to work remarkably well, in fact better + * then the ndbm hash function. Replace at your own risk] + * use: 65599 nice. + * 65587 even better. + */ +long +sdbm_hash(str, len) +register char *str; +register int len; +{ + register unsigned long n = 0; + +#ifdef DUFF +#define HASHC n = *str++ + 65599 * n + if (len > 0) { + register int loop = (len + 8 - 1) >> 3; + + switch(len & (8 - 1)) { + case 0: do { + HASHC; case 7: HASHC; + case 6: HASHC; case 5: HASHC; + case 4: HASHC; case 3: HASHC; + case 2: HASHC; case 1: HASHC; + } while (--loop); + } + + } +#else + while (len--) + n = *str++ + 65599 * n; +#endif + return n; +} + +/* ************************* */ + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Aake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. + * + * page-level routines + */ + +#define exhash(item) sdbm_hash((item).dptr, (item).dsize) + +/* + * forward + */ +static int seepair proto((char *, int, char *, int)); + +/* + * page format: + * +------------------------------+ + * ino | n | keyoff | datoff | keyoff | + * +------------+--------+--------+ + * | datoff | - - - ----> | + * +--------+---------------------+ + * | F R E E A R E A | + * +--------------+---------------+ + * | <---- - - - | data | + * +--------+-----+----+----------+ + * | key | data | key | + * +--------+----------+----------+ + * + * calculating the offsets for free area: if the number + * of entries (ino[0]) is zero, the offset to the END of + * the free area is the block size. Otherwise, it is the + * nth (ino[ino[0]]) entry's offset. + */ + +int +fitpair(pag, need) +char *pag; +int need; +{ + register int n; + register int off; + register int avail; + register short *ino = (short *) pag; + + off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ; + avail = off - (n + 1) * sizeof(short); + need += 2 * sizeof(short); + + debug(("free %d need %d\n", avail, need)); + + return need <= avail; +} + +void +putpair(pag, key, val) +char *pag; +datum key; +datum val; +{ + register int n; + register int off; + register short *ino = (short *) pag; + + off = ((n = ino[0]) > 0) ? ino[n] : PBLKSIZ; +/* + * enter the key first + */ + off -= key.dsize; + (void) memcpy(pag + off, key.dptr, key.dsize); + ino[n + 1] = off; +/* + * now the data + */ + off -= val.dsize; + (void) memcpy(pag + off, val.dptr, val.dsize); + ino[n + 2] = off; +/* + * adjust item count + */ + ino[0] += 2; +} + +datum +getpair(pag, key) +char *pag; +datum key; +{ + register int i; + register int n; + datum val; + register short *ino = (short *) pag; + + if ((n = ino[0]) == 0) + return nullitem; + + if ((i = seepair(pag, n, key.dptr, key.dsize)) == 0) + return nullitem; + + val.dptr = pag + ino[i + 1]; + val.dsize = ino[i] - ino[i + 1]; + return val; +} + +#ifdef SEEDUPS +int +duppair(pag, key) +char *pag; +datum key; +{ + register short *ino = (short *) pag; + return ino[0] > 0 && seepair(pag, ino[0], key.dptr, key.dsize) > 0; +} +#endif + +datum +getnkey(pag, num) +char *pag; +int num; +{ + datum key; + register int off; + register short *ino = (short *) pag; + + num = num * 2 - 1; + if (ino[0] == 0 || num > ino[0]) + return nullitem; + + off = (num > 1) ? ino[num - 1] : PBLKSIZ; + + key.dptr = pag + ino[num]; + key.dsize = off - ino[num]; + + return key; +} + +int +delpair(pag, key) +char *pag; +datum key; +{ + register int n; + register int i; + register short *ino = (short *) pag; + + if ((n = ino[0]) == 0) + return 0; + + if ((i = seepair(pag, n, key.dptr, key.dsize)) == 0) + return 0; +/* + * found the key. if it is the last entry + * [i.e. i == n - 1] we just adjust the entry count. + * hard case: move all data down onto the deleted pair, + * shift offsets onto deleted offsets, and adjust them. + * [note: 0 < i < n] + */ + if (i < n - 1) { + register int m; + register char *dst = pag + (i == 1 ? PBLKSIZ : ino[i - 1]); + register char *src = pag + ino[i + 1]; + register int zoo = dst - src; + + debug(("free-up %d ", zoo)); +/* + * shift data/keys down + */ + m = ino[i + 1] - ino[n]; +#ifdef DUFF +#define MOVB *--dst = *--src + if (m > 0) { + register int loop = (m + 8 - 1) >> 3; + + switch (m & (8 - 1)) { + case 0: do { + MOVB; case 7: MOVB; + case 6: MOVB; case 5: MOVB; + case 4: MOVB; case 3: MOVB; + case 2: MOVB; case 1: MOVB; + } while (--loop); + } + } +#else + dst -= m; + src -= m; + memmove(dst, src, m); +#endif +/* + * adjust offset index up + */ + while (i < n - 1) { + ino[i] = ino[i + 2] + zoo; + i++; + } + } + ino[0] -= 2; + return 1; +} + +/* + * search for the key in the page. + * return offset index in the range 0 < i < n. + * return 0 if not found. + */ +static int +seepair(pag, n, key, siz) +char *pag; +register int n; +register char *key; +register int siz; +{ + register int i; + register int off = PBLKSIZ; + register short *ino = (short *) pag; + + for (i = 1; i < n; i += 2) { + if (siz == off - ino[i] && + memcmp(key, pag + ino[i], siz) == 0) + return i; + off = ino[i + 1]; + } + return 0; +} + +void +splpage(pag, new, sbit) +char *pag; +char *new; +long sbit; +{ + datum key; + datum val; + + register int n; + register int off = PBLKSIZ; + char cur[PBLKSIZ]; + register short *ino = (short *) cur; + + (void) memcpy(cur, pag, PBLKSIZ); + (void) memset(pag, 0, PBLKSIZ); + (void) memset(new, 0, PBLKSIZ); + + n = ino[0]; + for (ino++; n > 0; ino += 2) { + key.dptr = cur + ino[0]; + key.dsize = off - ino[0]; + val.dptr = cur + ino[1]; + val.dsize = ino[0] - ino[1]; +/* + * select the page pointer (by looking at sbit) and insert + */ + (void) putpair((exhash(key) & sbit) ? new : pag, key, val); + + off = ino[1]; + n -= 2; + } + + debug(("%d split %d/%d\n", ((short *) cur)[0] / 2, + ((short *) new)[0] / 2, + ((short *) pag)[0] / 2)); +} + +/* + * check page sanity: + * number of entries should be something + * reasonable, and all offsets in the index should be in order. + * this could be made more rigorous. + */ +int +chkpage(pag) +char *pag; +{ + register int n; + register int off; + register short *ino = (short *) pag; + + if ((n = ino[0]) < 0 || n > PBLKSIZ / sizeof(short)) + return 0; + + if (n > 0) { + off = PBLKSIZ; + for (ino++; n > 0; ino += 2) { + if (ino[0] > off || ino[1] > off || + ino[1] > ino[0]) + return 0; + off = ino[1]; + n -= 2; + } + } + return 1; +} + +#endif /* SSL_USE_SDBM */ diff --git a/modules/ssl/ssl_util_sdbm.h b/modules/ssl/ssl_util_sdbm.h new file mode 100644 index 0000000000..723e8095b4 --- /dev/null +++ b/modules/ssl/ssl_util_sdbm.h @@ -0,0 +1,191 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_util_sdbm.c +** Built-in Simple DBM (Header) +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +/* + * sdbm - ndbm work-alike hashed database library + * based on Per-Ake Larson's Dynamic Hashing algorithms. BIT 18 (1978). + * author: oz@nexus.yorku.ca + * status: public domain. + */ + +#ifndef SSL_UTIL_SDBM_H +#define SSL_UTIL_SDBM_H + +#define DUFF /* go ahead and use the loop-unrolled version */ + +#include <stdio.h> + +#ifdef MOD_SSL +#define DBLKSIZ 16384 /* SSL cert chains require more */ +#define PBLKSIZ 8192 /* SSL cert chains require more */ +#define PAIRMAX 8008 /* arbitrary on PBLKSIZ-N */ +#else +#define DBLKSIZ 4096 +#define PBLKSIZ 1024 +#define PAIRMAX 1008 /* arbitrary on PBLKSIZ-N */ +#endif +#define SPLTMAX 10 /* maximum allowed splits */ + /* for a single insertion */ +#define DIRFEXT ".dir" +#define PAGFEXT ".pag" + +typedef struct { + int dirf; /* directory file descriptor */ + int pagf; /* page file descriptor */ + int flags; /* status/error flags, see below */ + long maxbno; /* size of dirfile in bits */ + long curbit; /* current bit number */ + long hmask; /* current hash mask */ + long blkptr; /* current block for nextkey */ + int keyptr; /* current key for nextkey */ + long blkno; /* current page to read/write */ + long pagbno; /* current page in pagbuf */ + char pagbuf[PBLKSIZ]; /* page file block buffer */ + long dirbno; /* current block in dirbuf */ + char dirbuf[DBLKSIZ]; /* directory file block buffer */ +} DBM; + +#define DBM_RDONLY 0x1 /* data base open read-only */ +#define DBM_IOERR 0x2 /* data base I/O error */ + +/* + * utility macros + */ +#define sdbm_rdonly(db) ((db)->flags & DBM_RDONLY) +#define sdbm_error(db) ((db)->flags & DBM_IOERR) + +#define sdbm_clearerr(db) ((db)->flags &= ~DBM_IOERR) /* ouch */ + +#define sdbm_dirfno(db) ((db)->dirf) +#define sdbm_pagfno(db) ((db)->pagf) + +typedef struct { + char *dptr; + int dsize; +} datum; + +extern datum nullitem; + +#ifdef __STDC__ +#define proto(p) p +#else +#define proto(p) () +#endif + +/* + * flags to sdbm_store + */ +#define DBM_INSERT 0 +#define DBM_REPLACE 1 + +/* + * ndbm interface + */ +extern DBM *sdbm_open proto((char *, int, int)); +extern void sdbm_close proto((DBM *)); +extern datum sdbm_fetch proto((DBM *, datum)); +extern int sdbm_delete proto((DBM *, datum)); +extern int sdbm_store proto((DBM *, datum, datum, int)); +extern datum sdbm_firstkey proto((DBM *)); +extern datum sdbm_nextkey proto((DBM *)); + +/* + * other + */ +extern DBM *sdbm_prep proto((char *, char *, int, int)); +extern long sdbm_hash proto((char *, int)); + +/* pair.h */ +extern int fitpair proto((char *, int)); +extern void putpair proto((char *, datum, datum)); +extern datum getpair proto((char *, datum)); +extern int delpair proto((char *, datum)); +extern int chkpage proto((char *)); +extern datum getnkey proto((char *, int)); +extern void splpage proto((char *, char *, long)); +extern int duppair proto((char *, datum)); + +/* tune.h */ +/* + * sdbm - ndbm work-alike hashed database library + * tuning and portability constructs [not nearly enough] + * author: oz@nexus.yorku.ca + */ + +#define BYTESIZ 8 + +/* + * important tuning parms (hah) + */ + +#define SEEDUPS /* always detect duplicates */ +#define BADMESS /* generate a message for worst case: + cannot make room after SPLTMAX splits */ +/* + * misc + */ +#ifdef DEBUG +#define debug(x) printf x +#else +#define debug(x) +#endif + +#endif /* SSL_UTIL_SDBM_H */ diff --git a/modules/ssl/ssl_util_ssl.c b/modules/ssl/ssl_util_ssl.c new file mode 100644 index 0000000000..19f6bd3629 --- /dev/null +++ b/modules/ssl/ssl_util_ssl.c @@ -0,0 +1,544 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_util_ssl.c +** Additional Utility Functions for OpenSSL +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#include "mod_ssl.h" + + +/* _________________________________________________________________ +** +** Additional High-Level Functions for OpenSSL +** _________________________________________________________________ +*/ + +int SSL_get_app_data2_idx(void) +{ + static int app_data2_idx = -1; + + if (app_data2_idx < 0) { + app_data2_idx = SSL_get_ex_new_index(0, + "Second Application Data for SSL", NULL, NULL, NULL); + app_data2_idx = SSL_get_ex_new_index(0, + "Second Application Data for SSL", NULL, NULL, NULL); + } + return(app_data2_idx); +} + +void *SSL_get_app_data2(SSL *ssl) +{ + return (void *)SSL_get_ex_data(ssl, SSL_get_app_data2_idx()); +} + +void SSL_set_app_data2(SSL *ssl, void *arg) +{ + SSL_set_ex_data(ssl, SSL_get_app_data2_idx(), (char *)arg); + return; +} + +/* _________________________________________________________________ +** +** High-Level Certificate / Private Key Loading +** _________________________________________________________________ +*/ + +X509 *SSL_read_X509(FILE *fp, X509 **x509, int (*cb)()) +{ + X509 *rc; + BIO *bioS; + BIO *bioF; + + /* 1. try PEM (= DER+Base64+headers) */ +#if SSL_LIBRARY_VERSION < 0x00904000 + rc = PEM_read_X509(fp, x509, cb); +#else + rc = PEM_read_X509(fp, x509, cb, NULL); +#endif + if (rc == NULL) { + /* 2. try DER+Base64 */ + fseek(fp, 0L, SEEK_SET); + if ((bioS = BIO_new(BIO_s_fd())) == NULL) + return NULL; + BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE); + if ((bioF = BIO_new(BIO_f_base64())) == NULL) { + BIO_free(bioS); + return NULL; + } + bioS = BIO_push(bioF, bioS); + rc = d2i_X509_bio(bioS, NULL); + BIO_free_all(bioS); + if (rc == NULL) { + /* 3. try plain DER */ + fseek(fp, 0L, SEEK_SET); + if ((bioS = BIO_new(BIO_s_fd())) == NULL) + return NULL; + BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE); + rc = d2i_X509_bio(bioS, NULL); + BIO_free(bioS); + } + } + if (rc != NULL && x509 != NULL) { + if (*x509 != NULL) + X509_free(*x509); + *x509 = rc; + } + return rc; +} + +#if SSL_LIBRARY_VERSION <= 0x00904100 +static EVP_PKEY *d2i_PrivateKey_bio(BIO *bio, EVP_PKEY **key) +{ + return ((EVP_PKEY *)ASN1_d2i_bio( + (char *(*)())EVP_PKEY_new, + (char *(*)())d2i_PrivateKey, + (bio), (unsigned char **)(key))); +} +#endif + +EVP_PKEY *SSL_read_PrivateKey(FILE *fp, EVP_PKEY **key, int (*cb)()) +{ + EVP_PKEY *rc; + BIO *bioS; + BIO *bioF; + + /* 1. try PEM (= DER+Base64+headers) */ +#if SSL_LIBRARY_VERSION < 0x00904000 + rc = PEM_read_PrivateKey(fp, key, cb); +#else + rc = PEM_read_PrivateKey(fp, key, cb, NULL); +#endif + if (rc == NULL) { + /* 2. try DER+Base64 */ + fseek(fp, 0L, SEEK_SET); + if ((bioS = BIO_new(BIO_s_fd())) == NULL) + return NULL; + BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE); + if ((bioF = BIO_new(BIO_f_base64())) == NULL) { + BIO_free(bioS); + return NULL; + } + bioS = BIO_push(bioF, bioS); + rc = d2i_PrivateKey_bio(bioS, NULL); + BIO_free_all(bioS); + if (rc == NULL) { + /* 3. try plain DER */ + fseek(fp, 0L, SEEK_SET); + if ((bioS = BIO_new(BIO_s_fd())) == NULL) + return NULL; + BIO_set_fd(bioS, fileno(fp), BIO_NOCLOSE); + rc = d2i_PrivateKey_bio(bioS, NULL); + BIO_free(bioS); + } + } + if (rc != NULL && key != NULL) { + if (*key != NULL) + EVP_PKEY_free(*key); + *key = rc; + } + return rc; +} + +/* _________________________________________________________________ +** +** Smart shutdown +** _________________________________________________________________ +*/ + +int SSL_smart_shutdown(SSL *ssl) +{ + int i; + int rc; + + /* + * Repeat the calls, because SSL_shutdown internally dispatches through a + * little state machine. Usually only one or two interation should be + * needed, so we restrict the total number of restrictions in order to + * avoid process hangs in case the client played bad with the socket + * connection and OpenSSL cannot recognize it. + */ + rc = 0; + for (i = 0; i < 4 /* max 2x pending + 2x data = 4 */; i++) { + if ((rc = SSL_shutdown(ssl))) + break; + } + return rc; +} + +/* _________________________________________________________________ +** +** Certificate Revocation List (CRL) Storage +** _________________________________________________________________ +*/ + +X509_STORE *SSL_X509_STORE_create(char *cpFile, char *cpPath) +{ + X509_STORE *pStore; + X509_LOOKUP *pLookup; + + if (cpFile == NULL && cpPath == NULL) + return NULL; + if ((pStore = X509_STORE_new()) == NULL) + return NULL; + if (cpFile != NULL) { + if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_file())) == NULL) { + X509_STORE_free(pStore); + return NULL; + } + X509_LOOKUP_load_file(pLookup, cpFile, X509_FILETYPE_PEM); + } + if (cpPath != NULL) { + if ((pLookup = X509_STORE_add_lookup(pStore, X509_LOOKUP_hash_dir())) == NULL) { + X509_STORE_free(pStore); + return NULL; + } + X509_LOOKUP_add_dir(pLookup, cpPath, X509_FILETYPE_PEM); + } + return pStore; +} + +int SSL_X509_STORE_lookup(X509_STORE *pStore, int nType, + X509_NAME *pName, X509_OBJECT *pObj) +{ + X509_STORE_CTX pStoreCtx; + int rc; + + X509_STORE_CTX_init(&pStoreCtx, pStore, NULL, NULL); + rc = X509_STORE_get_by_subject(&pStoreCtx, nType, pName, pObj); + X509_STORE_CTX_cleanup(&pStoreCtx); + return rc; +} + +/* _________________________________________________________________ +** +** Cipher Suite Spec String Creation +** _________________________________________________________________ +*/ + +char *SSL_make_ciphersuite(pool *p, SSL *ssl) +{ + STACK_OF(SSL_CIPHER) *sk; + SSL_CIPHER *c; + int i; + int l; + char *cpCipherSuite; + char *cp; + + if (ssl == NULL) + return ""; + if ((sk = SSL_get_ciphers(ssl)) == NULL) + return ""; + l = 0; + for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) { + c = sk_SSL_CIPHER_value(sk, i); + l += strlen(c->name)+2+1; + } + if (l == 0) + return ""; + cpCipherSuite = (char *)ap_palloc(p, l+1); + cp = cpCipherSuite; + for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) { + c = sk_SSL_CIPHER_value(sk, i); + l = strlen(c->name); + memcpy(cp, c->name, l); + cp += l; + *cp++ = '/'; + *cp++ = (c->valid == 1 ? '1' : '0'); + *cp++ = ':'; + } + *(cp-1) = NUL; + return cpCipherSuite; +} + +/* _________________________________________________________________ +** +** Certificate Checks +** _________________________________________________________________ +*/ + +/* check whether cert contains extended key usage with a SGC tag */ +BOOL SSL_X509_isSGC(X509 *cert) +{ + X509_EXTENSION *ext; + int ext_nid; + STACK *sk; + BOOL is_sgc; + int idx; + int i; + + is_sgc = FALSE; + idx = X509_get_ext_by_NID(cert, NID_ext_key_usage, -1); + if (idx >= 0) { + ext = X509_get_ext(cert, idx); + if ((sk = (STACK *)X509V3_EXT_d2i(ext)) != NULL) { + for (i = 0; i < sk_num(sk); i++) { + ext_nid = OBJ_obj2nid((ASN1_OBJECT *)sk_value(sk, i)); + if (ext_nid == NID_ms_sgc || ext_nid == NID_ns_sgc) { + is_sgc = TRUE; + break; + } + } + } + } + return is_sgc; +} + +/* retrieve basic constraints ingredients */ +BOOL SSL_X509_getBC(X509 *cert, int *ca, int *pathlen) +{ + X509_EXTENSION *ext; + BASIC_CONSTRAINTS *bc; + int idx; + BIGNUM *bn = NULL; + char *cp; + + if ((idx = X509_get_ext_by_NID(cert, NID_basic_constraints, -1)) < 0) + return FALSE; + ext = X509_get_ext(cert, idx); + if (ext == NULL) + return FALSE; + if ((bc = (BASIC_CONSTRAINTS *)X509V3_EXT_d2i(ext)) == NULL) + return FALSE; + *ca = bc->ca; + *pathlen = -1 /* unlimited */; + if (bc->pathlen != NULL) { + if ((bn = ASN1_INTEGER_to_BN(bc->pathlen, NULL)) == NULL) + return FALSE; + if ((cp = BN_bn2dec(bn)) == NULL) + return FALSE; + *pathlen = atoi(cp); + free(cp); + BN_free(bn); + } + BASIC_CONSTRAINTS_free(bc); + return TRUE; +} + +/* retrieve subject CommonName of certificate */ +BOOL SSL_X509_getCN(pool *p, X509 *xs, char **cppCN) +{ + X509_NAME *xsn; + X509_NAME_ENTRY *xsne; + int i, nid; + + xsn = X509_get_subject_name(xs); + for (i = 0; i < sk_X509_NAME_ENTRY_num(xsn->entries); i++) { + xsne = sk_X509_NAME_ENTRY_value(xsn->entries, i); + nid = OBJ_obj2nid(xsne->object); + if (nid == NID_commonName) { + *cppCN = ap_palloc(p, xsne->value->length+1); + ap_cpystrn(*cppCN, (char *)xsne->value->data, xsne->value->length+1); + (*cppCN)[xsne->value->length] = NUL; +#ifdef CHARSET_EBCDIC + ascii2ebcdic(*cppCN, *cppCN, strlen(*cppCN)); +#endif + return TRUE; + } + } + return FALSE; +} + +/* _________________________________________________________________ +** +** Low-Level CA Certificate Loading +** _________________________________________________________________ +*/ + +#ifdef SSL_EXPERIMENTAL_PROXY + +BOOL SSL_load_CrtAndKeyInfo_file(pool *p, STACK_OF(X509_INFO) *sk, char *filename) +{ + BIO *in; + + if ((in = BIO_new(BIO_s_file())) == NULL) + return FALSE; + if (BIO_read_filename(in, filename) <= 0) { + BIO_free(in); + return FALSE; + } + ERR_clear_error(); +#if SSL_LIBRARY_VERSION < 0x00904000 + PEM_X509_INFO_read_bio(in, sk, NULL); +#else + PEM_X509_INFO_read_bio(in, sk, NULL, NULL); +#endif + BIO_free(in); + return TRUE; +} + +BOOL SSL_load_CrtAndKeyInfo_path(pool *p, STACK_OF(X509_INFO) *sk, char *pathname) +{ + struct stat st; + DIR *dir; + pool *sp; + struct dirent *nextent; + char *fullname; + BOOL ok; + + sp = ap_make_sub_pool(p); + if ((dir = ap_popendir(sp, pathname)) == NULL) { + ap_destroy_pool(sp); + return FALSE; + } + ok = FALSE; + while ((nextent = readdir(dir)) != NULL) { + fullname = ap_pstrcat(sp, pathname, "/", nextent->d_name, NULL); + if (stat(fullname, &st) != 0) + continue; + if (!S_ISREG(st.st_mode)) + continue; + if (SSL_load_CrtAndKeyInfo_file(sp, sk, fullname)) + ok = TRUE; + } + ap_pclosedir(p, dir); + ap_destroy_pool(sp); + return ok; +} + +#endif /* SSL_EXPERIMENTAL_PROXY */ + +/* _________________________________________________________________ +** +** Extra Server Certificate Chain Support +** _________________________________________________________________ +*/ + +/* + * Read a file that optionally contains the server certificate in PEM + * format, possibly followed by a sequence of CA certificates that + * should be sent to the peer in the SSL Certificate message. + */ +int SSL_CTX_use_certificate_chain( + SSL_CTX *ctx, char *file, int skipfirst, int (*cb)()) +{ + BIO *bio; + X509 *x509; + unsigned long err; + int n; + + if ((bio = BIO_new(BIO_s_file_internal())) == NULL) + return -1; + if (BIO_read_filename(bio, file) <= 0) { + BIO_free(bio); + return -1; + } + /* optionally skip a leading server certificate */ + if (skipfirst) { +#if SSL_LIBRARY_VERSION < 0x00904000 + if ((x509 = PEM_read_bio_X509(bio, NULL, cb)) == NULL) { +#else + if ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) == NULL) { +#endif + BIO_free(bio); + return -1; + } + X509_free(x509); + } + /* free a perhaps already configured extra chain */ + if (ctx->extra_certs != NULL) { + sk_X509_pop_free(ctx->extra_certs, X509_free); + ctx->extra_certs = NULL; + } + /* create new extra chain by loading the certs */ + n = 0; +#if SSL_LIBRARY_VERSION < 0x00904000 + while ((x509 = PEM_read_bio_X509(bio, NULL, cb)) != NULL) { +#else + while ((x509 = PEM_read_bio_X509(bio, NULL, cb, NULL)) != NULL) { +#endif + if (!SSL_CTX_add_extra_chain_cert(ctx, x509)) { + X509_free(x509); + BIO_free(bio); + return -1; + } + n++; + } + /* Make sure that only the error is just an EOF */ + if ((err = ERR_peek_error()) > 0) { + if (!( ERR_GET_LIB(err) == ERR_LIB_PEM + && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { + BIO_free(bio); + return -1; + } + while (ERR_get_error() > 0) ; + } + BIO_free(bio); + return n; +} + +/* _________________________________________________________________ +** +** Session Stuff +** _________________________________________________________________ +*/ + +char *SSL_SESSION_id2sz(unsigned char *id, int idlen) +{ + static char str[(SSL_MAX_SSL_SESSION_ID_LENGTH+1)*2]; + char *cp; + int n; + + cp = str; + for (n = 0; n < idlen && n < SSL_MAX_SSL_SESSION_ID_LENGTH; n++) { + ap_snprintf(cp, sizeof(str)-(cp-str), "%02X", id[n]); + cp += 2; + } + *cp = NUL; + return str; +} + diff --git a/modules/ssl/ssl_util_ssl.h b/modules/ssl/ssl_util_ssl.h new file mode 100644 index 0000000000..23aaaaa40d --- /dev/null +++ b/modules/ssl/ssl_util_ssl.h @@ -0,0 +1,115 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_util_ssl.h +** Additional Utility Functions for OpenSSL +*/ + +/* ==================================================================== + * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +#ifndef SSL_UTIL_SSL_H +#define SSL_UTIL_SSL_H + +/* + * Determine SSL library version number + */ +#ifdef OPENSSL_VERSION_NUMBER +#define SSL_LIBRARY_VERSION OPENSSL_VERSION_NUMBER +#define SSL_LIBRARY_NAME "OpenSSL" +#define SSL_LIBRARY_TEXT OPENSSL_VERSION_TEXT +#else +#define SSL_LIBRARY_VERSION 0x0000 +#define SSL_LIBRARY_NAME "OtherSSL" +#define SSL_LIBRARY_TEXT "OtherSSL 0.0.0 00 XXX 0000" +#endif + +/* + * Support for retrieving/overriding states + */ +#ifndef SSL_get_state +#define SSL_get_state(ssl) SSL_state(ssl) +#endif +#define SSL_set_state(ssl,val) (ssl)->state = val + +/* + * Maximum length of a DER encoded session. + * FIXME: There is no define in OpenSSL, but OpenSSL uses 1024*10, + * so this value should be ok. Although we have no warm feeling. + */ +#define SSL_SESSION_MAX_DER 1024*10 + +/* + * Additional Functions + */ +int SSL_get_app_data2_idx(void); +void *SSL_get_app_data2(SSL *); +void SSL_set_app_data2(SSL *, void *); +X509 *SSL_read_X509(FILE *, X509 **, int (*)()); +EVP_PKEY *SSL_read_PrivateKey(FILE *, EVP_PKEY **, int (*)()); +int SSL_smart_shutdown(SSL *ssl); +X509_STORE *SSL_X509_STORE_create(char *, char *); +int SSL_X509_STORE_lookup(X509_STORE *, int, X509_NAME *, X509_OBJECT *); +char *SSL_make_ciphersuite(pool *, SSL *); +BOOL SSL_X509_isSGC(X509 *); +BOOL SSL_X509_getBC(X509 *, int *, int *); +BOOL SSL_X509_getCN(pool *, X509 *, char **); +#ifdef SSL_EXPERIMENTAL_PROXY +BOOL SSL_load_CrtAndKeyInfo_file(pool *, STACK_OF(X509_INFO) *, char *); +BOOL SSL_load_CrtAndKeyInfo_path(pool *, STACK_OF(X509_INFO) *, char *); +#endif /* SSL_EXPERIMENTAL_PROXY */ +int SSL_CTX_use_certificate_chain(SSL_CTX *, char *, int, int (*)()); +char *SSL_SESSION_id2sz(unsigned char *, int); + +#endif /* SSL_UTIL_SSL_H */ diff --git a/modules/ssl/ssl_util_table.c b/modules/ssl/ssl_util_table.c new file mode 100644 index 0000000000..f68c6dd0fe --- /dev/null +++ b/modules/ssl/ssl_util_table.c @@ -0,0 +1,2868 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_util_table.c +** High Performance Hash Table Functions +*/ + +/* ==================================================================== + * Copyright (c) 1999-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +/* + * Generic hash table handler + * Table 4.1.0 July-28-1998 + * + * This library is a generic open hash table with buckets and + * linked lists. It is pretty high performance. Each element + * has a key and a data. The user indexes on the key to find the + * data. + * + * Copyright 1998 by Gray Watson <gray@letters.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, + * and that the name of Gray Watson not be used in advertising or + * publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + * + * Modified in March 1999 by Ralf S. Engelschall <rse@engelschall.com> + * for use in the mod_ssl project: + * o merged table_loc.h header into table.c + * o removed fillproto-comments from table.h + * o removed mmap() support because it's too unportable + * o added support for MM library via ta_{malloc,calloc,realloc,free} + */ + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef WIN32 +#include <io.h> +#include <errno.h> +#else +#include <unistd.h> +#endif + +/* forward definitions for table.h */ +typedef struct table_st table_t; +typedef struct table_entry_st table_entry_t; + +#define TABLE_PRIVATE +#include "ssl_util_table.h" + +/****************************** local defines ******************************/ + +#ifndef BITSPERBYTE +#define BITSPERBYTE 8 +#endif +#ifndef BITS +#define BITS(type) (BITSPERBYTE * (int)sizeof(type)) +#endif + +#define TABLE_MAGIC 0xBADF00D /* very magic magicness */ +#define LINEAR_MAGIC 0xAD00D00 /* magic value for linear struct */ +#define DEFAULT_SIZE 1024 /* default table size */ +#define MAX_ALIGNMENT 128 /* max alignment value */ +#define MAX_SORT_SPLITS 128 /* qsort can handle 2^128 entries */ + +/* returns 1 when we should grow or shrink the table */ +#define SHOULD_TABLE_GROW(tab) ((tab)->ta_entry_n > (tab)->ta_bucket_n * 2) +#define SHOULD_TABLE_SHRINK(tab) ((tab)->ta_entry_n < (tab)->ta_bucket_n / 2) + +/* + * void HASH_MIX + * + * DESCRIPTION: + * + * Mix 3 32-bit values reversibly. For every delta with one or two bits + * set, and the deltas of all three high bits or all three low bits, + * whether the original value of a,b,c is almost all zero or is + * uniformly distributed. + * + * If HASH_MIX() is run forward or backward, at least 32 bits in a,b,c + * have at least 1/4 probability of changing. If mix() is run + * forward, every bit of c will change between 1/3 and 2/3 of the + * time. (Well, 22/100 and 78/100 for some 2-bit deltas.) + * + * HASH_MIX() takes 36 machine instructions, but only 18 cycles on a + * superscalar machine (like a Pentium or a Sparc). No faster mixer + * seems to work, that's the result of my brute-force search. There + * were about 2^68 hashes to choose from. I only tested about a + * billion of those. + */ +#define HASH_MIX(a, b, c) \ + do { \ + a -= b; a -= c; a ^= (c >> 13); \ + b -= c; b -= a; b ^= (a << 8); \ + c -= a; c -= b; c ^= (b >> 13); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 16); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 3); \ + b -= c; b -= a; b ^= (a << 10); \ + c -= a; c -= b; c ^= (b >> 15); \ + } while(0) + +#define TABLE_POINTER(table, type, pnt) (pnt) + +/* + * Macros to get at the key and the data pointers + */ +#define ENTRY_KEY_BUF(entry_p) ((entry_p)->te_key_buf) +#define ENTRY_DATA_BUF(tab_p, entry_p) \ + (ENTRY_KEY_BUF(entry_p) + (entry_p)->te_key_size) + +/* + * Table structures... + */ + +/* + * HACK: this should be equiv as the table_entry_t without the key_buf + * char. We use this with the ENTRY_SIZE() macro above which solves + * the problem with the lack of the [0] GNU hack. We use the + * table_entry_t structure to better map the memory and make things + * faster. + */ +typedef struct table_shell_st { + unsigned int te_key_size; /* size of data */ + unsigned int te_data_size; /* size of data */ + struct table_shell_st *te_next_p; /* pointer to next in the list */ + /* NOTE: this does not have the te_key_buf field here */ +} table_shell_t; + +/* + * Elements in the bucket linked-lists. The key[1] is the start of + * the key with the rest of the key and all of the data information + * packed in memory directly after the end of this structure. + * + * NOTE: if this structure is changed, the table_shell_t must be changed + * to match. + */ +struct table_entry_st { + unsigned int te_key_size; /* size of data */ + unsigned int te_data_size; /* size of data */ + struct table_entry_st *te_next_p; /* pointer to next in the list */ + unsigned char te_key_buf[1]; /* 1st byte of key buf */ +}; + +/* external structure for debuggers be able to see void */ +typedef table_entry_t table_entry_ext_t; + +/* main table structure */ +struct table_st { + unsigned int ta_magic; /* magic number */ + unsigned int ta_flags; /* table's flags defined in table.h */ + unsigned int ta_bucket_n; /* num of buckets, should be 2^X */ + unsigned int ta_entry_n; /* num of entries in all buckets */ + unsigned int ta_data_align; /* data alignment value */ + table_entry_t **ta_buckets; /* array of linked lists */ + table_linear_t ta_linear; /* linear tracking */ + unsigned long ta_file_size; /* size of on-disk space */ + void *(*ta_malloc)(size_t size); + void *(*ta_calloc)(size_t number, size_t size); + void *(*ta_realloc)(void *ptr, size_t size); + void (*ta_free)(void *ptr); +}; + +/* external table structure for debuggers */ +typedef table_t table_ext_t; + +/* local comparison functions */ +typedef int (*compare_t) (const void *element1_p, const void *element2_p, + table_compare_t user_compare, + const table_t * table_p); + +/* + * to map error to string + */ +typedef struct { + int es_error; /* error number */ + char *es_string; /* assocaited string */ +} error_str_t; + +static error_str_t errors[] = +{ + {TABLE_ERROR_NONE, "no error"}, + {TABLE_ERROR_PNT, "invalid table pointer"}, + {TABLE_ERROR_ARG_NULL, "buffer argument is null"}, + {TABLE_ERROR_SIZE, "incorrect size argument"}, + {TABLE_ERROR_OVERWRITE, "key exists and no overwrite"}, + {TABLE_ERROR_NOT_FOUND, "key does not exist"}, + {TABLE_ERROR_ALLOC, "error allocating memory"}, + {TABLE_ERROR_LINEAR, "linear access not in progress"}, + {TABLE_ERROR_OPEN, "could not open file"}, + {TABLE_ERROR_SEEK, "could not seek to position in file"}, + {TABLE_ERROR_READ, "could not read from file"}, + {TABLE_ERROR_WRITE, "could not write to file"}, + {TABLE_ERROR_EMPTY, "table is empty"}, + {TABLE_ERROR_NOT_EMPTY, "table contains data"}, + {TABLE_ERROR_ALIGNMENT, "invalid alignment value"}, + {0} +}; + +#define INVALID_ERROR "invalid error code" + +/****************************** local functions ******************************/ + +/* + * static table_entry_t *first_entry + * + * DESCRIPTION: + * + * Return the first entry in the table. It will set the linear + * structure counter to the position of the first entry. + * + * RETURNS: + * + * Success: A pointer to the first entry in the table. + * + * Failure: NULL if there is no first entry. + * + * ARGUMENTS: + * + * table_p - Table whose next entry we are finding. + * + * linear_p - Pointer to a linear structure which we will advance and + * then find the corresponding entry. + */ +static table_entry_t *first_entry(table_t * table_p, + table_linear_t * linear_p) +{ + table_entry_t *entry_p; + unsigned int bucket_c = 0; + + /* look for the first non-empty bucket */ + for (bucket_c = 0; bucket_c < table_p->ta_bucket_n; bucket_c++) { + entry_p = table_p->ta_buckets[bucket_c]; + if (entry_p != NULL) { + if (linear_p != NULL) { + linear_p->tl_bucket_c = bucket_c; + linear_p->tl_entry_c = 0; + } + return TABLE_POINTER(table_p, table_entry_t *, entry_p); + } + } + + return NULL; +} + +/* + * static table_entry_t *next_entry + * + * DESCRIPTION: + * + * Return the next entry in the table which is past the position in + * our linear pointer. It will advance the linear structure counters. + * + * RETURNS: + * + * Success: A pointer to the next entry in the table. + * + * Failure: NULL. + * + * ARGUMENTS: + * + * table_p - Table whose next entry we are finding. + * + * linear_p - Pointer to a linear structure which we will advance and + * then find the corresponding entry. + * + * error_p - Pointer to an integer which when the routine returns will + * contain a table error code. + */ +static table_entry_t *next_entry(table_t * table_p, table_linear_t * linear_p, + int *error_p) +{ + table_entry_t *entry_p; + int entry_c; + + /* can't next if we haven't first-ed */ + if (linear_p == NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_LINEAR; + return NULL; + } + + if (linear_p->tl_bucket_c >= table_p->ta_bucket_n) { + /* + * NOTE: this might happen if we delete an item which shortens the + * table bucket numbers. + */ + if (error_p != NULL) + *error_p = TABLE_ERROR_NOT_FOUND; + return NULL; + } + + linear_p->tl_entry_c++; + + /* find the entry which is the nth in the list */ + entry_p = table_p->ta_buckets[linear_p->tl_bucket_c]; + /* NOTE: we swap the order here to be more efficient */ + for (entry_c = linear_p->tl_entry_c; entry_c > 0; entry_c--) { + /* did we reach the end of the list? */ + if (entry_p == NULL) + break; + entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p)->te_next_p; + } + + /* did we find an entry in the current bucket? */ + if (entry_p != NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_NONE; + return TABLE_POINTER(table_p, table_entry_t *, entry_p); + } + + /* find the first entry in the next non-empty bucket */ + + linear_p->tl_entry_c = 0; + for (linear_p->tl_bucket_c++; linear_p->tl_bucket_c < table_p->ta_bucket_n; + linear_p->tl_bucket_c++) { + entry_p = table_p->ta_buckets[linear_p->tl_bucket_c]; + if (entry_p != NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_NONE; + return TABLE_POINTER(table_p, table_entry_t *, entry_p); + } + } + + if (error_p != NULL) + *error_p = TABLE_ERROR_NOT_FOUND; + return NULL; +} + +/* + * static unsigned int hash + * + * DESCRIPTION: + * + * Hash a variable-length key into a 32-bit value. Every bit of the + * key affects every bit of the return value. Every 1-bit and 2-bit + * delta achieves avalanche. About (6 * len + 35) instructions. The + * best hash table sizes are powers of 2. There is no need to use mod + * (sooo slow!). If you need less than 32 bits, use a bitmask. For + * example, if you need only 10 bits, do h = (h & hashmask(10)); In + * which case, the hash table should have hashsize(10) elements. + * + * By Bob Jenkins, 1996. bob_jenkins@compuserve.com. You may use + * this code any way you wish, private, educational, or commercial. + * It's free. See + * http://ourworld.compuserve.com/homepages/bob_jenkins/evahash.htm + * Use for hash table lookup, or anything where one collision in 2^^32 + * is acceptable. Do NOT use for cryptographic purposes. + * + * RETURNS: + * + * Returns a 32-bit hash value. + * + * ARGUMENTS: + * + * key - Key (the unaligned variable-length array of bytes) that we + * are hashing. + * + * length - Length of the key in bytes. + * + * init_val - Initialization value of the hash if you need to hash a + * number of strings together. For instance, if you are hashing N + * strings (unsigned char **)keys, do it like this: + * + * for (i=0, h=0; i<N; ++i) h = hash( keys[i], len[i], h); + */ +static unsigned int hash(const unsigned char *key, + const unsigned int length, + const unsigned int init_val) +{ + const unsigned char *key_p = key; + unsigned int a, b, c, len; + + /* set up the internal state */ + a = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + b = 0x9e3779b9; + c = init_val; /* the previous hash value */ + + /* handle most of the key */ + for (len = length; len >= 12; len -= 12) { + a += (key_p[0] + + ((unsigned long) key_p[1] << 8) + + ((unsigned long) key_p[2] << 16) + + ((unsigned long) key_p[3] << 24)); + b += (key_p[4] + + ((unsigned long) key_p[5] << 8) + + ((unsigned long) key_p[6] << 16) + + ((unsigned long) key_p[7] << 24)); + c += (key_p[8] + + ((unsigned long) key_p[9] << 8) + + ((unsigned long) key_p[10] << 16) + + ((unsigned long) key_p[11] << 24)); + HASH_MIX(a, b, c); + key_p += 12; + } + + c += length; + + /* all the case statements fall through to the next */ + switch (len) { + case 11: + c += ((unsigned long) key_p[10] << 24); + case 10: + c += ((unsigned long) key_p[9] << 16); + case 9: + c += ((unsigned long) key_p[8] << 8); + /* the first byte of c is reserved for the length */ + case 8: + b += ((unsigned long) key_p[7] << 24); + case 7: + b += ((unsigned long) key_p[6] << 16); + case 6: + b += ((unsigned long) key_p[5] << 8); + case 5: + b += key_p[4]; + case 4: + a += ((unsigned long) key_p[3] << 24); + case 3: + a += ((unsigned long) key_p[2] << 16); + case 2: + a += ((unsigned long) key_p[1] << 8); + case 1: + a += key_p[0]; + /* case 0: nothing left to add */ + } + HASH_MIX(a, b, c); + + return c; +} + +/* + * static int entry_size + * + * DESCRIPTION: + * + * Calculates the appropriate size of an entry to include the key and + * data sizes as well as any associated alignment to the data. + * + * RETURNS: + * + * The associated size of the entry. + * + * ARGUMENTS: + * + * table_p - Table associated with the entries whose size we are + * determining. + * + * key_size - Size of the entry key. + * + * data - Size of the entry data. + */ +static int entry_size(const table_t * table_p, const unsigned int key_size, + const unsigned int data_size) +{ + int size, left; + + /* initial size -- key is already aligned if right after struct */ + size = sizeof(struct table_shell_st) + key_size; + + /* if there is no alignment then it is easy */ + if (table_p->ta_data_align == 0) + return size + data_size; + /* add in our alignement */ + left = size & (table_p->ta_data_align - 1); + if (left > 0) + size += table_p->ta_data_align - left; + /* we add the data size here after the alignment */ + size += data_size; + + return size; +} + +/* + * static unsigned char *entry_data_buf + * + * DESCRIPTION: + * + * Companion to the ENTRY_DATA_BUF macro but this handles any + * associated alignment to the data in the entry. + * + * RETURNS: + * + * Pointer to the data segment of the entry. + * + * ARGUMENTS: + * + * table_p - Table associated with the entry. + * + * entry_p - Entry whose data pointer we are determining. + */ +static unsigned char *entry_data_buf(const table_t * table_p, + const table_entry_t * entry_p) +{ + const unsigned char *buf_p; + int size, pad; + + buf_p = entry_p->te_key_buf + entry_p->te_key_size; + + /* if there is no alignment then it is easy */ + if (table_p->ta_data_align == 0) + return (unsigned char *) buf_p; + /* we need the size of the space before the data */ + size = sizeof(struct table_shell_st) + entry_p->te_key_size; + + /* add in our alignment */ + pad = size & (table_p->ta_data_align - 1); + if (pad > 0) + pad = table_p->ta_data_align - pad; + return (unsigned char *) buf_p + pad; +} + +/******************************* sort routines *******************************/ + +/* + * static int our_compare + * + * DESCRIPTION: + * + * Compare two entries by calling user's compare program or by using + * memcmp. + * + * RETURNS: + * + * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. + * + * ARGUMENTS: + * + * p1 - First entry pointer to compare. + * + * p2 - Second entry pointer to compare. + * + * compare - User comparison function. Ignored. + * + * table_p - Associated table being ordered. Ignored. + */ +static int local_compare(const void *p1, const void *p2, + table_compare_t compare, const table_t * table_p) +{ + const table_entry_t *const *ent1_p = p1, *const *ent2_p = p2; + int cmp; + unsigned int size; + + /* compare as many bytes as we can */ + size = (*ent1_p)->te_key_size; + if ((*ent2_p)->te_key_size < size) + size = (*ent2_p)->te_key_size; + cmp = memcmp(ENTRY_KEY_BUF(*ent1_p), ENTRY_KEY_BUF(*ent2_p), size); + /* if common-size equal, then if next more bytes, it is larger */ + if (cmp == 0) + cmp = (*ent1_p)->te_key_size - (*ent2_p)->te_key_size; + return cmp; +} + +/* + * static int external_compare + * + * DESCRIPTION: + * + * Compare two entries by calling user's compare program or by using + * memcmp. + * + * RETURNS: + * + * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. + * + * ARGUMENTS: + * + * p1 - First entry pointer to compare. + * + * p2 - Second entry pointer to compare. + * + * user_compare - User comparison function. + * + * table_p - Associated table being ordered. + */ +static int external_compare(const void *p1, const void *p2, + table_compare_t user_compare, + const table_t * table_p) +{ + const table_entry_t *const *ent1_p = p1, *const *ent2_p = p2; + /* since we know we are not aligned we can use the EXTRY_DATA_BUF macro */ + return user_compare(ENTRY_KEY_BUF(*ent1_p), (*ent1_p)->te_key_size, + ENTRY_DATA_BUF(table_p, *ent1_p), + (*ent1_p)->te_data_size, + ENTRY_KEY_BUF(*ent2_p), (*ent2_p)->te_key_size, + ENTRY_DATA_BUF(table_p, *ent2_p), + (*ent2_p)->te_data_size); +} + +/* + * static int external_compare_align + * + * DESCRIPTION: + * + * Compare two entries by calling user's compare program or by using + * memcmp. Alignment information is necessary. + * + * RETURNS: + * + * < 0, == 0, or > 0 depending on whether p1 is > p2, == p2, < p2. + * + * ARGUMENTS: + * + * p1 - First entry pointer to compare. + * + * p2 - Second entry pointer to compare. + * + * user_compare - User comparison function. + * + * table_p - Associated table being ordered. + */ +static int external_compare_align(const void *p1, const void *p2, + table_compare_t user_compare, + const table_t * table_p) +{ + const table_entry_t *const *ent1_p = p1, *const *ent2_p = p2; + /* since we are aligned we have to use the entry_data_buf function */ + return user_compare(ENTRY_KEY_BUF(*ent1_p), (*ent1_p)->te_key_size, + entry_data_buf(table_p, *ent1_p), + (*ent1_p)->te_data_size, + ENTRY_KEY_BUF(*ent2_p), (*ent2_p)->te_key_size, + entry_data_buf(table_p, *ent2_p), + (*ent2_p)->te_data_size); +} + +/* + * static void split + * + * DESCRIPTION: + * + * This sorts an array of longs via the quick sort algorithm (it's + * pretty quick) + * + * RETURNS: + * + * None. + * + * ARGUMENTS: + * + * first_p - Start of the list that we are splitting. + * + * last_p - Last entry in the list that we are splitting. + * + * compare - Comparison function which is handling the actual + * elements. This is either a local function or a function to setup + * the problem element key and data pointers which then hands off to + * the user function. + * + * user_compare - User comparison function. Could be NULL if we are + * just using a local comparison function. + * + * table_p - Associated table being sorted. + */ +static void split(void *first_p, void *last_p, compare_t compare, + table_compare_t user_compare, table_t * table_p) +{ + void *pivot_p, *left_p, *right_p, *left_last_p, *right_first_p; + void *firsts[MAX_SORT_SPLITS], *lasts[MAX_SORT_SPLITS]; + int split_c = 0; + + for (;;) { + + /* no need to split the list if it is < 2 elements */ + while (first_p >= last_p) { + if (split_c == 0) { + /* we are done */ + return; + } + split_c--; + first_p = firsts[split_c]; + last_p = lasts[split_c]; + } + + left_p = first_p; + right_p = last_p; + pivot_p = first_p; + + do { + /* scan from right hand side */ + while (right_p > left_p + && compare(right_p, pivot_p, user_compare, table_p) > 0) + right_p = (char *) right_p - sizeof(table_entry_t *); + /* scan from left hand side */ + while (right_p > left_p + && compare(pivot_p, left_p, user_compare, table_p) >= 0) + left_p = (char *) left_p + sizeof(table_entry_t *); + /* if the pointers haven't met then swap values */ + if (right_p > left_p) { + /* swap_bytes(left_p, right_p) */ + table_entry_t *temp; + + temp = *(table_entry_t **) left_p; + *(table_entry_t **) left_p = *(table_entry_t **) right_p; + *(table_entry_t **) right_p = temp; + } + } while (right_p > left_p); + + /* now we swap the pivot with the right-hand side */ + { + /* swap_bytes(pivot_p, right_p); */ + table_entry_t *temp; + + temp = *(table_entry_t **) pivot_p; + *(table_entry_t **) pivot_p = *(table_entry_t **) right_p; + *(table_entry_t **) right_p = temp; + } + pivot_p = right_p; + + /* save the section to the right of the pivot in our stack */ + right_first_p = (char *) pivot_p + sizeof(table_entry_t *); + left_last_p = (char *) pivot_p - sizeof(table_entry_t *); + + /* do we need to save the righthand side? */ + if (right_first_p < last_p) { + if (split_c >= MAX_SORT_SPLITS) { + /* sanity check here -- we should never get here */ + abort(); + } + firsts[split_c] = right_first_p; + lasts[split_c] = last_p; + split_c++; + } + + /* do the left hand side of the pivot */ + /* first_p = first_p */ + last_p = left_last_p; + } +} + +/*************************** exported routines *******************************/ + +/* + * table_t *table_alloc + * + * DESCRIPTION: + * + * Allocate a new table structure. + * + * RETURNS: + * + * A pointer to the new table structure which must be passed to + * table_free to be deallocated. On error a NULL is returned. + * + * ARGUMENTS: + * + * bucket_n - Number of buckets for the hash table. Our current hash + * value works best with base two numbers. Set to 0 to take the + * library default of 1024. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + * + * malloc_f, realloc_f, free_f - Pointers to malloc(3)-, realloc(3)- + * and free(3)-style functions. + */ +table_t *table_alloc(const unsigned int bucket_n, int *error_p, + void *(*malloc_f)(size_t size), + void *(*calloc_f)(size_t number, size_t size), + void *(*realloc_f)(void *ptr, size_t size), + void (*free_f)(void *ptr)) +{ + table_t *table_p = NULL; + unsigned int buck_n; + + /* allocate a table structure */ + if (malloc_f != NULL) + table_p = malloc_f(sizeof(table_t)); + else + table_p = malloc(sizeof(table_t)); + if (table_p == NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_ALLOC; + return NULL; + } + + if (bucket_n > 0) + buck_n = bucket_n; + else + buck_n = DEFAULT_SIZE; + /* allocate the buckets which are NULLed */ + if (calloc_f != NULL) + table_p->ta_buckets = (table_entry_t **)calloc_f(buck_n, sizeof(table_entry_t *)); + else + table_p->ta_buckets = (table_entry_t **)calloc(buck_n, sizeof(table_entry_t *)); + if (table_p->ta_buckets == NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_ALLOC; + if (free_f != NULL) + free_f(table_p); + else + free(table_p); + return NULL; + } + + /* initialize structure */ + table_p->ta_magic = TABLE_MAGIC; + table_p->ta_flags = 0; + table_p->ta_bucket_n = buck_n; + table_p->ta_entry_n = 0; + table_p->ta_data_align = 0; + table_p->ta_linear.tl_magic = 0; + table_p->ta_linear.tl_bucket_c = 0; + table_p->ta_linear.tl_entry_c = 0; + table_p->ta_file_size = 0; + table_p->ta_malloc = malloc_f != NULL ? malloc_f : malloc; + table_p->ta_calloc = calloc_f != NULL ? calloc_f : calloc; + table_p->ta_realloc = realloc_f != NULL ? realloc_f : realloc; + table_p->ta_free = free_f != NULL ? free_f : free; + + if (error_p != NULL) + *error_p = TABLE_ERROR_NONE; + return table_p; +} + +/* + * int table_attr + * + * DESCRIPTION: + * + * Set the attributes for the table. The available attributes are + * specified at the top of table.h. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to a table structure which we will be altering. + * + * attr - Attribute(s) that we will be applying to the table. + */ +int table_attr(table_t * table_p, const int attr) +{ + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + table_p->ta_flags = attr; + + return TABLE_ERROR_NONE; +} + +/* + * int table_set_data_alignment + * + * DESCRIPTION: + * + * Set the alignment for the data in the table. For data elements + * sizeof(long) is recommended unless you use smaller data types + * exclusively. + * + * WARNING: This must be done before any data gets put into the table. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to a table structure which we will be altering. + * + * alignment - Alignment requested for the data. Must be a power of + * 2. Set to 0 for none. + */ +int table_set_data_alignment(table_t * table_p, const int alignment) +{ + int val; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + if (table_p->ta_entry_n > 0) + return TABLE_ERROR_NOT_EMPTY; + /* defaults */ + if (alignment < 2) + table_p->ta_data_align = 0; + else { + /* verify we have a base 2 number */ + for (val = 2; val < MAX_ALIGNMENT; val *= 2) { + if (val == alignment) + break; + } + if (val >= MAX_ALIGNMENT) + return TABLE_ERROR_ALIGNMENT; + table_p->ta_data_align = alignment; + } + + return TABLE_ERROR_NONE; +} + +/* + * int table_clear + * + * DESCRIPTION: + * + * Clear out and free all elements in a table structure. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer that we will be clearing. + */ +int table_clear(table_t * table_p) +{ + table_entry_t *entry_p, *next_p; + table_entry_t **bucket_p, **bounds_p; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + /* free the table allocation and table structure */ + bounds_p = table_p->ta_buckets + table_p->ta_bucket_n; + for (bucket_p = table_p->ta_buckets; bucket_p < bounds_p; bucket_p++) { + for (entry_p = *bucket_p; entry_p != NULL; entry_p = next_p) { + /* record the next pointer before we free */ + next_p = entry_p->te_next_p; + table_p->ta_free(entry_p); + } + + /* clear the bucket entry after we free its entries */ + *bucket_p = NULL; + } + + /* reset table state info */ + table_p->ta_entry_n = 0; + table_p->ta_linear.tl_magic = 0; + table_p->ta_linear.tl_bucket_c = 0; + table_p->ta_linear.tl_entry_c = 0; + + return TABLE_ERROR_NONE; +} + +/* + * int table_free + * + * DESCRIPTION: + * + * Deallocates a table structure. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer that we will be freeing. + */ +int table_free(table_t * table_p) +{ + int ret; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + ret = table_clear(table_p); + + if (table_p->ta_buckets != NULL) + table_p->ta_free(table_p->ta_buckets); + table_p->ta_magic = 0; + table_p->ta_free(table_p); + + return ret; +} + +/* + * int table_insert_kd + * + * DESCRIPTION: + * + * Like table_insert except it passes back a pointer to the key and + * the data buffers after they have been inserted into the table + * structure. + * + * This routine adds a key/data pair both of which are made up of a + * buffer of bytes and an associated size. Both the key and the data + * will be copied into buffers allocated inside the table. If the key + * exists already, the associated data will be replaced if the + * overwrite flag is set, otherwise an error is returned. + * + * NOTE: be very careful changing the values since the table library + * provides the pointers to its memory. The key can _never_ be + * changed otherwise you will not find it again. The data can be + * changed but its length can never be altered unless you delete and + * re-insert it into the table. + * + * WARNING: The pointers to the key and data are not in any specific + * alignment. Accessing the key and/or data as an short, integer, or + * long pointer directly can cause problems. + * + * WARNING: Replacing a data cell (not inserting) will cause the table + * linked list to be temporarily invalid. Care must be taken with + * multiple threaded programs which are relying on the first/next + * linked list to be always valid. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer into which we will be inserting a + * new key/data pair. + * + * key_buf - Buffer of bytes of the key that we are inserting. If you + * are storing an (int) as the key (for example) then key_buf should + * be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are storing an (int) as the key (for example) then key_size should + * be sizeof(int). + * + * data_buf - Buffer of bytes of the data that we are inserting. If + * it is NULL then the library will allocate space for the data in the + * table without copying in any information. If data_buf is NULL and + * data_size is 0 then the library will associate a NULL data pointer + * with the key. If you are storing a (long) as the data (for + * example) then data_buf should be a (long *). + * + * data_size - Size of the data_buf buffer. If set to < 0 then the + * library will do a strlen of data_buf and add 1 for the '\0'. If + * you are storing an (long) as the key (for example) then key_size + * should be sizeof(long). + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the key storage that was allocated in the table. If you are + * storing an (int) as the key (for example) then key_buf_p should be + * (int **) i.e. the address of a (int *). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table. If you are + * storing an (long) as the data (for example) then data_buf_p should + * be (long **) i.e. the address of a (long *). + * + * overwrite - Flag which, if set to 1, will allow the overwriting of + * the data in the table with the new data if the key already exists + * in the table. + */ +int table_insert_kd(table_t * table_p, + const void *key_buf, const int key_size, + const void *data_buf, const int data_size, + void **key_buf_p, void **data_buf_p, + const char overwrite_b) +{ + int bucket; + unsigned int ksize, dsize; + table_entry_t *entry_p, *last_p; + void *key_copy_p, *data_copy_p; + + /* check the arguments */ + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + if (key_buf == NULL) + return TABLE_ERROR_ARG_NULL; + /* data_buf can be null but size must be >= 0, if it isn't null size != 0 */ + if ((data_buf == NULL && data_size < 0) + || (data_buf != NULL && data_size == 0)) + return TABLE_ERROR_SIZE; + /* determine sizes of key and data */ + if (key_size < 0) + ksize = strlen((char *) key_buf) + sizeof(char); + else + ksize = key_size; + if (data_size < 0) + dsize = strlen((char *) data_buf) + sizeof(char); + else + dsize = data_size; + /* get the bucket number via a hash function */ + bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n; + + /* look for the entry in this bucket, only check keys of the same size */ + last_p = NULL; + for (entry_p = table_p->ta_buckets[bucket]; + entry_p != NULL; + last_p = entry_p, entry_p = entry_p->te_next_p) { + if (entry_p->te_key_size == ksize + && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0) + break; + } + + /* did we find it? then we are in replace mode. */ + if (entry_p != NULL) { + + /* can we not overwrite existing data? */ + if (!overwrite_b) { + if (key_buf_p != NULL) + *key_buf_p = ENTRY_KEY_BUF(entry_p); + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) + *data_buf_p = NULL; + else { + if (table_p->ta_data_align == 0) + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + else + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + return TABLE_ERROR_OVERWRITE; + } + + /* re-alloc entry's data if the new size != the old */ + if (dsize != entry_p->te_data_size) { + + /* + * First we delete it from the list to keep the list whole. + * This properly preserves the linked list in case we have a + * thread marching through the linked list while we are + * inserting. Maybe this is an unnecessary protection but it + * should not harm that much. + */ + if (last_p == NULL) + table_p->ta_buckets[bucket] = entry_p->te_next_p; + else + last_p->te_next_p = entry_p->te_next_p; + /* + * Realloc the structure which may change its pointer. NOTE: + * this may change any previous data_key_p and data_copy_p + * pointers. + */ + entry_p = (table_entry_t *) table_p->ta_realloc(entry_p, + entry_size(table_p, + entry_p->te_key_size, + dsize)); + if (entry_p == NULL) + return TABLE_ERROR_ALLOC; + /* add it back to the front of the list */ + entry_p->te_data_size = dsize; + entry_p->te_next_p = table_p->ta_buckets[bucket]; + table_p->ta_buckets[bucket] = entry_p; + } + + /* copy or replace data in storage */ + if (dsize > 0) { + if (table_p->ta_data_align == 0) + data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); + else + data_copy_p = entry_data_buf(table_p, entry_p); + if (data_buf != NULL) + memcpy(data_copy_p, data_buf, dsize); + } + else + data_copy_p = NULL; + if (key_buf_p != NULL) + *key_buf_p = ENTRY_KEY_BUF(entry_p); + if (data_buf_p != NULL) + *data_buf_p = data_copy_p; + /* returning from the section where we were overwriting table data */ + return TABLE_ERROR_NONE; + } + + /* + * It is a new entry. + */ + + /* allocate a new entry */ + entry_p = (table_entry_t *) table_p->ta_malloc(entry_size(table_p, ksize, dsize)); + if (entry_p == NULL) + return TABLE_ERROR_ALLOC; + /* copy key into storage */ + entry_p->te_key_size = ksize; + key_copy_p = ENTRY_KEY_BUF(entry_p); + memcpy(key_copy_p, key_buf, ksize); + + /* copy data in */ + entry_p->te_data_size = dsize; + if (dsize > 0) { + if (table_p->ta_data_align == 0) + data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); + else + data_copy_p = entry_data_buf(table_p, entry_p); + if (data_buf != NULL) + memcpy(data_copy_p, data_buf, dsize); + } + else + data_copy_p = NULL; + if (key_buf_p != NULL) + *key_buf_p = key_copy_p; + if (data_buf_p != NULL) + *data_buf_p = data_copy_p; + /* insert into list, no need to append */ + entry_p->te_next_p = table_p->ta_buckets[bucket]; + table_p->ta_buckets[bucket] = entry_p; + + table_p->ta_entry_n++; + + /* do we need auto-adjust? */ + if (table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST + && SHOULD_TABLE_GROW(table_p)) + return table_adjust(table_p, table_p->ta_entry_n); + return TABLE_ERROR_NONE; +} + +/* + * int table_insert + * + * DESCRIPTION: + * + * Exactly the same as table_insert_kd except it does not pass back a + * pointer to the key after they have been inserted into the table + * structure. This is still here for backwards compatibility. + * + * See table_insert_kd for more information. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer into which we will be inserting a + * new key/data pair. + * + * key_buf - Buffer of bytes of the key that we are inserting. If you + * are storing an (int) as the key (for example) then key_buf should + * be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are storing an (int) as the key (for example) then key_size should + * be sizeof(int). + * + * data_buf - Buffer of bytes of the data that we are inserting. If + * it is NULL then the library will allocate space for the data in the + * table without copying in any information. If data_buf is NULL and + * data_size is 0 then the library will associate a NULL data pointer + * with the key. If you are storing a (long) as the data (for + * example) then data_buf should be a (long *). + * + * data_size - Size of the data_buf buffer. If set to < 0 then the + * library will do a strlen of data_buf and add 1 for the '\0'. If + * you are storing an (long) as the key (for example) then key_size + * should be sizeof(long). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table. If you are + * storing an (long) as the data (for example) then data_buf_p should + * be (long **) i.e. the address of a (long *). + * + * overwrite - Flag which, if set to 1, will allow the overwriting of + * the data in the table with the new data if the key already exists + * in the table. + */ +int table_insert(table_t * table_p, + const void *key_buf, const int key_size, + const void *data_buf, const int data_size, + void **data_buf_p, const char overwrite_b) +{ + return table_insert_kd(table_p, key_buf, key_size, data_buf, data_size, + NULL, data_buf_p, overwrite_b); +} + +/* + * int table_retrieve + * + * DESCRIPTION: + * + * This routine looks up a key made up of a buffer of bytes and an + * associated size in the table. If found then it returns the + * associated data information. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer into which we will be searching + * for the key. + * + * key_buf - Buffer of bytes of the key that we are searching for. If + * you are looking for an (int) as the key (for example) then key_buf + * should be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are looking for an (int) as the key (for example) then key_size + * should be sizeof(int). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table and that is + * associated with the key. If a (long) was stored as the data (for + * example) then data_buf_p should be (long **) i.e. the address of a + * (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data stored in the table that is associated with + * the key. + */ +int table_retrieve(table_t * table_p, + const void *key_buf, const int key_size, + void **data_buf_p, int *data_size_p) +{ + int bucket; + unsigned int ksize; + table_entry_t *entry_p, **buckets; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + if (key_buf == NULL) + return TABLE_ERROR_ARG_NULL; + /* find key size */ + if (key_size < 0) + ksize = strlen((char *) key_buf) + sizeof(char); + else + ksize = key_size; + /* get the bucket number via a has function */ + bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n; + + /* look for the entry in this bucket, only check keys of the same size */ + buckets = table_p->ta_buckets; + for (entry_p = buckets[bucket]; + entry_p != NULL; + entry_p = entry_p->te_next_p) { + entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p); + if (entry_p->te_key_size == ksize + && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0) + break; + } + + /* not found? */ + if (entry_p == NULL) + return TABLE_ERROR_NOT_FOUND; + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) + *data_buf_p = NULL; + else { + if (table_p->ta_data_align == 0) + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + else + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + if (data_size_p != NULL) + *data_size_p = entry_p->te_data_size; + return TABLE_ERROR_NONE; +} + +/* + * int table_delete + * + * DESCRIPTION: + * + * This routine looks up a key made up of a buffer of bytes and an + * associated size in the table. If found then it will be removed + * from the table. The associated data can be passed back to the user + * if requested. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * NOTE: this could be an allocation error if the library is to return + * the data to the user. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we will be deleteing + * the key. + * + * key_buf - Buffer of bytes of the key that we are searching for to + * delete. If you are deleting an (int) key (for example) then + * key_buf should be a (int *). + * + * key_size - Size of the key_buf buffer. If set to < 0 then the + * library will do a strlen of key_buf and add 1 for the '\0'. If you + * are deleting an (int) key (for example) then key_size should be + * sizeof(int). + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table and that was + * associated with the key. If a (long) was stored as the data (for + * example) then data_buf_p should be (long **) i.e. the address of a + * (long *). If a pointer is passed in, the caller is responsible for + * freeing it after use. If data_buf_p is NULL then the library will + * free up the data allocation itself. + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that was stored in the table and that was + * associated with the key. + */ +int table_delete(table_t * table_p, + const void *key_buf, const int key_size, + void **data_buf_p, int *data_size_p) +{ + int bucket; + unsigned int ksize; + unsigned char *data_copy_p; + table_entry_t *entry_p, *last_p; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + if (key_buf == NULL) + return TABLE_ERROR_ARG_NULL; + /* get the key size */ + if (key_size < 0) + ksize = strlen((char *) key_buf) + sizeof(char); + else + ksize = key_size; + /* find our bucket */ + bucket = hash(key_buf, ksize, 0) % table_p->ta_bucket_n; + + /* look for the entry in this bucket, only check keys of the same size */ + for (last_p = NULL, entry_p = table_p->ta_buckets[bucket]; entry_p != NULL; + last_p = entry_p, entry_p = entry_p->te_next_p) { + if (entry_p->te_key_size == ksize + && memcmp(ENTRY_KEY_BUF(entry_p), key_buf, ksize) == 0) + break; + } + + /* did we find it? */ + if (entry_p == NULL) + return TABLE_ERROR_NOT_FOUND; + /* + * NOTE: we may want to adjust the linear counters here if the entry + * we are deleting is the one we are pointing on or is ahead of the + * one in the bucket list + */ + + /* remove entry from the linked list */ + if (last_p == NULL) + table_p->ta_buckets[bucket] = entry_p->te_next_p; + else + last_p->te_next_p = entry_p->te_next_p; + /* free entry */ + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) + *data_buf_p = NULL; + else { + /* + * if we were storing it compacted, we now need to malloc some + * space if the user wants the value after the delete. + */ + *data_buf_p = table_p->ta_malloc(entry_p->te_data_size); + if (*data_buf_p == NULL) + return TABLE_ERROR_ALLOC; + if (table_p->ta_data_align == 0) + data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); + else + data_copy_p = entry_data_buf(table_p, entry_p); + memcpy(*data_buf_p, data_copy_p, entry_p->te_data_size); + } + } + if (data_size_p != NULL) + *data_size_p = entry_p->te_data_size; + table_p->ta_free(entry_p); + + table_p->ta_entry_n--; + + /* do we need auto-adjust down? */ + if ((table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST) + && (table_p->ta_flags & TABLE_FLAG_ADJUST_DOWN) + && SHOULD_TABLE_SHRINK(table_p)) + return table_adjust(table_p, table_p->ta_entry_n); + return TABLE_ERROR_NONE; +} + +/* + * int table_delete_first + * + * DESCRIPTION: + * + * This is like the table_delete routines except it deletes the first + * key/data pair in the table instead of an entry corresponding to a + * particular key. The associated key and data information can be + * passed back to the user if requested. This routines is handy to + * clear out a table. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * NOTE: this could be an allocation error if the library is to return + * the data to the user. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we will be deleteing + * the first key. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the first key that was allocated in the table. + * If an (int) was stored as the first key (for example) then + * key_buf_p should be (int **) i.e. the address of a (int *). If a + * pointer is passed in, the caller is responsible for freeing it + * after use. If key_buf_p is NULL then the library will free up the + * key allocation itself. + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that was stored in the table and that was + * associated with the key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that was allocated in the table and that was + * associated with the key. If a (long) was stored as the data (for + * example) then data_buf_p should be (long **) i.e. the address of a + * (long *). If a pointer is passed in, the caller is responsible for + * freeing it after use. If data_buf_p is NULL then the library will + * free up the data allocation itself. + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that was stored in the table and that was + * associated with the key. + */ +int table_delete_first(table_t * table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + unsigned char *data_copy_p; + table_entry_t *entry_p; + table_linear_t linear; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + /* take the first entry */ + entry_p = first_entry(table_p, &linear); + if (entry_p == NULL) + return TABLE_ERROR_NOT_FOUND; + /* + * NOTE: we may want to adjust the linear counters here if the entry + * we are deleting is the one we are pointing on or is ahead of the + * one in the bucket list + */ + + /* remove entry from the linked list */ + table_p->ta_buckets[linear.tl_bucket_c] = entry_p->te_next_p; + + /* free entry */ + if (key_buf_p != NULL) { + if (entry_p->te_key_size == 0) + *key_buf_p = NULL; + else { + /* + * if we were storing it compacted, we now need to malloc some + * space if the user wants the value after the delete. + */ + *key_buf_p = table_p->ta_malloc(entry_p->te_key_size); + if (*key_buf_p == NULL) + return TABLE_ERROR_ALLOC; + memcpy(*key_buf_p, ENTRY_KEY_BUF(entry_p), entry_p->te_key_size); + } + } + if (key_size_p != NULL) + *key_size_p = entry_p->te_key_size; + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) + *data_buf_p = NULL; + else { + /* + * if we were storing it compacted, we now need to malloc some + * space if the user wants the value after the delete. + */ + *data_buf_p = table_p->ta_malloc(entry_p->te_data_size); + if (*data_buf_p == NULL) + return TABLE_ERROR_ALLOC; + if (table_p->ta_data_align == 0) + data_copy_p = ENTRY_DATA_BUF(table_p, entry_p); + else + data_copy_p = entry_data_buf(table_p, entry_p); + memcpy(*data_buf_p, data_copy_p, entry_p->te_data_size); + } + } + if (data_size_p != NULL) + *data_size_p = entry_p->te_data_size; + table_p->ta_free(entry_p); + + table_p->ta_entry_n--; + + /* do we need auto-adjust down? */ + if ((table_p->ta_flags & TABLE_FLAG_AUTO_ADJUST) + && (table_p->ta_flags & TABLE_FLAG_ADJUST_DOWN) + && SHOULD_TABLE_SHRINK(table_p)) + return table_adjust(table_p, table_p->ta_entry_n); + return TABLE_ERROR_NONE; +} + +/* + * int table_info + * + * DESCRIPTION: + * + * Get some information about a table_p structure. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting + * information. + * + * num_buckets_p - Pointer to an integer which, if not NULL, will + * contain the number of buckets in the table. + * + * num_entries_p - Pointer to an integer which, if not NULL, will + * contain the number of entries stored in the table. + */ +int table_info(table_t * table_p, int *num_buckets_p, int *num_entries_p) +{ + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + if (num_buckets_p != NULL) + *num_buckets_p = table_p->ta_bucket_n; + if (num_entries_p != NULL) + *num_entries_p = table_p->ta_entry_n; + return TABLE_ERROR_NONE; +} + +/* + * int table_adjust + * + * DESCRIPTION: + * + * Set the number of buckets in a table to a certain value. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer of which we are adjusting. + * + * bucket_n - Number buckets to adjust the table to. Set to 0 to + * adjust the table to its number of entries. + */ +int table_adjust(table_t * table_p, const int bucket_n) +{ + table_entry_t *entry_p, *next_p; + table_entry_t **buckets, **bucket_p, **bounds_p; + int bucket; + unsigned int buck_n; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + /* + * NOTE: we walk through the entries and rehash them. If we stored + * the hash value as a full int in the table-entry, all we would + * have to do is remod it. + */ + + /* normalize to the number of entries */ + if (bucket_n == 0) + buck_n = table_p->ta_entry_n; + else + buck_n = bucket_n; + /* we must have at least 1 bucket */ + if (buck_n == 0) + buck_n = 1; + /* make sure we have somethign to do */ + if (buck_n == table_p->ta_bucket_n) + return TABLE_ERROR_NONE; + /* allocate a new bucket list */ + buckets = (table_entry_t **) table_p->ta_calloc(buck_n, sizeof(table_entry_t *)); + if (table_p->ta_buckets == NULL) + return TABLE_ERROR_ALLOC; + /* + * run through each of the items in the current table and rehash + * them into the newest bucket sizes + */ + bounds_p = table_p->ta_buckets + table_p->ta_bucket_n; + for (bucket_p = table_p->ta_buckets; bucket_p < bounds_p; bucket_p++) { + for (entry_p = *bucket_p; entry_p != NULL; entry_p = next_p) { + + /* hash the old data into the new table size */ + bucket = hash(ENTRY_KEY_BUF(entry_p), entry_p->te_key_size, 0) % buck_n; + + /* record the next one now since we overwrite next below */ + next_p = entry_p->te_next_p; + + /* insert into new list, no need to append */ + entry_p->te_next_p = buckets[bucket]; + buckets[bucket] = entry_p; + + /* + * NOTE: we may want to adjust the bucket_c linear entry here to + * keep it current + */ + } + /* remove the old table pointers as we go by */ + *bucket_p = NULL; + } + + /* replace the table buckets with the new ones */ + table_p->ta_free(table_p->ta_buckets); + table_p->ta_buckets = buckets; + table_p->ta_bucket_n = buck_n; + + return TABLE_ERROR_NONE; +} + +/* + * const char *table_strerror + * + * DESCRIPTION: + * + * Return the corresponding string for the error number. + * + * RETURNS: + * + * Success - String equivalient of the error. + * + * Failure - String "invalid error code" + * + * ARGUMENTS: + * + * error - Error number that we are converting. + */ +const char *table_strerror(const int error) +{ + error_str_t *err_p; + + for (err_p = errors; err_p->es_error != 0; err_p++) { + if (err_p->es_error == error) + return err_p->es_string; + } + + return INVALID_ERROR; +} + +/* + * int table_type_size + * + * DESCRIPTION: + * + * Return the size of the internal table type. + * + * RETURNS: + * + * The size of the table_t type. + * + * ARGUMENTS: + * + * None. + */ +int table_type_size(void) +{ + return sizeof(table_t); +} + +/************************* linear access routines ****************************/ + +/* + * int table_first + * + * DESCRIPTION: + * + * Find first element in a table and pass back information about the + * key/data pair. If any of the key/data pointers are NULL then they + * are ignored. + * + * NOTE: This function is not reentrant. More than one thread cannot + * be doing a first and next on the same table at the same time. Use + * the table_first_r version below for this. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * first element. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the first key that is allocated in the table. If + * an (int) is stored as the first key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the first key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the first key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the first key. + */ +int table_first(table_t * table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + /* initialize our linear magic number */ + table_p->ta_linear.tl_magic = LINEAR_MAGIC; + + entry_p = first_entry(table_p, &table_p->ta_linear); + if (entry_p == NULL) + return TABLE_ERROR_NOT_FOUND; + if (key_buf_p != NULL) + *key_buf_p = ENTRY_KEY_BUF(entry_p); + if (key_size_p != NULL) + *key_size_p = entry_p->te_key_size; + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) + *data_buf_p = NULL; + else { + if (table_p->ta_data_align == 0) + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + else + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + if (data_size_p != NULL) + *data_size_p = entry_p->te_data_size; + return TABLE_ERROR_NONE; +} + +/* + * int table_next + * + * DESCRIPTION: + * + * Find the next element in a table and pass back information about + * the key/data pair. If any of the key/data pointers are NULL then + * they are ignored. + * + * NOTE: This function is not reentrant. More than one thread cannot + * be doing a first and next on the same table at the same time. Use + * the table_next_r version below for this. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * next element. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the next key that is allocated in the table. If + * an (int) is stored as the next key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the next key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the next key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the next key. + */ +int table_next(table_t * table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p; + int error; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + if (table_p->ta_linear.tl_magic != LINEAR_MAGIC) + return TABLE_ERROR_LINEAR; + /* move to the next entry */ + entry_p = next_entry(table_p, &table_p->ta_linear, &error); + if (entry_p == NULL) + return error; + if (key_buf_p != NULL) + *key_buf_p = ENTRY_KEY_BUF(entry_p); + if (key_size_p != NULL) + *key_size_p = entry_p->te_key_size; + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) + *data_buf_p = NULL; + else { + if (table_p->ta_data_align == 0) + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + else + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + if (data_size_p != NULL) + *data_size_p = entry_p->te_data_size; + return TABLE_ERROR_NONE; +} + +/* + * int table_this + * + * DESCRIPTION: + * + * Find the current element in a table and pass back information about + * the key/data pair. If any of the key/data pointers are NULL then + * they are ignored. + * + * NOTE: This function is not reentrant. Use the table_current_r + * version below. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * current element. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the current key that is allocated in the table. + * If an (int) is stored as the current key (for example) then + * key_buf_p should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the current key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the current key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the current key. + */ +int table_this(table_t * table_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p = NULL; + int entry_c; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + if (table_p->ta_linear.tl_magic != LINEAR_MAGIC) + return TABLE_ERROR_LINEAR; + /* if we removed an item that shorted the bucket list, we may get this */ + if (table_p->ta_linear.tl_bucket_c >= table_p->ta_bucket_n) { + /* + * NOTE: this might happen if we delete an item which shortens the + * table bucket numbers. + */ + return TABLE_ERROR_NOT_FOUND; + } + + /* find the entry which is the nth in the list */ + entry_p = table_p->ta_buckets[table_p->ta_linear.tl_bucket_c]; + /* NOTE: we swap the order here to be more efficient */ + for (entry_c = table_p->ta_linear.tl_entry_c; entry_c > 0; entry_c--) { + /* did we reach the end of the list? */ + if (entry_p == NULL) + break; + entry_p = TABLE_POINTER(table_p, table_entry_t *, entry_p)->te_next_p; + } + + /* is this a NOT_FOUND or a LINEAR error */ + if (entry_p == NULL) + return TABLE_ERROR_NOT_FOUND; + if (key_buf_p != NULL) + *key_buf_p = ENTRY_KEY_BUF(entry_p); + if (key_size_p != NULL) + *key_size_p = entry_p->te_key_size; + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) + *data_buf_p = NULL; + else { + if (table_p->ta_data_align == 0) + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + else + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + if (data_size_p != NULL) + *data_size_p = entry_p->te_data_size; + return TABLE_ERROR_NONE; +} + +/* + * int table_first_r + * + * DESCRIPTION: + * + * Reetrant version of the table_first routine above. Find first + * element in a table and pass back information about the key/data + * pair. If any of the key/data pointers are NULL then they are + * ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * first element. + * + * linear_p - Pointer to a table linear structure which is initialized + * here. The same pointer should then be passed to table_next_r + * below. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the first key that is allocated in the table. If + * an (int) is stored as the first key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the first key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the first key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the first key. + */ +int table_first_r(table_t * table_p, table_linear_t * linear_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + if (linear_p == NULL) + return TABLE_ERROR_ARG_NULL; + /* initialize our linear magic number */ + linear_p->tl_magic = LINEAR_MAGIC; + + entry_p = first_entry(table_p, linear_p); + if (entry_p == NULL) + return TABLE_ERROR_NOT_FOUND; + if (key_buf_p != NULL) + *key_buf_p = ENTRY_KEY_BUF(entry_p); + if (key_size_p != NULL) + *key_size_p = entry_p->te_key_size; + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) + *data_buf_p = NULL; + else { + if (table_p->ta_data_align == 0) + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + else + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + if (data_size_p != NULL) + *data_size_p = entry_p->te_data_size; + return TABLE_ERROR_NONE; +} + +/* + * int table_next_r + * + * DESCRIPTION: + * + * Reetrant version of the table_next routine above. Find next + * element in a table and pass back information about the key/data + * pair. If any of the key/data pointers are NULL then they are + * ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * next element. + * + * linear_p - Pointer to a table linear structure which is incremented + * here. The same pointer must have been passed to table_first_r + * first so that it can be initialized. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the next key that is allocated in the table. If + * an (int) is stored as the next key (for example) then key_buf_p + * should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL will be set + * to the size of the key that is stored in the table and that is + * associated with the next key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the next key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the next key. + */ +int table_next_r(table_t * table_p, table_linear_t * linear_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p; + int error; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + if (linear_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (linear_p->tl_magic != LINEAR_MAGIC) + return TABLE_ERROR_LINEAR; + /* move to the next entry */ + entry_p = next_entry(table_p, linear_p, &error); + if (entry_p == NULL) + return error; + if (key_buf_p != NULL) + *key_buf_p = ENTRY_KEY_BUF(entry_p); + if (key_size_p != NULL) + *key_size_p = entry_p->te_key_size; + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) + *data_buf_p = NULL; + else { + if (table_p->ta_data_align == 0) + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + else + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + if (data_size_p != NULL) + *data_size_p = entry_p->te_data_size; + return TABLE_ERROR_NONE; +} + +/* + * int table_this_r + * + * DESCRIPTION: + * + * Reetrant version of the table_this routine above. Find current + * element in a table and pass back information about the key/data + * pair. If any of the key/data pointers are NULL then they are + * ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * current element. + * + * linear_p - Pointer to a table linear structure which is accessed + * here. The same pointer must have been passed to table_first_r + * first so that it can be initialized. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of the current key that is allocated in the table. + * If an (int) is stored as the current key (for example) then + * key_buf_p should be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table and that is + * associated with the current key. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage that is allocated in the table and that is + * associated with the current key. If a (long) is stored as the data + * (for example) then data_buf_p should be (long **) i.e. the address + * of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table and that is + * associated with the current key. + */ +int table_this_r(table_t * table_p, table_linear_t * linear_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + table_entry_t *entry_p; + int entry_c; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + if (linear_p->tl_magic != LINEAR_MAGIC) + return TABLE_ERROR_LINEAR; + /* if we removed an item that shorted the bucket list, we may get this */ + if (linear_p->tl_bucket_c >= table_p->ta_bucket_n) { + /* + * NOTE: this might happen if we delete an item which shortens the + * table bucket numbers. + */ + return TABLE_ERROR_NOT_FOUND; + } + + /* find the entry which is the nth in the list */ + for (entry_c = linear_p->tl_entry_c, + entry_p = table_p->ta_buckets[linear_p->tl_bucket_c]; + entry_p != NULL && entry_c > 0; + entry_c--, entry_p = TABLE_POINTER(table_p, table_entry_t *, + entry_p)->te_next_p) { + } + + if (entry_p == NULL) + return TABLE_ERROR_NOT_FOUND; + if (key_buf_p != NULL) + *key_buf_p = ENTRY_KEY_BUF(entry_p); + if (key_size_p != NULL) + *key_size_p = entry_p->te_key_size; + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) + *data_buf_p = NULL; + else { + if (table_p->ta_data_align == 0) + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + else + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + if (data_size_p != NULL) + *data_size_p = entry_p->te_data_size; + return TABLE_ERROR_NONE; +} + +/******************************* file routines *******************************/ + +/* + * int table_read + * + * DESCRIPTION: + * + * Read in a table from a file that had been written to disk earlier + * via table_write. + * + * RETURNS: + * + * Success - Pointer to the new table structure which must be passed + * to table_free to be deallocated. + * + * Failure - NULL + * + * ARGUMENTS: + * + * path - Table file to read in. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +table_t *table_read(const char *path, int *error_p, + void *(*malloc_f)(size_t size), + void *(*calloc_f)(size_t number, size_t size), + void *(*realloc_f)(void *ptr, size_t size), + void (*free_f)(void *ptr)) +{ + unsigned int size; + int fd, ent_size; + FILE *infile; + table_entry_t entry, **bucket_p, *entry_p = NULL, *last_p; + unsigned long pos; + table_t *table_p; + + /* open the file */ + fd = open(path, O_RDONLY, 0); + if (fd < 0) { + if (error_p != NULL) + *error_p = TABLE_ERROR_OPEN; + return NULL; + } + + /* allocate a table structure */ + if (malloc_f != NULL) + table_p = malloc_f(sizeof(table_t)); + else + table_p = malloc(sizeof(table_t)); + if (table_p == NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_ALLOC; + return NULL; + } + + /* now open the fd to get buffered i/o */ + infile = fdopen(fd, "r"); + if (infile == NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_OPEN; + return NULL; + } + + /* read the main table struct */ + if (fread(table_p, sizeof(table_t), 1, infile) != 1) { + if (error_p != NULL) + *error_p = TABLE_ERROR_READ; + if (free_f != NULL) + free_f(table_p); + else + free(table_p); + return NULL; + } + table_p->ta_file_size = 0; + + table_p->ta_malloc = malloc_f != NULL ? malloc_f : malloc; + table_p->ta_calloc = calloc_f != NULL ? calloc_f : calloc; + table_p->ta_realloc = realloc_f != NULL ? realloc_f : realloc; + table_p->ta_free = free_f != NULL ? free_f : free; + + /* is the file contain bad info or maybe another system type? */ + if (table_p->ta_magic != TABLE_MAGIC) { + if (error_p != NULL) + *error_p = TABLE_ERROR_PNT; + return NULL; + } + + /* allocate the buckets */ + table_p->ta_buckets = (table_entry_t **)table_p->ta_calloc(table_p->ta_bucket_n, sizeof(table_entry_t *)); + if (table_p->ta_buckets == NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_ALLOC; + table_p->ta_free(table_p); + return NULL; + } + + if (fread(table_p->ta_buckets, sizeof(table_entry_t *), table_p->ta_bucket_n, + infile) != (size_t) table_p->ta_bucket_n) { + if (error_p != NULL) + *error_p = TABLE_ERROR_READ; + table_p->ta_free(table_p->ta_buckets); + table_p->ta_free(table_p); + return NULL; + } + + /* read in the entries */ + for (bucket_p = table_p->ta_buckets; + bucket_p < table_p->ta_buckets + table_p->ta_bucket_n; + bucket_p++) { + + /* skip null buckets */ + if (*bucket_p == NULL) + continue; + /* run through the entry list */ + last_p = NULL; + for (pos = *(unsigned long *) bucket_p;; + pos = (unsigned long) entry_p->te_next_p) { + + /* read in the entry */ + if (fseek(infile, pos, SEEK_SET) != 0) { + if (error_p != NULL) + *error_p = TABLE_ERROR_SEEK; + table_p->ta_free(table_p->ta_buckets); + if (entry_p != NULL) + table_p->ta_free(entry_p); + table_p->ta_free(table_p); + /* the other table elements will not be freed */ + return NULL; + } + if (fread(&entry, sizeof(struct table_shell_st), 1, infile) != 1) { + if (error_p != NULL) + *error_p = TABLE_ERROR_READ; + table_p->ta_free(table_p->ta_buckets); + if (entry_p != NULL) + table_p->ta_free(entry_p); + table_p->ta_free(table_p); + /* the other table elements will not be freed */ + return NULL; + } + + /* make a new entry */ + ent_size = entry_size(table_p, entry.te_key_size, entry.te_data_size); + entry_p = (table_entry_t *)table_p->ta_malloc(ent_size); + if (entry_p == NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_ALLOC; + table_p->ta_free(table_p->ta_buckets); + table_p->ta_free(table_p); + /* the other table elements will not be freed */ + return NULL; + } + entry_p->te_key_size = entry.te_key_size; + entry_p->te_data_size = entry.te_data_size; + entry_p->te_next_p = entry.te_next_p; + + if (last_p == NULL) + *bucket_p = entry_p; + else + last_p->te_next_p = entry_p; + /* determine how much more we have to read */ + size = ent_size - sizeof(struct table_shell_st); + if (fread(ENTRY_KEY_BUF(entry_p), sizeof(char), size, infile) != size) { + if (error_p != NULL) + *error_p = TABLE_ERROR_READ; + table_p->ta_free(table_p->ta_buckets); + table_p->ta_free(entry_p); + table_p->ta_free(table_p); + /* the other table elements will not be freed */ + return NULL; + } + + /* we are done if the next pointer is null */ + if (entry_p->te_next_p == (unsigned long) 0) + break; + last_p = entry_p; + } + } + + (void) fclose(infile); + + if (error_p != NULL) + *error_p = TABLE_ERROR_NONE; + return table_p; +} + +/* + * int table_write + * + * DESCRIPTION: + * + * Write a table from memory to file. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Pointer to the table that we are writing to the file. + * + * path - Table file to write out to. + * + * mode - Mode of the file. This argument is passed on to open when + * the file is created. + */ +int table_write(const table_t * table_p, const char *path, const int mode) +{ + int fd, rem, ent_size; + unsigned int bucket_c; + unsigned long size; + table_entry_t *entry_p, **buckets, **bucket_p, *next_p; + table_t tmain; + FILE *outfile; + + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + fd = open(path, O_WRONLY | O_CREAT, mode); + if (fd < 0) + return TABLE_ERROR_OPEN; + outfile = fdopen(fd, "w"); + if (outfile == NULL) + return TABLE_ERROR_OPEN; + /* allocate a block of sizes for each bucket */ + buckets = (table_entry_t **) table_p->ta_malloc(sizeof(table_entry_t *) * + table_p->ta_bucket_n); + if (buckets == NULL) + return TABLE_ERROR_ALLOC; + /* make a copy of the tmain struct */ + tmain = *table_p; + + /* start counting the bytes */ + size = 0; + size += sizeof(table_t); + + /* buckets go right after tmain struct */ + tmain.ta_buckets = (table_entry_t **) size; + size += sizeof(table_entry_t *) * table_p->ta_bucket_n; + + /* run through and count the buckets */ + for (bucket_c = 0; bucket_c < table_p->ta_bucket_n; bucket_c++) { + bucket_p = table_p->ta_buckets + bucket_c; + if (*bucket_p == NULL) { + buckets[bucket_c] = NULL; + continue; + } + buckets[bucket_c] = (table_entry_t *) size; + for (entry_p = *bucket_p; entry_p != NULL; entry_p = entry_p->te_next_p) { + size += entry_size(table_p, entry_p->te_key_size, entry_p->te_data_size); + /* + * We now have to round the file to the nearest long so the + * mmaping of the longs in the entry structs will work. + */ + rem = size & (sizeof(long) - 1); + if (rem > 0) + size += sizeof(long) - rem; + } + } + /* add a \0 at the end to fill the last section */ + size++; + + /* set the tmain fields */ + tmain.ta_linear.tl_magic = 0; + tmain.ta_linear.tl_bucket_c = 0; + tmain.ta_linear.tl_entry_c = 0; + tmain.ta_file_size = size; + + /* + * Now we can start the writing because we got the bucket offsets. + */ + + /* write the tmain table struct */ + size = 0; + if (fwrite(&tmain, sizeof(table_t), 1, outfile) != 1) { + table_p->ta_free(buckets); + return TABLE_ERROR_WRITE; + } + size += sizeof(table_t); + if (fwrite(buckets, sizeof(table_entry_t *), table_p->ta_bucket_n, + outfile) != (size_t) table_p->ta_bucket_n) { + table_p->ta_free(buckets); + return TABLE_ERROR_WRITE; + } + size += sizeof(table_entry_t *) * table_p->ta_bucket_n; + + /* write out the entries */ + for (bucket_p = table_p->ta_buckets; + bucket_p < table_p->ta_buckets + table_p->ta_bucket_n; + bucket_p++) { + for (entry_p = *bucket_p; entry_p != NULL; entry_p = entry_p->te_next_p) { + + ent_size = entry_size(table_p, entry_p->te_key_size, + entry_p->te_data_size); + size += ent_size; + /* round to nearest long here so we can write copy */ + rem = size & (sizeof(long) - 1); + if (rem > 0) + size += sizeof(long) - rem; + next_p = entry_p->te_next_p; + if (next_p != NULL) + entry_p->te_next_p = (table_entry_t *) size; + /* now write to disk */ + if (fwrite(entry_p, ent_size, 1, outfile) != 1) { + table_p->ta_free(buckets); + return TABLE_ERROR_WRITE; + } + + /* restore the next pointer */ + if (next_p != NULL) + entry_p->te_next_p = next_p; + /* now write the padding information */ + if (rem > 0) { + rem = sizeof(long) - rem; + /* + * NOTE: this won't leave fseek'd space at the end but we + * don't care there because there is no accessed memory + * afterwards. We write 1 \0 at the end to make sure. + */ + if (fseek(outfile, rem, SEEK_CUR) != 0) { + table_p->ta_free(buckets); + return TABLE_ERROR_SEEK; + } + } + } + } + /* + * Write a \0 at the end of the file to make sure that the last + * fseek filled with nulls. + */ + (void) fputc('\0', outfile); + + (void) fclose(outfile); + table_p->ta_free(buckets); + + return TABLE_ERROR_NONE; +} + +/******************************** table order ********************************/ + +/* + * table_entry_t *table_order + * + * DESCRIPTION: + * + * Order a table by building an array of table entry pointers and then + * sorting this array using the qsort function. To retrieve the + * sorted entries, you can then use the table_entry routine to access + * each entry in order. + * + * NOTE: This routine is now thread safe in that two table_order calls + * can now happen at the same time, even on the same table. + * + * RETURNS: + * + * An allocated list of entry pointers which must be freed later. + * Returns null on error. + * + * ARGUMENTS: + * + * table_p - Pointer to the table that we are ordering. + * + * compare - Comparison function defined by the user. Its definition + * is at the top of the table.h file. If this is NULL then it will + * order the table my memcmp-ing the keys. + * + * num_entries_p - Pointer to an integer which, if not NULL, will + * contain the number of entries in the returned entry pointer array. + * + * error_p - Pointer to an integer which, if not NULL, will contain a + * table error code. + */ +table_entry_t **table_order(table_t * table_p, table_compare_t compare, + int *num_entries_p, int *error_p) +{ + table_entry_t *entry_p, **entries, **entries_p; + table_linear_t linear; + compare_t comp_func; + int error; + + if (table_p == NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_ARG_NULL; + return NULL; + } + if (table_p->ta_magic != TABLE_MAGIC) { + if (error_p != NULL) + *error_p = TABLE_ERROR_PNT; + return NULL; + } + + /* there must be at least 1 element in the table for this to work */ + if (table_p->ta_entry_n == 0) { + if (error_p != NULL) + *error_p = TABLE_ERROR_EMPTY; + return NULL; + } + + entries = (table_entry_t **) table_p->ta_malloc(table_p->ta_entry_n * + sizeof(table_entry_t *)); + if (entries == NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_ALLOC; + return NULL; + } + + /* get a pointer to all entries */ + entry_p = first_entry(table_p, &linear); + if (entry_p == NULL) { + if (error_p != NULL) + *error_p = TABLE_ERROR_NOT_FOUND; + return NULL; + } + + /* add all of the entries to the array */ + for (entries_p = entries; + entry_p != NULL; + entry_p = next_entry(table_p, &linear, &error)) + *entries_p++ = entry_p; + if (error != TABLE_ERROR_NOT_FOUND) { + if (error_p != NULL) + *error_p = error; + return NULL; + } + + if (compare == NULL) { + /* this is regardless of the alignment */ + comp_func = local_compare; + } + else if (table_p->ta_data_align == 0) + comp_func = external_compare; + else + comp_func = external_compare_align; + /* now qsort the entire entries array from first to last element */ + split(entries, entries + table_p->ta_entry_n - 1, comp_func, compare, + table_p); + + if (num_entries_p != NULL) + *num_entries_p = table_p->ta_entry_n; + if (error_p != NULL) + *error_p = TABLE_ERROR_NONE; + return entries; +} + +/* + * int table_entry + * + * DESCRIPTION: + * + * Get information about an element. The element is one from the + * array returned by the table_order function. If any of the key/data + * pointers are NULL then they are ignored. + * + * RETURNS: + * + * Success - TABLE_ERROR_NONE + * + * Failure - Table error code. + * + * ARGUMENTS: + * + * table_p - Table structure pointer from which we are getting the + * element. + * + * entry_p - Pointer to a table entry from the array returned by the + * table_order function. + * + * key_buf_p - Pointer which, if not NULL, will be set to the address + * of the storage of this entry that is allocated in the table. If an + * (int) is stored as this entry (for example) then key_buf_p should + * be (int **) i.e. the address of a (int *). + * + * key_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the key that is stored in the table. + * + * data_buf_p - Pointer which, if not NULL, will be set to the address + * of the data storage of this entry that is allocated in the table. + * If a (long) is stored as this entry data (for example) then + * data_buf_p should be (long **) i.e. the address of a (long *). + * + * data_size_p - Pointer to an integer which, if not NULL, will be set + * to the size of the data that is stored in the table. + */ +int table_entry_info(table_t * table_p, table_entry_t * entry_p, + void **key_buf_p, int *key_size_p, + void **data_buf_p, int *data_size_p) +{ + if (table_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (table_p->ta_magic != TABLE_MAGIC) + return TABLE_ERROR_PNT; + if (entry_p == NULL) + return TABLE_ERROR_ARG_NULL; + if (key_buf_p != NULL) + *key_buf_p = ENTRY_KEY_BUF(entry_p); + if (key_size_p != NULL) + *key_size_p = entry_p->te_key_size; + if (data_buf_p != NULL) { + if (entry_p->te_data_size == 0) + *data_buf_p = NULL; + else { + if (table_p->ta_data_align == 0) + *data_buf_p = ENTRY_DATA_BUF(table_p, entry_p); + else + *data_buf_p = entry_data_buf(table_p, entry_p); + } + } + if (data_size_p != NULL) + *data_size_p = entry_p->te_data_size; + return TABLE_ERROR_NONE; +} diff --git a/modules/ssl/ssl_util_table.h b/modules/ssl/ssl_util_table.h new file mode 100644 index 0000000000..69c53bd8a0 --- /dev/null +++ b/modules/ssl/ssl_util_table.h @@ -0,0 +1,189 @@ +/* _ _ +** _ __ ___ ___ __| | ___ ___| | mod_ssl +** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL +** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org +** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org +** |_____| +** ssl_util_table.h +** High Performance Hash Table Header +*/ + +/* ==================================================================== + * Copyright (c) 1999-2001 Ralf S. Engelschall. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * 4. The names "mod_ssl" must not be used to endorse or promote + * products derived from this software without prior written + * permission. For written permission, please contact + * rse@engelschall.com. + * + * 5. Products derived from this software may not be called "mod_ssl" + * nor may "mod_ssl" appear in their names without prior + * written permission of Ralf S. Engelschall. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by + * Ralf S. Engelschall <rse@engelschall.com> for use in the + * mod_ssl project (http://www.modssl.org/)." + * + * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR + * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + */ + +/* + * Generic hash table defines + * Table 4.1.0 July-28-1998 + * + * This library is a generic open hash table with buckets and + * linked lists. It is pretty high performance. Each element + * has a key and a data. The user indexes on the key to find the + * data. + * + * Copyright 1998 by Gray Watson <gray@letters.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, + * and that the name of Gray Watson not be used in advertising or + * publicity pertaining to distribution of the document or software + * without specific, written prior permission. + * + * Gray Watson makes no representations about the suitability of the + * software described herein for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifndef SSL_UTIL_TABLE_H +#define SSL_UTIL_TABLE_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * To build a "key" in any of the below routines, pass in a pointer to + * the key and its size [i.e. sizeof(int), etc]. With any of the + * "key" or "data" arguments, if their size is < 0, it will do an + * internal strlen of the item and add 1 for the \0. + * + * If you are using firstkey() and nextkey() functions, be careful if, + * after starting your firstkey loop, you use delete or insert, it + * will not crash but may produce interesting results. If you are + * deleting from firstkey to NULL it will work fine. + */ + +/* return types for table functions */ +#define TABLE_ERROR_NONE 1 /* no error from function */ +#define TABLE_ERROR_PNT 2 /* bad table pointer */ +#define TABLE_ERROR_ARG_NULL 3 /* buffer args were null */ +#define TABLE_ERROR_SIZE 4 /* size of data was bad */ +#define TABLE_ERROR_OVERWRITE 5 /* key exists and we cant overwrite */ +#define TABLE_ERROR_NOT_FOUND 6 /* key does not exist */ +#define TABLE_ERROR_ALLOC 7 /* memory allocation error */ +#define TABLE_ERROR_LINEAR 8 /* no linear access started */ +#define TABLE_ERROR_OPEN 9 /* could not open file */ +#define TABLE_ERROR_SEEK 10 /* could not seek to pos in file */ +#define TABLE_ERROR_READ 11 /* could not read from file */ +#define TABLE_ERROR_WRITE 12 /* could not write to file */ +#define TABLE_ERROR_EMPTY 13 /* table is empty */ +#define TABLE_ERROR_NOT_EMPTY 14 /* table contains data */ +#define TABLE_ERROR_ALIGNMENT 15 /* invalid alignment value */ + +/* + * Table flags set with table_attr. + */ + +/* + * Automatically adjust the number of table buckets on the fly. + * Whenever the number of entries gets above some threshold, the + * number of buckets is realloced to a new size and each entry is + * re-hashed. Although this may take some time when it re-hashes, the + * table will perform better over time. + */ +#define TABLE_FLAG_AUTO_ADJUST (1<<0) + +/* + * If the above auto-adjust flag is set, also adjust the number of + * table buckets down as we delete entries. + */ +#define TABLE_FLAG_ADJUST_DOWN (1<<1) + +/* structure to walk through the fields in a linear order */ +typedef struct { + unsigned int tl_magic; /* magic structure to ensure correct init */ + unsigned int tl_bucket_c; /* where in the table buck array we are */ + unsigned int tl_entry_c; /* in the bucket, which entry we are on */ +} table_linear_t; + +typedef int (*table_compare_t)(const void *key1, const int key1_size, + const void *data1, const int data1_size, + const void *key2, const int key2_size, + const void *data2, const int data2_size); + +#ifndef TABLE_PRIVATE +typedef void table_t; +typedef void table_entry_t; +#endif + +/* + * Prototypes + */ +extern table_t *table_alloc(const unsigned int bucket_n, int *error_p, void *(*malloc_f)(size_t size), void *(*calloc_f)(size_t number, size_t size), void *(*realloc_f)(void *ptr, size_t size), void (*free_f)(void *ptr)); +extern int table_attr(table_t *table_p, const int attr); +extern int table_set_data_alignment(table_t *table_p, const int alignment); +extern int table_clear(table_t *table_p); +extern int table_free(table_t *table_p); +extern int table_insert_kd(table_t *table_p, const void *key_buf, const int key_size, const void *data_buf, const int data_size, void **key_buf_p, void **data_buf_p, const char overwrite_b); +extern int table_insert(table_t *table_p, const void *key_buf, const int key_size, const void *data_buf, const int data_size, void **data_buf_p, const char overwrite_b); +extern int table_retrieve(table_t *table_p, const void *key_buf, const int key_size, void **data_buf_p, int *data_size_p); +extern int table_delete(table_t *table_p, const void *key_buf, const int key_size, void **data_buf_p, int *data_size_p); +extern int table_delete_first(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p); +extern int table_info(table_t *table_p, int *num_buckets_p, int *num_entries_p); +extern int table_adjust(table_t *table_p, const int bucket_n); +extern const char *table_strerror(const int error); +extern int table_type_size(void); +extern int table_first(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p); +extern int table_next(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p); +extern int table_this(table_t *table_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p); +extern int table_first_r(table_t *table_p, table_linear_t *linear_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p); +extern int table_next_r(table_t *table_p, table_linear_t *linear_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p); +extern int table_this_r(table_t *table_p, table_linear_t *linear_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p); +extern table_t *table_read(const char *path, int *error_p, void *(*malloc_f)(size_t size), void *(*calloc_f)(size_t number, size_t size), void *(*realloc_f)(void *ptr, size_t size), void (*free_f)(void *ptr)); +extern int table_write(const table_t *table_p, const char *path, const int mode); +extern table_entry_t **table_order(table_t *table_p, table_compare_t compare, int *num_entries_p, int *error_p); +extern int table_entry_info(table_t *table_p, table_entry_t *entry_p, void **key_buf_p, int *key_size_p, void **data_buf_p, int *data_size_p); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ! SSL_UTIL_TABLE_H */ |