ASF Bugzilla – Attachment 6666 Details for
Bug 7831
[PATCH] JNDIRealm does not work with CLIENT-CERT auth method
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
Discussion base for a common solution on how to authenticate clients certificates
JNDIRealmCertAD.java (text/plain), 17.28 KB, created by
Mario Ivankovits
on 2003-06-06 07:07:56 UTC
(
hide
)
Description:
Discussion base for a common solution on how to authenticate clients certificates
Filename:
MIME Type:
Creator:
Mario Ivankovits
Created:
2003-06-06 07:07:56 UTC
Size:
17.28 KB
patch
obsolete
>package com.ops.webcontrol.tomcat; > >import org.apache.catalina.realm.GenericPrincipal; >import org.apache.catalina.realm.JNDIRealm; >import sun.security.util.DerValue; >import sun.security.util.ObjectIdentifier; >import sun.security.x509.X500Name; > >import javax.naming.Name; >import javax.naming.NameNotFoundException; >import javax.naming.NameParser; >import javax.naming.NamingEnumeration; >import javax.naming.NamingException; >import javax.naming.CommunicationException; >import javax.naming.directory.Attribute; >import javax.naming.directory.Attributes; >import javax.naming.directory.DirContext; >import javax.naming.directory.SearchControls; >import javax.naming.directory.SearchResult; >import java.io.IOException; >import java.security.Principal; >import java.security.cert.X509Certificate; >import java.text.MessageFormat; >import java.util.ArrayList; >import java.util.List; > >public class JNDIRealmCertAD extends JNDIRealm >{ > /** > * The message format used to search for a user by cert, with "{0}" marking > * the spot where the cert goes. > */ > protected String certSearch = null; > > /** > * The MessageFormat object associated with the current > * <code>certSearch</code>. > */ > protected MessageFormat certSearchFormat = null; > > /** > * The message format used to form the distinguished name of a > * user, with "{0}" marking the spot where the specified username > * goes. > */ > protected String certPattern = null; > > /** > * The MessageFormat object associated with the current > * <code>certPattern</code>. > */ > protected MessageFormat certPatternFormat = null; > > /** > * The Attribute where to find the userName. Default: cn > */ > protected String certUserName = "cn"; > > private final static ObjectIdentifier EMAIL; > > /** > * A private class representing a User > */ > private static class CertUser > { > String username = null; > String dn = null; > String password = null; > ArrayList roles = null; > > CertUser(String username, String dn, String password, ArrayList roles) > { > this.username = username; > this.dn = dn; > this.password = password; > this.roles = roles; > } > > } > > static > { > try > { > EMAIL = new ObjectIdentifier(new int[]{1, 2, 840, 113549, 1, 9, 1}); > } > catch (IOException e) > { > throw (IllegalArgumentException) new IllegalArgumentException().initCause(e); > } > } > > public Principal authenticate(X509Certificate certs[]) > { > if ((certs == null) || (certs.length < 1)) > return (null); > > // Check the validity of each certificate in the chain > if (debug >= 1) > log("Authenticating client certificate chain"); > > if (validate) > { > for (int i = 0; i < certs.length; i++) > { > if (debug >= 2) > log(" Checking validity for '" + > certs[i].getSubjectDN().getName() + "'"); > try > { > certs[i].checkValidity(); > } > catch (Exception e) > { > if (debug >= 2) > log(" Validity exception", e); > return (null); > } > } > } > > > String cert; > try > { > X509Certificate userCert = certs[0]; > X500Name user = (X500Name) userCert.getSubjectDN(); > > DerValue val = user.findMostSpecificAttribute(EMAIL); > if (val == null) > { > log(" Attribute EMAIL missing in Cert"); > return null; > } > String email = val.getAsString(); > X500Name issuer = (X500Name) userCert.getIssuerDN(); > > StringBuffer sb = new StringBuffer(250); > sb.append("X509:"); > > sb.append("<I>"); > sb.append("C="); > sb.append(issuer.getCountry()); > sb.append(",S="); > sb.append(issuer.getState()); > sb.append(",L="); > sb.append(issuer.getLocality()); > sb.append(",O="); > sb.append(issuer.getOrganization()); > sb.append(",OU="); > sb.append(issuer.getOrganizationalUnit()); > sb.append(",CN="); > sb.append(issuer.getCommonName()); > > sb.append("<S>"); > sb.append("CN="); > sb.append(user.getCommonName()); > sb.append(",E="); > sb.append(email); > > cert = sb.toString(); > } > catch (IOException e) > { > log("build cert", e); > return null; > } > > // Retrieve user information > DirContext context = null; > try > { > context = open(); > > CertUser user = null; > try > { > user = getCertUser(context, cert); > } > catch (CommunicationException e) > { > // If not a "Socket closed." error then rethrow. > if (!(e.getRootCause() instanceof IOException)) > throw(e); > > // log the exception so we know it's there. > log(sm.getString("jndiRealm.exception"), e); > > // close the connection so we know it will be reopened. > if (context != null) > close(context); > > // open a new directory context. > context = open(); > > // Try the query again. > user = getCertUser(context, cert); > > } > if (user == null) > return (null); > > // Search for additional roles > List roles = getRoles(context, user); > > // Create and return a suitable Principal for this user > return (new GenericPrincipal(this, user.username, null, roles)); > } > catch (NamingException e) > { > log("naming exception", e); > return null; > } > finally > { > if (context != null) > release(context); > } > } > > public Principal authenticate(String username, String credentials) > { > > DirContext context = null; > Principal principal = null; > > try > { > > // Ensure that we have a directory context available > context = open(); > > // Occassionally the directory context will timeout. Try one more > // time before giving up. > try > { > > // Authenticate the specified username if possible > principal = authenticate(context, username, credentials); > > } > catch (CommunicationException e) > { > // If not a "Socket closed." error then rethrow. > if (!(e.getRootCause() instanceof IOException)) > throw(e); > > // log the exception so we know it's there. > log(sm.getString("jndiRealm.exception"), e); > > // close the connection so we know it will be reopened. > if (context != null) > close(context); > > // open a new directory context. > context = open(); > > // Try the authentication again. > principal = authenticate(context, username, credentials); > > } > > > // Release this context > release(context); > > // Return the authenticated Principal (if any) > return (principal); > > } > catch (NamingException e) > { > > // Log the problem for posterity > log(sm.getString("jndiRealm.exception"), e); > > // Close the connection so that it gets reopened next time > if (context != null) > close(context); > > // Return "not authenticated" for this request > return (null); > > } > > } > > /** > * Return the message format pattern for selecting users in this Realm. > */ > public String getCertSearch() > { > return (this.certSearch); > } > > > /** > * Set the message format pattern for selecting users in this Realm. > * > * @param certSearch The new user search pattern > */ > public void setCertSearch(String certSearch) > { > > this.certSearch = certSearch; > if (certSearch == null) > certSearchFormat = null; > else > certSearchFormat = new MessageFormat(certSearch); > > } > > /** > * Return the message format pattern for selecting users in this Realm. > */ > public String getCertPattern() > { > return (this.certPattern); > } > > /** > * @return the attribute name where to find the user name. Default: cn > */ > public String getCertUserName() > { > return certUserName; > } > > /** > * @param certUserName the attribute name where to find the user name. Default: cn > */ > public void setCertUserName(String certUserName) > { > this.certUserName = certUserName; > } > > /** > * Set the message format pattern for selecting users in this Realm. > * > * @param certPattern The new cert pattern > */ > public void setCertPattern(String certPattern) > { > this.certPattern = certPattern; > if (certPattern == null) > certPatternFormat = null; > else > certPatternFormat = new MessageFormat(certPattern); > } > > /** > * 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. > * > * @param context The directory context we are searching > * @param user The User to be checked > * > * @exception NamingException if a directory server error occurs > */ > protected List getRoles(DirContext context, CertUser user) throws NamingException > { > if (user == null) > return (null); > > String dn = user.dn; > String username = user.username; > > if (dn == null || username == null) > return (null); > > if (debug >= 2) > log(" getRoles(" + dn + ")"); > > // Start with roles retrieved from the user entry > ArrayList list = user.roles; > if (list == null) > { > list = new ArrayList(); > } > > // Are we configured to do role searches? > if ((roleFormat == null) || (roleName == null)) > return (list); > > // Set up parameters for an appropriate search > String filter = roleFormat.format(new String[]{dn, username}); > SearchControls controls = new SearchControls(); > if (roleSubtree) > controls.setSearchScope(SearchControls.SUBTREE_SCOPE); > else > controls.setSearchScope(SearchControls.ONELEVEL_SCOPE); > controls.setReturningAttributes(new String[]{roleName}); > > // Perform the configured search and process the results > if (debug >= 3) > { > log(" Searching role base '" + roleBase + "' for attribute '" + > roleName + "'"); > log(" With filter expression '" + filter + "'"); > } > NamingEnumeration results = > context.search(roleBase, filter, controls); > if (results == null) > return (list); // Should never happen, but just in case ... > while (results.hasMore()) > { > SearchResult result = (SearchResult) results.next(); > Attributes attrs = result.getAttributes(); > if (attrs == null) > continue; > list = addAttributeValues(roleName, attrs, list); > } > > // Return the augmented list of roles > if (debug >= 2) > { > log(" Returning " + list.size() + " roles"); > for (int i = 0; i < list.size(); i++) > log(" Found role " + list.get(i)); > } > > return (list); > } > > /** > * Return a User object containing information about the user > * with the specified username, if found in the directory; > * otherwise return <code>null</code>. > * > * If the <code>userPassword</code> configuration attribute is > * specified, the value of that attribute is retrieved from the > * user's directory entry. If the <code>userRoleName</code> > * configuration attribute is specified, all values of that > * attribute are retrieved from the directory entry. > * > * @param context The directory context > * @param username Username to be looked up > * > * @exception NamingException if a directory server error occurs > */ > protected CertUser getCertUser(DirContext context, String username) throws NamingException > { > > CertUser user = null; > > // Get attributes to retrieve from user entry > ArrayList list = new ArrayList(); > > list.add(certUserName); > > if (userPassword != null) > list.add(userPassword); > if (userRoleName != null) > list.add(userRoleName); > String[] attrIds = new String[list.size()]; > list.toArray(attrIds); > > // Use pattern or search for user entry > if (certPatternFormat != null) > { > user = getCertUserByPattern(context, username, attrIds); > } > else > { > user = getCertUserBySearch(context, username, attrIds); > } > > return user; > } > > /** > * Use the <code>CertPattern</code> configuration attribute to > * locate the directory entry for the user with the specified > * username and return a User object; otherwise return > * <code>null</code>. > * > * @param context The directory context > * @param username The username > * @param attrIds String[]containing names of attributes to > * retrieve. > * > * @exception NamingException if a directory server error occurs > */ > protected CertUser getCertUserByPattern(DirContext context, > String username, > String[] attrIds) > throws NamingException > { > > if (debug >= 2) > log("lookupUser(" + username + ")"); > > if (username == null || certPatternFormat == null) > return (null); > > // Form the dn from the user pattern > String dn = certPatternFormat.format(new String[]{username}); > if (debug >= 3) > { > log(" dn=" + dn); > } > > // Get required attributes from user entry > Attributes attrs = null; > try > { > attrs = context.getAttributes(dn, attrIds); > } > catch (NameNotFoundException e) > { > return (null); > } > if (attrs == null) > return (null); > > // Retrieve value of userName > String cn = getAttributeValue(certUserName, attrs); > > // Retrieve value of userPassword > String password = null; > if (userPassword != null) > password = getAttributeValue(userPassword, attrs); > > // Retrieve values of userRoleName attribute > ArrayList roles = null; > if (userRoleName != null) > roles = addAttributeValues(userRoleName, attrs, roles); > > return new CertUser(cn, dn, password, roles); > } > > /** > * Return a String representing the value of the specified attribute. > * > * @param attrId Attribute name > * @param attrs Attributes containing the required value > * > * @exception NamingException if a directory server error occurs > */ > private String getAttributeValue(String attrId, Attributes attrs) throws NamingException > { > > if (debug >= 3) > log(" retrieving attribute " + attrId); > > if (attrId == null || attrs == null) > return null; > > Attribute attr = attrs.get(attrId); > if (attr == null) > return (null); > Object value = attr.get(); > if (value == null) > return (null); > String valueString = null; > if (value instanceof byte[]) > valueString = new String((byte[]) value); > else > valueString = value.toString(); > > return valueString; > } > > /** > * Add values of a specified attribute to a list > * > * @param attrId Attribute name > * @param attrs Attributes containing the new values > * @param values ArrayList containing values found so far > * > * @exception NamingException if a directory server error occurs > */ > private ArrayList addAttributeValues(String attrId, Attributes attrs, ArrayList values) throws NamingException > { > > if (debug >= 3) > log(" retrieving values for attribute " + attrId); > if (attrId == null || attrs == null) > return null; > if (values == null) > values = new ArrayList(); > Attribute attr = attrs.get(attrId); > if (attr == null) > return (null); > NamingEnumeration e = attr.getAll(); > while (e.hasMore()) > { > String value = (String) e.next(); > values.add(value); > } > return values; > } > > /** > * Search the directory to return a User object containing > * information about the user with the specified cert, if > * found in the directory; otherwise return <code>null</code>. > * > * @param context The directory context > * @param cert The cert > * @param attrIds String[]containing names of attributes to retrieve. > * > * @exception NamingException if a directory server error occurs > */ > protected CertUser getCertUserBySearch(DirContext context, > String cert, > String[] attrIds) > throws NamingException > { > > if (cert == null || certSearchFormat == null) > return (null); > > // Form the search filter > String filter = certSearchFormat.format(new String[]{cert}); > > // Set up the search controls > SearchControls constraints = new SearchControls(); > > if (userSubtree) > { > constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); > } > else > { > constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE); > } > > // Specify the attributes to be retrieved > if (attrIds == null) > attrIds = new String[0]; > constraints.setReturningAttributes(attrIds); > > if (debug > 3) > { > log(" Searching for " + cert); > log(" base: " + userBase + " filter: " + filter); > } > > NamingEnumeration results = > context.search(userBase, filter, constraints); > > > // Fail if no entries found > if (results == null || !results.hasMore()) > { > if (debug > 2) > { > log(" cert not found"); > } > return (null); > } > > // Get result for the first entry found > SearchResult result = (SearchResult) results.next(); > > // Check no further entries were found > if (results.hasMore()) > { > log("cert " + cert + " has multiple entries"); > return (null); > } > > // Get the entry's distinguished name > NameParser parser = context.getNameParser(""); > Name contextName = parser.parse(context.getNameInNamespace()); > Name baseName = parser.parse(userBase); > Name entryName = parser.parse(result.getName()); > Name name = contextName.addAll(baseName); > name = name.addAll(entryName); > String dn = name.toString(); > > if (debug > 2) > log(" entry found for " + cert + " with dn " + dn); > > // Get the entry's attributes > Attributes attrs = result.getAttributes(); > if (attrs == null) > return null; > > // Retrieve value of userName > String cn = getAttributeValue(certUserName, attrs); > > // Retrieve value of userPassword > String password = null; > if (userPassword != null) > password = getAttributeValue(userPassword, attrs); > > // Retrieve values of userRoleName attribute > ArrayList roles = null; > if (userRoleName != null) > roles = addAttributeValues(userRoleName, attrs, roles); > > return new CertUser(cn, dn, password, roles); > } >}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 7831
:
1499
| 6666 |
6735
|
14901