001    /** 
002     * 
003     * Copyright 2004 Hiram Chirino
004     * 
005     * Licensed under the Apache License, Version 2.0 (the "License"); 
006     * you may not use this file except in compliance with the License. 
007     * 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     **/
018    package org.activemq.security.jassjacc;
019    
020    import java.security.AccessControlContext;
021    import java.security.AccessControlException;
022    import java.security.AccessController;
023    import java.security.Permission;
024    import java.security.PrivilegedAction;
025    import java.util.Iterator;
026    
027    import javax.jms.JMSException;
028    import javax.security.auth.Subject;
029    import javax.security.auth.login.LoginContext;
030    import javax.security.jacc.PolicyConfiguration;
031    import javax.security.jacc.PolicyConfigurationFactory;
032    import javax.security.jacc.PolicyContext;
033    import javax.security.jacc.PolicyContextException;
034    
035    import org.activemq.broker.BrokerClient;
036    import org.activemq.message.ActiveMQDestination;
037    import org.activemq.message.ActiveMQMessage;
038    import org.activemq.message.ConnectionInfo;
039    import org.activemq.message.ConsumerInfo;
040    import org.activemq.message.ProducerInfo;
041    import org.activemq.security.SecurityAdapter;
042    
043    /**
044     * Implements SecurityAdapter that uses JASS to authenticate and
045     * JACC to authorize the user operations.
046     * 
047     * @version $Revision: 1.1.1.1 $
048     */
049    public class JassJaccSecurityAdapter implements SecurityAdapter {
050            
051            private String jassConfiguration;
052    
053            /**
054             * @param jassConfiguration
055             */
056            public JassJaccSecurityAdapter(String jassConfiguration) {
057                    this.jassConfiguration = jassConfiguration;
058            }
059            
060            
061            protected AccessControlContext getAccessControlContext(BrokerClient client) {
062                    
063                    Subject subject = client.getSubject();
064            if (subject == null) throw new IllegalArgumentException("Subject must not be null");
065    
066            AccessControlContext acc = (AccessControlContext) Subject.doAsPrivileged(subject, new PrivilegedAction() {
067                public Object run() {
068                    return AccessController.getContext();
069                }
070            }, null);        
071            
072                    return acc;
073            }
074            
075            static protected String getBrokerName(BrokerClient client) {
076                    return client.getBrokerConnector().getBrokerInfo().getBrokerName();
077            }
078    
079            public void authorizeConnection(BrokerClient client, ConnectionInfo info) throws JMSException {
080    
081                    // Set the TCCL since it seems JAAS needs it to find the login module classes.
082                    Thread.currentThread().setContextClassLoader(JassJaccSecurityAdapter.class.getClassLoader());
083                    
084                    // Do the login.
085            Subject subject = doJassLogin(info);
086            client.setSubject(subject);        
087                    
088            // Can the user really use the broker?
089                    PolicyContext.setContextID(getBrokerPoicyContextId(client));
090            AccessControlContext accessContext = getAccessControlContext(client);
091            if (accessContext != null) {
092                Permission permission = new JMSBrokerPermission(getBrokerName(client),JMSBrokerPermission.CONNECT_ACTION);
093                accessContext.checkPermission(permission);
094            }
095        }
096    
097            /**
098             * @param client
099             * @return
100             */
101            static private String getBrokerPoicyContextId(BrokerClient client) {
102                    return getBrokerPolicyContextId(getBrokerName(client));
103            }
104    
105            /**
106             * @param client
107             * @return
108             */
109            static private String getBrokerPolicyContextId(String brokerName) {
110                    return "org.activemq.broker:"+brokerName;
111            }
112    
113            /**
114             * @param info
115             * @param subject
116             * @return
117             */
118            private Subject doJassLogin(ConnectionInfo info) throws JMSException {
119                    try {
120                            LoginContext lc = new LoginContext(jassConfiguration,
121                                            new UsernamePasswordCallback(info.getUserName(), info
122                                                            .getPassword()));
123                            lc.login();
124                            return lc.getSubject();
125                    } catch (Exception e) {
126                            throw (JMSException)new JMSException("Login failed: "+e.getMessage()).initCause(e);
127                    }
128            }
129    
130            public void authorizeConsumer(BrokerClient client, ConsumerInfo info) throws JMSException {
131            PolicyContext.setContextID(getDestinationPoicyContextId(client, info.getDestination()));
132            AccessControlContext accessContext = getAccessControlContext(client);
133            if (accessContext != null) {
134                Permission permission = new JMSDestinationPermission(info.getDestination().getPhysicalName(),JMSDestinationPermission.CONSUME_ACTION);
135                accessContext.checkPermission(permission);
136            }
137        }
138    
139        public boolean authorizeReceive(BrokerClient client, ActiveMQMessage message) {
140            try {
141                PolicyContext.setContextID(getDestinationPoicyContextId(client, message.getJMSActiveMQDestination()));
142                AccessControlContext accessContext = getAccessControlContext(client);
143                if (accessContext != null) {
144                    Permission permission = new JMSDestinationPermission(message.getJMSActiveMQDestination().getPhysicalName(),JMSDestinationPermission.CONSUME_ACTION);
145                    accessContext.checkPermission(permission);
146                }
147            }
148            catch (AccessControlException e) {
149                return false;
150            }
151            return true;
152        }
153    
154    
155            /**
156             * @param client
157             * @param destination
158             * @return
159             */
160            static private String getDestinationPoicyContextId(BrokerClient client, ActiveMQDestination destination) {
161                    return getDestinationPoicyContextId(getBrokerName(client), destination);
162            }
163            
164            static private String getDestinationPoicyContextId(String brokerName, ActiveMQDestination destination) {
165                    ActiveMQDestination activeMQDestination = ((ActiveMQDestination)destination);
166                    return (activeMQDestination.isTopic()?"org.activemq.topic:":"org.activemq.queue:")+brokerName+":"+activeMQDestination.getPhysicalName();
167            }
168    
169            public void authorizeProducer(BrokerClient client, ProducerInfo info) throws JMSException {
170            PolicyContext.setContextID(getDestinationPoicyContextId(client, info.getDestination()));
171            AccessControlContext accessContext = getAccessControlContext(client);
172            if (accessContext != null) {
173                Permission permission = new JMSDestinationPermission(info.getDestination().getPhysicalName(),JMSDestinationPermission.PRODUCE_ACTION);
174                accessContext.checkPermission(permission);
175            }
176        }
177    
178            public void authorizeSendMessage(BrokerClient client, ActiveMQMessage message) throws JMSException {
179            PolicyContext.setContextID(getDestinationPoicyContextId(client, message.getJMSActiveMQDestination()));
180            AccessControlContext accessContext = getAccessControlContext(client);
181            if (accessContext != null) {
182                String physicalName = ((ActiveMQDestination)message.getJMSDestination()).getPhysicalName();
183                            Permission permission = new JMSDestinationPermission(message.getJMSActiveMQDestination().getPhysicalName(),JMSDestinationPermission.SEND_ACTION);
184                accessContext.checkPermission(permission);
185            }
186        }
187                    
188            /**
189             * Creates a JACC PolicyConfiguration for the broker security.
190             * 
191             * @param brokerSecurity
192             */
193            static public void secure(BrokerSecurityConfig brokerSecurity) {
194    
195            try {
196                    
197                            PolicyConfigurationFactory factory = PolicyConfigurationFactory.getPolicyConfigurationFactory();
198                            PolicyConfiguration policyConfiguration = factory.getPolicyConfiguration(
199                                            getBrokerPolicyContextId(brokerSecurity.getBrokerName()), true);
200    
201                            for (Iterator iter = brokerSecurity.getConnectRoles().iterator(); iter.hasNext();) {
202                                    String role = (String) iter.next();
203                                    policyConfiguration.addToRole(role, new JMSBrokerPermission(
204                                                    brokerSecurity.getBrokerName(), JMSBrokerPermission.CONNECT_ACTION));
205                            }
206    
207                            policyConfiguration.commit();
208    
209            } catch (ClassNotFoundException e) {
210                            e.printStackTrace();
211                    } catch (PolicyContextException e) {
212                            e.printStackTrace();
213                    }
214                    
215            }
216    
217            /**
218             * Creates a JACC PolicyConfiguration for the broker security.
219             * 
220             * @param brokerSecurity
221             */
222            static public void secure(DestinationSecurityConfig destinationSecurity) {
223    
224            try {
225                    
226                    String policyId = getDestinationPoicyContextId(destinationSecurity.getBrokerName(), destinationSecurity.getDestination());
227                            PolicyConfigurationFactory factory = PolicyConfigurationFactory.getPolicyConfigurationFactory();
228                            PolicyConfiguration policyConfiguration = factory.getPolicyConfiguration(policyId, true);
229    
230                            for (Iterator iter = destinationSecurity.getConsumeRoles().iterator(); iter.hasNext();) {
231                                    String role = (String) iter.next();
232                                    policyConfiguration.addToRole(role, new JMSDestinationPermission(
233                                                    destinationSecurity.getDestination().getPhysicalName(), JMSDestinationPermission.CONSUME_ACTION));
234                            }
235                            for (Iterator iter = destinationSecurity.getProduceRoles().iterator(); iter.hasNext();) {
236                                    String role = (String) iter.next();
237                                    policyConfiguration.addToRole(role, new JMSDestinationPermission(
238                                                    destinationSecurity.getDestination().getPhysicalName(), JMSDestinationPermission.PRODUCE_ACTION));
239                            }
240                            for (Iterator iter = destinationSecurity.getSendRoles().iterator(); iter.hasNext();) {
241                                    String role = (String) iter.next();
242                                    policyConfiguration.addToRole(role, new JMSDestinationPermission(
243                                                    destinationSecurity.getDestination().getPhysicalName(), JMSDestinationPermission.SEND_ACTION));
244                            }
245                            
246                            policyConfiguration.commit();
247    
248            } catch (ClassNotFoundException e) {
249                            e.printStackTrace();
250                    } catch (PolicyContextException e) {
251                            e.printStackTrace();
252                    }
253                    
254            }
255    
256    }