Index: java/org/apache/catalina/authenticator/AuthenticatorBase.java
===================================================================
--- java/org/apache/catalina/authenticator/AuthenticatorBase.java (revision 1616257)
+++ java/org/apache/catalina/authenticator/AuthenticatorBase.java (working copy)
@@ -19,17 +19,6 @@
package org.apache.catalina.authenticator;
-import java.io.IOException;
-import java.security.Principal;
-import java.security.cert.X509Certificate;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletResponse;
-
import org.apache.catalina.Authenticator;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
@@ -47,11 +36,22 @@
import org.apache.catalina.util.DateTool;
import org.apache.catalina.util.SessionIdGenerator;
import org.apache.catalina.valves.ValveBase;
+import org.apache.coyote.ActionCode;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.res.StringManager;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
/**
* Basic implementation of the Valve interface that enforces the
* <security-constraint>
elements in the web application
@@ -561,8 +561,7 @@
}
if (!authRequired && context.getPreemptiveAuthentication()) {
- X509Certificate[] certs = (X509Certificate[]) request.getAttribute(
- Globals.CERTIFICATES_ATTR);
+ final X509Certificate[] certs = getRequestCertificates(request);
authRequired = certs != null && certs.length > 0;
}
@@ -614,7 +613,34 @@
// ------------------------------------------------------ Protected Methods
+ /**
+ * Look for the X509 certificate chain in the Request under the key
+ * javax.servlet.request.X509Certificate
.
+ *
+ *
null
otherwise.
+ */
+ protected X509Certificate[] getRequestCertificates(final Request request) {
+ X509Certificate certs[] = (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR);
+ if ((certs == null) || (certs.length < 1)) {
+ try {
+ request.getCoyoteRequest().action(ActionCode.REQ_SSL_CERTIFICATE, null);
+
+ } catch (IllegalStateException ise) {
+ // Request body was too large for save buffer
+ return null;
+ }
+ certs = (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR);
+ }
+ return certs;
+ }
+
/**
* Associate the specified single sign on identifier with the
* specified Session.
Index: java/org/apache/catalina/authenticator/SSLAuthenticator.java
===================================================================
--- java/org/apache/catalina/authenticator/SSLAuthenticator.java (revision 1616257)
+++ java/org/apache/catalina/authenticator/SSLAuthenticator.java (working copy)
@@ -19,20 +19,17 @@
package org.apache.catalina.authenticator;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.deploy.LoginConfig;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Principal;
import java.security.cert.X509Certificate;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.apache.catalina.Globals;
-import org.apache.catalina.connector.Request;
-import org.apache.catalina.deploy.LoginConfig;
-import org.apache.coyote.ActionCode;
-
-
/**
* An Authenticator and Valve implementation of authentication
* that utilizes SSL certificates to identify client users.
@@ -129,22 +126,8 @@
if (containerLog.isDebugEnabled())
containerLog.debug(" Looking up certificates");
- X509Certificate certs[] = (X509Certificate[])
- request.getAttribute(Globals.CERTIFICATES_ATTR);
+ final X509Certificate certs[] = getRequestCertificates(request);
if ((certs == null) || (certs.length < 1)) {
- try {
- request.getCoyoteRequest().action
- (ActionCode.REQ_SSL_CERTIFICATE, null);
- } catch (IllegalStateException ise) {
- // Request body was too large for save buffer
- response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
- sm.getString("authenticator.certificates"));
- return false;
- }
- certs = (X509Certificate[])
- request.getAttribute(Globals.CERTIFICATES_ATTR);
- }
- if ((certs == null) || (certs.length < 1)) {
if (containerLog.isDebugEnabled())
containerLog.debug(" No certificates included with this request");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
@@ -169,7 +152,6 @@
}
-
@Override
protected String getAuthMethod() {
return HttpServletRequest.CLIENT_CERT_AUTH;
Index: test/org/apache/tomcat/util/net/TestClientCert.java
===================================================================
--- test/org/apache/tomcat/util/net/TestClientCert.java (revision 1616257)
+++ test/org/apache/tomcat/util/net/TestClientCert.java (working copy)
@@ -16,17 +16,26 @@
*/
package org.apache.tomcat.util.net;
+import org.apache.catalina.Context;
+import org.apache.catalina.authenticator.AuthenticatorBase;
+import org.apache.catalina.authenticator.SSLAuthenticator;
+import org.apache.catalina.startup.TestTomcat;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.ByteChunk;
+import org.junit.Assume;
+import org.junit.Test;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
import java.util.Arrays;
import static org.junit.Assert.assertEquals;
-import org.junit.Assume;
-import org.junit.Test;
-
-import org.apache.catalina.startup.Tomcat;
-import org.apache.catalina.startup.TomcatBaseTest;
-import org.apache.tomcat.util.buf.ByteChunk;
-
/**
* The keys and certificates used in this file are all available in svn and were
* generated using a test CA the files for which are in the Tomcat PMC private
@@ -34,12 +43,34 @@
*/
public class TestClientCert extends TomcatBaseTest {
+ private static final String HTTPS_PREFIX = "https://localhost:";
+ private static final String CONTEXT_PATH_SSL = "/ssl";
+ private static final String URI_UNPROTECTED = "/unprotected";
+ public static final String USERNAME = "CN=user1, C=US";
+ public static final String TESTROLE = "testrole";
+
+ private static final int SHORT_SESSION_TIMEOUT_MINS = 1;
+
+ private Tomcat tomcat;
+ private Context sslContext;
+
@Test
- public void testClientCertGet() throws Exception {
+ public void testPreemptiveClientCert() throws Exception {
Assume.assumeTrue("SSL renegotiation has to be supported for this test",
TesterSupport.isRenegotiationSupported(getTomcatInstance()));
// Unprotected resource
+ ByteChunk res = getUrl(HTTPS_PREFIX + getPort() + CONTEXT_PATH_SSL + URI_UNPROTECTED);
+ assertEquals("OK", res.toString());
+
+ }
+
+ @Test
+ public void testBug56825() throws Exception {
+ Assume.assumeTrue("SSL renegotiation has to be supported for this test",
+ TesterSupport.isRenegotiationSupported(getTomcatInstance()));
+
+ // Unprotected resource
ByteChunk res =
getUrl("https://localhost:" + getPort() + "/unprotected");
assertEquals("OK", res.toString());
@@ -103,13 +134,46 @@
super.setUp();
- Tomcat tomcat = getTomcatInstance();
+ tomcat = getTomcatInstance();
TesterSupport.configureClientCertContext(tomcat);
+ // add an SSL webapp with the SSLAuthenticator valve
+ setupSslContext();
+
// Start Tomcat
tomcat.start();
TesterSupport.configureClientSsl();
}
+
+ private void setupSslContext() throws Exception {
+ // Must have a real docBase for webapps - just use temp
+ sslContext = tomcat.addContext(CONTEXT_PATH_SSL, System.getProperty("java.io.tmpdir"));
+ sslContext.setSessionTimeout(SHORT_SESSION_TIMEOUT_MINS);
+ sslContext.setPreemptiveAuthentication(true);
+
+ // Add a servlet - the preemptive mode ensure the authentication will be done if a certificate
+ // is available whatever the resource accessed is private or public
+ // we don't need any servlet security configured here for this test
+ Tomcat.addServlet(sslContext, "TesterServlet", new HttpServlet() {
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ // if the user has not been authenticated then it's going to fail
+ resp.setContentType("text/plain");
+ PrintWriter out = resp.getWriter();
+ out.print(req.isUserInRole(TESTROLE) ? "OK" : "KO");
+ }
+ });
+ sslContext.addServletMapping(URI_UNPROTECTED, "TesterServlet");
+
+ // Configure the Realm at context level
+ TestTomcat.MapRealm realm = new TestTomcat.MapRealm();
+ realm.addUser(USERNAME, "not used for SSL");
+ realm.addUserRole(USERNAME, TESTROLE);
+ sslContext.setRealm(realm);
+
+ AuthenticatorBase basicAuthenticator = new SSLAuthenticator();
+ sslContext.getPipeline().addValve(basicAuthenticator);
+ }
}