View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.impl.auth;
28  
29  import org.apache.http.Header;
30  import org.apache.http.HttpRequest;
31  import org.apache.http.auth.AUTH;
32  import org.apache.http.auth.AuthenticationException;
33  import org.apache.http.auth.Credentials;
34  import org.apache.http.auth.InvalidCredentialsException;
35  import org.apache.http.auth.MalformedChallengeException;
36  import org.apache.http.auth.NTCredentials;
37  import org.apache.http.message.BufferedHeader;
38  import org.apache.http.util.Args;
39  import org.apache.http.util.CharArrayBuffer;
40  
41  /**
42   * NTLM is a proprietary authentication scheme developed by Microsoft
43   * and optimized for Windows platforms.
44   *
45   * @since 4.0
46   */
47  public class NTLMScheme extends AuthSchemeBase {
48  
49      enum State {
50          UNINITIATED,
51          CHALLENGE_RECEIVED,
52          MSG_TYPE1_GENERATED,
53          MSG_TYPE2_RECEVIED,
54          MSG_TYPE3_GENERATED,
55          FAILED,
56      }
57  
58      private final NTLMEngine engine;
59  
60      private State state;
61      private String challenge;
62  
63      public NTLMScheme(final NTLMEngine engine) {
64          super();
65          Args.notNull(engine, "NTLM engine");
66          this.engine = engine;
67          this.state = State.UNINITIATED;
68          this.challenge = null;
69      }
70  
71      /**
72       * @since 4.3
73       */
74      public NTLMScheme() {
75          this(new NTLMEngineImpl());
76      }
77  
78      @Override
79      public String getSchemeName() {
80          return "ntlm";
81      }
82  
83      @Override
84      public String getParameter(final String name) {
85          // String parameters not supported
86          return null;
87      }
88  
89      @Override
90      public String getRealm() {
91          // NTLM does not support the concept of an authentication realm
92          return null;
93      }
94  
95      @Override
96      public boolean isConnectionBased() {
97          return true;
98      }
99  
100     @Override
101     protected void parseChallenge(
102             final CharArrayBuffer buffer,
103             final int beginIndex, final int endIndex) throws MalformedChallengeException {
104         this.challenge = buffer.substringTrimmed(beginIndex, endIndex);
105         if (this.challenge.isEmpty()) {
106             if (this.state == State.UNINITIATED) {
107                 this.state = State.CHALLENGE_RECEIVED;
108             } else {
109                 this.state = State.FAILED;
110             }
111         } else {
112             if (this.state.compareTo(State.MSG_TYPE1_GENERATED) < 0) {
113                 this.state = State.FAILED;
114                 throw new MalformedChallengeException("Out of sequence NTLM response message");
115             } else if (this.state == State.MSG_TYPE1_GENERATED) {
116                 this.state = State.MSG_TYPE2_RECEVIED;
117             }
118         }
119     }
120 
121     @Override
122     public Header authenticate(
123             final Credentials credentials,
124             final HttpRequest request) throws AuthenticationException {
125         NTCredentials ntcredentials = null;
126         try {
127             ntcredentials = (NTCredentials) credentials;
128         } catch (final ClassCastException e) {
129             throw new InvalidCredentialsException(
130              "Credentials cannot be used for NTLM authentication: "
131               + credentials.getClass().getName());
132         }
133         String response = null;
134         if (this.state == State.FAILED) {
135             throw new AuthenticationException("NTLM authentication failed");
136         } else if (this.state == State.CHALLENGE_RECEIVED) {
137             response = this.engine.generateType1Msg(
138                     ntcredentials.getDomain(),
139                     ntcredentials.getWorkstation());
140             this.state = State.MSG_TYPE1_GENERATED;
141         } else if (this.state == State.MSG_TYPE2_RECEVIED) {
142             response = this.engine.generateType3Msg(
143                     ntcredentials.getUserName(),
144                     ntcredentials.getPassword(),
145                     ntcredentials.getDomain(),
146                     ntcredentials.getWorkstation(),
147                     this.challenge);
148             this.state = State.MSG_TYPE3_GENERATED;
149         } else {
150             throw new AuthenticationException("Unexpected state: " + this.state);
151         }
152         final CharArrayBuffer buffer = new CharArrayBuffer(32);
153         if (isProxy()) {
154             buffer.append(AUTH.PROXY_AUTH_RESP);
155         } else {
156             buffer.append(AUTH.WWW_AUTH_RESP);
157         }
158         buffer.append(": NTLM ");
159         buffer.append(response);
160         return new BufferedHeader(buffer);
161     }
162 
163     @Override
164     public boolean isComplete() {
165         return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED;
166     }
167 
168 }