[Pkg-fedora-ds-maintainers] [libapache2-mod-nss] 141/156: Add some basic functional tests.

Timo Aaltonen tjaalton-guest at moszumanska.debian.org
Wed Jul 2 13:55:37 UTC 2014


This is an automated email from the git hooks/post-receive script.

tjaalton-guest pushed a commit to branch master
in repository libapache2-mod-nss.

commit 07c27296c2c940cb119386304ebffb4ab41f0fb9
Author: Rob Crittenden <rcritten at redhat.com>
Date:   Wed Feb 26 16:37:51 2014 -0500

    Add some basic functional tests.
    
    This tests in an in-tree Apache instance using the local libmodnss.so
    shared library, so no pre-installation is necessary.
    
    The tests use python-nose and a hacked python-requests library. It is
    hacked so I can obtain the negotiated cipher and protocol as well as
    pass a few other things into it.
    
    Tests right now are limited to GET requests.
    
    A new user certificate for 'beta' was added to gencert to do pass/fail
    access control testing.
    
    The basic process of the tests are:
    
    - run setup.sh which sets up a new instance with createinstance.sh
      and does some variable substitution.
    - nosetests -v
    
    I picture multiple test "suites" of different configurations. Right now
    there is only one. A template file is provided for each suite.
    
    Tested only on Fedora 20 right now.
---
 Makefile.am            |  21 ++
 README                 |  22 +-
 gencert.in             |  22 ++
 test/createinstance.sh |  59 +++
 test/httpd.conf.tmpl   | 999 +++++++++++++++++++++++++++++++++++++++++++++++++
 test/setup.sh          |  55 +++
 test/suite1.tmpl       |  65 ++++
 test/test.py           | 138 +++++++
 test/test_config.py    | 186 +++++++++
 test/test_request.py   | 190 ++++++++++
 test/test_util.py      |  52 +++
 11 files changed, 1804 insertions(+), 5 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 46b9b2d..164cbaa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -78,3 +78,24 @@ LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) \
 
 LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
 	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+
+MAINTAINERCLEANFILES =		\
+	*~			\
+	Makefile.in		\
+	aclocal.m4		\
+	config.guess		\
+	config.sub		\
+	configure		\
+	depcomp			\
+	install-sh		\
+	ltmain.sh		\
+	missing			\
+	ylwrap
+
+test:
+	cd test;	\
+	./setup.sh;	\
+	nosetests -v;	\
+	cd ..
+
+.PHONY: all test clean
diff --git a/README b/README
index 2c2d946..8581698 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
 SYNOPSIS
 
- This Apache module provides strong cryptography for the Apache 2.0 webserver
- via the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS
+ This Apache module provides strong cryptography for the Apache 2.4 webserver
+ via the Secure Sockets Layer (v3) and Transport Layer Security (TLS
  v1) protocols by the help of the SSL/TLS implementation library NSS
 
  This module is based heavily on the mod_ssl package. In fact, it's more
@@ -9,7 +9,7 @@ SYNOPSIS
 
 BUILDING
 
