org.wildfly.security.sasl.util.AbstractSaslParticipant Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.security.sasl.util;
import org.wildfly.common.Assert;
import org.wildfly.security.mechanism._private.ElytronMessages;
import java.util.Map;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.SaslException;
/**
* A common base class for SASL participants.
*
* @author David M. Lloyd
*/
public abstract class AbstractSaslParticipant implements SaslWrapper {
/**
* An empty byte array.
*/
public static final byte[] NO_BYTES = new byte[0];
/**
* The SASL negotiation failure state.
*/
public static final int FAILED_STATE = -1;
/**
* The SASL negotiation completed state.
*/
public static final int COMPLETE_STATE = 0;
private final String mechanismName;
private final CallbackHandler callbackHandler;
private final String protocol;
private final String serverName;
private ElytronMessages log;
private org.wildfly.security.sasl._private.ElytronMessages legacyLog;
private int state = -1;
private SaslWrapper wrapper;
/**
* Construct a new instance.
*
* @param mechanismName the name of the defined mechanism
* @param protocol the protocol
* @param serverName the server name
* @param callbackHandler the callback handler
* @param log mechanism specific logger
*/
protected AbstractSaslParticipant(final String mechanismName, final String protocol, final String serverName, final CallbackHandler callbackHandler, ElytronMessages log) {
this.callbackHandler = callbackHandler;
this.mechanismName = mechanismName;
this.protocol = protocol;
this.serverName = serverName;
this.log = log;
}
/**
* Construct a new instance.
*
* @param mechanismName the name of the defined mechanism
* @param protocol the protocol
* @param serverName the server name
* @param callbackHandler the callback handler
*/
@Deprecated
protected AbstractSaslParticipant(final String mechanismName, final String protocol, final String serverName, final CallbackHandler callbackHandler) {
this.callbackHandler = callbackHandler;
this.mechanismName = mechanismName;
this.protocol = protocol;
this.serverName = serverName;
this.legacyLog = org.wildfly.security.sasl._private.ElytronMessages.sasl;
}
/**
* Handle callbacks, wrapping exceptions as needed (including unsupported callbacks).
*
* @param callbacks the callbacks to handle
* @throws SaslException if a callback failed
*/
protected void handleCallbacks(Callback... callbacks) throws SaslException {
try {
tryHandleCallbacks(callbacks);
} catch (UnsupportedCallbackException e) {
throw log != null ? log.mechCallbackHandlerFailedForUnknownReason(e).toSaslException() : legacyLog.mechCallbackHandlerFailedForUnknownReason(e).toSaslException();
}
}
/**
* Handle callbacks, wrapping exceptions as needed.
*
* @param callbacks the callbacks to handle
* @throws SaslException if a callback failed
* @throws UnsupportedCallbackException if a callback isn't supported
*/
protected void tryHandleCallbacks(Callback... callbacks) throws SaslException, UnsupportedCallbackException {
Assert.checkNotNullParam("callbackHandler", callbackHandler);
try {
callbackHandler.handle(callbacks);
} catch (SaslException | UnsupportedCallbackException e) {
throw e;
} catch (Throwable t) {
throw log != null ? log.mechCallbackHandlerFailedForUnknownReason(t).toSaslException() : legacyLog.mechCallbackHandlerFailedForUnknownReason(t).toSaslException();
}
}
public void init() {}
/**
* Get the name of this mechanism.
*
* @return the mechanism name
*/
public String getMechanismName() {
return mechanismName;
}
/**
* Get the protocol name.
*
* @return the protocol name
*/
protected String getProtocol() {
return protocol;
}
/**
* Get the server name.
*
* @return the server name
*/
protected String getServerName() {
return serverName;
}
/**
* Get the configured authentication callback handler.
*
* @return the callback handler
*/
protected CallbackHandler getCallbackHandler() {
return callbackHandler;
}
/**
* Get the current configured SASL wrapper, if any.
*
* @return the SASL wrapper, or {@code null} if none is configured
*/
protected SaslWrapper getWrapper() {
return wrapper;
}
/**
* Set the state to use for the next incoming message.
*
* @param newState the new state
*/
public void setNegotiationState(final int newState) {
state = newState;
}
/**
* Indicate that negotiation is complete. To re-initiate negotiation, call {@link #setNegotiationState(int)}.
*/
public void negotiationComplete() {
state = COMPLETE_STATE;
final String msg = "SASL Negotiation Completed";
if (log != null) {
log.tracef(msg);
} else {
legacyLog.tracef(msg);
}
}
protected byte[] evaluateMessage(final byte[] message) throws SaslException {
boolean ok = false;
try {
if (state == COMPLETE_STATE) {
throw log != null ? log.mechMessageAfterComplete().toSaslException() : legacyLog.mechMessageAfterComplete().toSaslException();
} else if (state == FAILED_STATE) {
throw log != null ? log.mechAuthenticationFailed().toSaslException() : legacyLog.mechAuthenticationFailed().toSaslException();
}
byte[] result = evaluateMessage(state, message);
ok = true;
return result;
} finally {
if (! ok) {
state = FAILED_STATE;
final String msg = "SASL Negotiation Failed";
if (log != null) {
log.tracef(msg);
} else {
legacyLog.tracef(msg);
}
}
}
}
protected abstract byte[] evaluateMessage(int state, final byte[] message) throws SaslException;
/**
* Set the current configured SASL wrapper, if any.
*
* @param wrapper the SASL wrapper, or {@code null} to disable wrapping
*/
protected void setWrapper(final SaslWrapper wrapper) {
this.wrapper = wrapper;
}
/**
* Wraps a byte array to be sent to the other participant.
*
* @param outgoing a non-{@code null} byte array containing the bytes to encode
* @param offset the first byte to encode
* @param len the number of bytes to use
* @return A non-{@code null} byte array containing the encoded bytes
* @exception SaslException if wrapping fails
* @exception IllegalStateException if wrapping is not configured
*/
public byte[] wrap(final byte[] outgoing, final int offset, final int len) throws SaslException {
if (isComplete() == false) throw log != null ? log.mechAuthenticationNotComplete() : legacyLog.mechAuthenticationNotComplete();
SaslWrapper wrapper = this.wrapper;
if (wrapper == null) {
throw log != null ? log.wrappingNotConfigured() : legacyLog.wrappingNotConfigured();
}
if(len == 0) {
return NO_BYTES;
}
return wrapper.wrap(outgoing, offset, len);
}
/**
* Unwraps a byte array received from the other participant.
*
* @param incoming a non-{@code null} byte array containing the bytes to decode
* @param offset the first byte to decode
* @param len the number of bytes to use
* @return A non-{@code null} byte array containing the decoded bytes
* @exception SaslException if wrapping fails
* @exception IllegalStateException if wrapping is not configured
*/
public byte[] unwrap(final byte[] incoming, final int offset, final int len) throws SaslException {
if (isComplete() == false) throw log.mechAuthenticationNotComplete();
SaslWrapper wrapper = this.wrapper;
if (wrapper == null) {
throw log.wrappingNotConfigured();
}
if(len == 0) {
return NO_BYTES;
}
return wrapper.unwrap(incoming, offset, len);
}
/**
* Determine whether the authentication exchange has completed.
*
* @return {@code true} if the exchange has completed
*/
public boolean isComplete() {
return state == COMPLETE_STATE;
}
/**
* A convenience method to throw a {@link IllegalStateException} is authentication is not yet complete.
*
* To be called by methods that must only be called after authentication is complete.
*/
protected void assertComplete() {
if (isComplete() == false) {
throw log.mechAuthenticationNotComplete();
}
}
/**
* Get a property negotiated between this participant and the other.
*
* @param propName the property name
* @return the property value or {@code null} if not defined
*/
public Object getNegotiatedProperty(final String propName) {
assertComplete();
return null;
}
/**
* Get a string property value from the given map.
*
* @param map the property map
* @param key the property
* @param defaultVal the value to return if the key is not in the map
* @return the value
*/
public String getStringProperty(Map map, String key, String defaultVal) {
final Object val = map.get(key);
if (val == null) {
return defaultVal;
} else {
return String.valueOf(val);
}
}
/**
* Get a string property value from the given map.
*
* @param map the property map
* @param key the property
* @param defaultVal the value to return if the key is not in the map
* @return the value
*/
public int getIntProperty(Map map, String key, int defaultVal) {
final Object val = map.get(key);
if (val == null) {
return defaultVal;
} else {
return Integer.parseInt(val.toString());
}
}
/**
* Dispose of this participant.
*
* @throws SaslException if disposal failed
*/
public void dispose() throws SaslException {
}
}