001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.transport.amqp.sasl; 018 019import java.security.Principal; 020import java.security.cert.X509Certificate; 021import java.util.Set; 022 023import org.apache.activemq.broker.BrokerService; 024import org.apache.activemq.command.ConnectionInfo; 025import org.apache.activemq.security.AuthenticationBroker; 026import org.apache.activemq.security.SecurityContext; 027import org.apache.activemq.transport.amqp.AmqpTransport; 028import org.apache.qpid.proton.engine.Sasl; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032/** 033 * SASL Authenitcation engine. 034 */ 035public class AmqpAuthenticator { 036 037 private static final Logger LOG = LoggerFactory.getLogger(AmqpAuthenticator.class); 038 039 private static final String[] mechanisms = new String[] { "PLAIN", "ANONYMOUS" }; 040 041 private final BrokerService brokerService; 042 private final AmqpTransport transport; 043 private final Sasl sasl; 044 045 private AuthenticationBroker authenticator; 046 047 public AmqpAuthenticator(AmqpTransport transport, Sasl sasl, BrokerService brokerService) { 048 this.brokerService = brokerService; 049 this.transport = transport; 050 this.sasl = sasl; 051 052 sasl.setMechanisms(mechanisms); 053 sasl.server(); 054 } 055 056 /** 057 * @return true if the SASL exchange has conpleted, regardless of success. 058 */ 059 public boolean isDone() { 060 return sasl.getOutcome() != Sasl.SaslOutcome.PN_SASL_NONE; 061 } 062 063 /** 064 * @return the list of all SASL mechanisms that are supported curretnly. 065 */ 066 public String[] getSupportedMechanisms() { 067 return mechanisms; 068 } 069 070 public void processSaslExchange(ConnectionInfo connectionInfo) { 071 if (sasl.getRemoteMechanisms().length > 0) { 072 073 SaslMechanism mechanism = getSaslMechanism(sasl.getRemoteMechanisms()); 074 if (mechanism != null) { 075 LOG.debug("SASL [{}} Handshake started.", mechanism.getMechanismName()); 076 077 mechanism.processSaslStep(sasl); 078 if (!mechanism.isFailed()) { 079 080 connectionInfo.setUserName(mechanism.getUsername()); 081 connectionInfo.setPassword(mechanism.getPassword()); 082 083 if (tryAuthenticate(connectionInfo, transport.getPeerCertificates())) { 084 sasl.done(Sasl.SaslOutcome.PN_SASL_OK); 085 } else { 086 sasl.done(Sasl.SaslOutcome.PN_SASL_AUTH); 087 } 088 089 LOG.debug("SASL [{}} Handshake complete.", mechanism.getMechanismName()); 090 } else { 091 LOG.debug("SASL [{}} Handshake failed: {}", mechanism.getMechanismName(), mechanism.getFailureReason()); 092 sasl.done(Sasl.SaslOutcome.PN_SASL_AUTH); 093 } 094 } else { 095 LOG.info("SASL: could not find supported mechanism"); 096 sasl.done(Sasl.SaslOutcome.PN_SASL_PERM); 097 } 098 } 099 } 100 101 //----- Internal implementation ------------------------------------------// 102 103 private SaslMechanism getSaslMechanism(String[] remoteMechanisms) { 104 String primary = remoteMechanisms[0]; 105 106 if (primary.equalsIgnoreCase("PLAIN")) { 107 return new PlainMechanism(); 108 } else if (primary.equalsIgnoreCase("ANONYMOUS")) { 109 return new AnonymousMechanism(); 110 } 111 112 return null; 113 } 114 115 private boolean tryAuthenticate(ConnectionInfo info, X509Certificate[] peerCertificates) { 116 try { 117 return getAuthenticator().authenticate(info.getUserName(), info.getPassword(), peerCertificates) != null; 118 } catch (Throwable error) { 119 return false; 120 } 121 } 122 123 private AuthenticationBroker getAuthenticator() { 124 if (authenticator == null) { 125 try { 126 authenticator = (AuthenticationBroker) brokerService.getBroker().getAdaptor(AuthenticationBroker.class); 127 } catch (Exception e) { 128 LOG.debug("Failed to lookup AuthenticationBroker from Broker, will use a default Noop version."); 129 } 130 131 if (authenticator == null) { 132 authenticator = new DefaultAuthenticationBroker(); 133 } 134 } 135 136 return authenticator; 137 } 138 139 private class DefaultAuthenticationBroker implements AuthenticationBroker { 140 141 @Override 142 public SecurityContext authenticate(String username, String password, X509Certificate[] peerCertificates) throws SecurityException { 143 return new SecurityContext(username) { 144 145 @Override 146 public Set<Principal> getPrincipals() { 147 return null; 148 } 149 }; 150 } 151 } 152}