- To build this you'll need NSPR 4.4.1 and NSS 3.9.3. It may work with earlier
+ To build this you'll need NSPR 4.9.+ and NSS 3.14.+. It may work with earlier
  versions but these are recommended (or tested). These can be retrieved from
  http://www.mozilla.org/. The --with-nspr and --with-nss tags require that
  the package be installed in the same parent directory (e.g. /opt/nspr,
@@ -22,6 +22,7 @@ BUILDING
  Build and install those packages somewhere then configure the module with
  something like:
 
+ % autoreconf -ivf
  % ./configure --with-apxs[=/path/to/apxs/] --with-nspr=/path/to/nspr/ --with-nss=/path/to/nss/
  % gmake all install
 
@@ -79,11 +80,13 @@ DOCUMENTATION
 
 REQUESTING A CERTIFICATE
 
- The NSS command-line tools may be used to generate a certificate request
+ You can use the provided gencert utility as a template for generating a
+ CA and a sample user and server certificate. Alterntaively, the NSS
+ command-line tools may be used to generate a certificate request
  suitable for submission to a local CA or a commerical CA like Verisign,
  and install the issued certificate into your local database. A sample
  request may look something like this. This assumes that your certificate
- database directory (NSSCertificateDatabase) is set to /opt/fortitude/alias
+ database directory (NSSCertificateDatabase) is set to /etc/httpd/alias
 
  Step 1 Create the database. This assumes you want your certificate database
         in /etc/httpd/alias
@@ -111,3 +114,12 @@ REQUESTING A CERTIFICATE
 
  % certutil -V -u V -d . -n Server-Cert
 
+TESTING
+
+ A few simple scripts are provided to stand up an in-tree Apache instance
+ against which some basic tests can be run to validate that the in-tree
+ library works.
+
+ From the source tree run:
+
+ % make test
diff --git a/gencert.in b/gencert.in
index f51ee3e..1128740 100755
--- a/gencert.in
+++ b/gencert.in
@@ -58,6 +58,7 @@ fi
 CA_CERTDN="CN=Certificate Shack, O=example.com, C=US"
 SERVER_CERTDN="CN=${FQDN}, O=example.com, C=US"
 ALPHA_CERTDN="E=alpha@${FQDN}, CN=Frank Alpha, UID=alpha, OU=People, O=example.com, C=US"
+BETA_CERTDN="E=beta@${FQDN}, CN=Anna Beta, UID=beta, OU=People, O=example.com, C=US"
 
 # size of the keys
 KEYSIZE=1024
@@ -137,6 +138,27 @@ $CERTUTIL -S -d $DEST -n alpha \
 
 echo ""
 echo "#####################################################################"
+echo "Generating user certificate for \"beta\"."
+echo "#####################################################################"
+(ps -elf; date; netstat -a) > $DEST/noise
+let CERTSERIAL=CERTSERIAL+1
+# 0 2 9 n  -> Key usage: Key Encipherment, Digital Signature
+# 0 9 n  -> SSL Client
+echo -e "0\n2\n9\nn\n0\n9\nn\n" | \
+$CERTUTIL -S -d $DEST -n beta \
+            -s "$BETA_CERTDN" \
+            -c cacert \
+            -t u,pu,u \
+            -g $KEYSIZE \
+            -m $CERTSERIAL \
+            -v $VALIDITY \
+            -f $DEST/pw.txt \
+            -z $DEST/noise \
+            -1 \
+            -5
+
+echo ""
+echo "#####################################################################"
 echo "Generating server certificate request"
 echo "#####################################################################"
 (ps -elf; date; netstat -a) > $DEST/noise
diff --git a/test/createinstance.sh b/test/createinstance.sh
new file mode 100755
index 0000000..1eaa644
--- /dev/null
+++ b/test/createinstance.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# Make a temporary Apache instance for testing.
+
+if [ " $#" -eq 0 ]; then
+    echo "Usage: $0 /path/to/instance"
+    exit 1
+fi
+target=$1
+
+echo "Creating instance in $target"
+mkdir -p $target
+
+cd $target
+mkdir alias
+mkdir conf
+mkdir conf.d
+mkdir logs
+mkdir run
+mkdir content
+mkdir cgi-bin
+mkdir lib
+
+# Create the content
+mkdir content/rc4_cipher
+mkdir content/acl
+
+cat > content/index.html << EOF
+<html>
+Basic index page
+</html
+EOF
+cp content/index.html content/acl/aclS01.html
+cp content/index.html content/acl/aclS02.html
+cp content/index.html content/acl/aclS03.html
+cp content/index.html content/secret-test.html
+
+ln -s /etc/httpd/modules modules
+
+dn="E=alpha@`hostname`,CN=Frank Alpha,UID=alpha,OU=People,O=example.com,C=US"
+cat > conf/htpasswd << EOF
+/${dn}:xxj31ZMTZzkVA
+EOF
+
+# Create start/stop scripts
+
+cat << EOF >  start
+#!/bin/sh
+HTTPD=/usr/sbin/httpd
+\$HTTPD -k start -d . -f ./conf/httpd.conf
+EOF
+
+cat << EOF > stop
+#!/bin/sh
+HTTPD=/usr/sbin/httpd
+\$HTTPD -k stop -d . -f ./conf/httpd.conf
+EOF
+
+chmod 0755 start stop
diff --git a/test/httpd.conf.tmpl b/test/httpd.conf.tmpl
new file mode 100644
index 0000000..36af91d
--- /dev/null
+++ b/test/httpd.conf.tmpl
@@ -0,0 +1,999 @@
+#
+# This is the main Apache HTTP server configuration file.  It contains the
+# configuration directives that give the server its instructions.
+# See <URL:http://httpd.apache.org/docs/2.2> for detailed information.
+# In particular, see
+# <URL:http://httpd.apache.org/docs/2.2/mod/directives.html>
+# for a discussion of each configuration directive.
+#
+# Do NOT simply read the instructions in here without understanding
+# what they do.  They're here only as hints or reminders.  If you are unsure
+# consult the online docs. You have been warned.
+#
+# The configuration directives are grouped into three basic sections:
+#  1. Directives that control the operation of the Apache server process as a
+#     whole (the 'global environment').
+#  2. Directives that define the parameters of the 'main' or 'default' server,
+#     which responds to requests that aren't handled by a virtual host.
+#     These directives also provide default values for the settings
+#     of all virtual hosts.
+#  3. Settings for virtual hosts, which allow Web requests to be sent to
+#     different IP addresses or hostnames and have them handled by the
+#     same Apache server process.
+#
+# Configuration and logfile names: If the filenames you specify for many
+# of the server's control files begin with "/" (or "drive:/" for Win32), the
+# server will use that explicit path.  If the filenames do *not* begin
+# with "/", the value of ServerRoot is prepended -- so "logs/foo.log"
+# with ServerRoot set to "/etc/httpd" will be interpreted by the
+# server as "/etc/httpd/logs/foo.log".
+#
+
+### Section 1: Global Environment
+#
+# The directives in this section affect the overall operation of Apache,
+# such as the number of concurrent requests it can handle or where it
+# can find its configuration files.
+#
+
+#
+# Don't give away too much information about all the subcomponents
+# we are running.  Comment out this line if you don't mind remote sites
+# finding out what major optional modules you are running
+ServerTokens ProductOnly
+
+#
+# ServerRoot: The top of the directory tree under which the server's
+# configuration, error, and log files are kept.
+#
+# NOTE!  If you intend to place this on an NFS (or otherwise network)
+# mounted filesystem then please read the LockFile documentation
+# (available at <URL:http://httpd.apache.org/docs/2.2/mod/mpm_common.html#lockfile>);
+# you will save yourself a lot of trouble.
+#
+# Do NOT add a slash at the end of the directory path.
+#
+ServerRoot "::SERVER_ROOT::"
+
+#
+# PidFile: The file in which the server should record its process
+# identification number when it starts.  Note the PIDFILE variable in
+# /etc/sysconfig/httpd must be set appropriately if this location is
+# changed.
+#
+PidFile run/httpd.pid
+
+#
+# Timeout: The number of seconds before receives and sends time out.
+#
+Timeout 60
+
+#
+# KeepAlive: Whether or not to allow persistent connections (more than
+# one request per connection). Set to "Off" to deactivate.
+#
+KeepAlive Off
+
+#
+# MaxKeepAliveRequests: The maximum number of requests to allow
+# during a persistent connection. Set to 0 to allow an unlimited amount.
+# We recommend you leave this number high, for maximum performance.
+#
+MaxKeepAliveRequests 100
+
+#
+# KeepAliveTimeout: Number of seconds to wait for the next request from the
+# same client on the same connection.
+#
+KeepAliveTimeout 5
+
+##
+## Server-Pool Size Regulation (MPM specific)
+##
+
+# prefork MPM
+# StartServers: number of server processes to start
+# MinSpareServers: minimum number of server processes which are kept spare
+# MaxSpareServers: maximum number of server processes which are kept spare
+# ServerLimit: maximum value for MaxClients for the lifetime of the server
+# MaxClients: maximum number of server processes allowed to start
+# MaxRequestsPerChild: maximum number of requests a server process serves
+<IfModule prefork.c>
+StartServers       8
+MinSpareServers    5
+MaxSpareServers   20
+ServerLimit      256
+MaxClients       256
+MaxRequestsPerChild  4000
+</IfModule>
+
+# worker MPM
+# StartServers: initial number of server processes to start
+# MaxClients: maximum number of simultaneous client connections
+# MinSpareThreads: minimum number of worker threads which are kept spare
+# MaxSpareThreads: maximum number of worker threads which are kept spare
+# ThreadsPerChild: constant number of worker threads in each server process
+# MaxRequestsPerChild: maximum number of requests a server process serves
+<IfModule worker.c>
+StartServers         4
+MaxClients         300
+MinSpareThreads     25
+MaxSpareThreads     75
+ThreadsPerChild     25
+MaxRequestsPerChild  0
+</IfModule>
+
+#
+# Listen: Allows you to bind Apache to specific IP addresses and/or
+# ports, instead of the default. See also the <VirtualHost>
+# directive.
+#
+# Change this to Listen on specific IP addresses as shown below to
+# prevent Apache from glomming onto all bound IP addresses.
+#
+#Listen 12.34.56.78:80
+#Listen 80
+
+#
+# Dynamic Shared Object (DSO) Support
+#
+# To be able to use the functionality of a module which was built as a DSO you
+# have to place corresponding `LoadModule' lines at this location so the
+# directives contained in it are actually available _before_ they are used.
+# Statically compiled modules (those listed by `httpd -l') do not need
+# to be loaded here.
+#
+# Example:
+# LoadModule foo_module modules/mod_foo.so
+#
+
+LoadModule access_compat_module modules/mod_access_compat.so
+LoadModule actions_module modules/mod_actions.so
+LoadModule alias_module modules/mod_alias.so
+LoadModule allowmethods_module modules/mod_allowmethods.so
+LoadModule auth_basic_module modules/mod_auth_basic.so
+#LoadModule auth_digest_module modules/mod_auth_digest.so
+LoadModule authn_anon_module modules/mod_authn_anon.so
+LoadModule authn_core_module modules/mod_authn_core.so
+LoadModule authn_dbd_module modules/mod_authn_dbd.so
+LoadModule authn_dbm_module modules/mod_authn_dbm.so
+LoadModule authn_file_module modules/mod_authn_file.so
+LoadModule authn_socache_module modules/mod_authn_socache.so
+LoadModule authz_core_module modules/mod_authz_core.so
+LoadModule authz_dbd_module modules/mod_authz_dbd.so
+LoadModule authz_dbm_module modules/mod_authz_dbm.so
+LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
+LoadModule authz_host_module modules/mod_authz_host.so
+LoadModule authz_owner_module modules/mod_authz_owner.so
+LoadModule authz_user_module modules/mod_authz_user.so
+LoadModule autoindex_module modules/mod_autoindex.so
+LoadModule cache_module modules/mod_cache.so
+LoadModule cache_disk_module modules/mod_cache_disk.so
+LoadModule data_module modules/mod_data.so
+LoadModule dbd_module modules/mod_dbd.so
+LoadModule deflate_module modules/mod_deflate.so
+LoadModule dir_module modules/mod_dir.so
+LoadModule dumpio_module modules/mod_dumpio.so
+LoadModule echo_module modules/mod_echo.so
+LoadModule env_module modules/mod_env.so
+LoadModule expires_module modules/mod_expires.so
+LoadModule ext_filter_module modules/mod_ext_filter.so
+LoadModule filter_module modules/mod_filter.so
+LoadModule headers_module modules/mod_headers.so
+LoadModule include_module modules/mod_include.so
+LoadModule info_module modules/mod_info.so
+LoadModule log_config_module modules/mod_log_config.so
+LoadModule logio_module modules/mod_logio.so
+LoadModule macro_module modules/mod_macro.so
+LoadModule mime_magic_module modules/mod_mime_magic.so
+LoadModule mime_module modules/mod_mime.so
+LoadModule negotiation_module modules/mod_negotiation.so
+LoadModule remoteip_module modules/mod_remoteip.so
+LoadModule reqtimeout_module modules/mod_reqtimeout.so
+LoadModule rewrite_module modules/mod_rewrite.so
+LoadModule setenvif_module modules/mod_setenvif.so
+LoadModule slotmem_plain_module modules/mod_slotmem_plain.so
+LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
+LoadModule socache_dbm_module modules/mod_socache_dbm.so
+LoadModule socache_memcache_module modules/mod_socache_memcache.so
+LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
+LoadModule status_module modules/mod_status.so
+LoadModule substitute_module modules/mod_substitute.so
+LoadModule suexec_module modules/mod_suexec.so
+LoadModule unique_id_module modules/mod_unique_id.so
+LoadModule unixd_module modules/mod_unixd.so
+LoadModule userdir_module modules/mod_userdir.so
+LoadModule version_module modules/mod_version.so
+LoadModule vhost_alias_module modules/mod_vhost_alias.so
+
+# Include the locally-built NSS module
+LoadModule nss_module lib/libmodnss.so
+
+# Include the configuration we care about
+Include /etc/httpd/conf.modules.d/00-mpm.conf
+Include /etc/httpd/conf.modules.d/01-cgi.conf
+
+#
+# Load config files from the config directory "/etc/httpd/conf.d".
+#
+#Include conf.d/*.conf
+
+#
+# ExtendedStatus controls whether Apache will generate "full" status
+# information (ExtendedStatus On) or just basic information (ExtendedStatus
+# Off) when the "server-status" handler is called. The default is Off.
+#
+#ExtendedStatus On
+
+#
+# If you wish httpd to run as a different user or group, you must run
+# httpd as root initially and it will switch.
+#
+# User/Group: The name (or #number) of the user/group to run httpd as.
+#  . On SCO (ODT 3) use "User nouser" and "Group nogroup".
+#  . On HPUX you may not be able to use shared memory as nobody, and the
+#    suggested workaround is to create a user www and use that user.
+#  NOTE that some kernels refuse to setgid(Group) or semctl(IPC_SET)
+#  when the value of (unsigned)Group is above 60000;
+#  don't use Group #-1 on these systems!
+#
+User ::SERVER_UID::
+Group ::SERVER_GID::
+
+### Section 2: 'Main' server configuration
+#
+# The directives in this section set up the values used by the 'main'
+# server, which responds to any requests that aren't handled by a
+# <VirtualHost> definition.  These values also provide defaults for
+# any <VirtualHost> containers you may define later in the file.
+#
+# All of these directives may appear inside <VirtualHost> containers,
+# in which case these default settings will be overridden for the
+# virtual host being defined.
+#
+
+#
+# ServerAdmin: Your address, where problems with the server should be
+# e-mailed.  This address appears on some server-generated pages, such
+# as error documents.  e.g. admin at your-domain.com
+#
+ServerAdmin root at localhost
+
+#
+# ServerName gives the name and port that the server uses to identify itself.
+# This can often be determined automatically, but we recommend you specify
+# it explicitly to prevent problems during startup.
+#
+# If this is not set to valid DNS name for your host, server-generated
+# redirections will not work.  See also the UseCanonicalName directive.
+#
+# If your host doesn't have a registered DNS name, enter its IP address here.
+# You will have to access it by its address anyway, and this will make
+# redirections work in a sensible way.
+#
+#ServerName www.example.com:80
+ServerName localhost:::SERVER_PORT::
+
+#
+# UseCanonicalName: Determines how Apache constructs self-referencing
+# URLs and the SERVER_NAME and SERVER_PORT variables.
+# When set "Off", Apache will use the Hostname and Port supplied
+# by the client.  When set "On", Apache will use the value of the
+# ServerName directive.
+#
+UseCanonicalName On
+
+#
+# DocumentRoot: The directory out of which you will serve your
+# documents. By default, all requests are taken from this directory, but
+# symbolic links and aliases may be used to point to other locations.
+#
+DocumentRoot "::TEST_ROOT::/content"
+
+#
+# Each directory to which Apache has access can be configured with respect
+# to which services and features are allowed and/or disabled in that
+# directory (and its subdirectories).
+#
+# First, we configure the "default" to be a very restrictive set of
+# features.
+#
+<Directory />
+    Options FollowSymLinks
+    AllowOverride None
+</Directory>
+
+#
+# Note that from this point forward you must specifically allow
+# particular features to be enabled - so if something's not working as
+# you might expect, make sure that you have specifically enabled it
+# below.
+#
+
+#
+# This should be changed to whatever you set DocumentRoot to.
+#
+<Directory "::TEST_ROOT::/content">
+
+    #
+    # Possible values for the Options directive are "None", "All",
+    # or any combination of:
+    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
+    #
+    # Note that "MultiViews" must be named *explicitly* --- "Options All"
+    # doesn't give it to you.
+    #
+    # The Options directive is both complicated and important.  Please see
+    # http://httpd.apache.org/docs/2.2/mod/core.html#options
+    # for more information.
+    #
+    Options Indexes FollowSymLinks
+
+    #
+    # AllowOverride controls what directives may be placed in .htaccess files.
+    # It can be "All", "None", or any combination of the keywords:
+    #   Options FileInfo AuthConfig Limit
+    #
+    AllowOverride All
+
+    #
+    # Controls who can get stuff from this server.
+    #
+    Order allow,deny
+    Allow from all
+
+</Directory>
+
+#
+# UserDir: The name of the directory that is appended onto a user's home
+# directory if a ~user request is received.
+#
+# The path to the end user account 'public_html' directory must be
+# accessible to the webserver userid.  This usually means that ~userid
+# must have permissions of 711, ~userid/public_html must have permissions
+# of 755, and documents contained therein must be world-readable.
+# Otherwise, the client will only receive a "403 Forbidden" message.
+#
+# See also: http://httpd.apache.org/docs/misc/FAQ.html#forbidden
+#
+<IfModule mod_userdir.c>
+    #
+    # UserDir is disabled by default since it can confirm the presence
+    # of a username on the system (depending on home directory
+    # permissions).
+    #
+    UserDir disabled
+
+    #
+    # To enable requests to /~user/ to serve the user's public_html
+    # directory, remove the "UserDir disabled" line above, and uncomment
+    # the following line instead:
+    #
+    #UserDir public_html
+
+</IfModule>
+
+#
+# Control access to UserDir directories.  The following is an example
+# for a site where these directories are restricted to read-only.
+#
+#<Directory /home/*/public_html>
+#    AllowOverride FileInfo AuthConfig Limit
+#    Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
+#    <Limit GET POST OPTIONS>
+#        Order allow,deny
+#        Allow from all
+#    </Limit>
+#    <LimitExcept GET POST OPTIONS>
+#        Order deny,allow
+#        Deny from all
+#    </LimitExcept>
+#</Directory>
+
+#
+# DirectoryIndex: sets the file that Apache will serve if a directory
+# is requested.
+#
+# The index.html.var file (a type-map) is used to deliver content-
+# negotiated documents.  The MultiViews Option can be used for the
+# same purpose, but it is much slower.
+#
+DirectoryIndex index.html index.html.var
+
+#
+# AccessFileName: The name of the file to look for in each directory
+# for additional configuration directives.  See also the AllowOverride
+# directive.
+#
+AccessFileName .htaccess
+
+#
+# The following lines prevent .htaccess and .htpasswd files from being
+# viewed by Web clients.
+#
+<FilesMatch "^\.ht">
+    Order allow,deny
+    Deny from all
+    Satisfy All
+</FilesMatch>
+
+#
+# TypesConfig describes where the mime.types file (or equivalent) is
+# to be found.
+#
+TypesConfig /etc/mime.types
+
+#
+# The mod_mime_magic module allows the server to use various hints from the
+# contents of the file itself to determine its type.  The MIMEMagicFile
+# directive tells the module where the hint definitions are located.
+#
+<IfModule mod_mime_magic.c>
+#   MIMEMagicFile /usr/share/magic.mime
+    MIMEMagicFile conf/magic
+</IfModule>
+
+#
+# HostnameLookups: Log the names of clients or just their IP addresses
+# e.g., www.apache.org (on) or 204.62.129.132 (off).
+# The default is off because it'd be overall better for the net if people
+# had to knowingly turn this feature on, since enabling it means that
+# each client request will result in AT LEAST one lookup request to the
+# nameserver.
+#
+HostnameLookups Off
+
+#
+# EnableMMAP: Control whether memory-mapping is used to deliver
+# files (assuming that the underlying OS supports it).
+# The default is on; turn this off if you serve from NFS-mounted
+# filesystems.  On some systems, turning it off (regardless of
+# filesystem) can improve performance; for details, please see
+# http://httpd.apache.org/docs/2.2/mod/core.html#enablemmap
+#
+#EnableMMAP off
+
+#
+# EnableSendfile: Control whether the sendfile kernel support is
+# used to deliver files (assuming that the OS supports it).
+# The default is on; turn this off if you serve from NFS-mounted
+# filesystems.  Please see
+# http://httpd.apache.org/docs/2.2/mod/core.html#enablesendfile
+#
+#EnableSendfile off
+
+#
+# ErrorLog: The location of the error log file.
+# If you do not specify an ErrorLog directive within a <VirtualHost>
+# container, error messages relating to that virtual host will be
+# logged here.  If you *do* define an error logfile for a <VirtualHost>
+# container, that host's errors will be logged there and not here.
+#
+ErrorLog logs/error_log
+
+#
+# LogLevel: Control the number of messages logged to the error_log.
+# Possible values include: debug, info, notice, warn, error, crit,
+# alert, emerg.
+#
+LogLevel debug
+
+#
+# The following directives define some format nicknames for use with
+# a CustomLog directive (see below).
+#
+LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+LogFormat "%h %l %u %t \"%r\" %>s %b" common
+LogFormat "%{Referer}i -> %U" referer
+LogFormat "%{User-agent}i" agent
+
+# "combinedio" includes actual counts of actual bytes received (%I) and sent (%O); this
+# requires the mod_logio module to be loaded.
+#LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
+
+#
+# The location and format of the access logfile (Common Logfile Format).
+# If you do not define any access logfiles within a <VirtualHost>
+# container, they will be logged here.  Contrariwise, if you *do*
+# define per-<VirtualHost> access logfiles, transactions will be
+# logged therein and *not* in this file.
+#
+#CustomLog logs/access_log common
+
+#
+# If you would like to have separate agent and referer logfiles, uncomment
+# the following directives.
+#
+#CustomLog logs/referer_log referer
+#CustomLog logs/agent_log agent
+
+#
+# For a single logfile with access, agent, and referer information
+# (Combined Logfile Format), use the following directive:
+#
+CustomLog logs/access_log combined
+
+#
+# Optionally add a line containing the server version and virtual host
+# name to server-generated pages (internal error documents, FTP directory
+# listings, mod_status and mod_info output etc., but not CGI generated
+# documents or custom error documents).
+# Set to "EMail" to also include a mailto: link to the ServerAdmin.
+# Set to one of:  On | Off | EMail
+#
+ServerSignature Off
+
+#
+# Aliases: Add here as many aliases as you need (with no limit). The format is
+# Alias fakename realname
+#
+# Note that if you include a trailing / on fakename then the server will
+# require it to be present in the URL.  So "/icons" isn't aliased in this
+# example, only "/icons/".  If the fakename is slash-terminated, then the
+# realname must also be slash terminated, and if the fakename omits the
+# trailing slash, the realname must also omit it.
+#
+# We include the /icons/ alias for FancyIndexed directory listings.  If you
+# do not use FancyIndexing, you may comment this out.
+#
+Alias /icons/ "/var/www/icons/"
+
+<Directory "/var/www/icons">
+    Options Indexes MultiViews FollowSymLinks
+    AllowOverride None
+    Order allow,deny
+    Allow from all
+</Directory>
+
+#
+# WebDAV module configuration section.
+#
+<IfModule mod_dav_fs.c>
+    # Location of the WebDAV lock database.
+    DAVLockDB /var/lib/dav/lockdb
+</IfModule>
+
+#
+# ScriptAlias: This controls which directories contain server scripts.
+# ScriptAliases are essentially the same as Aliases, except that
+# documents in the realname directory are treated as applications and
+# run by the server when requested rather than as documents sent to the client.
+# The same rules about trailing "/" apply to ScriptAlias directives as to
+# Alias.
+#
+ScriptAlias /cgi-bin/ "::TEST_ROOT::/cgi-bin/"
+
+#
+# "/var/www/cgi-bin" should be changed to whatever your ScriptAliased
+# CGI directory exists, if you have that configured.
+#
+<Directory "::TEST_ROOT::/cgi-bin">
+    AllowOverride None
+    Options None
+    Order allow,deny
+    Allow from all
+</Directory>
+
+#
+# Redirect allows you to tell clients about documents which used to exist in
+# your server's namespace, but do not anymore. This allows you to tell the
+# clients where to look for the relocated document.
+# Example:
+# Redirect permanent /foo http://www.example.com/bar
+
+#
+# Directives controlling the display of server-generated directory listings.
+#
+
+#
+# IndexOptions: Controls the appearance of server-generated directory
+# listings.
+#
+IndexOptions FancyIndexing VersionSort NameWidth=* HTMLTable Charset=UTF-8
+
+#
+# AddIcon* directives tell the server which icon to show for different
+# files or filename extensions.  These are only displayed for
+# FancyIndexed directories.
+#
+AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip
+
+AddIconByType (TXT,/icons/text.gif) text/*
+AddIconByType (IMG,/icons/image2.gif) image/*
+AddIconByType (SND,/icons/sound2.gif) audio/*
+AddIconByType (VID,/icons/movie.gif) video/*
+
+AddIcon /icons/binary.gif .bin .exe
+AddIcon /icons/binhex.gif .hqx
+AddIcon /icons/tar.gif .tar
+AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv
+AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip
+AddIcon /icons/a.gif .ps .ai .eps
+AddIcon /icons/layout.gif .html .shtml .htm .pdf
+AddIcon /icons/text.gif .txt
+AddIcon /icons/c.gif .c
+AddIcon /icons/p.gif .pl .py
+AddIcon /icons/f.gif .for
+AddIcon /icons/dvi.gif .dvi
+AddIcon /icons/uuencoded.gif .uu
+AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
+AddIcon /icons/tex.gif .tex
+AddIcon /icons/bomb.gif core
+
+AddIcon /icons/back.gif ..
+AddIcon /icons/hand.right.gif README
+AddIcon /icons/folder.gif ^^DIRECTORY^^
+AddIcon /icons/blank.gif ^^BLANKICON^^
+
+#
+# DefaultIcon is which icon to show for files which do not have an icon
+# explicitly set.
+#
+DefaultIcon /icons/unknown.gif
+
+#
+# AddDescription allows you to place a short description after a file in
+# server-generated indexes.  These are only displayed for FancyIndexed
+# directories.
+# Format: AddDescription "description" filename
+#
+#AddDescription "GZIP compressed document" .gz
+#AddDescription "tar archive" .tar
+#AddDescription "GZIP compressed tar archive" .tgz
+
+#
+# ReadmeName is the name of the README file the server will look for by
+# default, and append to directory listings.
+#
+# HeaderName is the name of a file which should be prepended to
+# directory indexes.
+ReadmeName README.html
+HeaderName HEADER.html
+
+#
+# IndexIgnore is a set of filenames which directory indexing should ignore
+# and not include in the listing.  Shell-style wildcarding is permitted.
+#
+IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t
+
+#
+# DefaultLanguage and AddLanguage allows you to specify the language of
+# a document. You can then use content negotiation to give a browser a
+# file in a language the user can understand.
+#
+# Specify a default language. This means that all data
+# going out without a specific language tag (see below) will
+# be marked with this one. You probably do NOT want to set
+# this unless you are sure it is correct for all cases.
+#
+# * It is generally better to not mark a page as
+# * being a certain language than marking it with the wrong
+# * language!
+#
+# DefaultLanguage nl
+#
+# Note 1: The suffix does not have to be the same as the language
+# keyword --- those with documents in Polish (whose net-standard
+# language code is pl) may wish to use "AddLanguage pl .po" to
+# avoid the ambiguity with the common suffix for perl scripts.
+#
+# Note 2: The example entries below illustrate that in some cases
+# the two character 'Language' abbreviation is not identical to
+# the two character 'Country' code for its country,
+# E.g. 'Danmark/dk' versus 'Danish/da'.
+#
+# Note 3: In the case of 'ltz' we violate the RFC by using a three char
+# specifier. There is 'work in progress' to fix this and get
+# the reference data for rfc1766 cleaned up.
+#
+# Catalan (ca) - Croatian (hr) - Czech (cs) - Danish (da) - Dutch (nl)
+# English (en) - Esperanto (eo) - Estonian (et) - French (fr) - German (de)
+# Greek-Modern (el) - Hebrew (he) - Italian (it) - Japanese (ja)
+# Korean (ko) - Luxembourgeois* (ltz) - Norwegian Nynorsk (nn)
+# Norwegian (no) - Polish (pl) - Portugese (pt)
+# Brazilian Portuguese (pt-BR) - Russian (ru) - Swedish (sv)
+# Simplified Chinese (zh-CN) - Spanish (es) - Traditional Chinese (zh-TW)
+#
+AddLanguage ca .ca
+AddLanguage cs .cz .cs
+AddLanguage da .dk
+AddLanguage de .de
+AddLanguage el .el
+AddLanguage en .en
+AddLanguage eo .eo
+AddLanguage es .es
+AddLanguage et .et
+AddLanguage fr .fr
+AddLanguage he .he
+AddLanguage hr .hr
+AddLanguage it .it
+AddLanguage ja .ja
+AddLanguage ko .ko
+AddLanguage ltz .ltz
+AddLanguage nl .nl
+AddLanguage nn .nn
+AddLanguage no .no
+AddLanguage pl .po
+AddLanguage pt .pt
+AddLanguage pt-BR .pt-br
+AddLanguage ru .ru
+AddLanguage sv .sv
+AddLanguage zh-CN .zh-cn
+AddLanguage zh-TW .zh-tw
+
+#
+# LanguagePriority allows you to give precedence to some languages
+# in case of a tie during content negotiation.
+#
+# Just list the languages in decreasing order of preference. We have
+# more or less alphabetized them here. You probably want to change this.
+#
+LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv zh-CN zh-TW
+
+#
+# ForceLanguagePriority allows you to serve a result page rather than
+# MULTIPLE CHOICES (Prefer) [in case of a tie] or NOT ACCEPTABLE (Fallback)
+# [in case no accepted languages matched the available variants]
+#
+ForceLanguagePriority Prefer Fallback
+
+#
+# Specify a default charset for all content served; this enables
+# interpretation of all content as UTF-8 by default.  To use the
+# default browser choice (ISO-8859-1), or to allow the META tags
+# in HTML content to override this choice, comment out this
+# directive:
+#
+AddDefaultCharset UTF-8
+
+#
+# AddType allows you to add to or override the MIME configuration
+# file mime.types for specific file types.
+#
+#AddType application/x-tar .tgz
+
+#
+# AddEncoding allows you to have certain browsers uncompress
+# information on the fly. Note: Not all browsers support this.
+# Despite the name similarity, the following Add* directives have nothing
+# to do with the FancyIndexing customization directives above.
+#
+#AddEncoding x-compress .Z
+#AddEncoding x-gzip .gz .tgz .svgz
+
+# If the AddEncoding directives above are commented-out, then you
+# probably should define those extensions to indicate media types:
+#
+AddType application/x-compress .Z
+AddType application/x-gzip .gz .tgz
+
+#
+#   MIME-types for downloading Certificates and CRLs
+#
+AddType application/x-x509-ca-cert .crt
+AddType application/x-pkcs7-crl    .crl
+
+#
+# AddHandler allows you to map certain file extensions to "handlers":
+# actions unrelated to filetype. These can be either built into the server
+# or added with the Action directive (see below)
+#
+# To use CGI scripts outside of ScriptAliased directories:
+# (You will also need to add "ExecCGI" to the "Options" directive.)
+#
+#AddHandler cgi-script .cgi
+
+#
+# For files that include their own HTTP headers:
+#
+#AddHandler send-as-is asis
+
+#
+# For type maps (negotiated resources):
+# (This is enabled by default to allow the Apache "It Worked" page
+#  to be distributed in multiple languages.)
+#
+AddHandler type-map var
+
+#
+# Filters allow you to process content before it is sent to the client.
+#
+# To parse .shtml files for server-side includes (SSI):
+# (You will also need to add "Includes" to the "Options" directive.)
+#
+AddType text/html .shtml
+AddOutputFilter INCLUDES .shtml
+
+#
+# Action lets you define media types that will execute a script whenever
+# a matching file is called. This eliminates the need for repeated URL
+# pathnames for oft-used CGI file processors.
+# Format: Action media/type /cgi-script/location
+# Format: Action handler-name /cgi-script/location
+#
+
+#
+# Customizable error responses come in three flavors:
+# 1) plain text 2) local redirects 3) external redirects
+#
+# Some examples:
+#ErrorDocument 500 "The server made a boo boo."
+#ErrorDocument 404 /missing.html
+#ErrorDocument 404 "/cgi-bin/missing_handler.pl"
+#ErrorDocument 402 http://www.example.com/subscription_info.html
+#
+
+#
+# Putting this all together, we can internationalize error responses.
+#
+# We use Alias to redirect any /error/HTTP_<error>.html.var response to
+# our collection of by-error message multi-language collections.  We use
+# includes to substitute the appropriate text.
+#
+# You can modify the messages' appearance without changing any of the
+# default HTTP_<error>.html.var files by adding the line:
+#
+#   Alias /error/include/ "/your/include/path/"
+#
+# which allows you to create your own set of files by starting with the
+# /var/www/error/include/ files and
+# copying them to /your/include/path/, even on a per-VirtualHost basis.
+#
+
+Alias /error/ "/var/www/error/"
+
+<IfModule mod_negotiation.c>
+<IfModule mod_include.c>
+    <Directory "/var/www/error">
+        AllowOverride None
+        Options IncludesNoExec
+        AddOutputFilter Includes html
+        AddHandler type-map var
+        Order allow,deny
+        Allow from all
+        LanguagePriority en es de fr
+        ForceLanguagePriority Prefer Fallback
+    </Directory>
+
+#    ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var
+#    ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var
+#    ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var
+#    ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var
+#    ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var
+#    ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var
+#    ErrorDocument 410 /error/HTTP_GONE.html.var
+#    ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var
+#    ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var
+#    ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var
+#    ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var
+#    ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var
+#    ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var
+#    ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var
+#    ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var
+#    ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var
+#    ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var
+
+</IfModule>
+</IfModule>
+
+#
+# The following directives modify normal HTTP response behavior to
+# handle known problems with browser implementations.
+#
+BrowserMatch "Mozilla/2" nokeepalive
+BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
+BrowserMatch "RealPlayer 4\.0" force-response-1.0
+BrowserMatch "Java/1\.0" force-response-1.0
+BrowserMatch "JDK/1\.0" force-response-1.0
+
+#
+# The following directive disables redirects on non-GET requests for
+# a directory that does not include the trailing slash.  This fixes a
+# problem with Microsoft WebFolders which does not appropriately handle
+# redirects for folders with DAV methods.
+# Same deal with Apple's DAV filesystem and Gnome VFS support for DAV.
+#
+BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully
+BrowserMatch "MS FrontPage" redirect-carefully
+BrowserMatch "^WebDrive" redirect-carefully
+BrowserMatch "^WebDAVFS/1.[0123]" redirect-carefully
+BrowserMatch "^gnome-vfs/1.0" redirect-carefully
+BrowserMatch "^XML Spy" redirect-carefully
+BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully
+
+#
+# Allow server status reports generated by mod_status,
+# with the URL of http://servername/server-status
+# Change the ".example.com" to match your domain to enable.
+#
+#<Location /server-status>
+#    SetHandler server-status
+#    Order deny,allow
+#    Deny from all
+#    Allow from .example.com
+#</Location>
+
+#
+# Allow remote server configuration reports, with the URL of
+#  http://servername/server-info (requires that mod_info.c be loaded).
+# Change the ".example.com" to match your domain to enable.
+#
+#<Location /server-info>
+#    SetHandler server-info
+#    Order deny,allow
+#    Deny from all
+#    Allow from .example.com
+#</Location>
+
+#
+# Proxy Server directives. Uncomment the following lines to
+# enable the proxy server:
+#
+#<IfModule mod_proxy.c>
+#ProxyRequests On
+#
+#<Proxy *>
+#    Order deny,allow
+#    Deny from all
+#    Allow from .example.com
+#</Proxy>
+
+#
+# Enable/disable the handling of HTTP/1.1 "Via:" headers.
+# ("Full" adds the server version; "Block" removes all outgoing Via: headers)
+# Set to one of: Off | On | Full | Block
+#
+#ProxyVia On
+
+#
+# To enable a cache of proxied content, uncomment the following lines.
+# See http://httpd.apache.org/docs/2.2/mod/mod_cache.html for more details.
+#
+#<IfModule mod_disk_cache.c>
+#   CacheEnable disk /
+#   CacheRoot "/var/cache/mod_proxy"
+#</IfModule>
+#
+
+#</IfModule>
+# End of proxy directives.
+
+### Section 3: Virtual Hosts
+#
+# VirtualHost: If you want to maintain multiple domains/hostnames on your
+# machine you can setup VirtualHost containers for them. Most configurations
+# use only name-based virtual hosts so the server doesn't need to worry about
+# IP addresses. This is indicated by the asterisks in the directives below.
+#
+# Please see the documentation at
+# <URL:http://httpd.apache.org/docs/2.2/vhosts/>
+# for further details before you try to setup virtual hosts.
+#
+# You may use the command line option '-S' to verify your virtual host
+# configuration.
+
+#
+# Use name-based virtual hosting.
+#
+#NameVirtualHost *:80
+#
+# NOTE: NameVirtualHost cannot be used without a port specifier
+# (e.g. :80) if mod_ssl is being used, due to the nature of the
+# SSL protocol.
+#
+
+#
+# VirtualHost example:
+# Almost any Apache directive may go into a VirtualHost container.
+# The first VirtualHost section is used for requests without a known
+# server name.
+#
+#<VirtualHost *:80>
+#    ServerAdmin webmaster at dummy-host.example.com
+#    DocumentRoot /www/docs/dummy-host.example.com
+#    ServerName dummy-host.example.com
+#    ErrorLog logs/dummy-host.example.com-error_log
+#    CustomLog logs/dummy-host.example.com-access_log common
+#</VirtualHost>
+
+Include conf/test.conf
diff --git a/test/setup.sh b/test/setup.sh
new file mode 100755
index 0000000..693d603
--- /dev/null
+++ b/test/setup.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+currentpath=`pwd`
+server_uid=$USER
+server_gid=$USER
+server_port=8000
+server_name=`hostname`
+
+test_root=$currentpath/work/httpd
+test_root_esc=`echo ${test_root} | sed -e 's/\\//\\\\\\//g'`
+
+if [ -e $test_root ]; then
+    if [ $# -gt 0 -a "$1X" = "forceX" ]; then
+        rm -rf work
+    else
+        echo "Test directory already exists"
+        exit 1
+    fi
+fi
+
+./createinstance.sh ${test_root}
+
+cp ../.libs/libmodnss.so ${test_root}/lib
+
+../gencert ${test_root}/alias
+echo internal:httptest > ${test_root}/conf/password.conf
+
+# Export the CA cert
+certutil -L -d ${test_root}/alias -n cacert -a > ${test_root}/alias/ca.pem
+
+# Export the client cert
+cd ${test_root}
+echo password > pw
+echo httptest > dbpw
+pk12util -o alpha.p12 -d alias -n alpha -w pw -k dbpw
+openssl pkcs12  -in alpha.p12 -clcerts -nokeys -out alpha.crt -passin pass:`cat pw`
+openssl pkcs12  -in alpha.p12 -nocerts -nodes -out alpha.key -passin pass:`cat pw`
+pk12util -o beta.p12 -d alias -n beta -w pw -k dbpw
+openssl pkcs12  -in beta.p12 -clcerts -nokeys -out beta.crt -passin pass:`cat pw`
+openssl pkcs12  -in beta.p12 -nocerts -nodes -out beta.key -passin pass:`cat pw`
+/bin/rm -f pw dbpw
+cd -
+
+if [ -f ${test_root}/sedfile ]
+then
+        rm ${test_root}/sedfile
+fi
+
+echo "s/::TEST_ROOT::/${test_root_esc}/g" >> ${test_root}/sedfile
+echo "s/::SERVER_ROOT::/${test_root_esc}/g" >> ${test_root}/sedfile
+echo "s/::SERVER_PORT::/${server_port}/g" >> ${test_root}/sedfile
+echo "s/::SERVER_NAME::/${server_name}/g" >> ${test_root}/sedfile
+echo "s/::SERVER_UID::/${server_uid}/g" >> ${test_root}/sedfile
+echo "s/::SERVER_GID::/${server_gid}/g" >> ${test_root}/sedfile
+
+cat httpd.conf.tmpl | sed  -f ${test_root}/sedfile > ${test_root}/conf/httpd.conf
diff --git a/test/suite1.tmpl b/test/suite1.tmpl
new file mode 100644
index 0000000..999c4d7
--- /dev/null
+++ b/test/suite1.tmpl
@@ -0,0 +1,65 @@
+<VirtualHost *:$SERVER_PORT>
+
+NSSEngine on
+NSSFIPS off
+NSSOCSP off
+NSSRenegotiation on
+
+NSSCipherSuite +rc4,+rc4export,+rc2,+rc2export,+des,+desede3,-fortezza,-fortezza_rc4_128_sha,-fortezza_null,+rsa_rc4_128_md5,+rsa_3des_sha,+rsa_des_sha,+rsa_rc4_40_md5,+rsa_rc2_40_md5,+rsa_null_md5,+rsa_des_56_sha,+rsa_rc4_56_sha,+rsa_aes_128_sha,+rsa_aes_256_sha,+fips_des_sha,+fips_3des_sha
+
+NSSProtocol SSLv3,TLSv1.0
+
+NSSNickname Server-Cert
+
+NSSCertificateDatabase $SERVER_ROOT/alias
+
+NSSVerifyClient none
+
+NSSUserName SSL_CLIENT_S_DN_UID
+
+<Location "/rc4_cipher">
+    NSSCipherSuite -rc4,-rc4export,-rc2,-rc2export,-des,-desede3,-fortezza,-fortezza_rc4_128_sha,-fortezza_null,+rsa_rc4_128_md5,-rsa_3des_sha,-rsa_des_sha,-rsa_rc4_40_md5,-rsa_rc2_40_md5,-rsa_null_md5,-rsa_des_56_sha,-rsa_rc4_56_sha,-rsa_aes_128_sha,-rsa_aes_256_sha,-fips_des_sha,-fips_3des_sha,-ecdhe_rsa_rc4_128_sha,-ecdhe_rsa_aes_128_sha,-rsa_rc4_128_sha
+</Location>
+
+<Location "/acl/aclS01.html">
+    NSSOptions +StdEnvVars +CompatEnvVars +ExportCertData
+    NSSVerifyClient require
+</Location>
+
+<Location "/acl/aclS02.html">
+    NSSOptions +StdEnvVars +CompatEnvVars +ExportCertData
+    NSSVerifyClient require
+    NSSRequire ( %{SSL_CLIENT_S_DN_UID} eq "alpha" \
+               or %{SSL_CLIENT_S_DN_UID} eq "gamma" ) \
+               and %{SSL_CLIENT_S_DN_O} eq "example.com" \
+               and %{SSL_CLIENT_S_DN_OU} eq "People"
+</Location>
+
+<Location "/acl/aclS03.html">
+    NSSOptions +StdEnvVars +CompatEnvVars +ExportCertData +FakeBasicAuth
+    NSSVerifyClient require
+    AuthType Basic
+    AuthName Cert
+    AuthUserFile conf/htpasswd
+    Require valid-user
+</Location>
+
+<Location "/secret-test.html">
+    NSSRequire %{SSL_CIPHER_USEKEYSIZE} > 40
+</Location>
+
+<Location "/secret-test-impossible.html">
+    NSSRequire %{SSL_CIPHER_USEKEYSIZE} > 4000
+</Location>
+</VirtualHost>
+
+# SSL configuration
+NSSPassPhraseDialog  file:$SERVER_ROOT/conf/password.conf
+
+NSSPassPhraseHelper /usr/sbin/nss_pcache
+
+NSSSessionCacheSize 10000
+NSSSessionCacheTimeout 100
+NSSSession3CacheTimeout 86400
+
+Listen 0.0.0.0:$SERVER_PORT
diff --git a/test/test.py b/test/test.py
new file mode 100644
index 0000000..e7136e6
--- /dev/null
+++ b/test/test.py
@@ -0,0 +1,138 @@
+from test_config import Declarative, write_template_file, restart_apache
+from test_config import stop_apache
+import requests.exceptions
+
+class test_suite1(Declarative):
+    @classmethod
+    def setUpClass(cls):
+        write_template_file('suite1.tmpl', 'work/httpd/conf/test.conf', {})
+        restart_apache()
+
+    @classmethod
+    def tearDownClass(cls):
+        stop_apache()
+
+    tests = [
+
+        dict(
+            desc='Basic SSL connection',
+            request=('/', {}),
+            expected=200,
+        ),
+
+        dict(
+            desc='Basic SSL connection, 404',
+            request=('/notfound', {}),
+            expected=404,
+        ),
+
+        dict(
+            desc='SSL connection, fail to verify',
+            request=('/', {'verify': True}),
+            expected=requests.exceptions.SSLError(),
+        ),
+
+        dict(
+            desc='SSL AES128-SHA cipher check',
+            request=('/index.html', {}),
+            expected=200,
+            cipher='AES128-SHA',
+        ),
+
+        dict(
+            desc='Default protocol check',
+            request=('/', {}),
+            expected=200,
+            protocol='TLSv1/SSLv3',
+        ),
+
+        dict(
+            desc='server-side RC4 cipher check',
+            request=('/rc4_cipher/', {'ciphers': 'ALL'}),
+            expected=200,
+            cipher='RC4-MD5',
+        ),
+
+        dict(
+            desc='client-side RC4 cipher check',
+            request=('/', {'ciphers': 'RC4-MD5'}),
+            expected=200,
+            cipher='RC4-MD5',
+        ),
+
+        dict(
+            desc='Basic client auth, no certificate',
+            request=('/acl/aclS01.html', {}),
+            expected=requests.exceptions.SSLError(),
+        ),
+
+        dict(
+            desc='Basic client auth, valid certificate',
+            request=('/acl/aclS01.html', {
+                      'key_file': 'work/httpd/alpha.key',
+                      'cert_file': 'work/httpd/alpha.crt',}
+            ),
+            expected=200,
+        ),
+
+        dict(
+            desc='NSSRequire auth, no certificate',
+            request=('/acl/aclS02.html', {}),
+            expected=requests.exceptions.SSLError(),
+        ),
+
+        dict(
+            desc='NSSRequire auth, valid certificate',
+            request=('/acl/aclS02.html', {
+                      'key_file': 'work/httpd/alpha.key',
+                      'cert_file': 'work/httpd/alpha.crt',}
+            ),
+            expected=200,
+        ),
+
+        dict(
+            desc='NSSRequire auth, not allowed certificate',
+            request=('/acl/aclS02.html', {
+                      'key_file': 'work/httpd/beta.key',
+                      'cert_file': 'work/httpd/beta.crt',}
+            ),
+            expected=403,
+        ),
+
+        dict(
+            desc='FakeBasicAuth, no certificate',
+            request=('/acl/aclS03.html', {}),
+            expected=requests.exceptions.SSLError(),
+        ),
+
+        dict(
+            desc='FakeBasicAuth, valid certificate',
+            request=('/acl/aclS03.html', {
+                      'key_file': 'work/httpd/alpha.key',
+                      'cert_file': 'work/httpd/alpha.crt',}
+            ),
+            expected=200,
+        ),
+
+        dict(
+            desc='FakeBasicAuth, not allowed user',
+            request=('/acl/aclS03.html', {
+                      'key_file': 'work/httpd/beta.key',
+                      'cert_file': 'work/httpd/beta.crt',}
+            ),
+            expected=401,
+        ),
+
+        dict(
+            desc='Secret key size',
+            request=('/secret-test.html', {}),
+            expected=200,
+        ),
+
+        dict(
+            desc='Impossible secret key size',
+            request=('/secret-test-impossible.html', {}),
+            expected=403,
+        ),
+
+    ]
diff --git a/test/test_config.py b/test/test_config.py
new file mode 100644
index 0000000..9990a92
--- /dev/null
+++ b/test/test_config.py
@@ -0,0 +1,186 @@
+# Copyright (C) 2013  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import re
+import ssl
+import time
+import string
+import requests
+import socket
+import subprocess
+import test_util
+import test_request
+
+# Utility functions to assist in creating Apache configuration based
+# on test suite
+
+PORT=8000
+FQDN = socket.gethostname()
+
+default_vars = dict(
+    SERVER_PORT = PORT,
+    SERVER_NAME = FQDN,
+    TEST_ROOT = '%s/work/httpd' % os.getcwd(),
+    SERVER_ROOT = '%s/work/httpd' % os.getcwd(),
+)
+
+def template_str(txt, vars):
+    val = string.Template(txt).substitute(vars)
+
+    # eval() is a special string one can insert into a template to have the
+    # Python interpreter evaluate the string. This is intended to allow
+    # math to be performed in templates.
+    pattern = re.compile('(eval\s*\(([^()]*)\))')
+    val = pattern.sub(lambda x: str(eval(x.group(2))), val)
+
+    return val
+
+def template_file(infilename, vars):
+    """Read a file and perform template substitutions"""
+    with open(infilename) as f:
+        return template_str(f.read(), vars)
+
+def write_template_file(infilename, outfilename, vars):
+    """Read a file and perform template substitutions"""
+    replacevars = dict(default_vars.items() + vars.items())
+    with open(outfilename, 'w') as f:
+        f.write('%s\n' % template_file(infilename, replacevars))
+
+def stop_apache():
+    """Stop the Apache process"""
+    cwd = os.getcwd()
+    # no try/except, just let it fail
+    os.chdir('work/httpd')
+
+    p = subprocess.Popen(['./stop'],
+                         close_fds=True)
+
+def restart_apache():
+    """Restart the Apache process"""
+    cwd = os.getcwd()
+    # no try/except, just let it fail
+    os.chdir('work/httpd')
+
+    p = subprocess.Popen(['./stop'],
+                         close_fds=True)
+    time.sleep(5)
+    p = subprocess.Popen(['./start'],
+                         close_fds=True)
+    os.chdir(cwd)
+    test_util.wait_for_open_ports(FQDN, PORT)
+
+EXPECTED = """Expected %r to raise %s.
+  options = %r
+  output = %r"""
+
+UNEXPECTED = """Expected %r to raise %s, but caught different.
+  options = %r
+  %s: %s"""
+
+class Declarative(object):
+    """A declarative-style test suite
+
+    A Declarative test suite is controlled by the ``tests``
+    class variable.
+
+    The ``tests`` is a list of dictionaries with the following keys:
+
+    ``desc``
+        A name/description of the test
+    ``request``
+        A (uri, options) triple specifying the uri to run
+    ``expected``
+        Can be either an ``errors.PublicError`` instance, in which case
+        the command must fail with the given error; or the
+        expected result.
+        The result is checked with ``tests.util.assert_deepequal``.
+    """
+
+    tests = tuple()
+
+    def test_generator(self):
+        """
+        Iterate through tests.
+
+        nose reports each one as a separate test.
+        """
+
+        # Iterate through the tests:
+        name = self.__class__.__name__
+        for (i, test) in enumerate(self.tests):
+            nice = '%s[%d]: %s: %s' % (
+                name, i, test['request'][0], test.get('desc', '')
+            )
+            func = lambda: self.check(nice, **test)
+            func.description = nice
+            yield (func,)
+
+    def make_request(self, uri, options):
+        session = requests.Session()
+        session.mount('https://', test_request.MyAdapter())
+        verify = dict(verify = options)
+        request = session.get('https://%s:%d%s' % (FQDN, PORT, uri), **verify)
+
+        return request
+
+    def check(self, nice, desc, request, expected, cipher=None, protocol=None):
+        # TODO: need way to set auth, etc.
+        (uri, options) = request
+        if not 'verify' in options:
+            options['verify'] = 'work/httpd/alias/ca.pem'
+        if isinstance(expected, Exception):
+            self.check_exception(nice, uri, options, expected)
+        else:
+            self.check_result(nice, uri, options, expected, cipher, protocol)
+
+    def check_exception(self, nice, uri, options, expected):
+        klass = expected.__class__
+        name = klass.__name__
+        try:
+            output = self.make_request(uri, options)
+        except StandardError, e:
+            pass
+        else:
+            raise AssertionError(
+                EXPECTED % (uri, name, options, output)
+            )
+        if not isinstance(e, klass):
+            raise AssertionError(
+                UNEXPECTED % (uri, name, options, e.__class__.__name__, e)
+            )
+
+
+    def check_result(self, nice, uri, options, expected, cipher=None, protocol=None):
+        name = expected.__class__.__name__
+        request = self.make_request(uri, options)
+        if cipher:
+            client_cipher = request.raw._pool._get_conn().client_cipher
+            if cipher != client_cipher[0]:
+                raise AssertionError(
+                    'Expected cipher %s, got %s' % (cipher, client_cipher[0])
+                )
+        if protocol:
+            client_cipher = request.raw._pool._get_conn().client_cipher
+            if protocol != client_cipher[1]:
+                raise AssertionError(
+                    'Expected cipher %s, got %s' % (cipher, client_cipher[1])
+                )
+        if expected != request.status_code:
+                raise AssertionError(
+                    'Expected status %s, got %s' % (expected, request.status_code)
+                )
diff --git a/test/test_request.py b/test/test_request.py
new file mode 100644
index 0000000..40d8024
--- /dev/null
+++ b/test/test_request.py
@@ -0,0 +1,190 @@
+#
+# Override a slew of methods to have more control over SSL
+
+import socket
+import requests
+import urlparse
+from urllib3.util import get_host
+from urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool
+import logging
+
+# Don't bend over backwards for ssl support, assume it is there.
+import ssl
+try: # Python 3
+    from http.client import HTTPConnection, HTTPException
+    from http.client import HTTP_PORT, HTTPS_PORT
+    from http.client import HTTPSConnection
+except ImportError:
+    from httplib import HTTPConnection, HTTPException
+    from httplib import HTTP_PORT, HTTPS_PORT
+    from httplib import HTTPSConnection
+
+try:
+    # python3.2+
+    from ssl import match_hostname, CertificateError
+except ImportError:
+    try:
+        # Older python where the backport from pypi is installed
+        from backports.ssl_match_hostname import match_hostname, CertificateError
+    except ImportError:
+        # Other older python we use the urllib3 bundled copy
+		from urllib3.packages.ssl_match_hostname import match_hostname, CertificateError
+
+log = logging.getLogger(__name__)
+
+
+def connection_from_url(url, **kw):
+    """
+    Given a url, return an :class:`.ConnectionPool` instance of its host.
+
+    This is a shortcut for not having to parse out the scheme, host, and port
+    of the url before creating an :class:`.ConnectionPool` instance.
+
+    :param url:
+        Absolute URL string that must include the scheme. Port is optional.
+
+    :param \**kw:
+        Passes additional parameters to the constructor of the appropriate
+        :class:`.ConnectionPool`. Useful for specifying things like
+        timeout, maxsize, headers, etc.
+
+    Example: ::
+
+        >>> conn = connection_from_url('http://google.com/')
+        >>> r = conn.request('GET', '/')
+    """
+    scheme, host, port = get_host(url)
+    if scheme == 'https':
+        return MyHTTPSConnectionPool(host, port=port, **kw)
+    else:
+        return HTTPConnectionPool(host, port=port, **kw)
+
+class MyHTTPSConnectionPool(HTTPSConnectionPool):
+    def __init__(self, host, port=None,
+                 strict=False, timeout=None, maxsize=1,
+                 block=False, headers=None,
+                 key_file=None, cert_file=None,
+                 cert_reqs='CERT_REQUIRED', ca_certs='/etc/ssl/certs/ca-certificates.crt', ssl_version=ssl.PROTOCOL_SSLv23, ciphers=None):
+
+        super(HTTPSConnectionPool, self).__init__(host, port,
+                                                  strict, timeout, maxsize,
+                                                  block, headers)
+        self.key_file = key_file
+        self.cert_file = cert_file
+        self.cert_reqs = cert_reqs
+        self.ca_certs = ca_certs
+        self.ssl_version = ssl_version
+        self.ciphers = ciphers
+
+    def _new_conn(self):
+        """
+        Return a fresh :class:`httplib.HTTPSConnection`.
+        """
+        self.num_connections += 1
+        log.info("Starting new HTTPS connection (%d): %s"
+                 % (self.num_connections, self.host))
+
+        #if not ssl: # Platform-specific: Python compiled without +ssl
+        #    if not HTTPSConnection or HTTPSConnection is object:
+        #        raise SSLError("Can't connect to HTTPS URL because the SSL "
+        #                       "module is not available.")
+
+        #    return HTTPSConnection(host=self.host, port=self.port)
+
+        connection = MyVerifiedHTTPSConnection(host=self.host, port=self.port)
+        connection.set_cert(key_file=self.key_file, cert_file=self.cert_file,
+                            cert_reqs=self.cert_reqs, ca_certs=self.ca_certs)
+        connection.set_ssl_version(self.ssl_version)
+        connection.set_ciphers(self.ciphers)
+        return connection
+
+class MyVerifiedHTTPSConnection(HTTPSConnection):
+    """
+    Based on httplib.HTTPSConnection but wraps the socket with
+    SSL certification.
+    """
+    cert_reqs = None
+    ca_certs = None
+    client_cipher = None
+
+    def set_cert(self, key_file=None, cert_file=None,
+                 cert_reqs='CERT_NONE', ca_certs=None):
+        ssl_req_scheme = {
+            'CERT_NONE': ssl.CERT_NONE,
+            'CERT_OPTIONAL': ssl.CERT_OPTIONAL,
+            'CERT_REQUIRED': ssl.CERT_REQUIRED
+        }
+
+        self.key_file = key_file
+        self.cert_file = cert_file
+        self.cert_reqs = ssl_req_scheme.get(cert_reqs) or ssl.CERT_NONE
+        self.ca_certs = ca_certs
+
+    def set_ssl_version(self, ssl_version=ssl.PROTOCOL_SSLv23):
+        self.ssl_version = ssl_version
+
+    def set_ciphers(self, ciphers=None):
+        self.ciphers = ciphers
+
+    def connect(self):
+        # Add certificate verification
+        sock = socket.create_connection((self.host, self.port), self.timeout)
+
+        # Wrap socket using verification with the root certs in
+        # trusted_root_certs
+        self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
+                                    cert_reqs=self.cert_reqs,
+                                    ca_certs=self.ca_certs,
+                                    ssl_version=self.ssl_version,
+                                    ciphers=self.ciphers)
+        if self.ca_certs:
+            match_hostname(self.sock.getpeercert(), self.host)
+
+    def close(self):
+        self.client_cipher = self.sock.cipher()
+        HTTPSConnection.close(self)
+
+class MyAdapter(requests.adapters.HTTPAdapter):
+
+    def get_connection(self, url, proxies=None):
+        """Returns a connection for the given URL."""
+
+        # proxies are not supported
+        return connection_from_url(url)
+
+    def cert_verify(self, conn, url, verify, cert):
+        # I'm overloading the content of verify since this API is so
+        # braindead. If verify is a dict then key 'verify' represents the
+        # original meaning, the other keys are my own.
+        if isinstance(verify, bool):
+            super(MyAdapter, self).cert_verify(conn, url, verify, cert)
+        elif isinstance(verify, dict):
+            if 'verify' in verify:
+                super(MyAdapter, self).cert_verify(conn, url,
+                                                   verify['verify'], cert)
+            if 'ssl_version' in verify:
+                conn.ssl_version = verify['ssl_version']
+            if 'ciphers' in verify:
+                conn.ciphers = verify['ciphers']
+            if 'cert_file' in verify:
+                conn.cert_file = verify['cert_file']
+            if 'key_file' in verify:
+                conn.key_file = verify['key_file']
+        else: # huh? Do nothing
+            pass
+
+"""
+s = requests.Session()
+s.mount('https://', MyAdapter())
+try:
+    r = s.get('https://darlene.greyoak.com:8000/', verify={'verify': False, 'ssl_version': ssl.PROTOCOL_SSLv23, 'ciphers': 'HIGH'})
+    cipher = r.raw._pool._get_conn().client_cipher
+except requests.exceptions.SSLError, e:
+    print e.message
+else:
+    print r.status_code
+    print cipher
+
+#request = requests.get('https://darlene.greyoak.com:8000/', verify=False)
+#print request.status_code
+"""
diff --git a/test/test_util.py b/test/test_util.py
new file mode 100644
index 0000000..f01d43a
--- /dev/null
+++ b/test/test_util.py
@@ -0,0 +1,52 @@
+import socket
+import time
+
+def host_port_open(host, port, socket_type=socket.SOCK_STREAM, socket_timeout=None):
+    for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket_type):
+        af, socktype, proto, canonname, sa = res
+        try:
+            try:
+                s = socket.socket(af, socktype, proto)
+            except socket.error:
+                s = None
+                continue
+
+            if socket_timeout is not None:
+                s.settimeout(socket_timeout)
+
+            s.connect(sa)
+
+            if socket_type == socket.SOCK_DGRAM:
+                s.send('')
+                s.recv(512)
+
+            return True
+        except socket.error, e:
+            pass
+        finally:
+            if s:
+                s.close()
+
+    return False
+
+def wait_for_open_ports(host, ports, timeout=0):
+    """
+    Wait until the specified port(s) on the remote host are open. Timeout
+    in seconds may be specified to limit the wait. If the timeout is
+    exceeded, socket.timeout exception is raised.
+    """
+    if not isinstance(ports, (tuple, list)):
+        ports = [ports]
+
+    op_timeout = time.time() + timeout
+
+    for port in ports:
+        while True:
+            port_open = host_port_open(host, port)
+
+            if port_open:
+                break
+            if timeout and time.time() > op_timeout: # timeout exceeded
+                raise socket.timeout()
+            time.sleep(1)
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-fedora-ds/libapache2-mod-nss.git



More information about the Pkg-fedora-ds-maintainers mailing list