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