001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with 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,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 *
020 */
021 package org.apache.qpid.server.security.auth.database;
022
023 import org.apache.log4j.Logger;
024 import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
025 import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
026 import org.apache.qpid.server.security.auth.sasl.amqplain.AmqPlainInitialiser;
027 import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5Initialiser;
028 import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser;
029
030 import javax.security.auth.callback.PasswordCallback;
031 import javax.security.auth.login.AccountNotFoundException;
032 import java.io.BufferedReader;
033 import java.io.File;
034 import java.io.FileNotFoundException;
035 import java.io.FileReader;
036 import java.io.IOException;
037 import java.security.Principal;
038 import java.util.HashMap;
039 import java.util.LinkedList;
040 import java.util.List;
041 import java.util.Map;
042 import java.util.regex.Pattern;
043
044 /**
045 * Represents a user database where the account information is stored in a simple flat file.
046 *
047 * The file is expected to be in the form: username:password username1:password1 ... usernamen:passwordn
048 *
049 * where a carriage return separates each username/password pair. Passwords are assumed to be in plain text.
050 */
051 public class PlainPasswordFilePrincipalDatabase implements PrincipalDatabase
052 {
053 private static final Logger _logger = Logger.getLogger(PlainPasswordFilePrincipalDatabase.class);
054
055 protected File _passwordFile;
056
057 protected Pattern _regexp = Pattern.compile(":");
058
059 protected Map<String, AuthenticationProviderInitialiser> _saslServers;
060
061 public PlainPasswordFilePrincipalDatabase()
062 {
063 _saslServers = new HashMap<String, AuthenticationProviderInitialiser>();
064
065 /**
066 * Create Authenticators for Plain Password file.
067 */
068
069 // Accept AMQPlain incomming and compare it to the file.
070 AmqPlainInitialiser amqplain = new AmqPlainInitialiser();
071 amqplain.initialise(this);
072
073 // Accept Plain incomming and compare it to the file.
074 PlainInitialiser plain = new PlainInitialiser();
075 plain.initialise(this);
076
077 // Accept MD5 incomming and Hash file value for comparison
078 CRAMMD5Initialiser cram = new CRAMMD5Initialiser();
079 cram.initialise(this);
080
081 _saslServers.put(amqplain.getMechanismName(), amqplain);
082 _saslServers.put(plain.getMechanismName(), plain);
083 _saslServers.put(cram.getMechanismName(), cram);
084 }
085
086 public void setPasswordFile(String passwordFile) throws FileNotFoundException
087 {
088 File f = new File(passwordFile);
089 _logger.info("PlainPasswordFile using file " + f.getAbsolutePath());
090 _passwordFile = f;
091 if (!f.exists())
092 {
093 throw new FileNotFoundException("Cannot find password file " + f);
094 }
095 if (!f.canRead())
096 {
097 throw new FileNotFoundException("Cannot read password file " + f +
098 ". Check permissions.");
099 }
100 }
101
102 public void setPassword(Principal principal, PasswordCallback callback) throws IOException,
103 AccountNotFoundException
104 {
105 if (_passwordFile == null)
106 {
107 throw new AccountNotFoundException("Unable to locate principal since no password file was specified during initialisation");
108 }
109 if (principal == null)
110 {
111 throw new IllegalArgumentException("principal must not be null");
112 }
113 char[] pwd = lookupPassword(principal.getName());
114 if (pwd != null)
115 {
116 callback.setPassword(pwd);
117 }
118 else
119 {
120 throw new AccountNotFoundException("No account found for principal " + principal);
121 }
122 }
123
124 public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException
125 {
126 try
127 {
128 char[] pwd = lookupPassword(principal);
129
130 return compareCharArray(pwd, password);
131 }
132 catch (IOException e)
133 {
134 return false;
135 }
136 }
137
138 public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException
139 {
140 return false; // updates denied
141 }
142
143 public boolean createPrincipal(Principal principal, char[] password)
144 {
145 return false; // updates denied
146 }
147
148 public boolean deletePrincipal(Principal principal) throws AccountNotFoundException
149 {
150 return false; // updates denied
151 }
152
153 public Map<String, AuthenticationProviderInitialiser> getMechanisms()
154 {
155 return _saslServers;
156 }
157
158 public List<Principal> getUsers()
159 {
160 return new LinkedList<Principal>(); //todo
161 }
162
163 public Principal getUser(String username)
164 {
165 try
166 {
167 if (lookupPassword(username) != null)
168 {
169 return new UsernamePrincipal(username);
170 }
171 }
172 catch (IOException e)
173 {
174 //fall through to null return
175 }
176 return null;
177 }
178
179 private boolean compareCharArray(char[] a, char[] b)
180 {
181 boolean equal = false;
182 if (a.length == b.length)
183 {
184 equal = true;
185 int index = 0;
186 while (equal && index < a.length)
187 {
188 equal = a[index] == b[index];
189 index++;
190 }
191 }
192 return equal;
193 }
194
195
196 /**
197 * Looks up the password for a specified user in the password file. Note this code is <b>not</b> secure since it
198 * creates strings of passwords. It should be modified to create only char arrays which get nulled out.
199 *
200 * @param name the name of the principal to lookup
201 *
202 * @return char[] of the password
203 *
204 * @throws java.io.IOException whilst accessing the file
205 */
206 private char[] lookupPassword(String name) throws IOException
207 {
208 BufferedReader reader = null;
209 try
210 {
211 reader = new BufferedReader(new FileReader(_passwordFile));
212 String line;
213
214 while ((line = reader.readLine()) != null)
215 {
216 if (!line.startsWith("#"))
217 {
218 String[] result = _regexp.split(line);
219 if (result == null || result.length < 2)
220 {
221 continue;
222 }
223
224 if (name.equals(result[0]))
225 {
226 return result[1].toCharArray();
227 }
228 }
229 }
230 return null;
231 }
232 finally
233 {
234 if (reader != null)
235 {
236 reader.close();
237 }
238 }
239 }
240
241 public void reload() throws IOException
242 {
243 //This PD is not cached, so do nothing.
244 }
245 }
|