From 8109c6c972e57371eab57449d2042ae040301fc9 Mon Sep 17 00:00:00 2001
From: "edewata@redhat.com" <edewata@vm-030.idm.lab.bos.redhat.com>
Date: Thu, 26 Jul 2012 09:38:01 -0500
Subject: [PATCH] Added SSL authenticator with fallback.

A custom Tomcat authenticator has been added to authenticate users
using client certificate if provided, otherwise it will fallback to
BASIC/FORM authentication.

The SSL connection has been configured with clientAuth="want" so
users can choose whether to provide a certificate or username and
password.

Ticket #107
---
 .classpath                                         |   1 +
 base/ca/shared/conf/acl.ldif                       |   8 +
 base/ca/shared/conf/server.xml                     |   2 +-
 base/ca/shared/webapps/ca/WEB-INF/auth.properties  |  15 +
 base/ca/shared/webapps/ca/WEB-INF/web.xml          |  29 ++
 base/common/shared/conf/context.xml                |  18 +
 base/common/shared/conf/server.xml                 |   3 +-
 base/common/src/CMakeLists.txt                     |   9 +-
 .../com/netscape/cmscore/realm/PKIJNDIRealm.java   | 380 +++++++++++----------
 .../realm/SSLAuthenticatorWithFallback.java        | 177 ++++++++++
 base/deploy/config/pkislots.cfg                    |   4 +
 base/deploy/src/scriptlets/pkiparser.py            |  28 +-
 base/deploy/src/scriptlets/slot_substitution.py    |  12 +
 base/setup/pki                                     |   1 +
 base/setup/pkicreate                               |   2 +-
 15 files changed, 508 insertions(+), 181 deletions(-)
 create mode 100644 base/ca/shared/webapps/ca/WEB-INF/auth.properties
 create mode 100644 base/common/src/com/netscape/cmscore/realm/SSLAuthenticatorWithFallback.java

diff --git a/.classpath b/.classpath
index 28dddffc296146b7c6bda79bf4dbf41561c359db..ca08aba4eacfade82f344a445c8caac7987c5bc0 100644
--- a/.classpath
+++ b/.classpath
@@ -44,6 +44,7 @@
 	<classpathentry kind="lib" path="/usr/share/java/apache-commons-lang.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/resteasy/resteasy-atom-provider.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/tomcat/catalina.jar"/>
+	<classpathentry kind="lib" path="/usr/share/java/tomcat/tomcat-coyote.jar"/>
 	<classpathentry kind="lib" path="/usr/share/java/tomcat/tomcat-util.jar"/>
 	<classpathentry kind="output" path="build/classes"/>
 </classpath>
diff --git a/base/ca/shared/conf/acl.ldif b/base/ca/shared/conf/acl.ldif
index ceea1f27a4d9c5b6c297c33752c3bf46ee756dca..d16628c1449b57037d921a19fa38341a1445415e 100644
--- a/base/ca/shared/conf/acl.ldif
+++ b/base/ca/shared/conf/acl.ldif
@@ -51,3 +51,11 @@ resourceACLS: certServer.ca.connectorInfo:read,modify:allow (modify,read) group=
 resourceACLS: certServer.ca.registerUser:read,modify:allow (modify,read) group="Enterprise CA Administrators" || group="Enterprise KRA Administrators" || group="Enterprise RA Administrators" || group="Enterprise OCSP Administrators" || group="Enterprise TKS Administrators" || group="Enterprise TPS Administrators":Only Enterprise Administrators are allowed to register a new agent
 resourceACLS: certServer.clone.configuration:read,modify:allow (modify,read) group="Enterprise CA Administrators" || group="Enterprise KRA Administrators" || group="Enterprise RA Administrators" || group="Enterprise OCSP Administrators" || group="Enterprise TKS Administrators":Only Enterprise Administrators are allowed to clone the configuration.
 resourceACLS: certServer.admin.ocsp:read,modify:allow (modify,read) group="Enterprise OCSP Administrators":Only Enterprise Administrators are allowed to read or update the OCSP configuration.
+resourceACLS: certServer.ca.certs:execute:allow (execute) group="Administrators" || group="Certificate Manager Agents":Admins and agents may execute cert operations
+resourceACLS: certServer.ca.certrequests:execute:allow (execute) group="Administrators" || group="Certificate Manager Agents":Admins and agents may execute cert request operations
+resourceACLS: certServer.ca.config:execute:allow (execute) group="Administrators" || group="Certificate Manager Agents":Admins and agents may execute config operations
+resourceACLS: certServer.ca.groups:execute:allow (execute) group="Administrators" || group="Certificate Manager Agents":Admins and agents may execute group operations
+resourceACLS: certServer.ca.keys:execute:allow (execute) group="Administrators" || group="Certificate Manager Agents":Admins and agents may execute key operations
+resourceACLS: certServer.ca.keyrequests:execute:allow (execute) group="Administrators" || group="Certificate Manager Agents":Admins and agents may execute key request operations
+resourceACLS: certServer.ca.profiles:execute:allow (execute) group="Administrators" || group="Certificate Manager Agents":Admins and agents may execute profile operations
+resourceACLS: certServer.ca.users:execute:allow (execute) group="Administrators" || group="Certificate Manager Agents":Admins and agents may execute user operations
diff --git a/base/ca/shared/conf/server.xml b/base/ca/shared/conf/server.xml
index 4056fbbb7e5c64c082c1b344c5f29f86f518efa3..60317d2fad41558b2b5a05ec986a5f7493efde5f 100644
--- a/base/ca/shared/conf/server.xml
+++ b/base/ca/shared/conf/server.xml
@@ -84,7 +84,7 @@ Tomcat Port         = [TOMCAT_SERVER_PORT] (for shutdown)
     -->
 
     [PKI_UNSECURE_PORT_SERVER_COMMENT]
-    <Connector name="[PKI_UNSECURE_PORT_CONNECTOR_NAME]" port="[PKI_UNSECURE_PORT]" protocol="HTTP/1.1" redirectPort="8443" 
+    <Connector name="[PKI_UNSECURE_PORT_CONNECTOR_NAME]" port="[PKI_UNSECURE_PORT]" protocol="HTTP/1.1" redirectPort="[PKI_SECURE_PORT]"
 	       maxHttpHeaderSize="8192"
 	       acceptCount="100" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
 	       enableLookups="false" connectionTimeout="20000" disableUploadTimeout="true"
