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

[ELY-2359] Support HTTP Digest when fronted by load balancer #1909

Open
wants to merge 1 commit into
base: 2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -50,7 +50,7 @@ public final class MechanismConfiguration {
private final Map<String, MechanismRealmConfiguration> mechanismRealms;
private final CredentialSource serverCredentialSource;

MechanismConfiguration(final Function<Principal, Principal> preRealmRewriter, final Function<Principal, Principal> postRealmRewriter, final Function<Principal, Principal> finalRewriter, final RealmMapper realmMapper, final Collection<MechanismRealmConfiguration> mechanismRealms, final CredentialSource serverCredentialSource) {
MechanismConfiguration(final Function<Principal, Principal> preRealmRewriter, final Function<Principal, Principal> postRealmRewriter, final Function<Principal, Principal> finalRewriter, final RealmMapper realmMapper, final Collection<MechanismRealmConfiguration> mechanismRealms, final CredentialSource serverCredentialSource, final boolean sessionBasedNonceManager) {
checkNotNullParam("mechanismRealms", mechanismRealms);
this.preRealmRewriter = preRealmRewriter;
this.postRealmRewriter = postRealmRewriter;
Expand Down Expand Up @@ -167,6 +167,7 @@ public static final class Builder {
private RealmMapper realmMapper;
private List<MechanismRealmConfiguration> mechanismRealms;
private CredentialSource serverCredentialSource = CredentialSource.NONE;
private boolean useSessionBasedNonceManager = false;

/**
* Construct a new instance.
Expand Down Expand Up @@ -271,6 +272,12 @@ public Builder setServerCredentialSource(final CredentialSource serverCredential
return this;
}

public Builder setUseSessionBasedNonceManager(final Boolean useSessionBasedNonceManager) {
checkNotNullParam("useSessionBasedNonceManager", useSessionBasedNonceManager);
this.useSessionBasedNonceManager = useSessionBasedNonceManager;
return this;
}

/**
* Build a new instance. If no mechanism realms are offered, an empty collection should be provided for
* {@code mechanismRealms}; otherwise, if the mechanism only supports one realm, the first will be used. If the
Expand All @@ -285,12 +292,12 @@ public MechanismConfiguration build() {
} else {
mechanismRealms = unmodifiableList(asList(mechanismRealms.toArray(NO_REALM_CONFIGS)));
}
return new MechanismConfiguration(preRealmRewriter, postRealmRewriter, finalRewriter, realmMapper, mechanismRealms, serverCredentialSource);
return new MechanismConfiguration(preRealmRewriter, postRealmRewriter, finalRewriter, realmMapper, mechanismRealms, serverCredentialSource, useSessionBasedNonceManager);
}
}

/**
* An empty mechanism configuration..
*/
public static final MechanismConfiguration EMPTY = new MechanismConfiguration(Function.identity(), Function.identity(), Function.identity(), null, emptyList(), CredentialSource.NONE);
public static final MechanismConfiguration EMPTY = new MechanismConfiguration(Function.identity(), Function.identity(), Function.identity(), null, emptyList(), CredentialSource.NONE, false);
}
Expand Up @@ -51,6 +51,7 @@ private HttpConstants() {

public static final String CONFIG_VALIDATE_DIGEST_URI = CONFIG_BASE + ".validate-digest-uri";
public static final String CONFIG_SKIP_CERTIFICATE_VERIFICATION = CONFIG_BASE + ".skip-certificate-verification";
public static final String CONFIG_SESSION_BASED_DIGEST_NONCE_MANAGER = CONFIG_BASE + ".use-session-based-digest-nonce-manager";

/**
* The context relative path of the login page.
Expand Down
Expand Up @@ -67,6 +67,7 @@
import org.wildfly.security.http.HttpServerMechanismsResponder;
import org.wildfly.security.http.HttpServerRequest;
import org.wildfly.security.http.HttpServerResponse;
import org.wildfly.security.http.Scope;
import org.wildfly.security.mechanism.AuthenticationMechanismException;
import org.wildfly.security.mechanism.digest.DigestQuote;
import org.wildfly.security.mechanism.digest.PasswordDigestObtainer;
Expand All @@ -82,10 +83,11 @@ final class DigestAuthenticationMechanism implements HttpServerAuthenticationMec
private static final String CHALLENGE_PREFIX = "Digest ";
private static final String OPAQUE_VALUE = "00000000000000000000000000000000";
private static final byte COLON = ':';
public static final String PERSISTENT_NONCE_MANAGER = "persistentNonceManager";

private final Supplier<Provider[]> providers;
private final CallbackHandler callbackHandler;
private final NonceManager nonceManager;
private NonceManager nonceManager;
private final String configuredRealm;
private final String domain;
private final String mechanismName;
Expand Down Expand Up @@ -116,6 +118,22 @@ public String getMechanismName() {

@Override
public void evaluateRequest(final HttpServerRequest request) throws HttpAuthenticationException {

// TODO can we pass HttpServerRequest to the NonceManager so it interacts directly
if (nonceManager instanceof PersistentNonceManager) {
if (request.getScope(Scope.SESSION) == null || !request.getScope(Scope.SESSION).exists()) {
request.getScope(Scope.SESSION).create();
}
PersistentNonceManager persistentNonceManager = (PersistentNonceManager) request.getScope(Scope.SESSION).getAttachment(PERSISTENT_NONCE_MANAGER);
if (persistentNonceManager != null) {
// executor is transient, so it will be always be null
if (persistentNonceManager.getExecutor() == null) {
persistentNonceManager.setDefaultExecutor();
}
nonceManager = persistentNonceManager;
}
}

List<String> authorizationValues = request.getRequestHeaderValues(AUTHORIZATION);

if (authorizationValues != null) {
Expand All @@ -128,14 +146,13 @@ public void evaluateRequest(final HttpServerRequest request) throws HttpAuthenti
return;
} catch (AuthenticationMechanismException e) {
httpDigest.trace("Failed to parse or validate the response", e);
request.badRequest(e.toHttpAuthenticationException(), response -> prepareResponse(selectRealm(), response, false));
request.badRequest(e.toHttpAuthenticationException(), response -> prepareResponse(selectRealm(), response, false, request));
return;
}
}
}
}

request.noAuthenticationInProgress(response -> prepareResponse(selectRealm(), response, false));
request.noAuthenticationInProgress(response -> prepareResponse(selectRealm(), response, false, request));
}

private void validateResponse(HashMap<String, byte[]> responseTokens, final HttpServerRequest request) throws AuthenticationMechanismException, HttpAuthenticationException {
Expand Down Expand Up @@ -213,7 +230,7 @@ private void validateResponse(HashMap<String, byte[]> responseTokens, final Http
if (username.length() == 0) {
httpDigest.trace("Failed: no username");
fail();
request.authenticationFailed(httpDigest.authenticationFailed(), httpResponse -> prepareResponse(selectedRealm, httpResponse, false));
request.authenticationFailed(httpDigest.authenticationFailed(), httpResponse -> prepareResponse(selectedRealm, httpResponse, false, request));
return;
}

Expand All @@ -222,7 +239,7 @@ private void validateResponse(HashMap<String, byte[]> responseTokens, final Http
if (hA1 == null) {
httpDigest.trace("Failed: unable to get expected proof");
fail();
request.authenticationFailed(httpDigest.authenticationFailed(), httpResponse -> prepareResponse(selectedRealm, httpResponse, false));
request.authenticationFailed(httpDigest.authenticationFailed(), httpResponse -> prepareResponse(selectedRealm, httpResponse, false, request));
return;
}

Expand All @@ -231,13 +248,13 @@ private void validateResponse(HashMap<String, byte[]> responseTokens, final Http
if (MessageDigest.isEqual(response, calculatedResponse) == false) {
httpDigest.trace("Failed: invalid proof");
fail();
request.authenticationFailed(httpDigest.mechResponseTokenMismatch(getMechanismName()), httpResponse -> prepareResponse(selectedRealm, httpResponse, false));
request.authenticationFailed(httpDigest.mechResponseTokenMismatch(getMechanismName()), httpResponse -> prepareResponse(selectedRealm, httpResponse, false, request));
return;
}

if (nonceValid == false) {
httpDigest.trace("Failed: invalid nonce");
request.authenticationInProgress(httpResponse -> prepareResponse(selectedRealm, httpResponse, true));
request.authenticationInProgress(httpResponse -> prepareResponse(selectedRealm, httpResponse, true, request));
return;
}

Expand Down Expand Up @@ -390,7 +407,7 @@ private String[] getAvailableRealms() throws AuthenticationMechanismException {
}
}

private void prepareResponse(String realmName, HttpServerResponse response, boolean stale) throws HttpAuthenticationException {
private void prepareResponse(String realmName, HttpServerResponse response, boolean stale, HttpServerRequest request) throws HttpAuthenticationException {
StringBuilder sb = new StringBuilder(CHALLENGE_PREFIX);
sb.append(REALM).append("=\"").append(DigestQuote.quote(realmName)).append("\"");

Expand All @@ -407,6 +424,10 @@ private void prepareResponse(String realmName, HttpServerResponse response, bool

response.addResponseHeader(WWW_AUTHENTICATE, sb.toString());
response.setStatusCode(UNAUTHORIZED);

if ((nonceManager instanceof PersistentNonceManager) && request.getScope(Scope.SESSION) != null) {
request.getScope(Scope.SESSION).setAttachment(PERSISTENT_NONCE_MANAGER, this.nonceManager);
}
}

private boolean authorize(String username) throws AuthenticationMechanismException {
Expand Down
Expand Up @@ -21,6 +21,7 @@
import static org.wildfly.common.Assert.checkNotNullParam;
import static org.wildfly.security.http.HttpConstants.CONFIG_CONTEXT_PATH;
import static org.wildfly.security.http.HttpConstants.CONFIG_REALM;
import static org.wildfly.security.http.HttpConstants.CONFIG_SESSION_BASED_DIGEST_NONCE_MANAGER;
import static org.wildfly.security.http.HttpConstants.DIGEST_NAME;
import static org.wildfly.security.http.HttpConstants.DIGEST_SHA256_NAME;
import static org.wildfly.security.http.HttpConstants.DIGEST_SHA512_256_NAME;
Expand Down Expand Up @@ -58,7 +59,7 @@ public DigestMechanismFactory() {
}

public DigestMechanismFactory(final Provider provider) {
this(new Provider[] { provider });
this(new Provider[]{provider});
}

public DigestMechanismFactory(final Provider... providers) {
Expand Down Expand Up @@ -98,8 +99,13 @@ public HttpServerAuthenticationMechanism createAuthenticationMechanism(String me
checkNotNullParam("properties", properties);
checkNotNullParam("callbackHandler", callbackHandler);

// TODO can I properties this with the PersistentNonceManager
if (properties.containsKey("nonceManager")) {
nonceManager = (NonceManager) properties.get("nonceManager");
} else if (properties.get(CONFIG_SESSION_BASED_DIGEST_NONCE_MANAGER) != null) {
if (Boolean.parseBoolean((String) properties.get(CONFIG_SESSION_BASED_DIGEST_NONCE_MANAGER))) {
nonceManager = new PersistentNonceManager(NonceManagerUtils.DEFAULT_VALIDITY_PERIOD, NonceManagerUtils.DEFAULT_NONCE_SESSION_TIME, true, NonceManagerUtils.DEFAULT_KEY_SIZE, SHA256, ElytronMessages.httpDigest);
}
}

switch (mechanismName) {
Expand Down