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 package org.apache.http.impl.auth;
28
29 import java.net.InetAddress;
30 import java.net.UnknownHostException;
31
32 import org.apache.commons.codec.binary.Base64;
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.http.Header;
36 import org.apache.http.HttpHost;
37 import org.apache.http.HttpRequest;
38 import org.apache.http.auth.AUTH;
39 import org.apache.http.auth.AuthenticationException;
40 import org.apache.http.auth.Credentials;
41 import org.apache.http.auth.InvalidCredentialsException;
42 import org.apache.http.auth.KerberosCredentials;
43 import org.apache.http.auth.MalformedChallengeException;
44 import org.apache.http.client.protocol.HttpClientContext;
45 import org.apache.http.conn.routing.HttpRoute;
46 import org.apache.http.message.BufferedHeader;
47 import org.apache.http.protocol.HttpContext;
48 import org.apache.http.util.Args;
49 import org.apache.http.util.CharArrayBuffer;
50 import org.ietf.jgss.GSSContext;
51 import org.ietf.jgss.GSSCredential;
52 import org.ietf.jgss.GSSException;
53 import org.ietf.jgss.GSSManager;
54 import org.ietf.jgss.GSSName;
55 import org.ietf.jgss.Oid;
56
57
58
59
60 public abstract class GGSSchemeBase extends AuthSchemeBase {
61
62 enum State {
63 UNINITIATED,
64 CHALLENGE_RECEIVED,
65 TOKEN_GENERATED,
66 FAILED,
67 }
68
69 private final Log log = LogFactory.getLog(getClass());
70
71 private final Base64 base64codec;
72 private final boolean stripPort;
73 private final boolean useCanonicalHostname;
74
75
76 private State state;
77
78
79 private byte[] token;
80
81 GGSSchemeBase(final boolean stripPort, final boolean useCanonicalHostname) {
82 super();
83 this.base64codec = new Base64(0);
84 this.stripPort = stripPort;
85 this.useCanonicalHostname = useCanonicalHostname;
86 this.state = State.UNINITIATED;
87 }
88
89 GGSSchemeBase(final boolean stripPort) {
90 this(stripPort, true);
91 }
92
93 GGSSchemeBase() {
94 this(true,true);
95 }
96
97 protected GSSManager getManager() {
98 return GSSManager.getInstance();
99 }
100
101 protected byte[] generateGSSToken(
102 final byte[] input, final Oid oid, final String authServer) throws GSSException {
103 return generateGSSToken(input, oid, authServer, null);
104 }
105
106
107
108
109 protected byte[] generateGSSToken(
110 final byte[] input, final Oid oid, final String authServer,
111 final Credentials credentials) throws GSSException {
112 final GSSManager manager = getManager();
113 final GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE);
114
115 final GSSCredential gssCredential;
116 if (credentials instanceof KerberosCredentials) {
117 gssCredential = ((KerberosCredentials) credentials).getGSSCredential();
118 } else {
119 gssCredential = null;
120 }
121
122 final GSSContext gssContext = createGSSContext(manager, oid, serverName, gssCredential);
123 return input != null
124 ? gssContext.initSecContext(input, 0, input.length)
125 : gssContext.initSecContext(new byte[] {}, 0, 0);
126 }
127
128 GSSContext createGSSContext(
129 final GSSManager manager,
130 final Oid oid,
131 final GSSName serverName,
132 final GSSCredential gssCredential) throws GSSException {
133 final GSSContext gssContext = manager.createContext(serverName.canonicalize(oid), oid, gssCredential,
134 GSSContext.DEFAULT_LIFETIME);
135 gssContext.requestMutualAuth(true);
136 return gssContext;
137 }
138
139
140
141 @Deprecated
142 protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException {
143 return null;
144 }
145
146
147
148
149
150 @SuppressWarnings("deprecation")
151 protected byte[] generateToken(
152 final byte[] input, final String authServer, final Credentials credentials) throws GSSException {
153 return generateToken(input, authServer);
154 }
155
156 @Override
157 public boolean isComplete() {
158 return this.state == State.TOKEN_GENERATED || this.state == State.FAILED;
159 }
160
161
162
163
164
165 @Override
166 @Deprecated
167 public Header authenticate(
168 final Credentials credentials,
169 final HttpRequest request) throws AuthenticationException {
170 return authenticate(credentials, request, null);
171 }
172
173 @Override
174 public Header authenticate(
175 final Credentials credentials,
176 final HttpRequest request,
177 final HttpContext context) throws AuthenticationException {
178 Args.notNull(request, "HTTP request");
179 switch (state) {
180 case UNINITIATED:
181 throw new AuthenticationException(getSchemeName() + " authentication has not been initiated");
182 case FAILED:
183 throw new AuthenticationException(getSchemeName() + " authentication has failed");
184 case CHALLENGE_RECEIVED:
185 try {
186 final HttpRoute/../../../../org/apache/http/conn/routing/HttpRoute.html#HttpRoute">HttpRoute route = (HttpRoute) context.getAttribute(HttpClientContext.HTTP_ROUTE);
187 if (route == null) {
188 throw new AuthenticationException("Connection route is not available");
189 }
190 HttpHost host;
191 if (isProxy()) {
192 host = route.getProxyHost();
193 if (host == null) {
194 host = route.getTargetHost();
195 }
196 } else {
197 host = route.getTargetHost();
198 }
199 final String authServer;
200 String hostname = host.getHostName();
201
202 if (this.useCanonicalHostname){
203 try {
204
205
206
207
208 hostname = resolveCanonicalHostname(hostname);
209 } catch (final UnknownHostException ignore){
210 }
211 }
212 if (this.stripPort) {
213 authServer = hostname;
214 } else {
215 authServer = hostname + ":" + host.getPort();
216 }
217
218 if (log.isDebugEnabled()) {
219 log.debug("init " + authServer);
220 }
221 token = generateToken(token, authServer, credentials);
222 state = State.TOKEN_GENERATED;
223 } catch (final GSSException gsse) {
224 state = State.FAILED;
225 if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL
226 || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) {
227 throw new InvalidCredentialsException(gsse.getMessage(), gsse);
228 }
229 if (gsse.getMajor() == GSSException.NO_CRED ) {
230 throw new InvalidCredentialsException(gsse.getMessage(), gsse);
231 }
232 if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN
233 || gsse.getMajor() == GSSException.DUPLICATE_TOKEN
234 || gsse.getMajor() == GSSException.OLD_TOKEN) {
235 throw new AuthenticationException(gsse.getMessage(), gsse);
236 }
237
238 throw new AuthenticationException(gsse.getMessage());
239 }
240 case TOKEN_GENERATED:
241 final String tokenstr = new String(base64codec.encode(token));
242 if (log.isDebugEnabled()) {
243 log.debug("Sending response '" + tokenstr + "' back to the auth server");
244 }
245 final CharArrayBuffer buffer = new CharArrayBuffer(32);
246 if (isProxy()) {
247 buffer.append(AUTH.PROXY_AUTH_RESP);
248 } else {
249 buffer.append(AUTH.WWW_AUTH_RESP);
250 }
251 buffer.append(": Negotiate ");
252 buffer.append(tokenstr);
253 return new BufferedHeader(buffer);
254 default:
255 throw new IllegalStateException("Illegal state: " + state);
256 }
257 }
258
259 @Override
260 protected void parseChallenge(
261 final CharArrayBuffer buffer,
262 final int beginIndex, final int endIndex) throws MalformedChallengeException {
263 final String challenge = buffer.substringTrimmed(beginIndex, endIndex);
264 if (log.isDebugEnabled()) {
265 log.debug("Received challenge '" + challenge + "' from the auth server");
266 }
267 if (state == State.UNINITIATED) {
268 token = Base64.decodeBase64(challenge.getBytes());
269 state = State.CHALLENGE_RECEIVED;
270 } else {
271 log.debug("Authentication already attempted");
272 state = State.FAILED;
273 }
274 }
275
276 private String resolveCanonicalHostname(final String host) throws UnknownHostException {
277 final InetAddress in = InetAddress.getByName(host);
278 final String canonicalServer = in.getCanonicalHostName();
279 if (in.getHostAddress().contentEquals(canonicalServer)) {
280 return host;
281 }
282 return canonicalServer;
283 }
284
285 }