diff --git a/base/ca/shared/webapps/ca/WEB-INF/auth.properties b/base/ca/shared/webapps/ca/WEB-INF/auth.properties
new file mode 100644
index 0000000000000000000000000000000000000000..158d7e69d058a3c5727c2f6ca7376d5e9e1e5da6
--- /dev/null
+++ b/base/ca/shared/webapps/ca/WEB-INF/auth.properties
@@ -0,0 +1,15 @@
+# Restful API auth/authz mapping info
+#
+# Format:
+# <Rest API URL> = <ACL Resource ID>,<ACL resource operation>
+# ex:      /ca/pki/users = certServer.ca.users,read
+
+/ca/pki/certs = certServer.ca.certs,execute
+/ca/pki/certrequests = certServer.ca.certrequests,execute
+/ca/pki/config = certServer.ca.config,execute
+/ca/pki/groups = certServer.ca.groups,execute
+/ca/pki/installer = certServer.ca.installer,execute
+/ca/pki/keys = certServer.ca.keys,execute
+/ca/pki/keyrequests = certServer.ca.keyrequests,execute
+/ca/pki/profiles = certServer.ca.profiles,execute
+/ca/pki/users = certServer.ca.users,execute
diff --git a/base/ca/shared/webapps/ca/WEB-INF/web.xml b/base/ca/shared/webapps/ca/WEB-INF/web.xml
index 8471d6cd4f3ac30a4296b120ebf2c897e0d5f7fa..a5983a4619913d8a617dbd3ecd4ee5b9705a6717 100644
--- a/base/ca/shared/webapps/ca/WEB-INF/web.xml
+++ b/base/ca/shared/webapps/ca/WEB-INF/web.xml
@@ -2380,5 +2380,34 @@
    <session-config>
         <session-timeout>30</session-timeout>
    </session-config>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>REST Interface</web-resource-name>
