RMIPasswordAuthenticator.java
001 /*
002  *
003  * Licensed to the Apache Software Foundation (ASF) under one
004  * or more contributor license agreements.  See the NOTICE file
005  * distributed with this work for additional information
006  * regarding copyright ownership.  The ASF licenses this file
007  * to you under the Apache License, Version 2.0 (the
008  * "License"); you may not use this file except in compliance
009  * with the License.  You may obtain a copy of the License at
010  *
011  *   http://www.apache.org/licenses/LICENSE-2.0
012  *
013  * Unless required by applicable law or agreed to in writing,
014  * software distributed under the License is distributed on an
015  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016  * KIND, either express or implied.  See the License for the
017  * specific language governing permissions and limitations
018  * under the License.
019  *
020  */
021 package org.apache.qpid.server.security.auth.rmi;
022 
023 import java.io.IOException;
024 import java.io.UnsupportedEncodingException;
025 import java.security.MessageDigest;
026 import java.security.NoSuchAlgorithmException;
027 import java.util.Arrays;
028 import java.util.Collections;
029 
030 import javax.management.remote.JMXAuthenticator;
031 import javax.management.remote.JMXPrincipal;
032 import javax.security.auth.Subject;
033 import javax.security.auth.callback.PasswordCallback;
034 import javax.security.auth.login.AccountNotFoundException;
035 
036 import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase;
037 import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase;
038 import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
039 import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
040 
041 public class RMIPasswordAuthenticator implements JMXAuthenticator
042 {
043     static final String UNABLE_TO_LOOKUP = "The broker was unable to lookup the user details";
044     static final String SHOULD_BE_STRING_ARRAY = "User details should be String[]";
045     static final String SHOULD_HAVE_2_ELEMENTS = "User details should have 2 elements, username, password";
046     static final String SHOULD_BE_NON_NULL = "Supplied username and password should be non-null";
047     static final String INVALID_CREDENTIALS = "Invalid user details supplied";
048     static final String CREDENTIALS_REQUIRED = "User details are required. " +
049                     "Please ensure you are using an up to date management console to connect.";
050     
051     public static final String DEFAULT_ENCODING = "utf-8";
052     private PrincipalDatabase _db = null;
053 
054     public RMIPasswordAuthenticator()
055     {
056     }
057     
058     public void setPrincipalDatabase(PrincipalDatabase pd)
059     {
060         this._db = pd;
061     }
062 
063     public Subject authenticate(Object credentialsthrows SecurityException
064     {
065         // Verify that credential's are of type String[].
066         if (!(credentials instanceof String[]))
067         {
068             if (credentials == null)
069             {
070                 throw new SecurityException(CREDENTIALS_REQUIRED);
071             }
072             else
073             {
074                 throw new SecurityException(SHOULD_BE_STRING_ARRAY);
075             }
076         }
077 
078         // Verify that required number of credential's.
079         final String[] userCredentials = (String[]) credentials;
080         if (userCredentials.length != 2)
081         {
082             throw new SecurityException(SHOULD_HAVE_2_ELEMENTS);
083         }
084 
085         String username = (StringuserCredentials[0];
086         String password = (StringuserCredentials[1];
087 
088         // Verify that all required credential's are actually present.
089         if (username == null || password == null)
090         {
091             throw new SecurityException(SHOULD_BE_NON_NULL);
092         }
093         
094         boolean authenticated = false;
095 
096         // Perform authentication
097         try
098         {
099             PasswordCallback pwCallback = new PasswordCallback("prompt",false);
100             UsernamePrincipal uname = new UsernamePrincipal(username);
101             
102             if (_db instanceof Base64MD5PasswordFilePrincipalDatabase)
103             {
104                 //retrieve the stored password for the given user
105                 _db.setPassword(uname, pwCallback);
106                 
107                 //compare the MD5Hash of the given password with the stored value
108                 if (Arrays.equals(getMD5Hash(password), pwCallback.getPassword()))
109                 {                  
110                     authenticated = true;
111                 }
112             }
113             else if (_db instanceof PlainPasswordFilePrincipalDatabase)
114             {
115                 //retrieve the users stored password and compare with given value
116                 _db.setPassword(uname, pwCallback);
117                 
118                 if (password.equals(new String(pwCallback.getPassword())))
119                 {
120                     authenticated = true;
121                 }
122             }
123             else
124             {
125                 throw new SecurityException(UNABLE_TO_LOOKUP);
126             }
127         }
128         catch (AccountNotFoundException e)
129         {
130             throw new SecurityException(INVALID_CREDENTIALS);
131         }
132         catch (UnsupportedEncodingException e)
133         {
134             throw new SecurityException(UNABLE_TO_LOOKUP);
135         }
136         catch (NoSuchAlgorithmException e)
137         {
138             throw new SecurityException(UNABLE_TO_LOOKUP);
139         }
140         catch (IOException e)
141         {
142             throw new SecurityException(UNABLE_TO_LOOKUP);
143         }
144 
145         if (authenticated)
146         {
147             //credential's check out, return the appropriate JAAS Subject
148             return new Subject(true,
149                     Collections.singleton(new JMXPrincipal(username)),
150                     Collections.EMPTY_SET,
151                     Collections.EMPTY_SET);
152         }
153         else
154         {
155             throw new SecurityException(INVALID_CREDENTIALS);
156         }
157     }
158     
159     public static char[] getMD5Hash(String textthrows NoSuchAlgorithmException, UnsupportedEncodingException
160     {
161         byte[] data = text.getBytes(DEFAULT_ENCODING);
162 
163         MessageDigest md = MessageDigest.getInstance("MD5");
164 
165         for (byte b : data)
166         {
167             md.update(b);
168         }
169 
170         byte[] digest = md.digest();
171 
172         char[] hash = new char[digest.length ];
173 
174         int index = 0;
175         for (byte b : digest)
176         {            
177             hash[index++(charb;
178         }
179 
180         return hash;
181     }
182 }