View Javadoc

1   /*
2    * $Id: NTLMScheme.java 22241 2011-06-22 14:47:53Z pablo.kraan $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  package org.mule.transport.http.ntlm;
11  
12  import jcifs.ntlmssp.NtlmMessage;
13  import jcifs.ntlmssp.Type2Message;
14  import org.apache.commons.httpclient.Credentials;
15  import org.apache.commons.httpclient.HttpMethod;
16  import org.apache.commons.httpclient.NTCredentials;
17  import org.apache.commons.httpclient.auth.AuthChallengeParser;
18  import org.apache.commons.httpclient.auth.AuthScheme;
19  import org.apache.commons.httpclient.auth.AuthenticationException;
20  import org.apache.commons.httpclient.auth.InvalidCredentialsException;
21  import org.apache.commons.httpclient.auth.MalformedChallengeException;
22  
23  import static jcifs.util.Base64.encode;
24  
25  /**
26   * Reimplements {@link org.apache.commons.httpclient.auth.NTLMScheme} using JCIFS
27   * org.apache.commons.httpclient.auth.NTLMScheme. <p>
28   * This class has to be registered manually in order to be used:
29   * <code>
30   * AuthPolicy.registerAuthScheme(AuthPolicy.NTLM, NTLMScheme.class);
31   * </code>
32   */
33  public class NTLMScheme implements AuthScheme
34  {
35  
36      private static enum AUTHENTICATION_STATE
37      {
38          UNINITIATED, INITIATED, TYPE1_MSG_GENERATED, TYPE2_MSG_RECEIVED, TYPE3_MSG_GENERATED, FAILED
39      }
40  
41      private static final String NOT_IMPLEMENTED_ERROR = "Not implemented as it is deprecated anyway in Httpclient 3.x";
42  
43      /**
44       * Authentication process authenticationState
45       */
46      private AUTHENTICATION_STATE authenticationState = AUTHENTICATION_STATE.UNINITIATED;
47  
48      /**
49       * NTLM challenge string received form the server.
50       */
51      private String receivedNtlmChallenge = null;
52  
53      /**
54       * Creates NTLM messages used during the authentication process.
55       */
56      private final NtlmMessageFactory ntlmMessageFactory = new NtlmMessageFactory();
57  
58      public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException
59      {
60          if (authenticationState == AUTHENTICATION_STATE.UNINITIATED)
61          {
62              throw new IllegalStateException("NTLM authentication process has not been initiated");
63          }
64  
65          NtlmMessage response;
66  
67          NTCredentials ntcredentials = getNTCredentials(credentials);
68          if (authenticationState == AUTHENTICATION_STATE.INITIATED || authenticationState == AUTHENTICATION_STATE.FAILED)
69          {
70              // Original implementation used ntCredential info instead of null values
71              response = ntlmMessageFactory.createType1Message(null, null);
72              authenticationState = AUTHENTICATION_STATE.TYPE1_MSG_GENERATED;
73          }
74          else
75          {
76              Type2Message type2MessageFromChallenge = ntlmMessageFactory.createType2Message(receivedNtlmChallenge);
77              response = ntlmMessageFactory.createType3Message(ntcredentials, type2MessageFromChallenge);
78              authenticationState = AUTHENTICATION_STATE.TYPE3_MSG_GENERATED;
79          }
80  
81          return ntlmMessageToString(response);
82      }
83  
84      private NTCredentials getNTCredentials(Credentials credentials) throws InvalidCredentialsException
85      {
86          try
87          {
88              return (NTCredentials) credentials;
89          }
90          catch (ClassCastException e)
91          {
92              throw new InvalidCredentialsException("Credentials cannot be used for NTLM authentication: "
93                                                    + credentials.getClass().getName());
94          }
95      }
96  
97      public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException
98      {
99          throw new RuntimeException(NOT_IMPLEMENTED_ERROR);
100     }
101 
102     public String getID()
103     {
104         throw new RuntimeException(NOT_IMPLEMENTED_ERROR);
105     }
106 
107     /**
108      * Returns the authentication parameter with the given name, if available.
109      * <p/>
110      * There are no valid parameters for NTLM authentication so this method
111      * <p/>
112      * always returns <tt>null</tt>.
113      * <p/>
114      *
115      * @param name The name of the parameter to be returned
116      * @return the parameter with the given name
117      */
118     public String getParameter(String name)
119     {
120         if (name == null)
121         {
122             throw new IllegalArgumentException("Parameter name may not be null");
123         }
124 
125         return null;
126     }
127 
128     /**
129      * The concept of an authentication realm is not supported by the NTLM
130      * authentication scheme. Always returns <code>null</code>.
131      *
132      * @return <code>null</code>
133      */
134 
135     public String getRealm()
136     {
137         return null;
138     }
139 
140     /**
141      * Returns textual designation of the NTLM authentication scheme.
142      *
143      * @return <code>ntlm</code>
144      */
145     public String getSchemeName()
146     {
147         return "ntlm";
148     }
149 
150     /**
151      * Tests if the NTLM authentication process has been completed.
152      *
153      * @return <tt>true</tt> if Basic authorization has been processed,
154      *         <tt>false</tt> otherwise.
155      */
156     public boolean isComplete()
157     {
158         return authenticationState == AUTHENTICATION_STATE.TYPE3_MSG_GENERATED || authenticationState == AUTHENTICATION_STATE.FAILED;
159     }
160 
161     /**
162      * Returns <tt>true</tt>. NTLM authentication scheme is connection based.
163      *
164      * @return <tt>true</tt>.
165      */
166     public boolean isConnectionBased()
167     {
168         return true;
169     }
170 
171     /**
172      * Processes the NTLM challenge.
173      *
174      * @param challenge the challenge string
175      * @throws MalformedChallengeException is thrown if the authentication challenge is malformed
176      */
177     public void processChallenge(final String challenge) throws MalformedChallengeException
178     {
179         String s = AuthChallengeParser.extractScheme(challenge);
180 
181         if (!s.equalsIgnoreCase(getSchemeName()))
182         {
183             throw new MalformedChallengeException("Invalid NTLM challenge: " + challenge);
184         }
185 
186         int i = challenge.indexOf(' ');
187 
188         if (i != -1)
189         {
190             s = challenge.substring(i, challenge.length());
191             receivedNtlmChallenge = s.trim();
192             authenticationState = AUTHENTICATION_STATE.TYPE2_MSG_RECEIVED;
193         }
194         else
195         {
196             receivedNtlmChallenge = null;
197             authenticationState = authenticationState == AUTHENTICATION_STATE.UNINITIATED ? AUTHENTICATION_STATE.INITIATED : AUTHENTICATION_STATE.FAILED;
198         }
199     }
200 
201     private String ntlmMessageToString(NtlmMessage ntlmMessage)
202     {
203         return "NTLM " + encode(ntlmMessage.toByteArray());
204     }
205 }