summaryrefslogtreecommitdiffstats
path: root/pki
diff options
context:
space:
mode:
Diffstat (limited to 'pki')
-rw-r--r--pki/.classpath1
-rw-r--r--pki/base/common/src/CMakeLists.txt22
-rw-r--r--pki/base/common/src/com/netscape/cmscore/realm/ACL.java193
-rw-r--r--pki/base/common/src/com/netscape/cmscore/realm/ACLEntry.java243
-rw-r--r--pki/base/common/src/com/netscape/cmscore/realm/PKIJNDIRealm.java943
-rw-r--r--pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java158
-rw-r--r--pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java32
-rw-r--r--pki/base/kra/shared/conf/acl.ldif10
-rw-r--r--pki/base/kra/shared/conf/server.xml50
-rw-r--r--pki/base/kra/shared/webapps/kra/WEB-INF/auth.properties16
-rw-r--r--pki/base/kra/shared/webapps/kra/WEB-INF/web.xml79
-rw-r--r--pki/specs/pki-core.spec21
12 files changed, 1751 insertions, 17 deletions
diff --git a/pki/.classpath b/pki/.classpath
index 48aa45698..c8f6d3be9 100644
--- a/pki/.classpath
+++ b/pki/.classpath
@@ -32,5 +32,6 @@
<classpathentry kind="lib" path="/usr/share/java/commons-codec.jar"/>
<classpathentry kind="lib" path="/usr/share/candlepin/lib/jaxb-impl-2.1.12.jar"/>
<classpathentry kind="lib" path="/usr/share/candlepin/lib/resteasy-jaxb-provider-2.2.1.GA.jar"/>
+ <classpathentry kind="lib" path="/usr/share/tomcat6/lib/catalina.jar"/>
<classpathentry kind="output" path="build/classes"/>
</classpath>
diff --git a/pki/base/common/src/CMakeLists.txt b/pki/base/common/src/CMakeLists.txt
index 6dfd322a1..a9d4a765c 100644
--- a/pki/base/common/src/CMakeLists.txt
+++ b/pki/base/common/src/CMakeLists.txt
@@ -23,6 +23,13 @@ find_file(COMMONS_CODEC_JAR
/usr/share/java
)
+find_file(TOMCAT_CATALINA_JAR
+ NAMES
+ catalina.jar
+ PATHS
+ /usr/share/java/tomcat6
+)
+
find_file(SERVLET_JAR
NAMES
servlet.jar
@@ -1024,6 +1031,12 @@ set(pki-cmscore_java_SRCS
com/netscape/cmscore/time/SimpleTimeSource.java
)
+set(pki-jndi-realm_SRCS
+ com/netscape/cmscore/realm/PKIJNDIRealm.java
+ com/netscape/cmscore/realm/ACLEntry.java
+ com/netscape/cmscore/realm/ACL.java
+)
+
set(pki-cmsbundle_RCS
LogMessages.properties
UserMessages.properties
@@ -1032,7 +1045,7 @@ set(pki-cmsbundle_RCS
set(CMAKE_JAVA_INCLUDE_PATH
${PKI_NSUTIL_JAR} ${PKI_CMSUTIL_JAR}
${LDAPJDK_JAR} ${SERVLET_JAR} ${VELOCITY_JAR} ${XALAN_JAR} ${XERCES_JAR}
- ${JSS_JAR} ${COMMONS_CODEC_JAR} ${SYMKEY_JAR} ${JAXRS_API_JAR} ${RESTEASY_JAXRS_JAR})
+ ${JSS_JAR} ${COMMONS_CODEC_JAR} ${TOMCAT_CATALINA_JAR} ${SYMKEY_JAR} ${JAXRS_API_JAR} ${RESTEASY_JAXRS_JAR})
set(CMAKE_JAVA_TARGET_VERSION ${APPLICATION_VERSION})
@@ -1073,3 +1086,10 @@ add_jar(pki-cmsbundle ${pki-cmsbundle_RCS})
add_dependencies(pki-cmsbundle pki-nsutil pki-cmsutil pki-certsrv pki-cms pki-cmscore)
install_jar(pki-cmsbundle ${JAVA_JAR_INSTALL_DIR}/pki)
set(PKI_CMSBUNDLE_JAR ${pki-cmsbundle_JAR_FILE} CACHE INTERNAL "pki-cmsbundle jar file")
+
+# build pki jndi realm
+set(CMAKE_JAR_CLASSES_PREFIX com/netscape/cmscore/realm)
+add_jar(pki-jndi-realm ${pki-jndi-realm_SRCS})
+install_jar(pki-jndi-realm ${JAVA_JAR_INSTALL_DIR}/pki)
+set(PKI_JNDI_REALM_JAR ${pki-jndi-realm_JAR_FILE} CACHE INTERNAL "pki-jndi-realm jar file")
+
diff --git a/pki/base/common/src/com/netscape/cmscore/realm/ACL.java b/pki/base/common/src/com/netscape/cmscore/realm/ACL.java
new file mode 100644
index 000000000..4d7303f9d
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/realm/ACL.java
@@ -0,0 +1,193 @@
+// --- 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) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.cmscore.realm;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * A class represents an access control list (ACL). An ACL
+ * is associated with an protected resources. The policy
+ * enforcer can verify the ACLs with the current
+ * context to see if the corresponding resource is accessible.
+ * <P>
+ * An <code>ACL</code> may contain one or more <code>ACLEntry</code>. However, in case of multiple <code>ACLEntry</code>
+ * , a subject must pass ALL of the <code>ACLEntry</code> evaluation for permission to be granted
+ * <P>
+ *
+ * @version $Revision$, $Date$
+ */
+public class ACL {
+
+ /**
+ *
+ */
+
+ protected Vector<ACLEntry> entries = new Vector<ACLEntry>(); // ACL entries
+ protected Vector<String> rights = null; // possible rights entries
+ protected String resourceACLs = null; // exact resourceACLs string on ldap server
+ protected String name = null; // resource name
+ protected String description = null; // resource description
+
+ /**
+ * Class constructor.
+ */
+ public ACL() {
+ }
+
+ /**
+ * Class constructor.
+ * Constructs an access control list associated
+ * with a resource name
+ *
+ * @param name resource name
+ * @param rights applicable rights defined for this resource
+ * @param resourceACLs the entire ACL specification. For example:
+ * "certServer.log.configuration:read,modify:
+ * allow (read,modify)
+ * group=\"Administrators\":
+ * Allow administrators to read and modify log
+ * configuration"
+ */
+ public ACL(String name, Vector<String> rights, String resourceACLs) {
+ setName(name);
+ if (rights != null) {
+ this.rights = rights;
+ } else {
+ this.rights = new Vector<String>();
+ }
+ this.resourceACLs = resourceACLs;
+
+ }
+
+ /**
+ * Sets the name of the resource governed by this
+ * access control.
+ *
+ * @param name name of the resource
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Retrieves the name of the resource governed by
+ * this access control.
+ *
+ * @return name of the resource
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Retrieves the exact string of the resourceACLs
+ *
+ * @return resource's acl
+ */
+ public String getResourceACLs() {
+ return resourceACLs;
+ }
+
+ /**
+ * Sets the description of the resource governed by this
+ * access control.
+ *
+ * @param description Description of the protected resource
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Retrieves the description of the resource governed by
+ * this access control.
+ *
+ * @return Description of the protected resource
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Adds an ACL entry to this list.
+ *
+ * @param entry the <code>ACLEntry</code> to be added to this resource
+ */
+ public void addEntry(ACLEntry entry) {
+ entries.addElement(entry);
+ }
+
+ /**
+ * Returns ACL entries.
+ *
+ * @return enumeration for the <code>ACLEntry</code> vector
+ */
+ public Enumeration<ACLEntry> entries() {
+ return entries.elements();
+ }
+
+ /**
+ * Returns the string reprsentation.
+ *
+ * @return the string representation of the ACL entries in the
+ * following format:
+ * <resource name>[<ACLEntry1>,<ACLEntry 2>,...<ACLEntry N>]
+ */
+ public String toString() {
+ String entries = "";
+ Enumeration<ACLEntry> e = entries();
+
+ for (; e.hasMoreElements();) {
+ ACLEntry entry = (ACLEntry) e.nextElement();
+
+ entries += entry.toString();
+ if (e.hasMoreElements())
+ entries += ",";
+ }
+ return getName() + "[" + entries + "]";
+ }
+
+ /**
+ * Adds an rights entry to this list.
+ *
+ * @param right The right to be added for this ACL
+ */
+ public void addRight(String right) {
+ rights.addElement(right);
+ }
+
+ /**
+ * Tells if the permission is one of the defined "rights"
+ *
+ * @param permission permission to be checked
+ * @return true if it's one of the "rights"; false otherwise
+ */
+ public boolean checkRight(String permission) {
+ return (rights.contains((Object) permission));
+ }
+
+ /**
+ * Returns rights entries.
+ *
+ * @return enumeration of rights defined for this ACL
+ */
+ public Enumeration<String> rights() {
+ return rights.elements();
+ }
+}
diff --git a/pki/base/common/src/com/netscape/cmscore/realm/ACLEntry.java b/pki/base/common/src/com/netscape/cmscore/realm/ACLEntry.java
new file mode 100644
index 000000000..8e502b02c
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/realm/ACLEntry.java
@@ -0,0 +1,243 @@
+// --- 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) 2007 Red Hat, Inc.
+// All rights reserved.
+// --- END COPYRIGHT BLOCK ---
+package com.netscape.cmscore.realm;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+
+/**
+ * A class represents an ACI entry of an access control list.
+ * <P>
+ *
+ * @version $Revision$, $Date$
+ */
+public class ACLEntry {
+ /**
+ *
+ */
+ protected Hashtable<String, String> mPerms = new Hashtable<String, String>();
+ protected String expressions = null;
+ protected boolean negative = false;
+ protected String ACLEntryString = null;
+
+ /**
+ * Class Constructor
+ */
+ public ACLEntry() {
+ }
+
+ /**
+ * Checks if this ACL entry is set to negative.
+ *
+ * @return true if this ACL entry expression is for "deny";
+ * false if this ACL entry expression is for "allow"
+ */
+ public boolean isNegative() {
+ return negative;
+ }
+
+ /**
+ * Sets this ACL entry negative. This ACL entry expression is for "deny".
+ */
+ public void setNegative() {
+ negative = true;
+ }
+
+ /**
+ * Sets the ACL entry string
+ *
+ * @param s string in the following format:
+ *
+ * <PRE>
+ * allow|deny (right[,right...]) attribute_expression
+ * </PRE>
+ */
+ public void setACLEntryString(String s) {
+ ACLEntryString = s;
+ }
+
+ /**
+ * Gets the ACL Entry String
+ *
+ * @return ACL Entry string in the following format:
+ *
+ * <PRE>
+ * allow|deny (right[,right...]) attribute_expression
+ * </PRE>
+ */
+ public String getACLEntryString() {
+ return ACLEntryString;
+ }
+
+ /**
+ * Adds permission to this entry. Permission must be one of the
+ * "rights" defined for each protected resource in its ACL
+ *
+ * @param acl the acl instance that this aclEntry is associated with
+ * @param permission one of the "rights" defined for each
+ * protected resource in its ACL
+ */
+ public void addPermission(ACL acl, String permission) {
+ if (acl.checkRight(permission) == true) {
+ mPerms.put(permission, permission);
+ } else {
+ // not a valid right...log it later
+ }
+ }
+
+ /**
+ * Returns a list of permissions associated with
+ * this entry.
+ *
+ * @return a list of permissions for this ACL entry
+ */
+ public Enumeration<String> permissions() {
+ return mPerms.elements();
+ }
+
+ /**
+ * Sets the expression associated with this entry.
+ *
+ * @param expressions the evaluator expressions. For example,
+ * group="Administrators"
+ */
+ public void setAttributeExpressions(String expressions) {
+ this.expressions = expressions;
+ }
+
+ /**
+ * Retrieves the expression associated with this entry.
+ *
+ * @return the evaluator expressions. For example,
+ * group="Administrators"
+ */
+ public String getAttributeExpressions() {
+ return expressions;
+ }
+
+ /**
+ * Checks to see if this <code>ACLEntry</code> contains a
+ * particular permission
+ *
+ * @param permission one of the "rights" defined for each
+ * protected resource in its ACL
+ * @return true if permission contained in the permission list
+ * for this <code>ACLEntry</code>; false otherwise.
+ */
+ public boolean containPermission(String permission) {
+ return (mPerms.get(permission) != null);
+ }
+
+ /**
+ * Checks if this entry has the given permission.
+ *
+ * @param permission one of the "rights" defined for each
+ * protected resource in its ACL
+ * @return true if the permission is allowed; false if the
+ * permission is denied. If a permission is not
+ * recognized by this ACL, it is considered denied
+ */
+ public boolean checkPermission(String permission) {
+ // default - if we dont know about the requested permission,
+ // don't grant permission
+ if (mPerms.get(permission) == null)
+ return false;
+ if (isNegative()) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Parse string in the following format:
+ *
+ * <PRE>
+ * allow|deny (right[,right...]) attribute_expression
+ * </PRE>
+ *
+ * into an instance of the <code>ACLEntry</code> class
+ *
+ * @param acl the acl instance associated with this aclentry
+ * @param aclEntryString aclEntryString in the specified format
+ * @return an instance of the <code>ACLEntry</code> class
+ */
+ public static ACLEntry parseACLEntry(ACL acl, String aclEntryString) {
+ if (aclEntryString == null) {
+ return null;
+ }
+
+ String te = aclEntryString.trim();
+
+ // locate first space
+ int i = te.indexOf(' ');
+ // prefix should be "allowed" or "deny"
+ String prefix = te.substring(0, i);
+ String suffix = te.substring(i + 1).trim();
+ ACLEntry entry = new ACLEntry();
+
+ if (prefix.equals("allow")) {
+ // do nothing
+ } else if (prefix.equals("deny")) {
+ entry.setNegative();
+ } else {
+ return null;
+ }
+ // locate the second space
+ i = suffix.indexOf(' ');
+ // this prefix should be rights list, delimited by ","
+ prefix = suffix.substring(1, i - 1);
+ // the suffix is the rest, which is the "expressions"
+ suffix = suffix.substring(i + 1).trim();
+
+ StringTokenizer st = new StringTokenizer(prefix, ",");
+
+ for (; st.hasMoreTokens();) {
+ entry.addPermission(acl, st.nextToken());
+ }
+ entry.setAttributeExpressions(suffix);
+ return entry;
+ }
+
+ /**
+ * Returns the string representation of this ACLEntry
+ *
+ * @return string representation of this ACLEntry
+ */
+ public String toString() {
+ String entry = "";
+
+ if (isNegative()) {
+ entry += "deny (";
+ } else {
+ entry += "allow (";
+ }
+ Enumeration<String> e = permissions();
+
+ for (; e.hasMoreElements();) {
+ String p = e.nextElement();
+
+ entry += p;
+ if (e.hasMoreElements())
+ entry += ",";
+ }
+ entry += ") " + getAttributeExpressions();
+ return entry;
+ }
+}
diff --git a/pki/base/common/src/com/netscape/cmscore/realm/PKIJNDIRealm.java b/pki/base/common/src/com/netscape/cmscore/realm/PKIJNDIRealm.java
new file mode 100644
index 000000000..720d9f52e
--- /dev/null
+++ b/pki/base/common/src/com/netscape/cmscore/realm/PKIJNDIRealm.java
@@ -0,0 +1,943 @@
+package com.netscape.cmscore.realm;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.Principal;
+
+import org.apache.catalina.Context;
+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 java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.PartialResultException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.servlet.http.HttpServletResponse;
+
+/*
+ * Self contained PKI JNDI Real that overrides the standard JNDI Realm
+ *
+ * The purpose is to move authentication and authorization code out of the core server.
+ * This realm can be used standalone with only the dependency of having tomcatjss and jss installed
+ * and having tomcatjss connectors configured in the tomcat instance.
+ *
+ * This realm allows for configurable SSL client authentication checking as well
+ * as checking against the standard PKI ACLs we have configured in our ldap database.
+ * Those not using a CS instance could either not configure the ACL checking or
+ * override this class to read in and evaluate their own ACL's.
+ *
+ * This code makes use and simplifies some existing ACL and authorization code
+ * from the main server for now.
+ *
+ */
+
+public class PKIJNDIRealm extends JNDIRealm {
+
+ private static final String DEF_CERT_ATTR = "userCert";
+ private static final String DEF_ACL_ATTR = "resource";
+
+ private static final String PROP_USER = "user";
+ private static final String PROP_GROUP = "group";
+ private static final String PROP_USER_ANYBODY = "anybody";
+ private static final String PROP_USER_EVERYBODY = "everybody";
+ private static final String CERT_VERSION = "2";
+ private static final String PROP_AUTH_FILE_PATH = "/WEB-INF/auth.properties";
+ private static final int EXPRESSION_SIZE = 2;
+
+ private Hashtable<String, ACL> acls = new Hashtable<String, ACL>();
+
+ private Properties authzProperties = null;
+
+ /* Look up the principal user based on the incoming client auth
+ * certificate.
+ * @param usercert Incoming client auth certificate presented by user
+ * @return Principal Object representing the authenticated user.
+ */
+ @Override
+ protected synchronized Principal getPrincipal(X509Certificate usercert) {
+
+ logDebug("Entering PKIJNDIRealm.getPrincipal");
+
+ if (usercert == null)
+ return null;
+
+ String uid = null;
+ String certUIDLabel = getCertUIDLabel();
+
+ if (certUIDLabel == null) {
+ // We have no uid label, attempt to construct a description
+
+ uid = 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");
+
+ logDebug(uid);
+
+ } else {
+
+ String certUIDSrchStr = certUIDLabel + "=";
+
+ StringTokenizer dnTokens =
+ new StringTokenizer(usercert.getSubjectDN().getName(), ",");
+
+ while (dnTokens.hasMoreTokens()) {
+ String token = dnTokens.nextToken();
+ int index = token.indexOf(certUIDSrchStr);
+
+ if (index != -1) {
+ // Found the entry with the cert's UID
+
+ 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;
+ }
+
+ if (uid != null) {
+ break;
+ }
+ }
+ }
+ }
+
+ //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
+
+ if (user != null) {
+ X509Certificate storedCert = getStoredUserCert();
+ setStoredUserCert(null);
+ //Compare the stored ldap cert with the incoming cert
+ if (usercert.equals(storedCert)) {
+ //Success, the incoming certificate matches the
+ //certificate stored in LDAP for this user.
+ return user;
+ }
+ }
+
+ setStoredUserCert(null);
+
+ return null;
+ }
+
+ /**
+ * Return a User object containing information about the user
+ * with the specified username, if found in the directory;
+ * otherwise return <code>null</code>.
+ * Override here to extract the client auth certificate from the
+ * ldap db.
+ *
+ * @param context The directory context
+ * @param username Username to be looked up
+ *
+ * @exception NamingException if a directory server error occurs
+ *
+ * @see #getUser(DirContext, String, String, int)
+ */
+ @Override
+ protected User getUser(DirContext context, String username)
+ throws NamingException {
+
+ //ToDo: Right now we support the Realm attribute
+ // userBase which only allows a single pattern from
+ // which to search for users in ldap.
+ // We need to use the "userPattern" attribute
+ // which supports multiple search patterns.
+ // This has not been done because the out of the box
+ // Support for SSL client auth does not appear to support
+ // the userPattern attribute. Certainly another method here
+ // could be overridden to get this working.
+
+ User certUser = super.getUser(context, username);
+
+ if (certUser != null) {
+ extractAndSaveStoredX509UserCert(context, certUser);
+ }
+
+ return certUser;
+ }
+
+ /**
+ * Perform access control based on the specified authorization constraint.
+ * Return <code>true</code> if this constraint is satisfied and processing
+ * should continue, or <code>false</code> otherwise.
+ * override to check for custom PKI ACL's authz permissions.
+ *
+ * @param request Request we are processing
+ * @param response Response we are creating
+ * @param constraints Security constraint we are enforcing
+ * @param context The Context to which client of this class is attached.
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ @Override
+ public boolean hasResourcePermission(Request request,
+ Response response,
+ SecurityConstraint[] constraints,
+ Context context)
+ throws IOException {
+
+ boolean allowed = super.hasResourcePermission(request, response, constraints, context);
+
+ if (allowed == true && hasResourceACLS()) {
+
+ loadAuthzProperties(context);
+
+ if (hasAuthzProperties()) {
+ //Let's check against our encoded acls.
+
+ String requestURI = request.getDecodedRequestURI();
+ Principal principal = request.getPrincipal();
+
+ String match = getACLEntryDataForURL(requestURI);
+
+ if (match != null) {
+ //first part is the resourceID, second part is the operation
+ String[] authzParams = match.split("\\,");
+
+ String resourceID = null;
+ String operation = null;
+
+ if (authzParams.length >= EXPRESSION_SIZE) {
+ resourceID = authzParams[0];
+ operation = authzParams[1];
+
+ if (resourceID != null) {
+ resourceID = resourceID.trim();
+ }
+
+ if (operation != null) {
+ operation = operation.trim();
+ }
+ }
+
+ allowed = checkACLPermission(principal, resourceID, operation);
+ logDebug("resourceID: " + resourceID + " operation: " + operation + " allowed: " + allowed);
+ }
+ }
+ }
+
+ // Return a "Forbidden" message denying access to this resource
+ if (!allowed) {
+ response.sendError
+ (HttpServletResponse.SC_FORBIDDEN,
+ sm.getString("realmBase.forbidden"));
+ }
+
+ return allowed;
+ }
+
+ /**
+ * Return a List of roles associated with the given User. Any
+ * roles present in the user's directory entry are supplemented by
+ * a directory search. If no roles are associated with this user,
+ * a zero-length List is returned.
+ * Override here to get the PKI Resource ACLs if so configured
+ *
+ * @param context The directory context we are searching
+ * @param user The User to be checked
+ *
+ * @exception NamingException if a directory server error occurs
+ */
+
+ @Override
+ protected List<String> getRoles(DirContext context, User user)
+ throws NamingException {
+
+ try {
+ getResourceACLS(context);
+ } catch (NamingException e) {
+ logDebug("No aclResources found.");
+ }
+
+ return super.getRoles(context, user);
+ }
+
+ /* Custom variables, see <Realm> element */
+
+ /* Attribute to find encoded Cert in ldap
+ * "userCertificate" is most common value.
+ */
+ private String certAttrName;
+
+ public String getCertAttrName() {
+ return (certAttrName != null) ? certAttrName : DEF_CERT_ATTR;
+ }
+
+ public void setCertAttrName(String certAttrName) {
+ this.certAttrName = certAttrName;
+ }
+
+ /* Attribute to find encoded acl resources in ldap
+ * "aclResources" is most common value.
+ */
+ private String aclAttrName;
+
+ public String getAclAttrName() {
+ return (aclAttrName != null) ? aclAttrName : DEF_ACL_ATTR;
+ }
+
+ public void setAclAttrName(String aclAttrName) {
+ this.aclAttrName = aclAttrName;
+ }
+
+ /* Attribute for base dn of acl resources in ldap
+ */
+
+ private String aclBase;
+
+ public String getAclBase() {
+ return aclBase;
+ }
+
+ public void setAclBase(String aclBase) {
+ this.aclBase = aclBase;
+ }
+
+ /* Substring label to search for user id in presented client auth cert.
+ * "UID" is most common value.
+ */
+
+ private String certUIDLabel;
+
+ public String getCertUIDLabel() {
+ return certUIDLabel;
+ }
+
+ public void setCertUIDStr(String certUIDLabel) {
+ this.certUIDLabel = certUIDLabel;
+ }
+
+ /* Saved user certificate object obtained during authentication
+ * from the user's LDAP record.
+ * Will be accessed later to compare with incoming client auth certificate.
+ */
+ private X509Certificate storedUserCert;
+
+ protected void setStoredUserCert(X509Certificate cert) {
+ this.storedUserCert = cert;
+ }
+
+ protected 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) {
+
+ 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;
+ }
+
+ ACL acl = acls.get(resourceId);
+
+ if (acl == null) {
+ //No such acl, assume true
+ return allowed;
+ }
+
+ Enumeration<ACLEntry> aclEntries = acl.entries();
+ while (aclEntries != null && aclEntries.hasMoreElements()) {
+ ACLEntry entry = aclEntries.nextElement();
+ boolean isEntryNegative = entry.isNegative();
+
+ String expressions = entry.getAttributeExpressions();
+
+ allowed = evaluateExpressions(principal, expressions);
+
+ if (isEntryNegative) {
+ allowed = !allowed;
+ }
+
+ //ToDo:
+ // Handle the more than one entry case.
+ // What to do if one of them fails.
+ }
+
+ return allowed;
+ }
+
+ // Evaluate an expression as part of a PKI ACL
+ // Ex: user=anybody , group=Data Recovery Manager Agents
+ private boolean evaluateExpression(Principal principal, String expression) {
+
+ boolean allowed = true;
+ if (principal == null || expression == null) {
+ return allowed;
+ }
+
+ String operation = getExpressionOperation(expression);
+
+ if (operation == null) {
+ return allowed;
+ }
+
+ String[] expBlock = expression.split(operation);
+
+ if (expBlock.length != 2) {
+ return allowed;
+ }
+
+ String left = expBlock[0];
+ String right = expBlock[1];
+
+ if (left != null) {
+ left.trim();
+ } else {
+ return allowed;
+ }
+ //Massage the right hand side of this expression to be a legal string value.
+ if (right != null) {
+ right.trim();
+ right = right.replace("\"", "");
+ right = right.trim();
+ } else {
+ return allowed;
+ }
+
+ boolean negate = false;
+
+ //Otherwise assume "="
+ if (operation.equals("!=")) {
+ negate = true;
+ }
+
+ allowed = false;
+ if (left.equals(PROP_GROUP)) {
+ // Check JNDI to see if the user has this role/group
+ if (hasRole(principal, right)) {
+ allowed = true;
+ }
+ } else if (left.equals(PROP_USER)) {
+ if (right.equals(PROP_USER_ANYBODY) || right.equals(PROP_USER_EVERYBODY)) {
+ allowed = true;
+ }
+ } else {
+ logDebug("Unknown expression.");
+ }
+
+ if (negate) {
+ allowed = !allowed;
+ }
+
+ return allowed;
+ }
+
+ // Convenience method to find the operation in an ACL expression
+ private String getExpressionOperation(String exp) {
+ //Support only = and !=
+
+ int i = exp.indexOf("!=");
+
+ if (i == -1) {
+ i = exp.indexOf("=");
+ if (i == -1) {
+ return null;
+ } else {
+ return "=";
+ }
+ } else {
+ return "!=";
+ }
+ }
+
+ // Take a set of expressions in an ACL and evaluate it
+ private boolean evaluateExpressions(Principal principal, String s) {
+
+ Vector<Object> v = new Vector<Object>();
+
+ while (s.length() > 0) {
+ int orIndex = s.indexOf("||");
+ int andIndex = s.indexOf("&&");
+
+ // this is the last expression
+ if (orIndex == -1 && andIndex == -1) {
+ boolean passed = evaluateExpression(principal, s.trim());
+
+ v.addElement(Boolean.valueOf(passed));
+ break;
+
+ // || first
+ } else if (andIndex == -1 || (orIndex != -1 && orIndex < andIndex)) {
+ String s1 = s.substring(0, orIndex);
+ boolean passed = evaluateExpression(principal, s1.trim());
+
+ v.addElement(new Boolean(passed));
+ v.addElement("||");
+ s = s.substring(orIndex + 2);
+ // && first
+ } else {
+ String s1 = s.substring(0, andIndex);
+ boolean passed = evaluateExpression(principal, s1.trim());
+
+ v.addElement(new Boolean(passed));
+ v.addElement("&&");
+ s = s.substring(andIndex + 2);
+ }
+ }
+
+ if (v.size() == 0) {
+ return false;
+ }
+
+ if (v.size() == 1) {
+ Boolean bool = (Boolean) v.remove(0);
+
+ return bool.booleanValue();
+ }
+
+ boolean left = false;
+ String op = "";
+ boolean right = false;
+
+ while (v.size() > 0) {
+ if (op.equals(""))
+ left = ((Boolean) v.remove(0)).booleanValue();
+ op = (String) v.remove(0);
+ right = ((Boolean) v.remove(0)).booleanValue();
+
+ if (op.equals("||")) {
+ if (left == false && right == false)
+ left = false;
+ left = true;
+ } else if (op.equals("&&")) {
+ if (left == true && right == true)
+ left = true;
+ left = false;
+ }
+ }
+
+ return left;
+
+ }
+
+ /* Attempt to get the stored user certificate object and save it for
+ * future reference. This all takes place within one command invocation from
+ * the getPrincipal method defined here.
+ */
+ private void extractAndSaveStoredX509UserCert(DirContext context, User certUser)
+ throws NamingException {
+
+ setStoredUserCert(null);
+
+ if (certUser != null && context != null) {
+
+ String certAttrStr = this.getCertAttrName();
+
+ // certAttrStr has a default value, can not be null
+ String[] attrs = new String[] { certAttrStr };
+
+ Attributes attributes = context.getAttributes(certUser.getDN(), attrs);
+
+ if (attributes == null) {
+ logErr("Can not get certificate attributes in extractAndSaveStoredX590UserCert.");
+ return;
+ }
+
+ Attribute certAttr = null;
+
+ certAttr = attributes.get(certAttrStr);
+
+ if (certAttr == null) {
+ logErr("Can not get certificate attribut in extractAndSaveStoredX509UserCert.");
+ return;
+ }
+
+ Object oAttr = null;
+
+ oAttr = certAttr.get();
+
+ if (oAttr == null) {
+ logErr("Can not get certificate attribute object in extractAndSaveStoredX509UserCert.");
+ return;
+ }
+
+ byte[] certData = null;
+
+ if (oAttr instanceof byte[]) {
+ certData = (byte[]) oAttr;
+ } else {
+ logErr("Can not get certificate data in extractAndSaveStoredX509UserCert.");
+ return;
+ }
+
+ ByteArrayInputStream inStream = null;
+ try {
+ X509Certificate x509Cert = null;
+ if (certData != null) {
+ inStream = new ByteArrayInputStream(certData);
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+ if (cf != null) {
+ x509Cert = (X509Certificate) cf.generateCertificate(inStream);
+ }
+
+ setStoredUserCert(x509Cert);
+ }
+ } catch (Exception e) {
+ logErr("Certificate encoding error in extractAndSaveStoredX509UserCert: " + e);
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ logErr("Can't close ByteArrayStream in extractAndSaveStoredX509UserCert: " + e);
+ }
+ }
+ }
+ }
+ }
+
+ // Search for the proper auth.properties entry corresponding
+ // to a particular incoming URL
+ // ToDo: In the admin interface, often the operation is sent
+ // as one of the parameters to the message.
+ // 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;
+
+ if (!hasAuthzProperties()) {
+ return null;
+ }
+
+ aclEntryData = authzProperties.getProperty(requestURI);
+
+ 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;
+ }
+ }
+ }
+
+ return aclEntryData;
+
+ }
+
+ //Go to the directory server, if configured, and go get the Resource ACLs
+ private synchronized void getResourceACLS(DirContext context) throws NamingException {
+
+ //for now lets support this on startup
+ if (hasResourceACLS()) {
+ return;
+ }
+
+ 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;
+ }
+
+ SearchResult result = null;
+ try {
+ result = results.next();
+ if (result != null) {
+
+ Attributes attrs = result.getAttributes();
+ if (attrs == null)
+ return;
+
+ Vector<String> aclVec = getAttributeValues(aclAttrName, attrs);
+
+ if (aclVec != null) {
+
+ Enumeration<String> vEnum = aclVec.elements();
+
+ while (vEnum.hasMoreElements())
+ {
+ String curAcl = vEnum.nextElement();
+ ACL acl = parseACL(curAcl);
+ if (acl != null) {
+ acls.put(acl.getName(), acl);
+ }
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ 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;
+ }
+ }
+
+ // Check to see if we have read in the auth properties file
+ private boolean hasAuthzProperties() {
+
+ if (this.authzProperties != null) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Parse ACL resource attributes
+ *
+ * @param res same format as the resource attribute:
+ *
+ * <PRE>
+ * <resource name>:<permission1,permission2,...permissionn>:
+ * <allow|deny> (<subset of the permission set>) <evaluator expression>
+ * </PRE>
+ * @exception EException ACL related parsing errors for res
+ * @return an ACL instance built from the parsed res
+ */
+ private ACL parseACL(String res) throws Exception {
+ if (res == null) {
+ throw new Exception("Bad input to parseACL.");
+ }
+
+ ACL acl = null;
+ Vector<String> rights = null;
+ int idx1 = res.indexOf(":");
+
+ if (idx1 <= 0) {
+ acl = new ACL(res, rights, res);
+ } else {
+ // getting resource id
+ String resource = res.substring(0, idx1);
+
+ if (resource == null) {
+ String infoMsg = "resource not specified in resource attribute:" +
+ res;
+
+ String[] params = new String[2];
+
+ params[0] = res;
+ params[1] = infoMsg;
+ throw new Exception(infoMsg);
+ }
+
+ // getting list of applicable rights
+ String st = res.substring(idx1 + 1);
+ int idx2 = st.indexOf(":");
+ String rightsString = null;
+
+ if (idx2 != -1)
+ rightsString = st.substring(0, idx2);
+ else {
+ String infoMsg =
+ "rights not specified in resource attribute:" + res;
+ String[] params = new String[2];
+
+ params[0] = res;
+ params[1] = infoMsg;
+ throw new Exception(infoMsg);
+ }
+
+ if (rightsString != null) {
+ rights = new Vector<String>();
+ StringTokenizer rtok = new StringTokenizer(rightsString, ",");
+
+ while (rtok.hasMoreTokens()) {
+ rights.addElement(rtok.nextToken());
+ }
+ }
+
+ acl = new ACL(resource, rights, res);
+
+ String stx = st.substring(idx2 + 1);
+ int idx3 = stx.indexOf(":");
+ String tr = stx.substring(0, idx3);
+
+ // getting list of acl entries
+ if (tr != null) {
+ StringTokenizer atok = new StringTokenizer(tr, ";");
+
+ while (atok.hasMoreTokens()) {
+ String acs = (String) atok.nextToken();
+
+ // construct ACL entry
+ ACLEntry entry = ACLEntry.parseACLEntry(acl, acs);
+
+ if (entry == null) {
+ String infoMsg = "parseACLEntry() call failed";
+ String[] params = new String[2];
+
+ params[0] = "ACLEntry = " + acs;
+ params[1] = infoMsg;
+ throw new Exception(infoMsg);
+ }
+
+ entry.setACLEntryString(acs);
+ acl.addEntry(entry);
+ }
+ } else {
+ // fine
+ String infoMsg = " not specified in resource attribute:" +
+
+ res;
+
+ String[] params = new String[2];
+
+ params[0] = res;
+ params[1] = infoMsg;
+ throw new Exception(infoMsg);
+ }
+
+ // getting description
+ String desc = stx.substring(idx3 + 1);
+
+ acl.setDescription(desc);
+ }
+
+ return (acl);
+ }
+
+ //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) {
+
+ if (authzProperties == null && context != null) {
+ ClassLoader loader = this.getClass().getClassLoader();
+ if (loader == null)
+ loader = ClassLoader.getSystemClassLoader();
+
+ InputStream inputStream = context.getServletContext().getResourceAsStream(PROP_AUTH_FILE_PATH);
+
+ if (inputStream == null)
+ return;
+
+ Properties properties = new Properties();
+
+ try {
+ properties.load(inputStream);
+ } catch (IOException e) {
+ properties = null;
+ } finally {
+
+ if (properties != null) {
+ authzProperties = properties;
+ }
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return;
+ }
+ }
+
+ /**
+ * Return a String representing the value of the specified attribute.
+ * Create our own since the super class has it as private
+ *
+ * @param attrId Attribute name
+ * @param attrs Attributes containing the required value
+ *
+ * @exception NamingException if a directory server error occurs
+ */
+ private Vector<String> getAttributeValues(String attrId, Attributes attrs)
+ throws NamingException {
+
+ if (attrId == null || attrs == null)
+ return null;
+
+ Vector<String> values = new Vector<String>();
+ Attribute attr = attrs.get(attrId);
+ if (attr == null)
+ return (null);
+ NamingEnumeration<?> value = attr.getAll();
+ if (value == null)
+ return (null);
+
+ while (value.hasMore()) {
+ Object obj = value.next();
+ String valueString = null;
+ if (obj instanceof byte[])
+ valueString = new String((byte[]) obj);
+ else
+ valueString = obj.toString();
+ values.add(valueString);
+ }
+ return values;
+ }
+
+ /*
+ * ToDo: Figure out how to do real logging
+ */
+ private void logErr(String msg) {
+ System.err.println(msg);
+ }
+
+ private void logDebug(String msg) {
+ System.out.println(msg);
+ }
+}
diff --git a/pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java b/pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java
index 51cead47b..651873b20 100644
--- a/pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java
+++ b/pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java
@@ -1,13 +1,26 @@
package com.netscape.cms.servlet.test;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.Socket;
+import java.net.URL;
+import java.net.UnknownHostException;
import java.util.Collection;
+import java.util.Enumeration;
import java.util.Iterator;
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
+import org.apache.commons.httpclient.protocol.Protocol;
+import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
+import org.jboss.resteasy.client.ClientExecutor;
import org.jboss.resteasy.client.ClientResponse;
import org.jboss.resteasy.client.ProxyFactory;
-
import com.netscape.certsrv.dbs.keydb.KeyId;
import com.netscape.certsrv.request.RequestId;
+import org.jboss.resteasy.client.core.executors.ApacheHttpClientExecutor;
import com.netscape.cms.servlet.admin.SystemCertificateResource;
import com.netscape.cms.servlet.cert.model.CertificateData;
import com.netscape.cms.servlet.key.KeyResource;
@@ -23,20 +36,146 @@ import com.netscape.cms.servlet.request.model.KeyRequestInfos;
import com.netscape.cms.servlet.request.model.RecoveryRequestData;
import com.netscape.cmsutil.util.Utils;
+import org.mozilla.jss.ssl.SSLCertificateApprovalCallback;
+import org.mozilla.jss.ssl.SSLClientCertificateSelectionCallback;
+import org.mozilla.jss.ssl.SSLSocket;
+
public class DRMRestClient {
+ // Callback to approve or deny returned SSL server certs
+ // Right now, simply approve the cert.
+ // ToDO: Look into taking this JSS http client code and move it into
+ // its own class to be used by possible future clients.
+ private class ServerCertApprovalCB implements SSLCertificateApprovalCallback {
+
+ public boolean approve(org.mozilla.jss.crypto.X509Certificate servercert,
+ SSLCertificateApprovalCallback.ValidityStatus status) {
+
+ //For now lets just accept the server cert. This is a test tool, being
+ // pointed at a well know kra instance.
+
+
+ if (servercert != null) {
+ System.out.println("Peer cert details: " +
+ "\n subject: " + servercert.getSubjectDN().toString() +
+ "\n issuer: " + servercert.getIssuerDN().toString() +
+ "\n serial: " + servercert.getSerialNumber().toString()
+ );
+ }
+
+ SSLCertificateApprovalCallback.ValidityItem item;
+
+ Enumeration<?> errors = status.getReasons();
+ int i = 0;
+ while (errors.hasMoreElements()) {
+ i++;
+ item = (SSLCertificateApprovalCallback.ValidityItem) errors.nextElement();
+ System.out.println("item " + i +
+ " reason=" + item.getReason() +
+ " depth=" + item.getDepth());
+
+ int reason = item.getReason();
+
+ if (reason ==
+ SSLCertificateApprovalCallback.ValidityStatus.UNTRUSTED_ISSUER ||
+ reason == SSLCertificateApprovalCallback.ValidityStatus.BAD_CERT_DOMAIN) {
+
+ //Allow these two since we haven't necessarily installed the CA cert for trust
+ // and we are choosing "localhost" as the host for this client.
+
+ return true;
+
+ }
+ }
+
+ //For other errors return false
+
+ return false;
+ }
+ }
+
+ private class JSSProtocolSocketFactory implements ProtocolSocketFactory {
+
+ @Override
+ public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+
+ SSLSocket sock = createJSSSocket(host,port, null, 0, null);
+ return (Socket) sock;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException,
+ UnknownHostException {
+
+ SSLSocket sock = createJSSSocket(host,port, clientHost, clientPort, null);
+ return (Socket) sock;
+ }
+
+ @Override
+ public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params)
+ throws IOException, UnknownHostException, ConnectTimeoutException {
+
+ SSLSocket sock = createJSSSocket(host, port, localAddress, localPort, null);
+ return (Socket) sock;
+ }
+ }
+
+ private SSLSocket createJSSSocket(String host, int port, InetAddress localAddress,
+ int localPort, SSLClientCertificateSelectionCallback clientCertSelectionCallback)
+ throws IOException, UnknownHostException, ConnectTimeoutException {
+
+ SSLSocket sock = new SSLSocket(InetAddress.getByName(host),
+ port,
+ localAddress,
+ localPort,
+ new ServerCertApprovalCB(),
+ null);
+
+ if(sock != null && clientCertNickname != null) {
+ sock.setClientCertNickname(clientCertNickname);
+ }
+
+ return sock;
+
+ }
private KeyResource keyClient;
private KeysResource keysClient;
private KeyRequestsResource keyRequestsClient;
private KeyRequestResource keyRequestClient;
private SystemCertificateResource systemCertClient;
- public DRMRestClient(String baseUri) {
- systemCertClient = ProxyFactory.create(SystemCertificateResource.class, baseUri);
- keyRequestsClient = ProxyFactory.create(KeyRequestsResource.class, baseUri);
- keyRequestClient = ProxyFactory.create(KeyRequestResource.class, baseUri);
- keysClient = ProxyFactory.create(KeysResource.class, baseUri);
- keyClient = ProxyFactory.create(KeyResource.class, baseUri);
+ private String clientCertNickname = null;
+
+ public DRMRestClient(String baseUri, String clientCertNick) throws MalformedURLException {
+
+ // For SSL we are assuming the caller has already intialized JSS and has
+ // a valid CryptoManager and CryptoToken
+ // optional clientCertNickname is provided for use if required.
+
+
+ URL url = new URL(baseUri);
+
+ String protocol = url.getProtocol();
+ int port = url.getPort();
+
+ clientCertNickname = null;
+ if(protocol != null && protocol.equals("https")) {
+ if (clientCertNick != null) {
+ clientCertNickname = clientCertNick;
+ }
+
+ Protocol.registerProtocol("https",
+ new Protocol(protocol, new JSSProtocolSocketFactory(), port));
+ }
+
+ HttpClient httpclient = new HttpClient();
+ ClientExecutor executor = new ApacheHttpClientExecutor(httpclient);
+
+ systemCertClient = ProxyFactory.create(SystemCertificateResource.class, baseUri, executor);
+ keyRequestsClient = ProxyFactory.create(KeyRequestsResource.class, baseUri, executor);
+ keyRequestClient = ProxyFactory.create(KeyRequestResource.class, baseUri, executor);
+ keysClient = ProxyFactory.create(KeysResource.class, baseUri, executor);
+ keyClient = ProxyFactory.create(KeyResource.class, baseUri, executor);
}
public String getTransportCert() {
@@ -124,7 +263,4 @@ public class DRMRestClient {
KeyData key = keyClient.retrieveKey(data);
return key;
}
-
-
-
-} \ No newline at end of file
+}
diff --git a/pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java b/pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
index 8020ec2ca..8d83247b8 100644
--- a/pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
+++ b/pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java
@@ -17,6 +17,7 @@
// --- END COPYRIGHT BLOCK ---
package com.netscape.cms.servlet.test;
+import java.net.MalformedURLException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
@@ -61,6 +62,8 @@ public class DRMTest {
String port = null;
String token_pwd = null;
String db_dir = "./";
+ String protocol = "http";
+ String clientCertNickname = "KRA Administrator of Instance pki-kra's SjcRedhat Domain ID";
// parse command line arguments
Options options = new Options();
@@ -68,6 +71,8 @@ public class DRMTest {
options.addOption("p", true, "Port of the DRM");
options.addOption("w", true, "Token password");
options.addOption("d", true, "Directory for tokendb");
+ options.addOption("s", true, "Attempt Optional Secure SSL connection");
+ options.addOption("c", true, "Optional SSL Client cert Nickname");
try {
CommandLineParser parser = new PosixParser();
@@ -97,6 +102,20 @@ public class DRMTest {
if (cmd.hasOption("d")) {
db_dir = cmd.getOptionValue("d");
}
+
+ if (cmd.hasOption("s")) {
+ if(cmd.getOptionValue("s") != null && cmd.getOptionValue("s").equals("true")) {
+ protocol = "https";
+ }
+ }
+
+ if (cmd.hasOption("c")) {
+ String nick = cmd.getOptionValue("c");
+
+ if (nick != null && protocol.equals("https")) {
+ clientCertNickname = nick;
+ }
+ }
} catch (ParseException e) {
System.err.println("Error in parsing command line options: " + e.getMessage());
@@ -173,8 +192,17 @@ public class DRMTest {
}
// Set base URI and get client
- String baseUri = "http://" + host + ":" + port + "/kra/pki";
- DRMRestClient client = new DRMRestClient(baseUri);
+
+
+ String baseUri = protocol + "://" + host + ":" + port + "/kra/pki";
+ DRMRestClient client;
+ try {
+ client = new DRMRestClient(baseUri, clientCertNickname);
+ } catch (MalformedURLException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ return;
+ }
// Test 1: Get transport certificate from DRM
transportCert = client.getTransportCert();
diff --git a/pki/base/kra/shared/conf/acl.ldif b/pki/base/kra/shared/conf/acl.ldif
index 4c219eaaa..38a9a088c 100644
--- a/pki/base/kra/shared/conf/acl.ldif
+++ b/pki/base/kra/shared/conf/acl.ldif
@@ -30,3 +30,13 @@ resourceACLS: certServer.kra.TokenKeyRecovery:submit:allow (submit) group="Data
resourceACLS: certServer.kra.registerUser:read,modify:allow (modify,read) group="Enterprise CA Administrators" || group="Enterprise KRA 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.kra.getTransportCert:read:allow (read) group="Enterprise CA Administrators" || group="Enterprise KRA Administrators" || group="Enterprise OCSP Administrators" || group="Enterprise TKS Administrators" || group="Enterprise TPS Administrators":Only Enterprise Administrators are allowed to retrieve the transport cert
resourceACLS: certServer.clone.configuration:read,modify:allow (modify,read) group="Enterprise CA Administrators" || group="Enterprise KRA Administrators" || group="Enterprise OCSP Administrators" || group="Enterprise TKS Administrators":Only Enterprise Administrators are allowed to clone the configuration.
+resourceACLS: certServer.kra.pki.key.retrieve:execute:allow (execute) group="Data Recovery Manager Agents":Data Recovery Manager Agents may retrieve archived key
+resourceACLS: certServer.kra.pki.keyrequests:read:allow (read) group="Data Recovery Manager Agents":Data Recovery Manager Agents may read keyrequests data
+resourceACLS: certServer.kra.pki.keyrequest:read:allow (read) group="Data Recovery Manager Agents":Data Recovery Manager Agents may read keyrequest data
+resourceACLS: certServer.kra.pki.keyrequest.archive:execute:allow (execute) group="Data Recovery Manager Agents":Data Recovery Manager Agents may issue archival request
+resourceACLS: certServer.kra.pki.keyrequest.recover:execute:allow (execute) group="Data Recovery Manager Agents":Data Recovery Manager Agents may issue recovery request
+resourceACLS: certServer.kra.pki.keyrequest.approve:execute:allow (execute) group="Data Recovery Manager Agents":Data Recovery Manager Agents may approve security data request
+resourceACLS: certServer.kra.pki.keyrequest.reject:execute:allow (execute) group="Data Recovery Manager Agents":Data Recovery Manager Agents may reject key security data request
+resourceACLS: certServer.kra.pki.keyrequest.cancel:execute:allow (execute) group="Data Recovery Manager Agents":Data Recovery Manager Agents may cancel security data request
+resourceACLS: certServer.kra.pki.keys:read:allow (read) group="Data Recovery Manager Agents":Data Recovery Manager Agents may read security data
+resourceACLS: certServer.kra.pki.config.cert.transport:read:allow (read) group="Data Recovery Manager Agents":Data Recovery Manager Agents may read transport cert data
diff --git a/pki/base/kra/shared/conf/server.xml b/pki/base/kra/shared/conf/server.xml
index fcd849ef2..58121d448 100644
--- a/pki/base/kra/shared/conf/server.xml
+++ b/pki/base/kra/shared/conf/server.xml
@@ -229,8 +229,58 @@ Tomcat Port = [TOMCAT_SERVER_PORT] (for shutdown)
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
+
+ <!--
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
+ -->
+
+ <!-- Custom PKIJNDI realm
+
+ Example:
+
+ <Realm className="com.netscape.cmscore.realm.PKIJNDIRealm" : classpath to realm
+ connectionURL="ldap://localhost:389" : standard JNDI connection URL
+ userBase="ou=people,dc=localhost-pki-kra" : standard JNDI userBase property
+ userSearch="(description={0})" : Attribute to search for user of incoming client auth certificate
+ : Use userSearch="(UID={0})" if wanting to search isolate user based on UID
+ : Also set the following: certUIDLabel="UID" or whatever the field containing
+ : the user's UID happens to be. This will cause the incoming's cert dn to be
+ : be searched for <certUIDLabel>=<uid value>
+
+ certAttrName="userCertificate" : Attribute containing user's client auth certificate
+ roleBase="ou=groups,dc=localhost-pki-kra" : Standard JNDI search base for roles or groups
+ roleName="cn" : Standard attribute name containg roles or groups
+ roleSubtree="true" : Standard JNDI roleSubtree property
+ roleSearch="(uniqueMember={0})" : How to search for a user in a specific role or group
+ connectionName="cn=Directory Manager" : Connection name, needs elevated privileges
+ connectionPassword="secret123" : Password for elevated user
+ aclBase ="cn=aclResources,dc=localhost-pki-kra" : Custom base location of PKI ACL's in directory
+ aclAttrName="resourceACLS" : Name of attribute containing PKI ACL's
+ />
+
+ Uncomment and customize below to activate Realm.
+ Also umcomment Security Constraints and login config values
+ in WEB-INF/web.xml as well.
+ -->
+
+ <!--
+ <Realm className="com.netscape.cmscore.realm.PKIJNDIRealm"
+ connectionURL="ldap://localhost:389"
+ userBase="ou=people,dc=localhost-pki-kra"
+ userSearch="(description={0})"
+ certAttrName="userCertificate"
+ roleBase="ou=groups,dc=localhost-pki-kra"
+ roleName="cn"
+ roleSubtree="true"
+ roleSearch="(uniqueMember={0})"
+ connectionName="cn=Directory Manager"
+ connectionPassword="netscape"
+ aclBase ="cn=aclResources,dc=localhost-pki-kra"
+ aclAttrName="resourceACLS"
+ />
+
+ -->
<!-- Define the default virtual host
Note: XML Schema validation will not work with Xerces 2.2.
diff --git a/pki/base/kra/shared/webapps/kra/WEB-INF/auth.properties b/pki/base/kra/shared/webapps/kra/WEB-INF/auth.properties
new file mode 100644
index 000000000..a206aa9e4
--- /dev/null
+++ b/pki/base/kra/shared/webapps/kra/WEB-INF/auth.properties
@@ -0,0 +1,16 @@
+# Restful API auth/authz mapping info
+#
+# Format:
+# <Rest API URL> = <ACL Resource ID>,<ACL resource operation>
+# ex: /kra/pki/key/retrieve = certServer.kra.pki.key.retrieve,execute
+
+/kra/pki/key/retrieve = certServer.kra.pki.key.retrieve,execute
+/kra/pki/keyrequests = certServer.kra.pki.keyrequests,read
+/kra/pki/keyrequest = certServer.kra.pki.keyrequest,read
+/kra/pki/keyrequest/archive = certServer.kra.pki.keyrequest.archive,execute
+/kra/pki/keyrequest/recover = certServer.kra.pki.keyrequest.recover,execute
+/kra/pki/keyrequest/approve = certServer.kra.pki.keyrequest.approve,execute
+/kra/pki/keyrequest/reject = certServer.kra.pki.keyrequest.reject,execute
+/kra/pki/keyrequest/cancel = certServer.kra.pki.keyrequest.cancel,execute
+/kra/pki/keys = certServer.kra.pki.keys,read
+/kra/pki/config/cert/transport = certServer.kra.pki.config.cert.transport,read
diff --git a/pki/base/kra/shared/webapps/kra/WEB-INF/web.xml b/pki/base/kra/shared/webapps/kra/WEB-INF/web.xml
index 529aeadbc..c6e9934eb 100644
--- a/pki/base/kra/shared/webapps/kra/WEB-INF/web.xml
+++ b/pki/base/kra/shared/webapps/kra/WEB-INF/web.xml
@@ -1034,5 +1034,82 @@
<session-config>
<session-timeout>30</session-timeout>
</session-config>
-</web-app>
+<!-- Default login configuration uses form-based authentication -->
+<!-- Security Constraint for agent access to the Security Data Rest Interface -->
+
+<!-- Uncomment to activate PKIJNDI realm as in conf/server.xml -->
+<!--
+<security-constraint>
+ <display-name>KRA Top Level Constraint</display-name>
+ <web-resource-collection>
+ <web-resource-name>KRA Protected Area</web-resource-name>
+ <url-pattern>/pki/*
+ </url-pattern>
+ </web-resource-collection>
+ <user-data-constraint>
+ <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+ </user-data-constraint>
+ <auth-constraint>
+ <role-name>*</role-name>
+ </auth-constraint>
+</security-constraint>
+-->
+
+<!-- Security Constraint to deny certain http methods for key/retrieve -->
+<!-- Uncomment to activate PKIJNDI realm as in conf/server.xml -->
+<!--
+<security-constraint>
+<display-name>Key forbidden</display-name>
+<web-resource-collection>
+ <web-resource-name>Key forbidden</web-resource-name>
+ <url-pattern>/pki/key/retrieve</url-pattern>
+ <http-method>GET</http-method>
+ <http-method>PUT</http-method>
+ <http-method>DELETE</http-method>
+</web-resource-collection>
+<auth-constraint/>
+</security-constraint>
+-->
+
+<!-- Security Constraint to deny certain http methods for keyrequest/* -->
+<!-- Uncomment to activate PKIJNDI realm as in conf/server.xml -->
+
+<!--
+<security-constraint>
+<display-name>KeyRequest forbidden</display-name>
+<web-resource-collection>
+ <web-resource-name>KeyRequest forbidden</web-resource-name>
+ <url-pattern>/pki/keyrequest/archive</url-pattern>
+ <url-pattern>/pki/keyrequest/recover</url-pattern>
+ <url-pattern>/pki/keyrequest/approve/*</url-pattern>
+ <url-pattern>/pki/keyrequest/reject/*</url-pattern>
+ <url-pattern>/pki/keyrequest/cancel/*</url-pattern>
+ <http-method>GET</http-method>
+ <http-method>PUT</http-method>
+ <http-method>DELETE</http-method>
+</web-resource-collection>
+<auth-constraint/>
+</security-constraint>
+-->
+
+
+<!-- Customized SSL Client auth login config
+ uncomment to activate PKIJNDI realm as in conf/server.xml
+-->
+
+<!--
+
+<login-config>
+ <realm-name>PKIJNDIRealm</realm-name>
+ <auth-method>CLIENT-CERT</auth-method>
+ <realm-name>Client Cert Protected Area</realm-name>
+</login-config>
+
+<security-role>
+ <role-name>*</role-name>
+</security-role>
+
+-->
+
+</web-app>
diff --git a/pki/specs/pki-core.spec b/pki/specs/pki-core.spec
index 6e19c008a..94e4a7fb3 100644
--- a/pki/specs/pki-core.spec
+++ b/pki/specs/pki-core.spec
@@ -7,7 +7,7 @@
Name: pki-core
Version: 10.0.0
-Release: %{?relprefix}7%{?prerel}%{?dist}
+Release: %{?relprefix}8%{?prerel}%{?dist}
Summary: Certificate System - PKI Core Components
URL: http://pki.fedoraproject.org/
License: GPLv2
@@ -722,6 +722,9 @@ echo "D /var/run/pki/tks 0755 root root -" >> %{buildroot}%{_sysconfdir}/tmpfil
%{__rm} %{buildroot}%{_initrddir}/pki-krad
%{__rm} %{buildroot}%{_initrddir}/pki-ocspd
%{__rm} %{buildroot}%{_initrddir}/pki-tksd
+# Create symlink to the pki-jndi-realm jar
+mkdir -p %{buildroot}%{_javadir}/tomcat6
+ln -s -f %{_javadir}/pki/pki-jndi-realm.jar %{buildroot}%{_javadir}/tomcat6/pki-jndi-realm.jar
%else
%{__rm} %{buildroot}%{_bindir}/pkicontrol
%{__rm} -rf %{buildroot}%{_sysconfdir}/systemd/system/pki-cad.target.wants
@@ -932,7 +935,6 @@ if [ -d /etc/sysconfig/pki/tks ]; then
fi
/bin/systemctl daemon-reload >/dev/null 2>&1 || :
-
%preun -n pki-ca
if [ $1 = 0 ] ; then
/bin/systemctl --no-reload disable pki-cad.target > /dev/null 2>&1 || :
@@ -1084,6 +1086,15 @@ fi
%{_javadir}/pki/pki-cmsbundle.jar
%{_javadir}/pki/pki-cmscore-%{version}.jar
%{_javadir}/pki/pki-cmscore.jar
+
+%if 0%{?fedora} >= 16
+# Create symlink to the pki-jndi-realm jar
+%{_javadir}/tomcat6/pki-jndi-realm.jar
+%endif
+
+%{_javadir}/pki/pki-jndi-realm-%{version}.jar
+%{_javadir}/pki/pki-jndi-realm.jar
+
%{_datadir}/pki/setup/
%files -n pki-common-javadoc
@@ -1222,6 +1233,12 @@ fi
%changelog
+
+* Fri Mar 09 2018 Jack Magne <jmagne@redhat.com> 10.0.0-5.a1
+- Added support for pki-jndi-realm in tomcat6 in pki-common
+ and pki-kra.
+- Ticket #69.
+
* Fri Mar 2 2012 Matthew Harmsen <mharmsen@redhat.com> 10.0.0-0.7.a1
- For 'mock' purposes, removed platform-specific logic from around
the 'patch' files so that ALL 'patch' files will be included in