blob: 7c99671165903d3281b78d9e79d92e9abc4825d9 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.qpid.client.transport;
import org.apache.qpid.client.HeartbeatListener;
import org.apache.qpid.transport.ConnectionHeartbeat;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.qpid.client.security.AMQCallbackHandler;
import org.apache.qpid.client.security.CallbackHandlerRegistry;
import org.apache.qpid.jms.ConnectionURL;
import org.apache.qpid.transport.ClientDelegate;
import org.apache.qpid.transport.Connection;
import org.apache.qpid.transport.ConnectionException;
import org.apache.qpid.transport.ConnectionOpenOk;
import org.apache.qpid.transport.ConnectionSettings;
import org.apache.qpid.util.Strings;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import javax.security.sasl.SaslException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*/
public class ClientConnectionDelegate extends ClientDelegate
{
private static final Logger LOGGER = LoggerFactory.getLogger(ClientConnectionDelegate.class);
private static final String KRB5_OID_STR = "1.2.840.113554.1.2.2";
protected static final Oid KRB5_OID;
static
{
Oid oid;
try
{
oid = new Oid(KRB5_OID_STR);
}
catch (GSSException ignore)
{
oid = null;
}
KRB5_OID = oid;
}
private final ConnectionURL _connectionURL;
private HeartbeatListener _heartbeatListener = HeartbeatListener.DEFAULT;
/**
* @param settings
* @param connectionURL
*/
public ClientConnectionDelegate(ConnectionSettings settings, ConnectionURL connectionURL)
{
super(settings);
this._connectionURL = connectionURL;
}
@Override
protected SaslClient createSaslClient(List<Object> brokerMechs) throws ConnectionException, SaslException
{
final String brokerMechanisms = Strings.join(" ", brokerMechs);
final String restrictionList = getConnectionSettings().getSaslMechs();
final String selectedMech = CallbackHandlerRegistry.getInstance().selectMechanism(brokerMechanisms, restrictionList);
if (selectedMech == null)
{
throw new ConnectionException("Client and broker have no SASL mechanisms in common." +
" Broker allows : " + brokerMechanisms +
" Client has : " + CallbackHandlerRegistry.getInstance().getMechanisms() +
" Client restricted itself to : " + (restrictionList != null ? restrictionList : "no restriction"));
}
if ((_connectionURL.getUsername() == null || _connectionURL.getPassword() == null)
&& CallbackHandlerRegistry.getInstance().isUserPassRequired(selectedMech))
{
throw new ConnectionException("Username and Password is required for the selected mechanism : " + selectedMech +
" Broker allows : " + brokerMechanisms +
" Client has : " + CallbackHandlerRegistry.getInstance().getMechanisms() +
" Client restricted itself to : " + (restrictionList != null ? restrictionList : "no restriction"));
}
Map<String,Object> saslProps = new HashMap<String,Object>();
if (getConnectionSettings().isUseSASLEncryption())
{
saslProps.put(Sasl.QOP, "auth-conf");
}
final AMQCallbackHandler handler = CallbackHandlerRegistry.getInstance().createCallbackHandler(selectedMech);
handler.initialise(_connectionURL);
final SaslClient sc = Sasl.createSaslClient(new String[] {selectedMech}, null, getConnectionSettings().getSaslProtocol(), getConnectionSettings().getSaslServerName(), saslProps, handler);
return sc;
}
@Override
public void connectionOpenOk(Connection conn, ConnectionOpenOk ok)
{
SaslClient sc = conn.getSaslClient();
if (sc != null)
{
if (sc.getMechanismName().equals("GSSAPI"))
{
String id = getKerberosUser();
if (id != null)
{
conn.setUserID(id);
}
}
else if (sc.getMechanismName().equals("EXTERNAL"))
{
if (conn.getSecurityLayer() != null)
{
conn.setUserID(conn.getSecurityLayer().getUserID());
}
}
}
super.connectionOpenOk(conn, ok);
}
private String getKerberosUser()
{
LOGGER.debug("Obtaining userID from kerberos");
String service = getConnectionSettings().getSaslProtocol() + "@" + getConnectionSettings().getSaslServerName();
GSSManager manager = GSSManager.getInstance();
try
{
GSSName acceptorName = manager.createName(service,
GSSName.NT_HOSTBASED_SERVICE, KRB5_OID);
GSSContext secCtx = manager.createContext(acceptorName,
KRB5_OID,
null,
GSSContext.INDEFINITE_LIFETIME);
secCtx.initSecContext(new byte[0], 0, 1);
if (secCtx.getSrcName() != null)
{
return secCtx.getSrcName().toString();
}
}
catch (GSSException e)
{
LOGGER.warn("Unable to retrieve userID from Kerberos due to error",e);
}
return null;
}
@Override
public void connectionHeartbeat(Connection conn, ConnectionHeartbeat heartbeat)
{
_heartbeatListener.heartbeatReceived();
}
public void setHeartbeatListener(HeartbeatListener listener)
{
_heartbeatListener = listener == null ? HeartbeatListener.DEFAULT : listener;
}
@Override
public void writerIdle(final Connection connection)
{
super.writerIdle(connection);
_heartbeatListener.heartbeatSent();
}
}