001 /**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements. See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package org.apache.activemq.jaas;
020
021 import java.io.File;
022 import java.io.IOException;
023 import java.security.cert.X509Certificate;
024 import java.util.Enumeration;
025 import java.util.HashSet;
026 import java.util.Map;
027 import java.util.Properties;
028 import java.util.Set;
029
030 import javax.security.auth.Subject;
031 import javax.security.auth.callback.CallbackHandler;
032 import javax.security.auth.login.LoginException;
033
034 /**
035 * A LoginModule allowing for SSL certificate based authentication based on Distinguished Names (DN) stored in text
036 * files.
037 *
038 * The DNs are parsed using a Properties class where each line is <user_name>=<user_DN>.
039 * This class also uses a group definition file where each line is <group_name>=<user_name_1>,<user_name_2>,etc.
040 * The user and group files' locations must be specified in the org.apache.activemq.jaas.textfiledn.user and
041 * org.apache.activemq.jaas.textfiledn.user properties respectively.
042 *
043 * NOTE: This class will re-read user and group files for every authentication (i.e it does live updates of allowed
044 * groups and users).
045 *
046 * @author sepandm@gmail.com (Sepand)
047 */
048 public class TextFileCertificateLoginModule extends CertificateLoginModule {
049
050 private final String USER_FILE = "org.apache.activemq.jaas.textfiledn.user";
051 private final String GROUP_FILE = "org.apache.activemq.jaas.textfiledn.group";
052
053 private File baseDir;
054 private String usersFilePathname;
055 private String groupsFilePathname;
056
057 /**
058 * Performs initialization of file paths.
059 *
060 * A standard JAAS override.
061 */
062 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
063 super.initialize(subject, callbackHandler, sharedState, options);
064 if (System.getProperty("java.security.auth.login.config") != null) {
065 baseDir = new File(System.getProperty("java.security.auth.login.config")).getParentFile();
066 } else {
067 baseDir = new File(".");
068 }
069
070 usersFilePathname = (String) options.get(USER_FILE)+"";
071 groupsFilePathname = (String) options.get(GROUP_FILE)+"";
072 }
073
074 /**
075 * Overriding to allow DN authorization based on DNs specified in text files.
076 *
077 * @param certs The certificate the incoming connection provided.
078 * @return The user's authenticated name or null if unable to authenticate the user.
079 * @throws LoginException Thrown if unable to find user file or connection certificate.
080 */
081 protected String getUserNameForCertificates(final X509Certificate[] certs) throws LoginException {
082 if (certs == null) {
083 throw new LoginException("Client certificates not found. Cannot authenticate.");
084 }
085
086 File usersFile = new File(baseDir,usersFilePathname);
087
088 Properties users = new Properties();
089
090 try {
091 users.load(new java.io.FileInputStream(usersFile));
092 } catch (IOException ioe) {
093 throw new LoginException("Unable to load user properties file " + usersFile);
094 }
095
096 String dn = getDistinguishedName(certs);
097
098 for(Enumeration vals = users.elements(), keys = users.keys(); vals.hasMoreElements(); ) {
099 if ( ((String)vals.nextElement()).equals(dn) ) {
100 return (String)keys.nextElement();
101 } else {
102 keys.nextElement();
103 }
104 }
105
106 return null;
107 }
108
109 /**
110 * Overriding to allow for group discovery based on text files.
111 *
112 * @param username The name of the user being examined. This is the same name returned by
113 * getUserNameForCertificates.
114 * @return A Set of name Strings for groups this user belongs to.
115 * @throws LoginException Thrown if unable to find group definition file.
116 */
117 protected Set getUserGroups(String username) throws LoginException {
118 File groupsFile = new File(baseDir, groupsFilePathname);
119
120 Properties groups = new Properties();
121 try {
122 groups.load(new java.io.FileInputStream(groupsFile));
123 } catch (IOException ioe) {
124 throw new LoginException("Unable to load group properties file " + groupsFile);
125 }
126 Set userGroups = new HashSet();
127 for (Enumeration enumeration = groups.keys(); enumeration.hasMoreElements();) {
128 String groupName = (String) enumeration.nextElement();
129 String[] userList = (groups.getProperty(groupName) + "").split(",");
130 for (int i = 0; i < userList.length; i++) {
131 if (username.equals(userList[i])) {
132 userGroups.add(groupName);
133 break;
134 }
135 }
136 }
137
138 return userGroups;
139 }
140 }