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 */ 017 018package org.apache.activemq.security; 019 020import java.security.cert.X509Certificate; 021 022import org.apache.activemq.broker.Broker; 023import org.apache.activemq.broker.BrokerFilter; 024import org.apache.activemq.broker.ConnectionContext; 025import org.apache.activemq.broker.Connector; 026import org.apache.activemq.broker.EmptyBroker; 027import org.apache.activemq.broker.TransportConnector; 028import org.apache.activemq.command.ActiveMQDestination; 029import org.apache.activemq.command.ConnectionInfo; 030import org.apache.activemq.transport.tcp.SslTransportServer; 031 032/** 033 * A JAAS Authentication Broker that uses different JAAS domain configurations 034 * depending if the connection is over an SSL enabled Connector or not. 035 * 036 * This allows you to, for instance, do DN based authentication for SSL connections 037 * and use a mixture of username/passwords and simple guest authentication for 038 * non-SSL connections. 039 * <p> 040 * An example <code>login.config</code> to do do this is: 041 * <pre> 042 * activemq-domain { 043 * org.apache.activemq.jaas.PropertiesLoginModule sufficient 044 * debug=true 045 * org.apache.activemq.jaas.properties.user="users.properties" 046 * org.apache.activemq.jaas.properties.group="groups.properties"; 047 * org.apache.activemq.jaas.GuestLoginModule sufficient 048 * debug=true 049 * org.apache.activemq.jaas.guest.user="guest" 050 * org.apache.activemq.jaas.guest.group="guests"; 051 * }; 052 * 053 * activemq-ssl-domain { 054 * org.apache.activemq.jaas.TextFileCertificateLoginModule required 055 * debug=true 056 * org.apache.activemq.jaas.textfiledn.user="dns.properties" 057 * org.apache.activemq.jaas.textfiledn.group="groups.properties"; 058 * }; 059 * </pre> 060 */ 061public class JaasDualAuthenticationBroker extends BrokerFilter implements AuthenticationBroker { 062 private final JaasCertificateAuthenticationBroker sslBroker; 063 private final JaasAuthenticationBroker nonSslBroker; 064 065 066 /*** Simple constructor. Leaves everything to superclass. 067 * 068 * @param next The Broker that does the actual work for this Filter. 069 * @param jaasConfiguration The JAAS domain configuration name for 070 * non-SSL connections (refer to JAAS documentation). 071 * @param jaasSslConfiguration The JAAS domain configuration name for 072 * SSL connections (refer to JAAS documentation). 073 */ 074 public JaasDualAuthenticationBroker(Broker next, String jaasConfiguration, String jaasSslConfiguration) { 075 super(next); 076 077 this.nonSslBroker = new JaasAuthenticationBroker(new EmptyBroker(), jaasConfiguration); 078 this.sslBroker = new JaasCertificateAuthenticationBroker(new EmptyBroker(), jaasSslConfiguration); 079 } 080 081 /** 082 * Overridden to allow for authentication using different Jaas 083 * configurations depending on if the connection is SSL or not. 084 * 085 * @param context The context for the incoming Connection. 086 * @param info The ConnectionInfo Command representing the incoming 087 * connection. 088 */ 089 @Override 090 public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception { 091 if (context.getSecurityContext() == null) { 092 boolean isSSL = false; 093 Connector connector = context.getConnector(); 094 if (connector instanceof TransportConnector) { 095 TransportConnector transportConnector = (TransportConnector) connector; 096 isSSL = transportConnector.getServer().isSslServer(); 097 } 098 099 if (isSSL) { 100 this.sslBroker.addConnection(context, info); 101 } else { 102 this.nonSslBroker.addConnection(context, info); 103 } 104 super.addConnection(context, info); 105 } 106 } 107 108 /** 109 * Overriding removeConnection to make sure the security context is cleaned. 110 */ 111 @Override 112 public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception { 113 boolean isSSL; 114 Connector connector = context.getConnector(); 115 if (connector instanceof TransportConnector) { 116 TransportConnector transportConnector = (TransportConnector) connector; 117 isSSL = (transportConnector.getServer() instanceof SslTransportServer); 118 } else { 119 isSSL = false; 120 } 121 super.removeConnection(context, info, error); 122 if (isSSL) { 123 this.sslBroker.removeConnection(context, info, error); 124 } else { 125 this.nonSslBroker.removeConnection(context, info, error); 126 } 127 } 128 129 @Override 130 public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout) throws Exception { 131 // Give both a chance to clear out their contexts 132 this.sslBroker.removeDestination(context, destination, timeout); 133 this.nonSslBroker.removeDestination(context, destination, timeout); 134 135 super.removeDestination(context, destination, timeout); 136 } 137 138 @Override 139 public SecurityContext authenticate(String username, String password, X509Certificate[] peerCertificates) throws SecurityException { 140 if (peerCertificates != null) { 141 return this.sslBroker.authenticate(username, password, peerCertificates); 142 } else { 143 return this.nonSslBroker.authenticate(username, password, peerCertificates); 144 } 145 } 146}