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
28
29
30
31
32
33 package org.metricshub.winrm.service.client.auth.ntlm;
34
35 import org.apache.http.Header;
36 import org.apache.http.HttpRequest;
37 import org.apache.http.auth.AUTH;
38 import org.apache.http.auth.AuthenticationException;
39 import org.apache.http.auth.Credentials;
40 import org.apache.http.auth.InvalidCredentialsException;
41 import org.apache.http.auth.MalformedChallengeException;
42 import org.apache.http.auth.NTCredentials;
43 import org.apache.http.impl.auth.AuthSchemeBase;
44 import org.apache.http.message.BufferedHeader;
45 import org.apache.http.protocol.HttpContext;
46 import org.apache.http.util.CharArrayBuffer;
47 import org.metricshub.winrm.Utils;
48
49
50
51
52
53
54
55
56
57 public class NTLMScheme extends AuthSchemeBase {
58
59 private enum State {
60 UNINITIATED,
61 CHALLENGE_RECEIVED,
62 MSG_TYPE1_GENERATED,
63 MSG_TYPE2_RECEVIED,
64 MSG_TYPE3_GENERATED,
65 FAILED
66 }
67
68 private final NTLMEngine engine;
69
70 private State state;
71 private String challenge;
72
73 public NTLMScheme(final NTLMEngine engine) {
74 super();
75 Utils.checkNonNull(engine, "engine");
76 this.engine = engine;
77 state = State.UNINITIATED;
78 challenge = null;
79 }
80
81 @Override
82 public String getSchemeName() {
83 return "ntlm";
84 }
85
86 @Override
87 public String getParameter(final String name) {
88
89 return null;
90 }
91
92 @Override
93 public String getRealm() {
94
95 return null;
96 }
97
98 @Override
99 public boolean isConnectionBased() {
100 return true;
101 }
102
103 @Override
104 protected void parseChallenge(final CharArrayBuffer buffer, final int beginIndex, final int endIndex)
105 throws MalformedChallengeException {
106 challenge = buffer.substringTrimmed(beginIndex, endIndex);
107 if (challenge.isEmpty()) {
108 if (state == State.UNINITIATED) {
109 state = State.CHALLENGE_RECEIVED;
110 } else {
111 state = State.FAILED;
112 }
113 } else {
114 if (state.compareTo(State.MSG_TYPE1_GENERATED) < 0) {
115 state = State.FAILED;
116 throw new MalformedChallengeException("Out of sequence NTLM response message");
117 } else if (state == State.MSG_TYPE1_GENERATED) {
118 state = State.MSG_TYPE2_RECEVIED;
119 }
120 }
121 }
122
123 @Override
124 public Header authenticate(final Credentials credentials, 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: " + credentials.getClass().getName()
131 );
132 }
133 String response = null;
134 if (state == State.FAILED) {
135 throw new AuthenticationException("NTLM authentication failed");
136 } else if (state == State.CHALLENGE_RECEIVED) {
137 response = this.engine.generateType1Msg(ntcredentials.getDomain(), ntcredentials.getWorkstation());
138 state = State.MSG_TYPE1_GENERATED;
139
140 if (credentials instanceof NTCredentialsWithEncryption) {
141 ((NTCredentialsWithEncryption) credentials).resetEncryption(request);
142 }
143 } else if (state == State.MSG_TYPE2_RECEVIED) {
144 final Type3Message responseO = engine.generateType3MsgObject(
145 ntcredentials.getUserName(),
146 ntcredentials.getPassword(),
147 ntcredentials.getDomain(),
148 ntcredentials.getWorkstation(),
149 challenge
150 );
151
152 response = responseO.getResponse();
153 state = State.MSG_TYPE3_GENERATED;
154 if (credentials instanceof NTCredentialsWithEncryption) {
155 ((NTCredentialsWithEncryption) credentials).initEncryption(responseO, request);
156 }
157 } else {
158 throw new AuthenticationException("Unexpected state: " + state);
159 }
160 final CharArrayBuffer buffer = new CharArrayBuffer(32);
161 if (isProxy()) {
162 buffer.append(AUTH.PROXY_AUTH_RESP);
163 } else {
164 buffer.append(AUTH.WWW_AUTH_RESP);
165 }
166 buffer.append(": NTLM ");
167 buffer.append(response);
168 return new BufferedHeader(buffer);
169 }
170
171 @Override
172 public boolean isComplete() {
173 return state == State.MSG_TYPE3_GENERATED || state == State.FAILED;
174 }
175
176 @Override
177 public Header authenticate(final Credentials credentials, final HttpRequest request, final HttpContext context)
178 throws AuthenticationException {
179 return authenticate(credentials, request);
180 }
181 }