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 org.apache.commons.codec.binary.Base64;
22 import org.apache.commons.httpclient.Credentials;
23 import org.apache.commons.httpclient.Header;
24 import org.apache.commons.httpclient.HttpMethod;
25 import org.apache.commons.httpclient.URIException;
26 import org.apache.commons.httpclient.auth.*;
27 import org.apache.commons.httpclient.util.EncodingUtil;
28 import org.ietf.jgss.GSSException;
29
30 import javax.security.auth.login.LoginException;
31
32
33
34
35 public class SpNegoScheme implements AuthScheme {
36 private static final int UNINITIATED = 0;
37 private static final int INITIATED = 1;
38 private static final int TYPE1_MSG_GENERATED = 2;
39 private static final int TYPE2_MSG_RECEIVED = 3;
40 private static final int TYPE3_MSG_GENERATED = 4;
41 private static final int FAILED = Integer.MAX_VALUE;
42
43 private byte[] serverToken;
44
45
46
47 private int state;
48
49
50
51
52
53
54
55 public void processChallenge(final String challenge) throws MalformedChallengeException {
56 String authScheme = AuthChallengeParser.extractScheme(challenge);
57 if (!authScheme.equalsIgnoreCase(getSchemeName())) {
58 throw new MalformedChallengeException("Invalid Negotiate challenge: " + challenge);
59 }
60 int spaceIndex = challenge.indexOf(' ');
61 if (spaceIndex != -1) {
62
63 serverToken = Base64.decodeBase64(EncodingUtil.getBytes(
64 challenge.substring(spaceIndex).trim(), "ASCII"));
65 this.state = TYPE2_MSG_RECEIVED;
66 } else {
67 this.serverToken = null;
68 if (this.state == UNINITIATED) {
69 this.state = INITIATED;
70 } else {
71 this.state = FAILED;
72 }
73 }
74 }
75
76
77
78
79
80
81
82 public String getSchemeName() {
83 return "Negotiate";
84 }
85
86
87
88
89
90
91 public String getParameter(String s) {
92 return null;
93 }
94
95
96
97
98
99
100 public String getRealm() {
101 return null;
102 }
103
104
105
106
107 @Deprecated
108 public String getID() {
109 throw new UnsupportedOperationException();
110 }
111
112
113
114
115
116
117 public boolean isConnectionBased() {
118 return true;
119 }
120
121
122
123
124
125
126 public boolean isComplete() {
127 return state == TYPE3_MSG_GENERATED || state == FAILED;
128 }
129
130
131
132
133
134
135
136
137
138 @Deprecated
139 public String authenticate(final Credentials credentials, String method, String uri) {
140 throw new UnsupportedOperationException();
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 public String authenticate(Credentials credentials, HttpMethod httpMethod) throws AuthenticationException {
157 if (this.state == UNINITIATED) {
158 throw new IllegalStateException("Negotiate authentication process has not been initiated");
159 }
160 String host = null;
161 try {
162 host = httpMethod.getURI().getHost();
163 } catch (URIException e) {
164
165 }
166 if (host == null) {
167 Header header = httpMethod.getRequestHeader("Host");
168 if (header != null) {
169 host = header.getValue();
170 if (host.indexOf(':') >= 0) {
171 host = host.substring(0, host.indexOf(':'));
172 }
173 }
174 }
175 if (host == null) {
176 throw new IllegalStateException("Negotiate authentication failed: empty host");
177 }
178
179
180 String response;
181 try {
182 if (this.state == INITIATED || this.state == FAILED) {
183
184 response = EncodingUtil.getAsciiString(Base64.encodeBase64(KerberosHelper.initSecurityContext("HTTP", host, new byte[0])));
185 this.state = TYPE1_MSG_GENERATED;
186 } else {
187
188 response = EncodingUtil.getAsciiString(Base64.encodeBase64(KerberosHelper.initSecurityContext("HTTP", host, serverToken)));
189 this.state = TYPE3_MSG_GENERATED;
190 }
191 } catch (GSSException gsse) {
192 state = FAILED;
193 if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL
194 || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED)
195 throw new InvalidCredentialsException(gsse.getMessage(), gsse);
196 if (gsse.getMajor() == GSSException.NO_CRED)
197 throw new CredentialsNotAvailableException(gsse.getMessage(), gsse);
198 if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN
199 || gsse.getMajor() == GSSException.DUPLICATE_TOKEN
200 || gsse.getMajor() == GSSException.OLD_TOKEN)
201 throw new AuthChallengeException(gsse.getMessage(), gsse);
202
203 throw new AuthenticationException(gsse.getMessage(), gsse);
204 } catch (LoginException e) {
205 state = FAILED;
206 throw new InvalidCredentialsException(e.getMessage(), e);
207 }
208 return "Negotiate " + response;
209 }
210
211 }