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.hc.client5.http.impl.auth;
28
29 import java.security.Principal;
30
31 import org.apache.hc.client5.http.auth.AuthChallenge;
32 import org.apache.hc.client5.http.auth.AuthScheme;
33 import org.apache.hc.client5.http.auth.StandardAuthScheme;
34 import org.apache.hc.client5.http.auth.AuthScope;
35 import org.apache.hc.client5.http.auth.AuthenticationException;
36 import org.apache.hc.client5.http.auth.Credentials;
37 import org.apache.hc.client5.http.auth.CredentialsProvider;
38 import org.apache.hc.client5.http.auth.MalformedChallengeException;
39 import org.apache.hc.client5.http.auth.NTCredentials;
40 import org.apache.hc.client5.http.protocol.HttpClientContext;
41 import org.apache.hc.core5.http.HttpHost;
42 import org.apache.hc.core5.http.HttpRequest;
43 import org.apache.hc.core5.http.protocol.HttpContext;
44 import org.apache.hc.core5.util.Args;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48
49
50
51
52
53
54 public final class NTLMScheme implements AuthScheme {
55
56 private static final Logger LOG = LoggerFactory.getLogger(NTLMScheme.class);
57
58 enum State {
59 UNINITIATED,
60 CHALLENGE_RECEIVED,
61 MSG_TYPE1_GENERATED,
62 MSG_TYPE2_RECEIVED,
63 MSG_TYPE3_GENERATED,
64 FAILED,
65 }
66
67 private final NTLMEngine engine;
68
69 private State state;
70 private String challenge;
71 private NTCredentials credentials;
72
73 public NTLMScheme(final NTLMEngine engine) {
74 super();
75 Args.notNull(engine, "NTLM engine");
76 this.engine = engine;
77 this.state = State.UNINITIATED;
78 }
79
80
81
82
83 public NTLMScheme() {
84 this(new NTLMEngineImpl());
85 }
86
87 @Override
88 public String getName() {
89 return StandardAuthScheme.NTLM;
90 }
91
92 @Override
93 public boolean isConnectionBased() {
94 return true;
95 }
96
97 @Override
98 public String getRealm() {
99 return null;
100 }
101
102 @Override
103 public void processChallenge(
104 final AuthChallenge authChallenge,
105 final HttpContext context) throws MalformedChallengeException {
106 Args.notNull(authChallenge, "AuthChallenge");
107
108 this.challenge = authChallenge.getValue();
109 if (this.challenge == null || this.challenge.isEmpty()) {
110 if (this.state == State.UNINITIATED) {
111 this.state = State.CHALLENGE_RECEIVED;
112 } else {
113 this.state = State.FAILED;
114 }
115 } else {
116 if (this.state.compareTo(State.MSG_TYPE1_GENERATED) < 0) {
117 this.state = State.FAILED;
118 throw new MalformedChallengeException("Out of sequence NTLM response message");
119 } else if (this.state == State.MSG_TYPE1_GENERATED) {
120 this.state = State.MSG_TYPE2_RECEIVED;
121 }
122 }
123 }
124
125 @Override
126 public boolean isResponseReady(
127 final HttpHost host,
128 final CredentialsProvider credentialsProvider,
129 final HttpContext context) throws AuthenticationException {
130
131 Args.notNull(host, "Auth host");
132 Args.notNull(credentialsProvider, "CredentialsProvider");
133
134 final AuthScope authScope = new AuthScope(host, null, getName());
135 final Credentials credentials = credentialsProvider.getCredentials(
136 authScope, context);
137 if (credentials instanceof NTCredentials) {
138 this.credentials = (NTCredentials) credentials;
139 return true;
140 }
141
142 if (LOG.isDebugEnabled()) {
143 final HttpClientContext clientContext = HttpClientContext.adapt(context);
144 final String exchangeId = clientContext.getExchangeId();
145 LOG.debug("{} No credentials found for auth scope [{}]", exchangeId, authScope);
146 }
147 return false;
148 }
149
150 @Override
151 public Principal getPrincipal() {
152 return this.credentials != null ? this.credentials.getUserPrincipal() : null;
153 }
154
155 @Override
156 public String generateAuthResponse(
157 final HttpHost host,
158 final HttpRequest request,
159 final HttpContext context) throws AuthenticationException {
160 if (this.credentials == null) {
161 throw new AuthenticationException("NT credentials not available");
162 }
163 final String response;
164 if (this.state == State.FAILED) {
165 throw new AuthenticationException("NTLM authentication failed");
166 } else if (this.state == State.CHALLENGE_RECEIVED) {
167 response = this.engine.generateType1Msg(
168 this.credentials.getNetbiosDomain(),
169 this.credentials.getWorkstation());
170 this.state = State.MSG_TYPE1_GENERATED;
171 } else if (this.state == State.MSG_TYPE2_RECEIVED) {
172 response = this.engine.generateType3Msg(
173 this.credentials.getUserName(),
174 this.credentials.getPassword(),
175 this.credentials.getNetbiosDomain(),
176 this.credentials.getWorkstation(),
177 this.challenge);
178 this.state = State.MSG_TYPE3_GENERATED;
179 } else {
180 throw new AuthenticationException("Unexpected state: " + this.state);
181 }
182 return StandardAuthScheme.NTLM + " " + response;
183 }
184
185 @Override
186 public boolean isChallengeComplete() {
187 return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED;
188 }
189
190 @Override
191 public String toString() {
192 return getName() + "{" + this.state + " " + challenge + '}';
193 }
194
195 }