+            <url-pattern>/pki/certs/*</url-pattern>
+            <url-pattern>/pki/certrequests/*</url-pattern>
+            <url-pattern>/pki/config/*</url-pattern>
+            <url-pattern>/pki/groups/*</url-pattern>
+            <url-pattern>/pki/keys/*</url-pattern>
+            <url-pattern>/pki/keyrequests/*</url-pattern>
+            <url-pattern>/pki/profiles/*</url-pattern>
+            <url-pattern>/pki/users/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>*</role-name>
+        </auth-constraint>
+        <user-data-constraint>
+            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+        </user-data-constraint>
+    </security-constraint>
+
+    <login-config>
+        <realm-name>Certificate Authority</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>*</role-name>
+    </security-role>
+
 </web-app>
 
diff --git a/base/common/shared/conf/context.xml b/base/common/shared/conf/context.xml
index 4b00dbe3caf7027d699ce3ed8ffee55fc3a2168b..589d1fb44c177e8569d3c82c628003add6a1b379 100644
--- a/base/common/shared/conf/context.xml
+++ b/base/common/shared/conf/context.xml
@@ -39,4 +39,22 @@
     <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
     -->
 
+    <Valve className="com.netscape.cmscore.realm.SSLAuthenticatorWithFallback"
+        fallbackMethod="BASIC" />
+
+    <Realm className="com.netscape.cmscore.realm.PKIJNDIRealm"
+        connectionURL="[PKI_DS_URI]"
+        userBase="ou=people,[PKI_DS_BASE_DN]"
+        userSearch="(uid={0})"
+        certAttrName="userCertificate"
+        certUIDLabel="UID"
+        roleBase="ou=groups,[PKI_DS_BASE_DN]"
+        roleName="cn"
+        roleSubtree="true"
+        roleSearch="(uniqueMember={0})"
+        connectionName="[PKI_DS_BIND_DN]"
+        connectionPassword="[PKI_DS_PASSWORD]"
+        aclBase ="cn=aclResources,[PKI_DS_BASE_DN]"
+        aclAttrName="resourceACLS" />
+
 </Context>
diff --git a/base/common/shared/conf/server.xml b/base/common/shared/conf/server.xml
index 375764294887dd83e995a954d1b2f3b8260501cd..7972973e371cec46e4d9e1ce87ba97a783c9561f 100644
--- a/base/common/shared/conf/server.xml
+++ b/base/common/shared/conf/server.xml
@@ -117,7 +117,7 @@ Tomcat Port         = [TOMCAT_SERVER_PORT] (for shutdown)
     -->
 
     [PKI_UNSECURE_PORT_SERVER_COMMENT]
-    <Connector name="[PKI_UNSECURE_PORT_CONNECTOR_NAME]" port="[PKI_UNSECURE_PORT]" protocol="HTTP/1.1" redirectPort="8443"
+    <Connector name="[PKI_UNSECURE_PORT_CONNECTOR_NAME]" port="[PKI_UNSECURE_PORT]" protocol="HTTP/1.1" redirectPort="[PKI_SECURE_PORT]"
            maxHttpHeaderSize="8192"
            acceptCount="100" maxThreads="150" minSpareThreads="25"
            enableLookups="false" connectionTimeout="20000" disableUploadTimeout="true"
@@ -186,7 +186,6 @@ Tomcat Port         = [TOMCAT_SERVER_PORT] (for shutdown)
            ocspTimeout="10"
            strictCiphers="false"
            clientAuth="[PKI_AGENT_CLIENTAUTH]"
-           clientauth="[PKI_AGENT_CLIENTAUTH]"
            sslOptions="[TOMCAT_SSL_OPTIONS]"
            ssl2Ciphers="[TOMCAT_SSL2_CIPHERS]"
            ssl3Ciphers="[TOMCAT_SSL3_CIPHERS]"
diff --git a/base/common/src/CMakeLists.txt b/base/common/src/CMakeLists.txt
index bf8ea6f847bb569cc6e2021a61ddfbe956b9517d..669ebb5fe6d9a1cf93f2cec01178a52a7cad4799 100644
--- a/base/common/src/CMakeLists.txt
+++ b/base/common/src/CMakeLists.txt
@@ -58,6 +58,13 @@ find_file(TOMCAT_UTIL_JAR
         /usr/share/java/tomcat
 )
 
+find_file(TOMCAT_COYOTE_JAR
+    NAMES
+        tomcat-coyote.jar
+    PATHS
+        /usr/share/java/tomcat
+)
+
 find_file(SERVLET_JAR
     NAMES
         servlet.jar
@@ -348,7 +355,7 @@ javac(pki-jndi-realm-classes
         ${LDAPJDK_JAR} ${SERVLET_JAR} ${VELOCITY_JAR} ${XALAN_JAR} ${XERCES_JAR}
         ${JSS_JAR} ${COMMONS_CODEC_JAR} ${COMMONS_HTTPCLIENT_JAR}
         ${APACHE_COMMONS_CLI_JAR} ${APACHE_COMMONS_LANG_JAR}
-        ${TOMCAT_CATALINA_JAR} ${TOMCAT_UTIL_JAR} ${SYMKEY_JAR}
+        ${TOMCAT_CATALINA_JAR} ${TOMCAT_UTIL_JAR} ${TOMCAT_COYOTE_JAR} ${SYMKEY_JAR}
         ${JAXRS_API_JAR} ${RESTEASY_JAXRS_JAR} ${RESTEASY_ATOM_PROVIDER_JAR}
         ${HTTPCLIENT_JAR} ${HTTPCORE_JAR}
     OUTPUT_DIR
diff --git a/base/common/src/com/netscape/cmscore/realm/PKIJNDIRealm.java b/base/common/src/com/netscape/cmscore/realm/PKIJNDIRealm.java
index bd551baf01ce0a5be6668094c475895f9e24f821..8cec6d7a2cf697fb501ccacff0848d9d2e296dc7 100644
--- a/base/common/src/com/netscape/cmscore/realm/PKIJNDIRealm.java
+++ b/base/common/src/com/netscape/cmscore/realm/PKIJNDIRealm.java
@@ -6,6 +6,7 @@ import java.io.InputStream;
 import java.security.Principal;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.List;
@@ -15,7 +16,6 @@ import java.util.Vector;
 
 import javax.naming.NamingEnumeration;
 import javax.naming.NamingException;
-import javax.naming.PartialResultException;
 import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
 import javax.naming.directory.DirContext;
@@ -24,11 +24,12 @@ import javax.naming.directory.SearchResult;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Wrapper;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
 import org.apache.catalina.deploy.SecurityConstraint;
 import org.apache.catalina.realm.JNDIRealm;
-import org.apache.catalina.Wrapper;
 
 /*
  *  Self contained PKI JNDI Real that overrides the standard JNDI Realm
@@ -64,6 +65,40 @@ public class PKIJNDIRealm extends JNDIRealm {
 
     private Properties authzProperties = null;
 
+    public PKIJNDIRealm() {
+        logDebug("Creating PKI realm");
+    }
+
+    @Override
+    protected void startInternal() throws LifecycleException {
+        logDebug("Starting PKI realm");
+        super.startInternal();
+
+        try {
+            loadResourceACLs();
+
+        } catch (NamingException e) {
+            throw new LifecycleException("Failed loading resource ACLs", e);
+        }
+
+    }
+
+    @Override
+    public Principal authenticate(String username, String password) {
+        logDebug("Authenticating username "+username+" with password.");
+        return super.authenticate(username, password);
+    }
+
+    @Override
+    public Principal authenticate(X509Certificate certs[]) {
+        logDebug("Authenticating certificate chain:");
+        for (X509Certificate cert : certs) {
+            logDebug("  "+cert.getSubjectDN());
+        }
+       return super.authenticate(certs);
+
+    }
+
     /* Look up the principal user based on the incoming client auth
      * certificate.
      * @param usercert Incoming client auth certificate presented by user
@@ -72,62 +107,61 @@ public class PKIJNDIRealm extends JNDIRealm {
     @Override
     protected synchronized Principal getPrincipal(X509Certificate usercert) {
 
-        logDebug("Entering PKIJNDIRealm.getPrincipal");
-
         if (usercert == null)
             return null;
 
-        String uid = null;
-        String certUIDLabel = getCertUIDLabel();
+        // Call the getPrincipal method of the base JNDIRealm class
+        // based on the just calculated uid. During the next call
+        // one of our methods to extract and store the user's ldap stored
+        // client cert will be invoked
+
+        Principal user;
 
         if (certUIDLabel == null) {
             // We have no uid label, attempt to construct a description
 
-            uid = CERT_VERSION + ";" + usercert.getSerialNumber() + ";"
+            String certId = CERT_VERSION + ";" + usercert.getSerialNumber() + ";"
                     + usercert.getIssuerDN() + ";" + usercert.getSubjectDN();
 
             //The description field is devoid of spaces and the email label is E
             //instead of EMAIL
-            uid = uid.replaceAll(", ", ",");
-            uid = uid.replaceAll("EMAILADDRESS", "E");
+            certId = certId.replaceAll(", ", ",");
+            certId = certId.replaceAll("EMAILADDRESS", "E");
 
-            logDebug(uid);
+            logDebug("Cert ID: "+certId);
+            user = getPrincipal(certId);
 
         } else {
 
-            String certUIDSrchStr = certUIDLabel + "=";
-
             StringTokenizer dnTokens =
                     new StringTokenizer(usercert.getSubjectDN().getName(), ",");
 
+            String uid = null;
+
             while (dnTokens.hasMoreTokens()) {
-                String token = dnTokens.nextToken();
-                int index = token.indexOf(certUIDSrchStr);
+                String token = dnTokens.nextToken().trim();
 
-                if (index != -1) {
-                    // Found the entry with the cert's UID
+                int index = token.indexOf('=');
+                if (index == -1) continue;
 
-                    try {
-                        uid = token.substring(index + certUIDSrchStr.length());
-                    } catch (IndexOutOfBoundsException e) {
-                        logErr("Out of Bounds Exception when attempting to extract UID from incomgin certificate.");
-                        return null;
-                    }
+                String rdn = token.substring(0, index);
+                if (!certUIDLabel.equals(rdn)) continue;
 
-                    if (uid != null) {
-                        break;
-                    }
-                }
+                // Found the entry with the cert's UID
+
+                uid = token.substring(index+1);
+
+                // Found UID value
+
+                break;
             }
+
+            logDebug("UID: "+uid);
+            if (uid == null) return null;
+
+            user = getPrincipal(uid);
         }
 
-        //Call the getPrincipal method of the base JNDIRealm class
-        //based on the just calculated uid. During the next call
-        // one of our methods to extract and store the user's ldap stored
-        //client cert will be invoked
-
-        Principal user = getPrincipal(uid);
-
         //ToDo: Possibly perform some more cert verficiation
         // such as OCSP, even though the tomcat jss connector
         // can already be configured for OCSP
@@ -207,9 +241,10 @@ public class PKIJNDIRealm extends JNDIRealm {
 
         boolean allowed = super.hasResourcePermission(request, response, constraints, context);
 
+        logDebug("Resource permission: "+allowed);
         Wrapper wrapper = request.getWrapper();
 
-        if (allowed == true && hasResourceACLS()) {
+        if (allowed) {
 
             loadAuthzProperties(context);
 
@@ -217,7 +252,6 @@ public class PKIJNDIRealm extends JNDIRealm {
                 //Let's check against our encoded acls.
 
                 String requestURI = request.getDecodedRequestURI();
-                Principal principal = request.getPrincipal();
 
                 String match = getACLEntryDataForURL(requestURI);
 
@@ -241,8 +275,12 @@ public class PKIJNDIRealm extends JNDIRealm {
                         }
                     }
 
+                    Principal principal = request.getPrincipal();
+                    logDebug("Principal: "+principal);
+                    logDebug("Resource ID: " + resourceID);
+                    logDebug("Operation: " + operation);
+
                     allowed = checkACLPermission(principal, resourceID, operation, wrapper);
-                    logDebug("resourceID: " + resourceID + " operation: " + operation + " allowed: " + allowed);
                 }
             }
         }
@@ -254,6 +292,8 @@ public class PKIJNDIRealm extends JNDIRealm {
                             sm.getString("realmBase.forbidden"));
         }
 
+        logDebug("Permission: "+request.getRequestURI()+" => "+allowed);
+
         return allowed;
     }
 
@@ -274,13 +314,13 @@ public class PKIJNDIRealm extends JNDIRealm {
     protected List<String> getRoles(DirContext context, User user)
             throws NamingException {
 
-        try {
-            getResourceACLS(context);
-        } catch (NamingException e) {
-            logDebug("No aclResources found.");
-        }
+        List<String> roles = super.getRoles(context, user);
 
-        return super.getRoles(context, user);
+        logDebug("Roles:");
+        for (String role : roles) {
+            logDebug("  "+role);
+        }
+        return roles;
     }
 
     /* Custom variables, see <Realm> element */
