View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.geronimo.javamail.authentication;
21  
22  import java.io.UnsupportedEncodingException ;
23  import java.util.Map;        
24  import java.util.Properties; 
25  
26  import javax.mail.MessagingException;
27  
28  import javax.security.auth.callback.Callback;
29  import javax.security.auth.callback.CallbackHandler;
30  import javax.security.auth.callback.NameCallback;
31  import javax.security.auth.callback.PasswordCallback;
32  import javax.security.sasl.Sasl; 
33  import javax.security.sasl.SaslClient; 
34  import javax.security.sasl.SaslException; 
35  import javax.security.sasl.RealmCallback; 
36  import javax.security.sasl.RealmChoiceCallback; 
37  
38  public class SASLAuthenticator implements ClientAuthenticator, CallbackHandler {
39      // The realm we're authenticating within 
40      protected String realm; 
41      // the user we're authenticating
42      protected String username;
43      // the user's password (the "shared secret")
44      protected String password;
45      // the authenticator we're proxying 
46      protected SaslClient authenticator; 
47  
48      protected boolean complete = false;
49  
50      /**
51       * Main constructor.
52       * 
53       * @param username
54       *            The login user name.
55       * @param password
56       *            The login password.
57       */
58      public SASLAuthenticator(String[] mechanisms, Properties properties, String protocol, String host, String realm, 
59              String authorizationID, String username, String password) throws MessagingException {
60          this.realm = realm; 
61          this.username = username;
62          this.password = password;
63          try {
64              authenticator = Sasl.createSaslClient(mechanisms, authorizationID, protocol, host, (Map)properties, 
65                  this); 
66          } catch (SaslException e) {
67          } 
68      }
69      
70      
71      /**
72       * Respond to the hasInitialResponse query. We defer this to the Sasl client.  
73       * 
74       * @return The SaslClient response to the same query. 
75       */
76      public boolean hasInitialResponse() {
77          return authenticator.hasInitialResponse(); 
78      }
79  
80      /**
81       * Indicate whether the challenge/response process is complete.
82       * 
83       * @return True if the last challenge has been processed, false otherwise.
84       */
85      public boolean isComplete() {
86          return authenticator.hasInitialResponse(); 
87      }
88  
89      /**
90       * Retrieve the authenticator mechanism name.
91       * 
92       * @return Always returns the string "PLAIN"
93       */
94      public String getMechanismName() {
95          // the authenticator selects this for us. 
96          return authenticator.getMechanismName(); 
97      }
98  
99      /**
100      * Evaluate a login challenge, returning the a result string that
101      * should satisfy the clallenge.  This is forwarded to the 
102      * SaslClient, which will use the CallBackHandler to retrieve the 
103      * information it needs for the given protocol. 
104      * 
105      * @param challenge
106      *            The decoded challenge data, as byte array.
107      * 
108      * @return A formatted challege response, as an array of bytes.
109      * @exception MessagingException
110      */
111     public byte[] evaluateChallenge(byte[] challenge) throws MessagingException {
112         // for an initial response challenge, there's no challenge date.  The SASL 
113         // client still expects a byte array argument. 
114         if (challenge == null) {
115             challenge = new byte[0];
116         }
117         
118         try {
119             return authenticator.evaluateChallenge(challenge);
120         } catch (SaslException e) {
121             // got an error, fail this
122             throw new MessagingException("Error performing SASL validation", e);
123         }
124     }
125     
126     public void handle(Callback[] callBacks) {
127         for (int i = 0; i < callBacks.length; i++) {
128             Callback callBack = callBacks[i]; 
129             // requesting the user name 
130             if (callBack instanceof NameCallback) {
131                 ((NameCallback)callBack).setName(username); 
132             }
133             // need the password 
134             else if (callBack instanceof PasswordCallback) {
135                 ((PasswordCallback)callBack).setPassword(password.toCharArray()); 
136             }
137             // direct request for the realm information 
138             else if (callBack instanceof RealmCallback) {
139                 RealmCallback realmCallback = (RealmCallback)callBack; 
140                 // we might not have a realm, so use the default from the 
141                 // callback item 
142                 if (realm == null) {
143                     realmCallback.setText(realmCallback.getDefaultText()); 
144                 }
145                 else { 
146                     realmCallback.setText(realm); 
147                 }
148             }
149             // asked to select the realm information from a list 
150             else if (callBack instanceof RealmChoiceCallback) {
151                 RealmChoiceCallback realmCallback = (RealmChoiceCallback)callBack; 
152                 // if we don't have a realm, just tell it to use the default 
153                 if (realm == null) {
154                     realmCallback.setSelectedIndex(realmCallback.getDefaultChoice()); 
155                 }
156                 else {
157                     // locate our configured one in the list 
158                     String[] choices = realmCallback.getChoices(); 
159 
160                     for (int j = 0; j < choices.length; j++) {
161                         // set the index to any match and get out of here. 
162                         if (choices[j].equals(realm)) {
163                             realmCallback.setSelectedIndex(j); 
164                             break; 
165                         }
166                     }
167                     // NB:  If there was no match, we don't set anything.  
168                     // this should cause an authentication failure. 
169                 }
170             }
171         }
172     }
173 }
174