1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package davmail.http;
20
21 import jcifs.ntlmssp.NtlmFlags;
22 import jcifs.ntlmssp.Type1Message;
23 import jcifs.ntlmssp.Type2Message;
24 import jcifs.ntlmssp.Type3Message;
25 import org.apache.commons.codec.binary.Base64;
26 import org.apache.commons.httpclient.Credentials;
27 import org.apache.commons.httpclient.HttpMethod;
28 import org.apache.commons.httpclient.NTCredentials;
29 import org.apache.commons.httpclient.auth.*;
30 import org.apache.commons.httpclient.util.EncodingUtil;
31
32 import java.io.IOException;
33
34
35
36
37 public class NTLMv2Scheme implements AuthScheme {
38 private static final int UNINITIATED = 0;
39 private static final int INITIATED = 1;
40 private static final int TYPE1_MSG_GENERATED = 2;
41 private static final int TYPE2_MSG_RECEIVED = 3;
42 private static final int TYPE3_MSG_GENERATED = 4;
43 private static final int FAILED = Integer.MAX_VALUE;
44
45 private Type2Message type2Message;
46
47
48
49 private int state;
50
51
52
53
54
55
56
57
58 public void processChallenge(final String challenge) throws MalformedChallengeException {
59 String authScheme = AuthChallengeParser.extractScheme(challenge);
60 if (!authScheme.equalsIgnoreCase(getSchemeName())) {
61 throw new MalformedChallengeException("Invalid NTLM challenge: " + challenge);
62 }
63 int spaceIndex = challenge.indexOf(' ');
64 if (spaceIndex != -1) {
65 try {
66 type2Message = new Type2Message(Base64.decodeBase64(EncodingUtil.getBytes(
67 challenge.substring(spaceIndex).trim(), "ASCII")));
68 } catch (IOException e) {
69 throw new MalformedChallengeException("Invalid NTLM challenge: " + challenge, e);
70 }
71 this.state = TYPE2_MSG_RECEIVED;
72 } else {
73 this.type2Message = null;
74 if (this.state == UNINITIATED) {
75 this.state = INITIATED;
76 } else {
77 this.state = FAILED;
78 }
79 }
80 }
81
82
83
84
85
86
87
88 public String getSchemeName() {
89 return "ntlm";
90 }
91
92
93
94
95
96
97 public String getParameter(String s) {
98 return null;
99 }
100
101
102
103
104
105
106 public String getRealm() {
107 return null;
108 }
109
110
111
112
113 @Deprecated
114 public String getID() {
115 throw new UnsupportedOperationException();
116 }
117
118
119
120
121
122
123 public boolean isConnectionBased() {
124 return true;
125 }
126
127
128
129
130
131
132 public boolean isComplete() {
133 return state == TYPE3_MSG_GENERATED || state == FAILED;
134 }
135
136
137
138
139
140
141
142
143
144 @Deprecated
145 public String authenticate(final Credentials credentials, String method, String uri) {
146 throw new UnsupportedOperationException();
147 }
148
149
150
151
152
153
154
155
156
157
158
159
160
161 public String authenticate(Credentials credentials, HttpMethod httpMethod) throws AuthenticationException {
162 if (this.state == UNINITIATED) {
163 throw new IllegalStateException("NTLM authentication process has not been initiated");
164 }
165
166 NTCredentials ntcredentials;
167 try {
168 ntcredentials = (NTCredentials) credentials;
169 } catch (ClassCastException e) {
170 throw new InvalidCredentialsException(
171 "Credentials cannot be used for NTLM authentication: "
172 + credentials.getClass().getName());
173 }
174 String response;
175 if (this.state == INITIATED || this.state == FAILED) {
176 int flags = NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 | NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
177 NtlmFlags.NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED | NtlmFlags.NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED |
178 NtlmFlags.NTLMSSP_NEGOTIATE_NTLM | NtlmFlags.NTLMSSP_REQUEST_TARGET |
179 NtlmFlags.NTLMSSP_NEGOTIATE_OEM | NtlmFlags.NTLMSSP_NEGOTIATE_UNICODE |
180 NtlmFlags.NTLMSSP_NEGOTIATE_56 | NtlmFlags.NTLMSSP_NEGOTIATE_128;
181 Type1Message type1Message = new Type1Message(flags, ntcredentials.getDomain(), ntcredentials.getHost());
182 response = EncodingUtil.getAsciiString(Base64.encodeBase64(type1Message.toByteArray()));
183 this.state = TYPE1_MSG_GENERATED;
184 } else {
185 int flags = NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 | NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
186 NtlmFlags.NTLMSSP_NEGOTIATE_NTLM | NtlmFlags.NTLMSSP_NEGOTIATE_UNICODE;
187 Type3Message type3Message = new Type3Message(type2Message, ntcredentials.getPassword(),
188 ntcredentials.getDomain(), ntcredentials.getUserName(), ntcredentials.getHost(), flags);
189 response = EncodingUtil.getAsciiString(Base64.encodeBase64(type3Message.toByteArray()));
190 this.state = TYPE3_MSG_GENERATED;
191 }
192 return "NTLM " + response;
193 }
194
195
196 }