Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[CVE-2020-1732] Adjust JASPIC integration to create a new ServerAuthModule for each request. #1

Merged
merged 1 commit into from
Feb 14, 2020
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 @@ -17,6 +17,7 @@
package org.glassfish.soteria.mechanisms.jaspic;

import java.util.Map;
import java.util.function.Supplier;

import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.message.AuthException;
Expand All @@ -30,23 +31,23 @@
/**
* This class functions as a kind of factory-factory for {@link ServerAuthConfig} instances, which are by themselves factories
* for {@link ServerAuthContext} instances, which are delegates for the actual {@link ServerAuthModule} (SAM) that we're after.
*
*
* @author Arjan Tijms
*/
public class DefaultAuthConfigProvider implements AuthConfigProvider {

private static final String CALLBACK_HANDLER_PROPERTY_NAME = "authconfigprovider.client.callbackhandler";

private Map<String, String> providerProperties;
private ServerAuthModule serverAuthModule;
private Supplier<ServerAuthModule> serverAuthModuleSupplier;

public DefaultAuthConfigProvider(ServerAuthModule serverAuthModule) {
this.serverAuthModule = serverAuthModule;
public DefaultAuthConfigProvider(Supplier<ServerAuthModule> serverAuthModuleSupplier) {
this.serverAuthModuleSupplier = serverAuthModuleSupplier;
}

/**
* Constructor with signature and implementation that's required by API.
*
*
* @param properties provider properties
* @param factory the auth config factory
*/
Expand All @@ -72,7 +73,7 @@ public DefaultAuthConfigProvider(Map<String, String> properties, AuthConfigFacto
public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler) throws AuthException,
SecurityException {
return new DefaultServerAuthConfig(layer, appContext, handler == null ? createDefaultCallbackHandler() : handler,
providerProperties, serverAuthModule);
providerProperties, serverAuthModuleSupplier);
}

@Override
Expand All @@ -89,7 +90,7 @@ public void refresh() {
* Creates a default callback handler via the system property "authconfigprovider.client.callbackhandler", as seemingly
* required by the API (API uses wording "may" create default handler). TODO: Isn't
* "authconfigprovider.client.callbackhandler" JBoss specific?
*
*
* @return
* @throws AuthException
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.glassfish.soteria.mechanisms.jaspic;

import java.util.Map;
import java.util.function.Supplier;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
Expand All @@ -29,7 +30,7 @@
/**
* This class functions as a kind of factory for {@link ServerAuthContext} instances, which are delegates for the actual
* {@link ServerAuthModule} (SAM) that we're after.
*
*
* @author Arjan Tijms
*/
public class DefaultServerAuthConfig implements ServerAuthConfig {
Expand All @@ -38,21 +39,21 @@ public class DefaultServerAuthConfig implements ServerAuthConfig {
private String appContext;
private CallbackHandler handler;
private Map<String, String> providerProperties;
private ServerAuthModule serverAuthModule;
private Supplier<ServerAuthModule> serverAuthModuleSupplier;

public DefaultServerAuthConfig(String layer, String appContext, CallbackHandler handler,
Map<String, String> providerProperties, ServerAuthModule serverAuthModule) {
Map<String, String> providerProperties, Supplier<ServerAuthModule> serverAuthModuleSupplier) {
this.layer = layer;
this.appContext = appContext;
this.handler = handler;
this.providerProperties = providerProperties;
this.serverAuthModule = serverAuthModule;
this.serverAuthModuleSupplier = serverAuthModuleSupplier;
}

@Override
public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject,
@SuppressWarnings("rawtypes") Map properties) throws AuthException {
return new DefaultServerAuthContext(handler, serverAuthModule);
return new DefaultServerAuthContext(handler, serverAuthModuleSupplier);
}

// ### The methods below mostly just return what has been passed into the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.glassfish.soteria.mechanisms.jaspic;

import java.util.Collections;
import java.util.function.Supplier;

import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
Expand All @@ -34,15 +35,15 @@
* <p>
* Since this simple example only has a single SAM, we delegate directly to that one. Note that this {@link ServerAuthContext}
* and the {@link ServerAuthModule} (SAM) share a common base interface: {@link ServerAuth}.
*
*
* @author Arjan Tijms
*/
public class DefaultServerAuthContext implements ServerAuthContext {

private final ServerAuthModule serverAuthModule;

public DefaultServerAuthContext(CallbackHandler handler, ServerAuthModule serverAuthModule) throws AuthException {
this.serverAuthModule = serverAuthModule;
public DefaultServerAuthContext(CallbackHandler handler, Supplier<ServerAuthModule> serverAuthModuleSupplier) throws AuthException {
this.serverAuthModule = serverAuthModuleSupplier.get();
serverAuthModule.initialize(null, null, handler, Collections.<String, String> emptyMap());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.security.PrivilegedAction;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;

import javax.security.enterprise.AuthenticationStatus;
import javax.security.auth.Subject;
Expand All @@ -43,42 +44,44 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.glassfish.soteria.cdi.spi.CDIPerRequestInitializer;

/**
* A set of utility methods for using the JASPIC API
*
*
* @author Arjan Tijms
*
*/
public final class Jaspic {

public static final String IS_AUTHENTICATION = "org.glassfish.soteria.security.message.request.authentication";
public static final String IS_AUTHENTICATION_FROM_FILTER = "org.glassfish.soteria.security.message.request.authenticationFromFilter";
public static final String IS_SECURE_RESPONSE = "org.glassfish.soteria.security.message.request.secureResponse";
public static final String IS_REFRESH = "org.glassfish.soteria.security.message.request.isRefresh";
public static final String DID_AUTHENTICATION = "org.glassfish.soteria.security.message.request.didAuthentication";

public static final String AUTH_PARAMS = "org.glassfish.soteria.security.message.request.authParams";

public static final String LOGGEDIN_USERNAME = "org.glassfish.soteria.security.message.loggedin.username";
public static final String LOGGEDIN_ROLES = "org.glassfish.soteria.security.message.loggedin.roles";
public static final String LAST_AUTH_STATUS = "org.glassfish.soteria.security.message.authStatus";

public static final String CONTEXT_REGISTRATION_ID = "org.glassfish.soteria.security.message.registrationId";

// Key in the MessageInfo Map that when present AND set to true indicated a protected resource is being accessed.
// When the resource is not protected, GlassFish omits the key altogether. WebSphere does insert the key and sets
// it to false.
private static final String IS_MANDATORY = "javax.security.auth.message.MessagePolicy.isMandatory";
private static final String REGISTER_SESSION = "javax.servlet.http.registerSession";

private Jaspic() {}

public static boolean authenticate(HttpServletRequest request, HttpServletResponse response, AuthenticationParameters authParameters) {
try {
// JASPIC 1.1 does not have any way to distinguish between a
// SAM called at start of a request or following request#authenticate.
// See https://java.net/jira/browse/JASPIC_SPEC-5

// We now add this as a request attribute instead, but should better
// be the MessageInfo map
request.setAttribute(IS_AUTHENTICATION, true);
Expand All @@ -101,10 +104,10 @@ public static AuthenticationParameters getAuthParameters(HttpServletRequest requ
if (authParameters == null) {
authParameters = new AuthenticationParameters();
}

return authParameters;
}

public static void logout(HttpServletRequest request, HttpServletResponse response) {
try {
request.logout();
Expand All @@ -131,21 +134,21 @@ public Void run() {
public static boolean isRegisterSession(MessageInfo messageInfo) {
return Boolean.valueOf((String)messageInfo.getMap().get(REGISTER_SESSION));
}

public static boolean isProtectedResource(MessageInfo messageInfo) {
return Boolean.valueOf((String) messageInfo.getMap().get(IS_MANDATORY));
}

@SuppressWarnings("unchecked")
public static void setRegisterSession(MessageInfo messageInfo, String username, Set<String> roles) {
messageInfo.getMap().put("javax.servlet.http.registerSession", TRUE.toString());

HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
request.setAttribute(LOGGEDIN_USERNAME, username);
// TODO: check for existing roles and add
request.setAttribute(LOGGEDIN_ROLES, roles);
}

public static boolean isAuthenticationRequest(HttpServletRequest request) {
return TRUE.equals(request.getAttribute(IS_AUTHENTICATION));
}
Expand Down Expand Up @@ -202,70 +205,70 @@ public static AuthStatus fromAuthenticationStatus(AuthenticationStatus authentic
throw new IllegalStateException("Unhandled status:" + authenticationStatus.name());
}
}

/**
* Should be called when the callback handler is used with the intention that an actual
* user is going to be authenticated (as opposed to using the handler for the "do nothing" protocol
* which uses the unauthenticated identity).
*
*
* @param request The involved HTTP servlet request.
*
*
*/
public static void setDidAuthentication(HttpServletRequest request) {
request.setAttribute(DID_AUTHENTICATION, TRUE);
}

/**
* Gets the app context ID from the servlet context.
*
*
* <p>
* The app context ID is the ID that JASPIC associates with the given application.
* In this case that given application is the web application corresponding to the
* ServletContext.
*
*
* @param context the servlet context for which to obtain the JASPIC app context ID
* @return the app context ID for the web application corresponding to the given context
*/
public static String getAppContextID(ServletContext context) {
return context.getVirtualServerName() + " " + context.getContextPath();
}

/**
* Registers a server auth module as the one and only module for the application corresponding to
* the given servlet context.
*
*
* <p>
* This will override any other modules that have already been registered, either via proprietary
* means or using the standard API.
*
* @param serverAuthModule the server auth module to be registered
*
* @param cdiPerRequestInitializer A {@link CDIPerRequestInitializer} to pass to the {@link ServerAuthModule}
* @param servletContext the context of the app for which the module is registered
* @return A String identifier assigned by an underlying factory corresponding to an underlying factory-factory-factory registration
*/
public static String registerServerAuthModule(ServerAuthModule serverAuthModule, ServletContext servletContext) {
public static String registerServerAuthModule(CDIPerRequestInitializer cdiPerRequestInitializer, ServletContext servletContext) {

// Register the factory-factory-factory for the SAM
String registrationId = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return AuthConfigFactory.getFactory().registerConfigProvider(
new DefaultAuthConfigProvider(serverAuthModule),
"HttpServlet",
getAppContextID(servletContext),
new DefaultAuthConfigProvider(() -> new HttpBridgeServerAuthModule(cdiPerRequestInitializer)),
"HttpServlet",
getAppContextID(servletContext),
"Default single SAM authentication config provider");
}
});

// Remember the registration ID returned by the factory, so we can unregister the JASPIC module when the web module
// is undeployed. JASPIC being the low level API that it is won't do this automatically.
servletContext.setAttribute(CONTEXT_REGISTRATION_ID, registrationId);

return registrationId;
}

/**
* Deregisters the server auth module (and encompassing wrappers/factories) that was previously registered via a call
* to registerServerAuthModule.
*
*
* @param servletContext the context of the app for which the module is deregistered
*/
public static void deregisterServerAuthModule(ServletContext servletContext) {
Expand All @@ -278,6 +281,6 @@ public Boolean run() {
});
}
}


}
Loading