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 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
43
44
45
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
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
86 return null;
87 }
88
89 @Override
90 public String getRealm() {
91
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 }