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.*;
25 import org.apache.http.conn.ManagedHttpClientConnection;
26 import org.apache.http.impl.auth.AuthSchemeBase;
27 import org.apache.http.impl.auth.NTLMEngineException;
28 import org.apache.http.message.BufferedHeader;
29 import org.apache.http.protocol.ExecutionContext;
30 import org.apache.http.protocol.HttpContext;
31 import org.apache.http.util.Args;
32 import org.apache.http.util.CharArrayBuffer;
33
34 import javax.net.ssl.SSLPeerUnverifiedException;
35 import java.security.cert.Certificate;
36
37
38
39
40 public class DavMailNTLMScheme extends AuthSchemeBase {
41 enum State {
42 UNINITIATED,
43 CHALLENGE_RECEIVED,
44 MSG_TYPE1_GENERATED,
45 MSG_TYPE2_RECEVIED,
46 MSG_TYPE3_GENERATED,
47 FAILED,
48 }
49
50 private State state;
51 private final DavMailNTLMEngineImpl engine;
52 private HttpContext httpContext;
53
54 private String challenge;
55
56 public DavMailNTLMScheme() {
57 this.engine = new DavMailNTLMEngineImpl();
58 this.state = State.UNINITIATED;
59 this.challenge = null;
60 }
61
62 @Override
63 protected void parseChallenge(CharArrayBuffer buffer, int beginIndex, int endIndex) throws MalformedChallengeException {
64 this.challenge = buffer.substringTrimmed(beginIndex, endIndex);
65 if (this.challenge.isEmpty()) {
66 if (this.state == State.UNINITIATED) {
67 this.state = State.CHALLENGE_RECEIVED;
68 } else {
69 this.state = State.FAILED;
70 }
71 } else {
72 if (this.state.compareTo(State.MSG_TYPE1_GENERATED) < 0) {
73 this.state = State.FAILED;
74 throw new MalformedChallengeException("Out of sequence NTLM response message");
75 } else if (this.state == State.MSG_TYPE1_GENERATED) {
76 this.state = State.MSG_TYPE2_RECEVIED;
77 }
78 }
79 }
80
81 @Override
82 public String getSchemeName() {
83 return "ntlm";
84 }
85
86 @Override
87 public String getParameter(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 public boolean isComplete() {
105 return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED;
106 }
107
108 @Override
109 public Header authenticate(
110 final Credentials credentials,
111 final HttpRequest request,
112 final HttpContext httpContext) throws AuthenticationException {
113 this.httpContext = httpContext;
114 return authenticate(credentials, request);
115 }
116
117
118 @Override
119 public Header authenticate(Credentials credentials, HttpRequest request) throws AuthenticationException {
120 NTCredentials ntcredentials = null;
121 try {
122 ntcredentials = (NTCredentials) credentials;
123 } catch (final ClassCastException e) {
124 throw new InvalidCredentialsException(
125 "Credentials cannot be used for NTLM authentication: "
126 + credentials.getClass().getName());
127 }
128 String response = null;
129 if (this.state == State.FAILED) {
130 throw new AuthenticationException("NTLM authentication failed");
131 } else if (this.state == State.CHALLENGE_RECEIVED) {
132 response = this.engine.generateType1Msg(
133 ntcredentials.getDomain(),
134 ntcredentials.getWorkstation());
135 this.state = State.MSG_TYPE1_GENERATED;
136 } else if (this.state == State.MSG_TYPE2_RECEVIED) {
137
138 ManagedHttpClientConnection routedConnection = (ManagedHttpClientConnection) httpContext.getAttribute(ExecutionContext.HTTP_CONNECTION);
139 try {
140 Certificate[] certificates = routedConnection.getSSLSession().getPeerCertificates();
141 this.engine.setPeerServerCertificate(certificates[0]);
142 } catch (SSLPeerUnverifiedException e) {
143 throw new NTLMEngineException(e.getMessage(), e);
144 }
145 response = this.engine.generateType3Msg(
146 ntcredentials.getUserName(),
147 ntcredentials.getPassword(),
148 ntcredentials.getDomain(),
149 ntcredentials.getWorkstation(),
150 this.challenge);
151 this.state = State.MSG_TYPE3_GENERATED;
152 } else {
153 throw new AuthenticationException("Unexpected state: " + this.state);
154 }
155 final CharArrayBuffer buffer = new CharArrayBuffer(32);
156 if (isProxy()) {
157 buffer.append(AUTH.PROXY_AUTH_RESP);
158 } else {
159 buffer.append(AUTH.WWW_AUTH_RESP);
160 }
161 buffer.append(": NTLM ");
162 buffer.append(response);
163 return new BufferedHeader(buffer);
164 }
165 }