View Javadoc

1   /*
2    * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/auth/NTLMScheme.java,v 1.21 2004/05/13 04:02:00 mbecke Exp $
3    * $Revision: 156690 $
4    * $Date: 2005-03-09 16:40:30 -0500 (Wed, 09 Mar 2005) $
5    *
6    * ====================================================================
7    *
8    *  Copyright 2002-2004 The Apache Software Foundation
9    *
10   *  Licensed under the Apache License, Version 2.0 (the "License");
11   *  you may not use this file except in compliance with the License.
12   *  You may obtain a copy of the License at
13   *
14   *      http://www.apache.org/licenses/LICENSE-2.0
15   *
16   *  Unless required by applicable law or agreed to in writing, software
17   *  distributed under the License is distributed on an "AS IS" BASIS,
18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   *  See the License for the specific language governing permissions and
20   *  limitations under the License.
21   * ====================================================================
22   *
23   * This software consists of voluntary contributions made by many
24   * individuals on behalf of the Apache Software Foundation.  For more
25   * information on the Apache Software Foundation, please see
26   * <http://www.apache.org/>.
27   *
28   */
29  
30  package org.apache.commons.httpclient.auth;
31  
32  import org.apache.commons.httpclient.Credentials;
33  import org.apache.commons.httpclient.HttpMethod;
34  import org.apache.commons.httpclient.NTCredentials;
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  
38  /*** An implementation of the Microsoft proprietary NTLM authentication scheme.  For a detailed
39   * explanation of the NTLM scheme please see <a href="http://davenport.sourceforge.net/ntlm.html">
40   * http://davenport.sourceforge.net/ntlm.html</a>.
41   * 
42   * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
43   * @author Rodney Waldhoff
44   * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
45   * @author Ortwin Gl???ck
46   * @author Sean C. Sullivan
47   * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
48   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
49   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
50   */
51  public class NTLMScheme implements AuthScheme {
52  
53      /*** Log object for this class. */
54      private static final Log LOG = LogFactory.getLog(NTLMScheme.class);
55  
56      /*** NTLM challenge string. */
57      private String ntlmchallenge = null;
58  
59      private static final int UNINITIATED         = 0;
60      private static final int INITIATED           = 1;
61      private static final int TYPE1_MSG_GENERATED = 2;
62      private static final int TYPE2_MSG_RECEIVED  = 3;
63      private static final int TYPE3_MSG_GENERATED = 4;
64      private static final int FAILED              = Integer.MAX_VALUE;
65  
66      /*** Authentication process state */
67      private int state;
68      
69      /***
70       * Default constructor for the NTLM authentication scheme.
71       * 
72       * @since 3.0
73       */
74      public NTLMScheme() {
75          super();
76          this.state = UNINITIATED;
77      }
78  
79      /***
80       * Constructor for the NTLM authentication scheme.
81       * 
82       * @param challenge The authentication challenge
83       * 
84       * @throws MalformedChallengeException is thrown if the authentication challenge
85       * is malformed
86       */
87      public NTLMScheme(final String challenge) throws MalformedChallengeException {
88          super();
89          processChallenge(challenge);
90      }
91  
92      /***
93       * Processes the NTLM challenge.
94       *  
95       * @param challenge the challenge string
96       * 
97       * @throws MalformedChallengeException is thrown if the authentication challenge
98       * is malformed
99       * 
100      * @since 3.0
101      */
102     public void processChallenge(final String challenge) throws MalformedChallengeException {
103         String s = AuthChallengeParser.extractScheme(challenge);
104         if (!s.equalsIgnoreCase(getSchemeName())) {
105             throw new MalformedChallengeException("Invalid NTLM challenge: " + challenge);
106         }
107         int i = challenge.indexOf(' ');
108         if (i != -1) {
109             s = challenge.substring(i, challenge.length());
110             this.ntlmchallenge = s.trim();
111             this.state = TYPE2_MSG_RECEIVED;
112         } else {
113             this.ntlmchallenge = "";
114             if (this.state == UNINITIATED) {
115                 this.state = INITIATED;
116             } else {
117                 this.state = FAILED;
118             }
119         }
120     }
121 
122     /***
123      * Tests if the NTLM authentication process has been completed.
124      * 
125      * @return <tt>true</tt> if Basic authorization has been processed,
126      *   <tt>false</tt> otherwise.
127      * 
128      * @since 3.0
129      */
130     public boolean isComplete() {
131         return this.state == TYPE3_MSG_GENERATED || this.state == FAILED;
132     }
133 
134     /***
135      * Returns textual designation of the NTLM authentication scheme.
136      * 
137      * @return <code>ntlm</code>
138      */
139     public String getSchemeName() {
140         return "ntlm";
141     }
142 
143     /***
144      * The concept of an authentication realm is not supported by the NTLM 
145      * authentication scheme. Always returns <code>null</code>.
146      * 
147      * @return <code>null</code>
148      */
149     public String getRealm() {
150         return null;
151     }
152     
153     /***
154      * Returns a String identifying the authentication challenge.  This is
155      * used, in combination with the host and port to determine if
156      * authorization has already been attempted or not.  Schemes which
157      * require multiple requests to complete the authentication should
158      * return a different value for each stage in the request.
159      * 
160      * <p>Additionally, the ID should take into account any changes to the
161      * authentication challenge and return a different value when appropriate.
162      * For example when the realm changes in basic authentication it should be
163      * considered a different authentication attempt and a different value should
164      * be returned.</p>
165      * 
166      * @return String a String identifying the authentication challenge.  The
167      * returned value may be null.
168      * 
169      * @deprecated no longer used
170      */
171     public String getID() {
172         return ntlmchallenge;
173     }
174     
175     /***
176      * Returns the authentication parameter with the given name, if available.
177      * 
178      * <p>There are no valid parameters for NTLM authentication so this method always returns
179      * <tt>null</tt>.</p>
180      * 
181      * @param name The name of the parameter to be returned
182      * 
183      * @return the parameter with the given name
184      */
185     public String getParameter(String name) {
186         if (name == null) {
187             throw new IllegalArgumentException("Parameter name may not be null"); 
188         }
189         return null;
190     }
191 
192     /***
193      * Returns <tt>true</tt>. NTLM authentication scheme is connection based.
194      * 
195      * @return <tt>true</tt>.
196      * 
197      * @since 3.0
198      */
199     public boolean isConnectionBased() {
200         return true;    
201     }
202 
203     /***
204      * Create a NTLM authorization string for the given
205      * challenge and NT credentials.
206      *
207      * @param challenge The challenge.
208      * @param credentials {@link NTCredentials}
209      *
210      * @return a ntlm authorization string
211      * @throws AuthenticationException is thrown if authentication fails
212      * 
213      * @deprecated Use non-static {@link #authenticate(Credentials, HttpMethod)}
214      */
215     public static String authenticate(
216      final NTCredentials credentials, final String challenge) 
217       throws AuthenticationException {
218 
219         LOG.trace("enter NTLMScheme.authenticate(NTCredentials, String)");
220 
221         if (credentials == null) {
222             throw new IllegalArgumentException("Credentials may not be null");
223         }
224         
225         NTLM ntlm = new NTLM();
226         String s = ntlm.getResponseFor(challenge,
227         credentials.getUserName(), credentials.getPassword(),
228         credentials.getHost(), credentials.getDomain());
229         return "NTLM " + s;
230     }
231 
232     /***
233      * Create a NTLM authorization string for the given
234      * challenge and NT credentials.
235      *
236      * @param challenge The challenge.
237      * @param credentials {@link NTCredentials}
238      * @param charset The charset to use for encoding the credentials
239      *
240      * @return a ntlm authorization string
241      * @throws AuthenticationException is thrown if authentication fails
242      * 
243      * @deprecated Use non-static {@link #authenticate(Credentials, HttpMethod)}
244      * 
245      * @since 3.0
246      */
247     public static String authenticate(
248         final NTCredentials credentials, 
249 		final String challenge,
250 		String charset
251     ) throws AuthenticationException {
252 
253         LOG.trace("enter NTLMScheme.authenticate(NTCredentials, String)");
254 
255         if (credentials == null) {
256             throw new IllegalArgumentException("Credentials may not be null");
257         }
258         
259         NTLM ntlm = new NTLM();
260         ntlm.setCredentialCharset(charset);
261         String s = ntlm.getResponseFor(
262             challenge,
263             credentials.getUserName(), 
264 			credentials.getPassword(),
265 			credentials.getHost(), 
266 			credentials.getDomain());
267         return "NTLM " + s;
268     }
269     
270     /***
271      * Produces NTLM authorization string for the given set of 
272      * {@link Credentials}.
273      * 
274      * @param credentials The set of credentials to be used for athentication
275      * @param method Method name is ignored by the NTLM authentication scheme
276      * @param uri URI is ignored by the NTLM authentication scheme
277      * @throws InvalidCredentialsException if authentication credentials
278      *         are not valid or not applicable for this authentication scheme
279      * @throws AuthenticationException if authorization string cannot 
280      *   be generated due to an authentication failure
281      * 
282      * @return an NTLM authorization string
283      * 
284      * @deprecated Use {@link #authenticate(Credentials, HttpMethod)}
285      */
286     public String authenticate(Credentials credentials, String method, String uri) 
287       throws AuthenticationException {
288         LOG.trace("enter NTLMScheme.authenticate(Credentials, String, String)");
289 
290         NTCredentials ntcredentials = null;
291         try {
292             ntcredentials = (NTCredentials) credentials;
293         } catch (ClassCastException e) {
294             throw new InvalidCredentialsException(
295              "Credentials cannot be used for NTLM authentication: " 
296               + credentials.getClass().getName());
297         }
298         return NTLMScheme.authenticate(ntcredentials, this.ntlmchallenge);
299     }
300     
301     /***
302      * Produces NTLM authorization string for the given set of 
303      * {@link Credentials}.
304      * 
305      * @param credentials The set of credentials to be used for athentication
306      * @param method The method being authenticated
307      * 
308      * @throws InvalidCredentialsException if authentication credentials
309      *         are not valid or not applicable for this authentication scheme
310      * @throws AuthenticationException if authorization string cannot 
311      *   be generated due to an authentication failure
312      * 
313      * @return an NTLM authorization string
314      * 
315      * @since 3.0
316      */
317     public String authenticate(
318         Credentials credentials, 
319         HttpMethod method
320     ) throws AuthenticationException {
321         LOG.trace("enter NTLMScheme.authenticate(Credentials, HttpMethod)");
322 
323         if (this.state == UNINITIATED) {
324             throw new IllegalStateException("NTLM authentication process has not been initiated");
325         }
326 
327         NTCredentials ntcredentials = null;
328         try {
329             ntcredentials = (NTCredentials) credentials;
330         } catch (ClassCastException e) {
331             throw new InvalidCredentialsException(
332                     "Credentials cannot be used for NTLM authentication: " 
333                     + credentials.getClass().getName());
334         }
335         NTLM ntlm = new NTLM();
336         ntlm.setCredentialCharset(method.getParams().getCredentialCharset());
337         String response = null;
338         if (this.state == INITIATED || this.state == FAILED) {
339             response = ntlm.getType1Message(
340                 ntcredentials.getHost(), 
341                 ntcredentials.getDomain());
342             this.state = TYPE1_MSG_GENERATED;
343         } else {
344             response = ntlm.getType3Message(
345                 ntcredentials.getUserName(), 
346                 ntcredentials.getPassword(),
347                 ntcredentials.getHost(), 
348                 ntcredentials.getDomain(),
349                 ntlm.parseType2Message(this.ntlmchallenge));
350             this.state = TYPE3_MSG_GENERATED;
351         }
352         return "NTLM " + response;
353     }    
354 }