1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package davmail.http;
21
22 import org.apache.http.Header;
23 import org.apache.http.HttpRequest;
24 import org.apache.http.auth.AUTH;
25 import org.apache.http.auth.AuthenticationException;
26 import org.apache.http.auth.Credentials;
27 import org.apache.http.auth.InvalidCredentialsException;
28 import org.apache.http.auth.MalformedChallengeException;
29 import org.apache.http.auth.NTCredentials;
30 import org.apache.http.conn.ManagedHttpClientConnection;
31 import org.apache.http.impl.auth.AuthSchemeBase;
32 import org.apache.http.impl.auth.NTLMEngineException;
33 import org.apache.http.message.BufferedHeader;
34 import org.apache.http.protocol.HttpContext;
35 import org.apache.http.protocol.HttpCoreContext;
36 import org.apache.http.util.CharArrayBuffer;
37
38 import javax.net.ssl.SSLPeerUnverifiedException;
39 import java.security.cert.Certificate;
40
41
42
43
44 public class DavMailNTLMScheme extends AuthSchemeBase {
45 enum State {
46 UNINITIATED,
47 CHALLENGE_RECEIVED,
48 MSG_TYPE1_GENERATED,
49 MSG_TYPE2_RECEVIED,
50 MSG_TYPE3_GENERATED,
51 FAILED,
52 }
53
54 private State state;
55 private final DavMailNTLMEngineImpl engine;
56 private HttpContext httpContext;
57
58 private String challenge;
59
60 public DavMailNTLMScheme() {
61 this.engine = new DavMailNTLMEngineImpl();
62 this.state = State.UNINITIATED;
63 this.challenge = null;
64 }
65
66 @Override
67 protected void parseChallenge(CharArrayBuffer buffer, int beginIndex, int endIndex) throws MalformedChallengeException {
68 this.challenge = buffer.substringTrimmed(beginIndex, endIndex);
69 if (this.challenge.isEmpty()) {
70 if (this.state == State.UNINITIATED) {
71 this.state = State.CHALLENGE_RECEIVED;
72 } else {
73 this.state = State.FAILED;
74 }
75 } else {
76 if (this.state.compareTo(State.MSG_TYPE1_GENERATED) < 0) {
77 this.state = State.FAILED;
78 throw new MalformedChallengeException("Out of sequence NTLM response message");
79 } else if (this.state == State.MSG_TYPE1_GENERATED) {
80 this.state = State.MSG_TYPE2_RECEVIED;
81 }
82 }
83 }
84
85 @Override
86 public String getSchemeName() {
87 return "ntlm";
88 }
89
90 @Override
91 public String getParameter(String name) {
92
93 return null;
94 }
95
96 @Override
97 public String getRealm() {
98
99 return null;
100 }
101
102 @Override
103 public boolean isConnectionBased() {
104 return true;
105 }
106
107 @Override
108 public boolean isComplete() {
109 return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED;
110 }
111
112 @Override
113 public Header authenticate(
114 final Credentials credentials,
115 final HttpRequest request,
116 final HttpContext httpContext) throws AuthenticationException {
117 this.httpContext = httpContext;
118 return authenticate(credentials, request);
119 }
120
121
122 @Override
123 @SuppressWarnings("deprecation")
124 public Header authenticate(Credentials credentials, HttpRequest request) throws AuthenticationException {
125 NTCredentials ntcredentials;
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;
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
143 ManagedHttpClientConnection routedConnection = (ManagedHttpClientConnection) httpContext.getAttribute(HttpCoreContext.HTTP_CONNECTION);
144 try {
145 Certificate[] certificates = routedConnection.getSSLSession().getPeerCertificates();
146 this.engine.setPeerServerCertificate(certificates[0]);
147 } catch (SSLPeerUnverifiedException e) {
148 throw new NTLMEngineException(e.getMessage(), e);
149 }
150 response = this.engine.generateType3Msg(
151 ntcredentials.getUserName(),
152 ntcredentials.getPassword(),
153 ntcredentials.getDomain(),
154 ntcredentials.getWorkstation(),
155 this.challenge);
156 this.state = State.MSG_TYPE3_GENERATED;
157 } else {
158 throw new AuthenticationException("Unexpected state: " + this.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 }