@@ -334,7 +374,7 @@ public class PKIJNDIRealm extends JNDIRealm {
         return certUIDLabel;
     }
 
-    public void setCertUIDStr(String certUIDLabel) {
+    public void setCertUIDLabel(String certUIDLabel) {
         this.certUIDLabel = certUIDLabel;
     }
 
@@ -344,39 +384,33 @@ public class PKIJNDIRealm extends JNDIRealm {
      */
     private X509Certificate storedUserCert;
 
-    protected void setStoredUserCert(X509Certificate cert) {
+    public void setStoredUserCert(X509Certificate cert) {
         this.storedUserCert = cert;
     }
 
-    protected X509Certificate getStoredUserCert() {
+    public X509Certificate getStoredUserCert() {
         return storedUserCert;
     }
 
     // Check a PKI  ACL resourceID and operation for permissions
     // If the check fails the user (principal) is not authorized to access the resource
-    private boolean checkACLPermission(Principal principal, String resourceId, String operation, Wrapper wrapper) {
+    public boolean checkACLPermission(Principal principal, String resourceId, String operation, Wrapper wrapper) {
 
-        boolean allowed = true;
-
-        if (!hasAuthzProperties() || !hasResourceACLS()) {
-            //We arent' configured for this sort of authz
-            return allowed;
-        }
-
-        if (principal == null || resourceId == null || operation == null) {
-            return allowed;
-        }
+        if (principal == null || resourceId == null || operation == null)
+            return false;
 
         ACL acl = acls.get(resourceId);
 
-        if (acl == null) {
-            //No such acl, assume true
-            return allowed;
-        }
+        if (acl == null)
+            return false;
+
+        boolean allowed = false;
 
         Enumeration<ACLEntry> aclEntries = acl.entries();
         while (aclEntries != null && aclEntries.hasMoreElements()) {
             ACLEntry entry = aclEntries.nextElement();
+            logDebug("Evaluating ACL: "+acl);
+
             boolean isEntryNegative = entry.isNegative();
 
             String expressions = entry.getAttributeExpressions();
@@ -402,24 +436,26 @@ public class PKIJNDIRealm extends JNDIRealm {
     }
 
     // Evaluate an expression as part of a PKI ACL
-    // Ex: user=anybody , group=Data Recovery Manager Agents
-    private boolean evaluateExpression(Principal principal, String expression, Wrapper wrapper) {
+    // Ex: user=anybody, group=Data Recovery Manager Agents
+    public boolean evaluateExpression(Principal principal, String expression, Wrapper wrapper) {
 
-        boolean allowed = true;
-        if (principal == null || expression == null) {
-            return allowed;
+        if (principal == null)
+            return false;
+
+        if (expression == null) {
+            return false;
         }
 
         String operation = getExpressionOperation(expression);
 
         if (operation == null) {
-            return allowed;
+            return false;
         }
 
         String[] expBlock = expression.split(operation);
 
         if (expBlock.length != 2) {
-            return allowed;
+            return false;
         }
 
         String left = expBlock[0];
@@ -428,14 +464,15 @@ public class PKIJNDIRealm extends JNDIRealm {
         if (left != null) {
             left = left.trim();
         } else {
-            return allowed;
+            return false;
         }
+
         //Massage the right hand side of this expression to be a legal string value.
         if (right != null) {
             right = right.replace("\"", "");
             right = right.trim();
         } else {
-            return allowed;
+            return false;
         }
 
         boolean negate = false;
@@ -445,29 +482,38 @@ public class PKIJNDIRealm extends JNDIRealm {
             negate = true;
         }
 
-        allowed = false;
+        boolean allowed = false;
+
         if (left.equals(PROP_GROUP)) {
-            // Check JNDI to see if the user has this role/group
             if (hasRole(wrapper, principal, right)) {
+                logDebug(principal.getName()+" matches "+right);
                 allowed = true;
             }
+
         } else if (left.equals(PROP_USER)) {
             if (right.equals(PROP_USER_ANYBODY) || right.equals(PROP_USER_EVERYBODY)) {
+                logDebug(principal.getName()+" matches "+right);
+                allowed = true;
+
+            } else if (right.equals(principal.getName())) {
+                logDebug(principal.getName()+" matches "+right);
                 allowed = true;
             }
         } else {
-            logDebug("Unknown expression.");
+            logDebug("Invalid expression: "+expression);
         }
 
         if (negate) {
             allowed = !allowed;
         }
 
+        logDebug(expression+" => "+allowed);
+
         return allowed;
     }
 
     // Convenience method to find the operation in an ACL expression
-    private String getExpressionOperation(String exp) {
+    public String getExpressionOperation(String exp) {
         //Support only = and !=
 
         int i = exp.indexOf("!=");
@@ -485,9 +531,10 @@ public class PKIJNDIRealm extends JNDIRealm {
     }
 
     // Take a set of expressions in an ACL and evaluate it
-    private boolean evaluateExpressions(Principal principal, String s, Wrapper wrapper) {
+    // TODO: Use proper parser
+    public boolean evaluateExpressions(Principal principal, String s, Wrapper wrapper) {
 
-        Vector<Object> v = new Vector<Object>();
+        ArrayList<Object> v = new ArrayList<Object>();
 
         while (s.length() > 0) {
             int orIndex = s.indexOf("||");
@@ -496,8 +543,7 @@ public class PKIJNDIRealm extends JNDIRealm {
             // this is the last expression
             if (orIndex == -1 && andIndex == -1) {
                 boolean passed = evaluateExpression(principal, s.trim(), wrapper);
-
-                v.addElement(Boolean.valueOf(passed));
+                v.add(passed);
                 break;
 
                 // || first
@@ -505,16 +551,16 @@ public class PKIJNDIRealm extends JNDIRealm {
                 String s1 = s.substring(0, orIndex);
                 boolean passed = evaluateExpression(principal, s1.trim(), wrapper);
 
-                v.addElement(Boolean.valueOf(passed));
-                v.addElement("||");
+                v.add(passed);
+                v.add("||");
                 s = s.substring(orIndex + 2);
                 // && first
             } else {
                 String s1 = s.substring(0, andIndex);
                 boolean passed = evaluateExpression(principal, s1.trim(), wrapper);
 
-                v.addElement(Boolean.valueOf(passed));
-                v.addElement("&&");
+                v.add(passed);
+                v.add("&&");
                 s = s.substring(andIndex + 2);
             }
         }
@@ -535,18 +581,16 @@ public class PKIJNDIRealm extends JNDIRealm {
 
         while (v.size() > 0) {
             if (op.equals(""))
-                left = ((Boolean) v.remove(0)).booleanValue();
+                left = (boolean) v.remove(0);
             op = (String) v.remove(0);
-            right = ((Boolean) v.remove(0)).booleanValue();
+            right = (boolean) v.remove(0);
+
+            logDebug("Evaluating "+left+" "+op+" "+right);
 
             if (op.equals("||")) {
-                if (left == false && right == false)
-                    left = false;
-                left = true;
+                left = left || right;
             } else if (op.equals("&&")) {
-                if (left == true && right == true)
-                    left = true;
-                left = false;
+                left = left && right;
             }
         }
 
@@ -558,7 +602,7 @@ public class PKIJNDIRealm extends JNDIRealm {
      * future reference. This all takes place within one command invocation from
      * the getPrincipal method defined here.
      */
-    private void extractAndSaveStoredX509UserCert(DirContext context, User certUser)
+    public void extractAndSaveStoredX509UserCert(DirContext context, User certUser)
             throws NamingException {
 
         setStoredUserCert(null);
@@ -638,88 +682,81 @@ public class PKIJNDIRealm extends JNDIRealm {
     // There may be a way to extract this information at this level.
     // The parameter name to scan for could be configured with the Realm.
 
-    private String getACLEntryDataForURL(String requestURI) {
-        String aclEntryData;
+    public String getACLEntryDataForURL(String requestURI) {
 
         if (!hasAuthzProperties()) {
             return null;
         }
 
-        aclEntryData = authzProperties.getProperty(requestURI);
+        logDebug("Checking path: "+requestURI);
+        String aclEntryData = authzProperties.getProperty(requestURI);
+
+        if (aclEntryData != null) {
+            logDebug("Found exact match: "+aclEntryData);
+            return aclEntryData;
+        }
+
+        // Check for a partial match such as
+        // ex: /kra/pki/keyrequest/2
+        // ToDo: Check into more sophisticated
+        // methods of doing this mapping.
+        // Perhaps Rest gives us this more
+        // sophisticated mapping ability.
+
+        Properties props = authzProperties;
+        Enumeration<?> e = props.propertyNames();
+
+        while (e.hasMoreElements()) {
+            String key = (String) e.nextElement();
+            if (requestURI.startsWith(key)) {
+                aclEntryData = props.getProperty(key);
+                logDebug("Found partial match ["+key+"]: "+aclEntryData);
+                break;
+            }
+        }
 
         if (aclEntryData == null) {
-            //Check for a partial match such as
-            // ex: /kra/pki/keyrequest/2
-            // ToDo: Check into more sophisticated
-            // methods of doing this mapping.
-            // Perhaps Rest gives us this more
-            // sophisticated mapping ability.
-
-            Properties props = authzProperties;
-            Enumeration<?> e = props.propertyNames();
-
-            while (e.hasMoreElements()) {
-                String key = (String) e.nextElement();
-                if (requestURI.startsWith(key)) {
-                    aclEntryData = props.getProperty(key);
-                    break;
-                }
-            }
+            logDebug("No match found");
         }
 
         return aclEntryData;
 
     }
 
-    //Go to the directory server, if configured, and go get the Resource ACLs
-    private synchronized void getResourceACLS(DirContext context) throws NamingException {
+    public synchronized void loadResourceACLs() throws NamingException {
 
-        //for now lets support this on startup
-        if (hasResourceACLS()) {
-            return;
-        }
+        logDebug("Loading resource ACLs");
+
+        DirContext context = open();
 
         String filter = "(" + aclAttrName + "=*)";
 
         SearchControls constraints = new SearchControls();
-
         constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
-
         constraints.setReturningAttributes(null);
 
         NamingEnumeration<SearchResult> results =
                 context.search(aclBase, filter, constraints);
 
-        try {
-            if (results == null || !results.hasMore()) {
-                return;
-            }
-        } catch (PartialResultException ex) {
-            throw ex;
-        }
+        if (!results.hasMore())
+            return;
 
-        SearchResult result = null;
         try {
-            result = results.next();
-            if (result != null) {
-
-                Attributes attrs = result.getAttributes();
-                if (attrs == null)
-                    return;
+            SearchResult result = results.next();
+            Attributes attrs = result.getAttributes();
 
-                Vector<String> aclVec = getAttributeValues(aclAttrName, attrs);
+            Vector<String> aclVec = getAttributeValues(aclAttrName, attrs);
 
-                if (aclVec != null) {
+            if (aclVec != null) {
 
-                    Enumeration<String> vEnum = aclVec.elements();
+                Enumeration<String> vEnum = aclVec.elements();
 
-                    while (vEnum.hasMoreElements())
-                    {
-                        String curAcl = vEnum.nextElement();
-                        ACL acl = parseACL(curAcl);
-                        if (acl != null) {
-                            acls.put(acl.getName(), acl);
-                        }
+                while (vEnum.hasMoreElements())
+                {
+                    String curAcl = vEnum.nextElement();
+                    ACL acl = parseACL(curAcl);
+                    if (acl != null) {
+                        acls.put(acl.getName(), acl);
                     }
                 }
             }
@@ -728,21 +765,15 @@ public class PKIJNDIRealm extends JNDIRealm {
             e.printStackTrace();
             return;
         }
-
     }
 
     // Check to see if we have obtained the PKI ACLs from the ldap server
-    private boolean hasResourceACLS() {
-
-        if (acls != null && acls.size() > 0) {
-            return true;
-        } else {
-            return false;
-        }
+    public boolean hasResourceACLs() {
+        return acls.size() > 0;
     }
 
     // Check to see if we have read in the auth properties file
-    private boolean hasAuthzProperties() {
+    public boolean hasAuthzProperties() {
 
         if (this.authzProperties != null) {
             return true;
@@ -763,7 +794,8 @@ public class PKIJNDIRealm extends JNDIRealm {
      * @exception EException ACL related parsing errors for res
      * @return an ACL instance built from the parsed res
      */
-    private ACL parseACL(String res) throws Exception {
+    public ACL parseACL(String res) throws Exception {
+
         if (res == null) {
             throw new Exception("Bad input to parseACL.");
         }
@@ -868,34 +900,32 @@ public class PKIJNDIRealm extends JNDIRealm {
     //Load the custom mapping file auth.properties, which maps urls to acl resourceID and operation value
     //example entry: /kra/pki/config/cert/transport = certServer.kra.pki.config.cert.transport,read
     // ToDo: Look into a more sophisticated method than this simple properties file if appropriate.
-    private synchronized void loadAuthzProperties(Context context) {
+    public synchronized void loadAuthzProperties(Context context) throws IOException {
 
         if (authzProperties == null && context != null) {
 
             InputStream inputStream = context.getServletContext().getResourceAsStream(PROP_AUTH_FILE_PATH);
 
-            if (inputStream == null)
-                return;
-
-            Properties properties = new Properties();
+            if (inputStream == null) {
+                logDebug("Resource "+PROP_AUTH_FILE_PATH+" not found.");
+                throw new IOException("Resource "+PROP_AUTH_FILE_PATH+" not found.");
+            }
 
             try {
+                logDebug("Loading authorization properties");
+
+                Properties properties = new Properties();
                 properties.load(inputStream);
-            } catch (IOException e) {
-                properties = null;
+
+                authzProperties = properties;
+
             } finally {
-
-                if (properties != null) {
-                    authzProperties = properties;
-                }
                 try {
                     inputStream.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
-
-            return;
         }
     }
 
@@ -908,7 +938,7 @@ public class PKIJNDIRealm extends JNDIRealm {
      *
      * @exception NamingException if a directory server error occurs
      */
-    private Vector<String> getAttributeValues(String attrId, Attributes attrs)
+    public Vector<String> getAttributeValues(String attrId, Attributes attrs)
             throws NamingException {
 
         if (attrId == null || attrs == null)
@@ -934,14 +964,14 @@ public class PKIJNDIRealm extends JNDIRealm {
         return values;
     }
 
-   /*
-    * ToDo: Figure out how to do real logging
-    */
-   private void logErr(String msg) {
-       System.err.println(msg);
-   }
+    /*
+     * ToDo: Figure out how to do real logging
+     */
+    public void logErr(String msg) {
+        System.err.println(msg);
+    }
 
-   private void logDebug(String msg) {
-       System.out.println(msg);
-   }
+    public void logDebug(String msg) {
+        System.out.println("PKIJNDIRealm: "+msg);
+    }
 }
diff --git a/base/common/src/com/netscape/cmscore/realm/SSLAuthenticatorWithFallback.java b/base/common/src/com/netscape/cmscore/realm/SSLAuthenticatorWithFallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..dbc1663ebf20ce872f85f56f8c1dfe50e724a4b4
--- /dev/null
+++ b/base/common/src/com/netscape/cmscore/realm/SSLAuthenticatorWithFallback.java
@@ -0,0 +1,177 @@
+// --- BEGIN COPYRIGHT BLOCK ---
+// 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; version 2 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//
+// (C) 2012 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+
+package com.netscape.cmscore.realm;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Globals;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.authenticator.AuthenticatorBase;
+import org.apache.catalina.authenticator.BasicAuthenticator;
+import org.apache.catalina.authenticator.FormAuthenticator;
+import org.apache.catalina.authenticator.SSLAuthenticator;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.deploy.LoginConfig;
+
+/**
+ * @author Endi S. Dewata
+ */
+public class SSLAuthenticatorWithFallback extends AuthenticatorBase {
+
+    public final static String BASIC_AUTHENTICATOR = "BASIC";
+    public final static String FORM_AUTHENTICATOR = "FORM";
+
+    String fallbackMethod = BASIC_AUTHENTICATOR;
+
+    AuthenticatorBase sslAuthenticator = new SSLAuthenticator();
+    AuthenticatorBase fallbackAuthenticator;
+
+    public SSLAuthenticatorWithFallback() {
+        log("Creating SSL authenticator with fallback");
+    }
+
+    @Override
+    public String getInfo() {
+        return "SSL authenticator with "+fallbackMethod+" fallback.";
+    }
+
+    public String getFallbackMethod() {
+        return fallbackMethod;
+    }
+
+    public void setFallbackMethod(String fallbackMethod) {
+        log("fallback method: "+fallbackMethod);
+        this.fallbackMethod = fallbackMethod;
+
+        if (BASIC_AUTHENTICATOR.equalsIgnoreCase(fallbackMethod)) {
+            fallbackAuthenticator = new BasicAuthenticator();
+
+        } else if (FORM_AUTHENTICATOR.equalsIgnoreCase(fallbackMethod)) {
+            fallbackAuthenticator = new FormAuthenticator();
+
+        } else {
+            throw new Error("Invalid authenticator fallback: "+fallbackMethod);
+        }
+    }
+
+    @Override
+    public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws IOException {
+
+        log("Authenticating client");
+
+        X509Certificate certs[] = (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR);
+        boolean result;
+
+        if (certs != null && certs.length > 0) {
+            log("Authenticate with client certificate authentication");
+            HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper(response) {
+                public void setHeader(String name, String value) {
+                    log("SSL auth header: "+name+"="+value);
+                };
+                public void sendError(int code) {
+                    log("SSL auth return code: "+code);
+                }
+            };
+            result = sslAuthenticator.authenticate(request, wrapper, config);
+
+        } else {
+            log("Authenticating with "+fallbackMethod+" authenication");
+            HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper(response) {
+                public void setHeader(String name, String value) {
+                    log("Fallback auth header: "+name+"="+value);
+                };
+                public void sendError(int code) {
+                    log("Fallback auth return code: "+code);
+                }
+            };
+            result = fallbackAuthenticator.authenticate(request, wrapper, config);
+        }
+
+        if (result)
+            return true;
+
+        log("Result: "+result);
+
+        StringBuilder value = new StringBuilder(16);
+        value.append("Basic realm=\"");
+        if (config.getRealmName() == null) {
+            value.append(REALM_NAME);
+        } else {
+            value.append(config.getRealmName());
+        }
+        value.append('\"');
+        response.setHeader(AUTH_HEADER_NAME, value.toString());
+        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+
+        return false;
+    }
+
+    @Override
+    protected String getAuthMethod() {
+        return HttpServletRequest.CLIENT_CERT_AUTH;
+    };
+
+    /**
+     * This is needed to set the containerLog in ValveBase.
+     */
+    @Override
+    protected void initInternal() throws LifecycleException {
+        log("Initializing authenticators");
+        super.initInternal();
+        sslAuthenticator.init();
+        fallbackAuthenticator.init();
+    }
+
+    @Override
+    public void startInternal() throws LifecycleException {
+        log("Starting authenticators");
+        super.startInternal();
+        sslAuthenticator.start();
+        fallbackAuthenticator.start();
+    }
+
+    @Override
+    public void stopInternal() throws LifecycleException {
+        log("Stopping authenticators");
+        super.stopInternal();
+        sslAuthenticator.stop();
+        fallbackAuthenticator.stop();
+    }
+
+    /**
+     * This is needed to set the context in AuthenticatorBase.
+     */
+    @Override
+    public void setContainer(Container container) {
+        log("Setting container");
+        super.setContainer(container);
+        sslAuthenticator.setContainer(container);
+        fallbackAuthenticator.setContainer(container);
+    }
+
+    public void log(String message) {
+        System.out.println("SSLAuthenticatorWithFallback: "+message);
+    }
+}
diff --git a/base/deploy/config/pkislots.cfg b/base/deploy/config/pkislots.cfg
index ee75154ce5ca1b683a261af704d7fd639bf5b3f6..9ce2231a93431d736f38180a7d368ebe168ff02d 100644
--- a/base/deploy/config/pkislots.cfg
+++ b/base/deploy/config/pkislots.cfg
@@ -42,6 +42,10 @@ PKI_CLOSE_AJP_PORT_COMMENT_SLOT=[PKI_CLOSE_AJP_PORT_COMMENT]
 PKI_CLOSE_ENABLE_PROXY_COMMENT_SLOT=[PKI_CLOSE_ENABLE_PROXY_COMMENT]
 PKI_CLOSE_SEPARATE_PORTS_SERVER_COMMENT_SLOT=[PKI_CLOSE_SEPARATE_PORTS_SERVER_COMMENT]
 PKI_CLOSE_SEPARATE_PORTS_WEB_COMMENT_SLOT=[PKI_CLOSE_SEPARATE_PORTS_WEB_COMMENT]
+PKI_DS_BASE_DN_SLOT=[PKI_DS_BASE_DN]
+PKI_DS_BIND_DN_SLOT=[PKI_DS_BIND_DN]
+PKI_DS_PASSWORD_SLOT=[PKI_DS_PASSWORD]
+PKI_DS_URI_SLOT=[PKI_DS_URI]
 PKI_EE_SECURE_CLIENT_AUTH_PORT_SLOT=[PKI_EE_SECURE_CLIENT_AUTH_PORT]
 PKI_EE_SECURE_CLIENT_AUTH_PORT_CONNECTOR_NAME_SLOT=[PKI_EE_SECURE_CLIENT_AUTH_PORT_CONNECTOR_NAME]
 PKI_EE_SECURE_CLIENT_AUTH_PORT_SERVER_COMMENT_SLOT=[PKI_EE_SECURE_CLIENT_AUTH_PORT_SERVER_COMMENT]
diff --git a/base/deploy/src/scriptlets/pkiparser.py b/base/deploy/src/scriptlets/pkiparser.py
index 6e14041806d9b269e57e1cc8f0a1c2ede1276f34..f9292131dd224582c2f7eb78e687c537fb83966a 100644
--- a/base/deploy/src/scriptlets/pkiparser.py
+++ b/base/deploy/src/scriptlets/pkiparser.py
@@ -507,6 +507,9 @@ def compose_pki_master_dictionary():
             config.pki_master_dict['pki_source_server_xml'] =\
                 os.path.join(config.pki_master_dict['pki_source_shared_path'],
                              "server.xml")
+            config.pki_master_dict['pki_source_context_xml'] =\
+                os.path.join(config.pki_master_dict['pki_source_shared_path'],
+                             "context.xml")
             config.pki_master_dict['pki_source_tomcat_conf'] =\
                 os.path.join(config.pki_master_dict['pki_source_shared_path'],
                              "tomcat.conf")
@@ -1077,6 +1080,10 @@ def compose_pki_master_dictionary():
                 os.path.join(
                     config.pki_master_dict['pki_instance_configuration_path'],
                     "server.xml")
+            config.pki_master_dict['pki_target_context_xml'] =\
+                os.path.join(
+                    config.pki_master_dict['pki_instance_configuration_path'],
+                    "context.xml")
             config.pki_master_dict['pki_target_tomcat_conf_instance_id'] =\
                 config.pki_master_dict['pki_root_prefix'] +\
                 "/etc/sysconfig/" +\
@@ -1090,6 +1097,11 @@ def compose_pki_master_dictionary():
                     config.pki_master_dict['pki_tomcat_webapps_root_path'],
                     "index.jsp")
             # in-place slot substitution name/value pairs
+            config.pki_master_dict['pki_target_auth_properties'] =\
+                os.path.join(
+                    config.pki_master_dict['pki_tomcat_webapps_subsystem_path'],
+                    "WEB-INF",
+                    "auth.properties")
             config.pki_master_dict['pki_target_velocity_properties'] =\
                 os.path.join(
                     config.pki_master_dict['pki_tomcat_webapps_subsystem_path'],
@@ -1224,7 +1236,7 @@ def compose_pki_master_dictionary():
             ['PKI_ADMIN_SECURE_PORT_SERVER_COMMENT_SLOT'] =\
                 ""
             config.pki_master_dict['PKI_AGENT_CLIENTAUTH_SLOT'] =\
-                "agent"
+                "want"
             config.pki_master_dict['PKI_AGENT_SECURE_PORT_SLOT'] =\
                 config.pki_master_dict['pki_https_port']
             config.pki_master_dict['PKI_AJP_PORT_SLOT'] =\
@@ -1691,6 +1703,20 @@ def compose_pki_master_dictionary():
             # Guess that the Directory Server resides on the local host
             config.pki_master_dict['pki_ds_hostname'] =\
                 config.pki_master_dict['pki_hostname']
+        config.pki_master_dict['PKI_DS_BASE_DN_SLOT'] =\
+            config.pki_master_dict['pki_ds_base_dn']
+        config.pki_master_dict['PKI_DS_BIND_DN_SLOT'] =\
+            config.pki_master_dict['pki_ds_bind_dn']
+        config.pki_master_dict['PKI_DS_PASSWORD_SLOT'] =\
+            config.pki_sensitive_dict['pki_ds_password']
+        if config.str2bool(config.pki_master_dict['pki_ds_secure_connection']):
+            config.pki_master_dict['PKI_DS_URI_SLOT'] =\
+                "ldaps://" + config.pki_master_dict['pki_ds_hostname'] +\
+                    ":" + config.pki_master_dict['pki_ds_ldaps_port']
+        else:
+            config.pki_master_dict['PKI_DS_URI_SLOT'] =\
+                "ldap://" + config.pki_master_dict['pki_ds_hostname'] +\
+                    ":" + config.pki_master_dict['pki_ds_ldap_port']
         # Jython scriptlet
         # 'Backup' Configuration name/value pairs
         #
diff --git a/base/deploy/src/scriptlets/slot_substitution.py b/base/deploy/src/scriptlets/slot_substitution.py
index 3467596e8de7df52afa4aaa497ca225cea576077..482d1d9cbc4916587a337288e5fd3eb930987e55 100644
--- a/base/deploy/src/scriptlets/slot_substitution.py
+++ b/base/deploy/src/scriptlets/slot_substitution.py
@@ -54,6 +54,10 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                 master['pki_target_server_xml'],
                 overwrite_flag=True)
             util.file.copy_with_slot_substitution(
+                master['pki_source_context_xml'],
+                master['pki_target_context_xml'],
+                overwrite_flag=True)
+            util.file.copy_with_slot_substitution(
                 master['pki_source_tomcat_conf'],
                 master['pki_target_tomcat_conf_instance_id'],
                 uid=0, gid=0, overwrite_flag=True)
@@ -66,6 +70,8 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                 master['pki_target_index_jsp'],
                 overwrite_flag=True)
             util.file.apply_slot_substitution(
+                master['pki_target_auth_properties'])
+            util.file.apply_slot_substitution(
                 master['pki_target_velocity_properties'])
             util.file.apply_slot_substitution(
                 master['pki_target_subsystem_web_xml'])
@@ -109,6 +115,10 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                 master['pki_target_server_xml'],
                 overwrite_flag=True)
             util.file.copy_with_slot_substitution(
+                master['pki_source_context_xml'],
+                master['pki_target_context_xml'],
+                overwrite_flag=True)
+            util.file.copy_with_slot_substitution(
                 master['pki_source_tomcat_conf'],
                 master['pki_target_tomcat_conf_instance_id'],
                 uid=0, gid=0, overwrite_flag=True)
@@ -121,6 +131,8 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
                 master['pki_target_index_jsp'],
                 overwrite_flag=True)
             util.file.apply_slot_substitution(
+                master['pki_target_auth_properties'])
+            util.file.apply_slot_substitution(
                 master['pki_target_velocity_properties'])
             util.file.apply_slot_substitution(
                 master['pki_target_subsystem_web_xml'])
diff --git a/base/setup/pki b/base/setup/pki
index a2d5a69d6ee524350c9d9bd8fd3c9797ad9ac0b9..90c863f35804dd04934666a4ce53922ae932c5dd 100755
--- a/base/setup/pki
+++ b/base/setup/pki
@@ -75,6 +75,7 @@ $ENV{CLASSPATH} = "/usr/share/java/${PRODUCT}/pki-certsrv.jar:"
                 . "/usr/share/java/${PRODUCT}/pki-cms.jar:"
                 . "/usr/share/java/${PRODUCT}/pki-nsutil.jar:"
                 . "/usr/share/java/apache-commons-cli.jar:"
+                . "/usr/share/java/apache-commons-codec.jar:"
                 . "/usr/share/java/apache-commons-lang.jar:"
                 . "/usr/share/java/apache-commons-logging.jar:"
                 . "/usr/share/java/commons-httpclient.jar:"
diff --git a/base/setup/pkicreate b/base/setup/pkicreate
index 6abb737550ce370c572eae6b37f0ad78e72ff1b8..cc4ee703fa159e95c812c33e555ab5f6e08d25fd 100755
--- a/base/setup/pkicreate
+++ b/base/setup/pkicreate
@@ -2560,7 +2560,7 @@ LoadModule nss_module  /opt/fortitude/modules.local/libmodnss.so
             $slot_hash{$PKI_EE_SECURE_CLIENT_AUTH_PORT_COMMENT_SERVER_SLOT}    = "";
 
             # Set appropriate "clientAuth" parameter for "Shared Ports"
-            $slot_hash{$PKI_AGENT_CLIENTAUTH_SLOT} = "agent";
+            $slot_hash{$PKI_AGENT_CLIENTAUTH_SLOT} = "want";
 
             # Comment out the "Admin/EE" Ports
             $slot_hash{$PKI_OPEN_SEPARATE_PORTS_COMMENT_SERVER_SLOT}  = $PKI_OPEN_COMMENT;
-- 
1.7.11.2

