diff options
Diffstat (limited to 'pki/base')
-rw-r--r-- | pki/base/common/src/CMakeLists.txt | 22 | ||||
-rw-r--r-- | pki/base/common/src/com/netscape/cmscore/realm/ACL.java | 193 | ||||
-rw-r--r-- | pki/base/common/src/com/netscape/cmscore/realm/ACLEntry.java | 243 | ||||
-rw-r--r-- | pki/base/common/src/com/netscape/cmscore/realm/PKIJNDIRealm.java | 943 | ||||
-rw-r--r-- | pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMRestClient.java | 158 | ||||
-rw-r--r-- | pki/base/kra/functional/src/com/netscape/cms/servlet/test/DRMTest.java | 32 | ||||
-rw-r--r-- | pki/base/kra/shared/conf/acl.ldif | 10 | ||||
-rw-r--r-- | pki/base/kra/shared/conf/server.xml | 50 | ||||
-rw-r--r-- | pki/base/kra/shared/webapps/kra/WEB-INF/auth.properties | 16 | ||||
-rw-r--r-- | pki/base/kra/shared/webapps/kra/WEB-INF/web.xml | 79 |
10 files changed, 1731 insertions, 15 deletions
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> |