/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.activemq.artemis.spi.core.security; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import javax.security.cert.X509Certificate; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.security.Principal; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration; import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnection; import org.apache.activemq.artemis.core.security.CheckType; import org.apache.activemq.artemis.core.security.Role; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler; import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal; import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal; import org.apache.activemq.artemis.utils.CertificateUtil; import org.jboss.logging.Logger; /** * This implementation delegates to the JAAS security interfaces. * * The {@link Subject} returned by the login context is expecting to have a set of {@link RolePrincipal} for each * role of the user. */ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager3 { private static final Logger logger = Logger.getLogger(ActiveMQJAASSecurityManager.class); private static final String WILDCARD = "*"; private String configurationName; private String certificateConfigurationName; private SecurityConfiguration configuration; private SecurityConfiguration certificateConfiguration; private String rolePrincipalClass = "org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal"; public ActiveMQJAASSecurityManager() { } public ActiveMQJAASSecurityManager(String configurationName) { this.configurationName = configurationName; } public ActiveMQJAASSecurityManager(String configurationName, String certificateConfigurationName) { this.configurationName = configurationName; this.certificateConfigurationName = certificateConfigurationName; } public ActiveMQJAASSecurityManager(String configurationName, SecurityConfiguration configuration) { this.configurationName = configurationName; this.configuration = configuration; } public ActiveMQJAASSecurityManager(String configurationName, String certificateConfigurationName, SecurityConfiguration configuration, SecurityConfiguration certificateConfiguration) { this.configurationName = configurationName; this.configuration = configuration; this.certificateConfigurationName = certificateConfigurationName; this.certificateConfiguration = certificateConfiguration; } @Override public boolean validateUser(String user, String password) { throw new UnsupportedOperationException("Invoke validateUser(String, String, X509Certificate[]) instead"); } @Override public String validateUser(final String user, final String password, X509Certificate[] certificates) { try { return getUserFromSubject(getAuthenticatedSubject(user, password, certificates)); } catch (LoginException e) { if (logger.isDebugEnabled()) { logger.debug("Couldn't validate user", e); } return null; } } public String getUserFromSubject(Subject subject) { String validatedUser = ""; Set<UserPrincipal> users = subject.getPrincipals(UserPrincipal.class); // should only ever be 1 UserPrincipal for (UserPrincipal userPrincipal : users) { validatedUser = userPrincipal.getName(); } return validatedUser; } @Override public boolean validateUserAndRole(String user, String password, Set<Role> roles, CheckType checkType) { throw new UnsupportedOperationException("Invoke validateUserAndRole(String, String, Set<Role>, CheckType, String, RemotingConnection) instead"); } @Override public String validateUserAndRole(final String user, final String password, final Set<Role> roles, final CheckType checkType, final String address, final RemotingConnection connection) { X509Certificate[] certificates = null; if (connection != null && connection.getTransportConnection() instanceof NettyConnection) { certificates = CertificateUtil.getCertsFromChannel(((NettyConnection) connection.getTransportConnection()).getChannel()); } Subject localSubject; try { localSubject = getAuthenticatedSubject(user, password, certificates); } catch (LoginException e) { if (logger.isDebugEnabled()) { logger.debug("Couldn't validate user", e); } return null; } boolean authorized = false; if (localSubject != null) { Set<RolePrincipal> rolesWithPermission = getPrincipalsInRole(checkType, roles); // Check the caller's roles Set<Principal> rolesForSubject = new HashSet<>(); try { rolesForSubject.addAll(localSubject.getPrincipals(Class.forName(rolePrincipalClass).asSubclass(Principal.class))); } catch (Exception e) { logger.info("Can't find roles for the subject", e); } if (rolesForSubject.size() > 0 && rolesWithPermission.size() > 0) { Iterator<Principal> rolesForSubjectIter = rolesForSubject.iterator(); while (!authorized && rolesForSubjectIter.hasNext()) { Iterator<RolePrincipal> rolesWithPermissionIter = rolesWithPermission.iterator(); Principal subjectRole = rolesForSubjectIter.next(); while (!authorized && rolesWithPermissionIter.hasNext()) { Principal roleWithPermission = rolesWithPermissionIter.next(); authorized = subjectRole.equals(roleWithPermission); } } } if (logger.isTraceEnabled()) { logger.trace("user " + (authorized ? " is " : " is NOT ") + "authorized"); } } if (authorized) { return getUserFromSubject(localSubject); } else { return null; } } private Subject getAuthenticatedSubject(final String user, final String password, final X509Certificate[] certificates) throws LoginException { LoginContext lc; ClassLoader currentLoader = Thread.currentThread().getContextClassLoader(); ClassLoader thisLoader = this.getClass().getClassLoader(); try { if (thisLoader != currentLoader) { Thread.currentThread().setContextClassLoader(thisLoader); } if (certificateConfigurationName != null && certificateConfigurationName.length() > 0 && certificates != null) { lc = new LoginContext(certificateConfigurationName, null, new JaasCallbackHandler(user, password, certificates), certificateConfiguration); } else { lc = new LoginContext(configurationName, null, new JaasCallbackHandler(user, password, certificates), configuration); } lc.login(); return lc.getSubject(); } finally { if (thisLoader != currentLoader) { Thread.currentThread().setContextClassLoader(currentLoader); } } } private Set<RolePrincipal> getPrincipalsInRole(final CheckType checkType, final Set<Role> roles) { Set principals = new HashSet<>(); for (Role role : roles) { if (checkType.hasRole(role)) { try { principals.add(createGroupPrincipal(role.getName(), rolePrincipalClass)); } catch (Exception e) { logger.info("Can't add role principal", e); } } } return principals; } public void setConfigurationName(final String configurationName) { this.configurationName = configurationName; } public void setConfiguration(SecurityConfiguration configuration) { this.configuration = configuration; } public void setCertificateConfigurationName(final String certificateConfigurationName) { this.certificateConfigurationName = certificateConfigurationName; } public void setCertificateConfiguration(SecurityConfiguration certificateConfiguration) { this.certificateConfiguration = certificateConfiguration; } public SecurityConfiguration getConfiguration() { if (configuration == null) { configuration = new SecurityConfiguration(); } return configuration; } public SecurityConfiguration getCertificateConfiguration() { if (certificateConfiguration == null) { certificateConfiguration = new SecurityConfiguration(); } return certificateConfiguration; } public String getRolePrincipalClass() { return rolePrincipalClass; } public void setRolePrincipalClass(String rolePrincipalClass) { this.rolePrincipalClass = rolePrincipalClass; } public static Object createGroupPrincipal(String name, String groupClass) throws Exception { if (WILDCARD.equals(name)) { // simple match all group principal - match any name and class return new Principal() { @Override public String getName() { return WILDCARD; } @Override public boolean equals(Object other) { return true; } @Override public int hashCode() { return WILDCARD.hashCode(); } }; } Object[] param = new Object[]{name}; Class<?> cls = Class.forName(groupClass); Constructor<?>[] constructors = cls.getConstructors(); int i; Object instance; for (i = 0; i < constructors.length; i++) { Class<?>[] paramTypes = constructors[i].getParameterTypes(); if (paramTypes.length != 0 && paramTypes[0].equals(String.class)) { break; } } if (i < constructors.length) { instance = constructors[i].newInstance(param); } else { instance = cls.newInstance(); Method[] methods = cls.getMethods(); i = 0; for (i = 0; i < methods.length; i++) { Class<?>[] paramTypes = methods[i].getParameterTypes(); if (paramTypes.length != 0 && methods[i].getName().equals("setName") && paramTypes[0].equals(String.class)) { break; } } if (i < methods.length) { methods[i].invoke(instance, param); } else { throw new NoSuchMethodException(); } } return instance; } }