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