/** * 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.activemq.transport.amqp.sasl; import java.security.Principal; import java.security.cert.X509Certificate; import java.util.Set; import org.apache.activemq.broker.BrokerService; import org.apache.activemq.command.ConnectionInfo; import org.apache.activemq.security.AuthenticationBroker; import org.apache.activemq.security.SecurityContext; import org.apache.activemq.transport.amqp.AmqpTransport; import org.apache.qpid.proton.engine.Sasl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * SASL Authentication engine. */ public class AmqpAuthenticator { private static final Logger LOG = LoggerFactory.getLogger(AmqpAuthenticator.class); private static final String[] mechanisms = new String[] { "PLAIN", "ANONYMOUS" }; private final BrokerService brokerService; private final AmqpTransport transport; private final Sasl sasl; private AuthenticationBroker authenticator; public AmqpAuthenticator(AmqpTransport transport, Sasl sasl, BrokerService brokerService) { this.brokerService = brokerService; this.transport = transport; this.sasl = sasl; sasl.setMechanisms(mechanisms); sasl.server(); } /** * @return true if the SASL exchange has completed, regardless of success. */ public boolean isDone() { return sasl.getOutcome() != Sasl.SaslOutcome.PN_SASL_NONE; } /** * @return the list of all SASL mechanisms that are supported currently. */ public String[] getSupportedMechanisms() { return mechanisms; } public void processSaslExchange(ConnectionInfo connectionInfo) { if (sasl.getRemoteMechanisms().length > 0) { SaslMechanism mechanism = getSaslMechanism(sasl.getRemoteMechanisms()); if (mechanism != null) { LOG.debug("SASL [{}} Handshake started.", mechanism.getMechanismName()); mechanism.processSaslStep(sasl); if (!mechanism.isFailed()) { connectionInfo.setUserName(mechanism.getUsername()); connectionInfo.setPassword(mechanism.getPassword()); if (tryAuthenticate(connectionInfo, transport.getPeerCertificates())) { sasl.done(Sasl.SaslOutcome.PN_SASL_OK); } else { sasl.done(Sasl.SaslOutcome.PN_SASL_AUTH); } LOG.debug("SASL [{}} Handshake complete.", mechanism.getMechanismName()); } else { LOG.debug("SASL [{}} Handshake failed: {}", mechanism.getMechanismName(), mechanism.getFailureReason()); sasl.done(Sasl.SaslOutcome.PN_SASL_AUTH); } } else { LOG.info("SASL: could not find supported mechanism"); sasl.done(Sasl.SaslOutcome.PN_SASL_PERM); } } } //----- Internal implementation ------------------------------------------// private SaslMechanism getSaslMechanism(String[] remoteMechanisms) { String primary = remoteMechanisms[0]; if (primary.equalsIgnoreCase("PLAIN")) { return new PlainMechanism(); } else if (primary.equalsIgnoreCase("ANONYMOUS")) { return new AnonymousMechanism(); } return null; } private boolean tryAuthenticate(ConnectionInfo info, X509Certificate[] peerCertificates) { try { return getAuthenticator().authenticate(info.getUserName(), info.getPassword(), peerCertificates) != null; } catch (Throwable error) { return false; } } private AuthenticationBroker getAuthenticator() { if (authenticator == null) { try { authenticator = (AuthenticationBroker) brokerService.getBroker().getAdaptor(AuthenticationBroker.class); } catch (Exception e) { LOG.debug("Failed to lookup AuthenticationBroker from Broker, will use a default Noop version."); } if (authenticator == null) { authenticator = new DefaultAuthenticationBroker(); } } return authenticator; } private class DefaultAuthenticationBroker implements AuthenticationBroker { @Override public SecurityContext authenticate(String username, String password, X509Certificate[] peerCertificates) throws SecurityException { return new SecurityContext(username) { @Override public Set<Principal> getPrincipals() { return null; } }; } } }