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 079 connectionInfo.setUserName(mechanism.getUsername()); 080 connectionInfo.setPassword(mechanism.getPassword()); 081 082 if (tryAuthenticate(connectionInfo, transport.getPeerCertificates())) { 083 sasl.done(Sasl.SaslOutcome.PN_SASL_OK); 084 } else { 085 sasl.done(Sasl.SaslOutcome.PN_SASL_AUTH); 086 } 087 088 LOG.debug("SASL [{}} Handshake complete.", mechanism.getMechanismName()); 089 } else { 090 LOG.info("SASL: could not find supported mechanism"); 091 sasl.done(Sasl.SaslOutcome.PN_SASL_PERM); 092 } 093 } 094 } 095 096 //----- Internal implementation ------------------------------------------// 097 098 private SaslMechanism getSaslMechanism(String[] remoteMechanisms) { 099 String primary = remoteMechanisms[0]; 100 101 if (primary.equalsIgnoreCase("PLAIN")) { 102 return new PlainMechanism(); 103 } else if (primary.equalsIgnoreCase("ANONYMOUS")) { 104 return new AnonymousMechanism(); 105 } 106 107 return null; 108 } 109 110 private boolean tryAuthenticate(ConnectionInfo info, X509Certificate[] peerCertificates) { 111 try { 112 return getAuthenticator().authenticate(info.getUserName(), info.getPassword(), peerCertificates) != null; 113 } catch (Throwable error) { 114 return false; 115 } 116 } 117 118 private AuthenticationBroker getAuthenticator() { 119 if (authenticator == null) { 120 try { 121 authenticator = (AuthenticationBroker) brokerService.getBroker().getAdaptor(AuthenticationBroker.class); 122 } catch (Exception e) { 123 LOG.debug("Failed to lookup AuthenticationBroker from Broker, will use a default Noop version."); 124 } 125 126 if (authenticator == null) { 127 authenticator = new DefaultAuthenticationBroker(); 128 } 129 } 130 131 return authenticator; 132 } 133 134 private class DefaultAuthenticationBroker implements AuthenticationBroker { 135 136 @Override 137 public SecurityContext authenticate(String username, String password, X509Certificate[] peerCertificates) throws SecurityException { 138 return new SecurityContext(username) { 139 140 @Override 141 public Set<Principal> getPrincipals() { 142 return null; 143 } 144 }; 145 } 146 } 147}