Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Issue #5933 ClientCertAuthenticator is not using SslContextFactory from server #5934

Merged
merged 1 commit into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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