/** * 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 io.hawtjms.provider.amqp; import io.hawtjms.jms.meta.JmsConnectionInfo; import io.hawtjms.sasl.Mechanism; import io.hawtjms.sasl.SaslMechanismFinder; import javax.jms.JMSSecurityException; import org.apache.qpid.proton.engine.Sasl; /** * Manage the SASL authentication process */ public class AmqpSaslAuthenticator { private final Sasl sasl; private final JmsConnectionInfo info; private Mechanism mechanism; /** * Create the authenticator and initialize it. * * @param sasl * The Proton SASL entry point this class will use to manage the authentication. * @param info * The Connection information used to provide credentials to the remote peer. */ public AmqpSaslAuthenticator(Sasl sasl, JmsConnectionInfo info) { this.sasl = sasl; this.info = info; } /** * Process the SASL authentication cycle until such time as an outcome is * determine. This method must be called by the managing entity until the * return value is true indicating a successful authentication or a * JMSSecurityException is thrown indicating that the handshake failed. * * @throws JMSSecurityException */ public boolean authenticate() throws JMSSecurityException { switch(sasl.getState()) { case PN_SASL_IDLE: handleSaslInit(); break; case PN_SASL_STEP: handleSaslStep(); break; case PN_SASL_FAIL: handleSaslFail(); break; case PN_SASL_PASS: return true; default: } return false; } private void handleSaslInit() throws JMSSecurityException { String[] remoteMechanisms = sasl.getRemoteMechanisms(); if (remoteMechanisms != null && remoteMechanisms.length != 0) { mechanism = SaslMechanismFinder.findMatchingMechanism(remoteMechanisms); if (mechanism != null) { mechanism.setUsername(info.getUsername()); mechanism.setPassword(info.getPassword()); // TODO - set additional options from URI. // TODO - set a host value. sasl.setMechanisms(mechanism.getName()); byte[] response = mechanism.getInitialResponse(); if (response != null && response.length != 0) { sasl.send(response, 0, response.length); } } else { // TODO - Better error message. throw new JMSSecurityException("Could not find a matching SASL mechanism for the remote peer."); } } } private void handleSaslStep() { if (sasl.pending() != 0) { byte[] challenge = new byte[sasl.pending()]; sasl.recv(challenge, 0, challenge.length); byte[] response = mechanism.getChallengeResponse(challenge); sasl.send(response, 0, response.length); } } private void handleSaslFail() throws JMSSecurityException { // TODO - Better error message. throw new JMSSecurityException("Client failed to authenticate"); } }