Skip to content

Commit

Permalink
Issue #5933 ClientCertAuthenticator is not using SslContextFactory (#…
Browse files Browse the repository at this point in the history
…5934)

Added SslClientCertAuthenticator
Co-authored-by: olivier lamy <oliver.lamy@gmail.com>
Signed-off-by: Greg Wilkins <gregw@webtide.com>

Co-authored-by: gregw <gregw@webtide.com>
  • Loading branch information
olamy and gregw authored Feb 10, 2021
1 parent 2549df9 commit 68790d8
Show file tree
Hide file tree
Showing 9 changed files with 414 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

package org.eclipse.jetty.security;

import java.util.Collection;
import javax.servlet.ServletContext;

import org.eclipse.jetty.security.Authenticator.AuthConfiguration;
Expand All @@ -21,8 +22,12 @@
import org.eclipse.jetty.security.authentication.ConfigurableSpnegoAuthenticator;
import org.eclipse.jetty.security.authentication.DigestAuthenticator;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.security.authentication.SslClientCertAuthenticator;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The Default Authenticator Factory.
Expand All @@ -31,6 +36,7 @@
* <li>{@link org.eclipse.jetty.security.authentication.DigestAuthenticator}</li>
* <li>{@link org.eclipse.jetty.security.authentication.FormAuthenticator}</li>
* <li>{@link org.eclipse.jetty.security.authentication.ClientCertAuthenticator}</li>
* <li>{@link SslClientCertAuthenticator}</li>
* </ul>
* All authenticators derived from {@link org.eclipse.jetty.security.authentication.LoginAuthenticator} are
* wrapped with a {@link org.eclipse.jetty.security.authentication.DeferredAuthentication}
Expand All @@ -45,6 +51,9 @@
*/
public class DefaultAuthenticatorFactory implements Authenticator.Factory
{

private static final Logger LOG = LoggerFactory.getLogger(DefaultAuthenticatorFactory.class);

LoginService _loginService;

@Override
Expand All @@ -64,7 +73,25 @@ else if (Constraint.__SPNEGO_AUTH.equalsIgnoreCase(auth))
else if (Constraint.__NEGOTIATE_AUTH.equalsIgnoreCase(auth)) // see Bug #377076
authenticator = new ConfigurableSpnegoAuthenticator(Constraint.__NEGOTIATE_AUTH);
if (Constraint.__CERT_AUTH.equalsIgnoreCase(auth) || Constraint.__CERT_AUTH2.equalsIgnoreCase(auth))
authenticator = new ClientCertAuthenticator();
{
Collection<SslContextFactory> sslContextFactories = server.getBeans(SslContextFactory.class);
if (sslContextFactories.size() != 1)
{
if (sslContextFactories.size() > 1)
{
LOG.info("Multiple SslContextFactory instances discovered. Directly configure a SslClientCertAuthenticator to use one.");
}
else
{
LOG.debug("No SslContextFactory instances discovered. Directly configure a SslClientCertAuthenticator to use one.");
}
authenticator = new ClientCertAuthenticator();
}
else
{
authenticator = new SslClientCertAuthenticator(sslContextFactories.iterator().next());
}
}

return authenticator;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.security.Password;

@Deprecated
/**
* @deprecated Prefer using {@link SslClientCertAuthenticator}
*/
public class ClientCertAuthenticator extends LoginAuthenticator
{
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//
// ========================================================================
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.security.authentication;

import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Objects;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.security.ServerAuthException;
import org.eclipse.jetty.security.UserAuthentication;
import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.Authentication.User;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.ssl.SslContextFactory;

/**
* CLIENT-CERT authenticator.
*
* <p>This {@link org.eclipse.jetty.security.Authenticator} implements client certificate authentication.
* The client certificates available in the request will be verified against the configured {@link SslContextFactory} instance
* </p>
*/
public class SslClientCertAuthenticator
extends LoginAuthenticator
{

/**
* Set to true if SSL certificate validation is not required
* per default it's true as this is the goal of this implementation
*/
private boolean validateCerts = true;

private SslContextFactory sslContextFactory;

public SslClientCertAuthenticator(SslContextFactory sslContextFactory)
{
super();
Objects.nonNull(sslContextFactory);
this.sslContextFactory = sslContextFactory;
}

@Override
public String getAuthMethod()
{
return Constraint.__CERT_AUTH;
}

@Override
public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
{
if (!mandatory)
return new DeferredAuthentication(this);

HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
X509Certificate[] certs = (X509Certificate[])request.getAttribute("javax.servlet.request.X509Certificate");

try
{
// Need certificates.
if (certs != null && certs.length > 0)
{

if (validateCerts)
{
sslContextFactory.validateCerts(certs);
}

for (X509Certificate cert : certs)
{
if (cert == null)
continue;

Principal principal = cert.getSubjectDN();
if (principal == null)
principal = cert.getIssuerDN();
final String username = principal == null ? "clientcert" : principal.getName();

UserIdentity user = login(username, "", req);
if (user != null)
{
return new UserAuthentication(getAuthMethod(), user);
}
// try with null password
user = login(username, null, req);
if (user != null)
{
return new UserAuthentication(getAuthMethod(), user);
}
// try with certs sig against login service as previous behaviour
final char[] credential = Base64.getEncoder().encodeToString(cert.getSignature()).toCharArray();
user = login(username, credential, req);
if (user != null)
{
return new UserAuthentication(getAuthMethod(), user);
}
}
}

if (!DeferredAuthentication.isDeferred(response))
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return Authentication.SEND_FAILURE;
}

return Authentication.UNAUTHENTICATED;
}
catch (Exception e)
{
throw new ServerAuthException(e.getMessage());
}
}

@Override
public boolean secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, User validatedUser) throws ServerAuthException
{
return true;
}

/**
* @return true if SSL certificate has to be validated
*/
public boolean isValidateCerts()
{
return validateCerts;
}

/**
* @param validateCerts true if SSL certificates have to be validated
*/
public void setValidateCerts(boolean validateCerts)
{
validateCerts = validateCerts;
}

}
Loading

0 comments on commit 68790d8

Please sign in to comment.