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