craigmcc 01/07/30 13:04:05 Modified: catalina/src/share/org/apache/catalina Realm.java catalina/src/share/org/apache/catalina/authenticator LocalStrings.properties SSLAuthenticator.java catalina/src/share/org/apache/catalina/realm RealmBase.java Log: Realm: Add a new authenticate() method signature that takes a client certificate chain (presumably provided when the CLIENT-CERT login method is selected. RealmBase: Provide a default implementation of the new authenticate() signature that does the following: * (Optionally, but default=true) Ask the certificate chain to check validity on each included certificate. * Call the getPrincipal() method of the actual implementation class to return a Principal based on the username of the first certificate in the chain (i.e. the client itself). As a side effect of this change, role lookups for CLIENT-CERT authenticated principals will now work the same as for BASIC, DIGEST, and FORM based authentications. SSLAuthenticator: Use the new Realm.authenticate() signature in a manner similar to that used by the other Authenticator implementations. This is a partial solution to BugTraq #4485977. Revision Changes Path 1.4 +15 -4 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/Realm.java Index: Realm.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/Realm.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- Realm.java 2001/07/22 20:13:30 1.3 +++ Realm.java 2001/07/30 20:04:04 1.4 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/Realm.java,v 1.3 2001/07/22 20:13:30 pier Exp $ - * $Revision: 1.3 $ - * $Date: 2001/07/22 20:13:30 $ + * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/Realm.java,v 1.4 2001/07/30 20:04:04 craigmcc Exp $ + * $Revision: 1.4 $ + * $Date: 2001/07/30 20:04:04 $ * * ==================================================================== * @@ -67,6 +67,7 @@ import java.beans.PropertyChangeListener; import java.security.Principal; +import java.security.cert.X509Certificate; /** @@ -77,7 +78,7 @@ * Container. * * @author Craig R. McClanahan - * @version $Revision: 1.3 $ $Date: 2001/07/22 20:13:30 $ + * @version $Revision: 1.4 $ $Date: 2001/07/30 20:04:04 $ */ public interface Realm { @@ -158,6 +159,16 @@ String nonce, String nc, String cnonce, String qop, String realm, String md5a2); + + + /** + * Return the Principal associated with the specified chain of X509 + * client certificates. If there is none, return <code>null</code>. + * + * @param certs Array of client certificates, with the first one in + * the array being the certificate of the client itself. + */ + public Principal authenticate(X509Certificate certs[]); /** 1.3 +1 -0 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/authenticator/LocalStrings.properties Index: LocalStrings.properties =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/authenticator/LocalStrings.properties,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- LocalStrings.properties 2000/09/12 00:10:09 1.2 +++ LocalStrings.properties 2001/07/30 20:04:04 1.3 @@ -7,4 +7,5 @@ authenticator.notAuthenticated=Configuration error: Cannot perform access control without an authenticated principal authenticator.notContext=Configuration error: Must be attached to a Context authenticator.notStarted=Security Interceptor has not yet been started +authenticator.unauthorized=Cannot authenticate with the provided credentials authenticator.userDataConstraint=This request violates a User Data constraint for this application 1.8 +19 -23 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/authenticator/SSLAuthenticator.java Index: SSLAuthenticator.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/authenticator/SSLAuthenticator.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- SSLAuthenticator.java 2001/07/22 20:09:19 1.7 +++ SSLAuthenticator.java 2001/07/30 20:04:04 1.8 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/authenticator/SSLAuthenticator.java,v 1.7 2001/07/22 20:09:19 pier Exp $ - * $Revision: 1.7 $ - * $Date: 2001/07/22 20:09:19 $ + * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/authenticator/SSLAuthenticator.java,v 1.8 2001/07/30 20:04:04 craigmcc Exp $ + * $Revision: 1.8 $ + * $Date: 2001/07/30 20:04:04 $ * * ==================================================================== * @@ -86,7 +86,7 @@ * that utilizes SSL certificates to identify client users. * * @author Craig R. McClanahan - * @version $Revision: 1.7 $ $Date: 2001/07/22 20:09:19 $ + * @version $Revision: 1.8 $ $Date: 2001/07/30 20:04:04 $ */ public final class SSLAuthenticator @@ -137,10 +137,15 @@ // Have we already authenticated someone? Principal principal = ((HttpServletRequest) request.getRequest()).getUserPrincipal(); - if (principal != null) + if (principal != null) { + if (debug >= 1) + log("Already authenticated '" + principal.getName() + "'"); return (true); + } // Retrieve the certificate chain for this client + HttpServletResponse hres = + (HttpServletResponse) response.getResponse(); if (debug >= 1) log(" Looking up certificates"); X509Certificate certs[] = (X509Certificate[]) @@ -148,28 +153,19 @@ if ((certs == null) || (certs.length < 1)) { if (debug >= 1) log(" No certificates included with this request"); - ((HttpServletResponse) response.getResponse()). - sendError(HttpServletResponse.SC_BAD_REQUEST, - sm.getString("authenticator.certificates")); + hres.sendError(HttpServletResponse.SC_BAD_REQUEST, + sm.getString("authenticator.certificates")); return (false); } - principal = certs[0].getSubjectDN(); - // Check the validity of each certificate in the chain - for (int i = 0; i < certs.length; i++) { + // Authenticate the specified certificate chain + principal = context.getRealm().authenticate(certs); + if (principal == null) { if (debug >= 1) - log(" Checking validity for '" + - certs[i].getSubjectDN().getName() + "'"); - try { - certs[i].checkValidity(); - } catch (Exception e) { - if (debug >= 1) - log(" Validity exception", e); - ((HttpServletResponse) response.getResponse()). - sendError(HttpServletResponse.SC_FORBIDDEN, - sm.getString("authenticator.invalid")); - return (false); - } + log(" Realm.authenticate() returned false"); + hres.sendError(HttpServletResponse.SC_UNAUTHORIZED, + sm.getString("authenticator.unauthorized")); + return (false); } // Cache the principal (if requested) and record this authentication 1.5 +69 -4 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/realm/RealmBase.java Index: RealmBase.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/realm/RealmBase.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- RealmBase.java 2001/07/22 20:25:11 1.4 +++ RealmBase.java 2001/07/30 20:04:04 1.5 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/realm/RealmBase.java,v 1.4 2001/07/22 20:25:11 pier Exp $ - * $Revision: 1.4 $ - * $Date: 2001/07/22 20:25:11 $ + * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/realm/RealmBase.java,v 1.5 2001/07/30 20:04:04 craigmcc Exp $ + * $Revision: 1.5 $ + * $Date: 2001/07/30 20:04:04 $ * * ==================================================================== * @@ -70,6 +70,7 @@ import java.security.Principal; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; import java.io.File; import org.apache.catalina.Container; import org.apache.catalina.Lifecycle; @@ -94,7 +95,7 @@ * location) are identical to those currently supported by Tomcat 3.X. * * @author Craig R. McClanahan - * @version $Revision: 1.4 $ $Date: 2001/07/22 20:25:11 $ + * @version $Revision: 1.5 $ $Date: 2001/07/30 20:04:04 $ */ public abstract class RealmBase @@ -175,6 +176,12 @@ protected PropertyChangeSupport support = new PropertyChangeSupport(this); + /** + * Should we validate client certificate chains when they are presented? + */ + protected boolean validate = true; + + // ------------------------------------------------------------- Properties @@ -257,6 +264,28 @@ } + /** + * Return the "validate certificate chains" flag. + */ + public boolean getValidate() { + + return (this.validate); + + } + + + /** + * Set the "validate certificate chains" flag. + * + * @param validate The new validate certificate chains flag + */ + public void setValidate(boolean validate) { + + this.validate = validate; + + } + + // --------------------------------------------------------- Public Methods @@ -356,6 +385,42 @@ return null; } + + + /** + * Return the Principal associated with the specified chain of X509 + * client certificates. If there is none, return <code>null</code>. + * + * @param certs Array of client certificates, with the first one in + * the array being the certificate of the client itself. + */ + 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); + } + } + } + + // Check the existence of the client Principal in our database + return (getPrincipal(certs[0].getSubjectDN().getName())); + + } /**