org.wildfly.security.sasl.gssapi.AbstractGssapiMechanism 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.gssapi;
import java.util.Collections;
import java.util.Map;
import javax.security.auth.callback.CallbackHandler;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslException;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.MessageProp;
import org.wildfly.common.Assert;
import org.wildfly.security.sasl.WildFlySasl;
import org.wildfly.security.sasl.util.AbstractSaslParticipant;
import org.wildfly.security.sasl.util.SaslWrapper;
import static org.wildfly.security.mechanism._private.ElytronMessages.saslGssapi;
/**
* Base class for the SaslServer and SaslClient implementations implementing the GSSAPI mechanism as defined by RFC 4752
*
* @author Darran Lofthouse
*/
abstract class AbstractGssapiMechanism extends AbstractSaslParticipant {
private static final String AUTH = "auth";
private static final String AUTH_INT = "auth-int";
private static final String AUTH_CONF = "auth-conf";
private static final byte NO_SECURITY_LAYER = (byte) 0x01;
private static final byte INTEGRITY_PROTECTION = (byte) 0x02;
private static final byte CONFIDENTIALITY_PROTECTION = (byte) 0x04;
protected static final int DEFAULT_MAX_BUFFER_SIZE = (int) 0xFFFFFF; // 3 bytes
protected GSSContext gssContext;
protected final int configuredMaxReceiveBuffer;
protected int actualMaxReceiveBuffer;
protected int maxBuffer;
protected final boolean relaxComplianceChecks;
protected final QOP[] orderedQops;
protected QOP selectedQop;
protected AbstractGssapiMechanism(String mechanismName, String protocol, String serverName, Map props,
final CallbackHandler callbackHandler) throws SaslException {
super(mechanismName, protocol, serverName, callbackHandler, saslGssapi);
Assert.checkNotNullParam("callbackHandler", callbackHandler);
if (props == null) props = Collections.emptyMap();
if (props.containsKey(Sasl.MAX_BUFFER)) {
configuredMaxReceiveBuffer = Integer.parseInt((String) props.get(Sasl.MAX_BUFFER));
if (configuredMaxReceiveBuffer > DEFAULT_MAX_BUFFER_SIZE) {
throw saslGssapi.mechReceiveBufferIsGreaterThanMaximum(configuredMaxReceiveBuffer, DEFAULT_MAX_BUFFER_SIZE).toSaslException();
}
} else {
configuredMaxReceiveBuffer = DEFAULT_MAX_BUFFER_SIZE;
}
if (props.containsKey(WildFlySasl.RELAX_COMPLIANCE)) {
relaxComplianceChecks = Boolean.parseBoolean((String) props.get(WildFlySasl.RELAX_COMPLIANCE));
} else {
relaxComplianceChecks = false;
}
orderedQops = parsePreferredQop((String) props.get(Sasl.QOP));
if (saslGssapi.isTraceEnabled()) {
saslGssapi.tracef("configuredMaxReceiveBuffer=%d", configuredMaxReceiveBuffer);
saslGssapi.tracef("relaxComplianceChecks=%b", relaxComplianceChecks);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < orderedQops.length; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append(orderedQops[i]);
}
saslGssapi.tracef("QOP={%s}", sb.toString());
}
}
/**
* Converts bytes in network byte order to an integer starting from the specified offset.
*
* This method is implemented in the context of the GSSAPI mechanism, it is assumed that the size of the byte array is
* appropriate.
*/
protected int networkOrderBytesToInt(final byte[] bytes, final int start, final int length) {
int result = 0;
for (int i = start; i < length + start; i++) {
result <<= 8;
result |= (int)bytes[i] & 0xFF;
}
return result;
}
/**
* Obtain a 3 byte representation of an int, as an internal method it is assumed the maximum value of the int has already
* takine into account that it needs to fit into tree bytes,
*/
protected byte[] intToNetworkOrderBytes(final int value) {
byte[] response = new byte[3];
int workingValue = value;
for (int i = response.length - 1; i >= 0; i--) {
response[i] = (byte) (workingValue & 0xFF);
workingValue >>>= 8;
}
return response;
}
@Override
public void dispose() throws SaslException {
try {
saslGssapi.trace("dispose");
gssContext.dispose();
} catch (GSSException e) {
throw saslGssapi.mechUnableToDisposeGssContext(e).toSaslException();
} finally {
gssContext = null;
}
}
protected QOP[] parsePreferredQop(final String qop) throws SaslException {
if (qop != null) {
String[] qopNames = qop.trim().split("\\s*,\\s*");
if (qopNames.length > 0) {
QOP[] preferredQop = new QOP[qopNames.length];
for (int i = 0; i < qopNames.length; i++) {
QOP mapped = QOP.mapFromName(qopNames[i]);
if (mapped == null) {
throw saslGssapi.mechUnexpectedQop(qopNames[i]).toSaslException();
}
preferredQop[i] = mapped;
}
return preferredQop;
}
}
return new QOP[] { QOP.AUTH };
}
@Override
public Object getNegotiatedProperty(String propName) {
assertComplete();
switch (propName) {
case Sasl.QOP:
return selectedQop.getName();
case Sasl.MAX_BUFFER:
return Integer.toString(actualMaxReceiveBuffer != 0 ? actualMaxReceiveBuffer : configuredMaxReceiveBuffer);
case Sasl.RAW_SEND_SIZE:
return Integer.toString(maxBuffer);
}
return null;
}
protected enum QOP {
AUTH(AbstractGssapiMechanism.AUTH, NO_SECURITY_LAYER), AUTH_INT(AbstractGssapiMechanism.AUTH_INT, INTEGRITY_PROTECTION), AUTH_CONF(
AbstractGssapiMechanism.AUTH_CONF, CONFIDENTIALITY_PROTECTION);
private final String name;
private final byte value;
private QOP(final String name, final byte value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public byte getValue() {
return value;
}
public boolean includedBy(final byte securityLayer) {
return (securityLayer & value) == value;
}
public static QOP mapFromValue(final byte value) {
switch (value) {
case NO_SECURITY_LAYER:
return AUTH;
case INTEGRITY_PROTECTION:
return AUTH_INT;
case CONFIDENTIALITY_PROTECTION:
return AUTH_CONF;
default:
return null;
}
}
public static QOP mapFromName(final String name) {
switch (name) {
case AbstractGssapiMechanism.AUTH:
return AUTH;
case AbstractGssapiMechanism.AUTH_INT:
return AUTH_INT;
case AbstractGssapiMechanism.AUTH_CONF:
return AUTH_CONF;
default:
return null;
}
}
}
protected class GssapiWrapper implements SaslWrapper {
private final boolean confidential;
protected GssapiWrapper(final boolean confidential) {
this.confidential = confidential;
}
@Override
public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException {
MessageProp prop = new MessageProp(0, confidential);
try {
byte[] response = gssContext.wrap(outgoing, offset, len, prop);
saslGssapi.tracef("Wrapping message of length '%d' resulting message of length '%d'", len, response.length);
return response;
} catch (GSSException e) {
throw saslGssapi.mechUnableToWrapMessage(e).toSaslException();
}
}
@Override
public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException {
MessageProp prop = new MessageProp(0, confidential);
try {
byte[] response = gssContext.unwrap(incoming, offset, len, prop);
saslGssapi.tracef("Unwrapping message of length '%d' resulting message of length '%d'", len, response.length);
return response;
} catch (GSSException e) {
throw saslGssapi.mechUnableToUnwrapMessage(e).toSaslException();
}
}
}
}