1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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 }