Author: schultz Date: Thu Mar 8 17:22:29 2012 New Revision: 1298476 URL: http://svn.apache.org/viewvc?rev=1298476&view=rev Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=52500 Added configurable mechanism to retrieve user names from X509 client certificates. Based on a patch provided by Michael Furman.
Added: tomcat/trunk/java/org/apache/catalina/realm/X509SubjectDnRetriever.java tomcat/trunk/java/org/apache/catalina/realm/X509UsernameRetriever.java Modified: tomcat/trunk/java/org/apache/catalina/realm/LocalStrings.properties tomcat/trunk/java/org/apache/catalina/realm/RealmBase.java tomcat/trunk/webapps/docs/config/realm.xml Modified: tomcat/trunk/java/org/apache/catalina/realm/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/realm/LocalStrings.properties?rev=1298476&r1=1298475&r2=1298476&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/realm/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/catalina/realm/LocalStrings.properties Thu Mar 8 17:22:29 2012 @@ -60,6 +60,11 @@ realmBase.hasRoleSuccess=Username {0} ha realmBase.authenticateFailure=Username {0} NOT successfully authenticated realmBase.authenticateSuccess=Username {0} successfully authenticated realmBase.gssNameFail=Failed to extract name from established GSSContext +realmBase.gotX509Username=Got user name from X509 certificate: {0} +realmBase.createUsernameRetriever.ClassCastException=Class {0} is not an X509UsernameRetriever. +realmBase.createUsernameRetriever.ClassNotFoundException=Cannot find class {0}. +realmBase.createUsernameRetriever.InstantiationException=Cannot create object of type {0}. +realmBase.createUsernameRetriever.IllegalAccessException=Cannot create object of type {0}. userDatabaseRealm.lookup=Exception looking up UserDatabase under key {0} userDatabaseRealm.noDatabase=No UserDatabase component found under key {0} dataSourceRealm.authenticateFailure=Username {0} NOT successfully authenticated Modified: tomcat/trunk/java/org/apache/catalina/realm/RealmBase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/realm/RealmBase.java?rev=1298476&r1=1298475&r2=1298476&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/realm/RealmBase.java (original) +++ tomcat/trunk/java/org/apache/catalina/realm/RealmBase.java Thu Mar 8 17:22:29 2012 @@ -136,6 +136,16 @@ public abstract class RealmBase extends */ protected boolean validate = true; + /** + * The name of the class to use for retrieving user names from X509 + * certificates. + */ + protected String x509UsernameRetrieverClassName; + + /** + * The object that will extract user names from X509 client certificates. + */ + protected X509UsernameRetriever x509UsernameRetriever; /** * The all role mode. @@ -266,6 +276,29 @@ public abstract class RealmBase extends } + /** + * Gets the name of the class that will be used to extract user names + * from X509 client certificates. + * @return The name of the class that will be used to extract user names + * from X509 client certificates. + */ + public String getX509UsernameRetrieverClassName() + { + return x509UsernameRetrieverClassName; + } + + /** + * Sets the name of the class that will be used to extract user names + * from X509 client certificates. The class must implement + * {@see X509UsernameRetriever}. + * + * @param className The name of the class that will be used to extract user names + * from X509 client certificates. + */ + public void setX509UsernameRetrieverClassName(String className) + { + this.x509UsernameRetrieverClassName = className; + } public boolean isStripRealmForGss() { return stripRealmForGss; @@ -1034,6 +1067,8 @@ public abstract class RealmBase extends if (container != null) { this.containerLog = container.getLogger(); } + + x509UsernameRetriever = createUsernameRetriever(x509UsernameRetrieverClassName); } /** @@ -1191,7 +1226,12 @@ public abstract class RealmBase extends * Return the Principal associated with the given certificate. */ protected Principal getPrincipal(X509Certificate usercert) { - return(getPrincipal(usercert.getSubjectDN().getName())); + String username = x509UsernameRetriever.getUsername(usercert); + + if(log.isDebugEnabled()) + log.debug(sm.getString("realmBase.gotX509Username", username)); + + return(getPrincipal(username)); } @@ -1391,4 +1431,23 @@ public abstract class RealmBase extends } } + private static X509UsernameRetriever createUsernameRetriever(String className) + throws LifecycleException { + if(null == className || "".equals(className.trim())) + return new X509SubjectDnRetriever(); + + try { + @SuppressWarnings("unchecked") + Class<? extends X509UsernameRetriever> clazz = (Class<? extends X509UsernameRetriever>)Class.forName(className); + return (X509UsernameRetriever)clazz.newInstance(); + } catch (ClassNotFoundException e) { + throw new LifecycleException(sm.getString("realmBase.createUsernameRetriever.ClassNotFoundException", className), e); + } catch (InstantiationException e) { + throw new LifecycleException(sm.getString("realmBase.createUsernameRetriever.InstantiationException", className), e); + } catch (IllegalAccessException e) { + throw new LifecycleException(sm.getString("realmBase.createUsernameRetriever.IllegalAccessException", className), e); + } catch (ClassCastException e) { + throw new LifecycleException(sm.getString("realmBase.createUsernameRetriever.ClassCastException", className), e); + } + } } Added: tomcat/trunk/java/org/apache/catalina/realm/X509SubjectDnRetriever.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/realm/X509SubjectDnRetriever.java?rev=1298476&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/catalina/realm/X509SubjectDnRetriever.java (added) +++ tomcat/trunk/java/org/apache/catalina/realm/X509SubjectDnRetriever.java Thu Mar 8 17:22:29 2012 @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.realm; + +import java.security.cert.X509Certificate; + +/** + * An X509UsernameRetriever that returns a certificate's entire + * SubjectDN as the username. + */ +public class X509SubjectDnRetriever + implements X509UsernameRetriever { + public String getUsername(X509Certificate clientCert) { + return clientCert.getSubjectDN().getName(); + } +} Added: tomcat/trunk/java/org/apache/catalina/realm/X509UsernameRetriever.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/realm/X509UsernameRetriever.java?rev=1298476&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/catalina/realm/X509UsernameRetriever.java (added) +++ tomcat/trunk/java/org/apache/catalina/realm/X509UsernameRetriever.java Thu Mar 8 17:22:29 2012 @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.realm; + +import java.security.cert.X509Certificate; + +/** + * Provides an interface for retrieving a user name from an X509Certificate. + */ +public interface X509UsernameRetriever { + /** + * Gets a user name from an X509Certificate. + * + * @param cert The certificate containing the user name. + * @return An appropriate user name obtained from one or more fields + * in the certificate. + */ + public String getUsername(X509Certificate clientCert); +} Modified: tomcat/trunk/webapps/docs/config/realm.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/realm.xml?rev=1298476&r1=1298475&r2=1298476&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/config/realm.xml (original) +++ tomcat/trunk/webapps/docs/config/realm.xml Thu Mar 8 17:22:29 2012 @@ -188,6 +188,14 @@ attributes.</p> </attribute> + <attribute name="X509UsernameRetrieverClassName" required="false"> + <p>When using X509 client certificates, this specifies the class name + that will be used to retrieve the user name from the certificate. + The class must implement the + <code>org.apache.catalina.realm.X509UsernameRetriever</code> + interface. The default is to use the certificate's SubjectDN + as the username.</p> + </attribute> </attributes> <p>See the <a href="../realm-howto.html">Container-Managed Security Guide</a> for more @@ -288,6 +296,14 @@ attributes.</p> </attribute> + <attribute name="X509UsernameRetrieverClassName" required="false"> + <p>When using X509 client certificates, this specifies the class name + that will be used to retrieve the user name from the certificate. + The class must implement the + <code>org.apache.catalina.realm.X509UsernameRetriever</code> + interface. The default is to use the certificate's SubjectDN + as the username.</p> + </attribute> </attributes> <p>See the <a href="../realm-howto.html#DataSourceRealm"> @@ -575,6 +591,14 @@ expression.</p> </attribute> + <attribute name="X509UsernameRetrieverClassName" required="false"> + <p>When using X509 client certificates, this specifies the class name + that will be used to retrieve the user name from the certificate. + The class must implement the + <code>org.apache.catalina.realm.X509UsernameRetriever</code> + interface. The default is to use the certificate's SubjectDN + as the username.</p> + </attribute> </attributes> <p>See the <a href="../realm-howto.html">Container-Managed Security Guide</a> for more @@ -611,6 +635,14 @@ and role information.</p> </attribute> + <attribute name="X509UsernameRetrieverClassName" required="false"> + <p>When using X509 client certificates, this specifies the class name + that will be used to retrieve the user name from the certificate. + The class must implement the + <code>org.apache.catalina.realm.X509UsernameRetriever</code> + interface. The default is to use the certificate's SubjectDN + as the username.</p> + </attribute> </attributes> <p>See the @@ -668,6 +700,14 @@ name. If not specified, the default is <code>true</code>.</p> </attribute> + <attribute name="X509UsernameRetrieverClassName" required="false"> + <p>When using X509 client certificates, this specifies the class name + that will be used to retrieve the user name from the certificate. + The class must implement the + <code>org.apache.catalina.realm.X509UsernameRetriever</code> + interface. The default is to use the certificate's SubjectDN + as the username.</p> + </attribute> </attributes> <p>The XML document referenced by the <code>pathname</code> attribute must @@ -765,6 +805,14 @@ <code>false</code>.</p> </attribute> + <attribute name="X509UsernameRetrieverClassName" required="false"> + <p>When using X509 client certificates, this specifies the class name + that will be used to retrieve the user name from the certificate. + The class must implement the + <code>org.apache.catalina.realm.X509UsernameRetriever</code> + interface. The default is to use the certificate's SubjectDN + as the username.</p> + </attribute> </attributes> <p>See the <a href="../realm-howto.html">Container-Managed Security --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org