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