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 org.apache.commons.codec.binary.Base64;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.http.Header;
33 import org.apache.http.HttpHost;
34 import org.apache.http.HttpRequest;
35 import org.apache.http.auth.AUTH;
36 import org.apache.http.auth.AuthenticationException;
37 import org.apache.http.auth.ContextAwareAuthScheme;
38 import org.apache.http.auth.Credentials;
39 import org.apache.http.auth.InvalidCredentialsException;
40 import org.apache.http.auth.MalformedChallengeException;
41 import org.apache.http.client.protocol.HttpClientContext;
42 import org.apache.http.conn.routing.HttpRoute;
43 import org.apache.http.message.BufferedHeader;
44 import org.apache.http.protocol.HttpContext;
45 import org.apache.http.util.Args;
46 import org.apache.http.util.CharArrayBuffer;
47 import org.ietf.jgss.GSSContext;
48 import org.ietf.jgss.GSSException;
49 import org.ietf.jgss.GSSManager;
50 import org.ietf.jgss.GSSName;
51 import org.ietf.jgss.Oid;
52
53
54
55
56 public abstract class GGSSchemeBase extends AuthSchemeBase {
57
58 enum State {
59 UNINITIATED,
60 CHALLENGE_RECEIVED,
61 TOKEN_GENERATED,
62 FAILED,
63 }
64
65 private final Log log = LogFactory.getLog(getClass());
66
67 private final Base64 base64codec;
68 private final boolean stripPort;
69
70
71 private State state;
72
73
74 private byte[] token;
75
76 GGSSchemeBase(final boolean stripPort) {
77 super();
78 this.base64codec = new Base64(0);
79 this.stripPort = stripPort;
80 this.state = State.UNINITIATED;
81 }
82
83 GGSSchemeBase() {
84 this(false);
85 }
86
87 protected GSSManager getManager() {
88 return GSSManager.getInstance();
89 }
90
91 protected byte[] generateGSSToken(
92 final byte[] input, final Oid oid, final String authServer) throws GSSException {
93 byte[] token = input;
94 if (token == null) {
95 token = new byte[0];
96 }
97 final GSSManager manager = getManager();
98 final GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE);
99 final GSSContext gssContext = manager.createContext(
100 serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME);
101 gssContext.requestMutualAuth(true);
102 gssContext.requestCredDeleg(true);
103 return gssContext.initSecContext(token, 0, token.length);
104 }
105
106 protected abstract byte[] generateToken(
107 byte[] input, final String authServer) throws GSSException;
108
109 public boolean isComplete() {
110 return this.state == State.TOKEN_GENERATED || this.state == State.FAILED;
111 }
112
113
114
115
116 @Deprecated
117 public Header authenticate(
118 final Credentials credentials,
119 final HttpRequest request) throws AuthenticationException {
120 return authenticate(credentials, request, null);
121 }
122
123 @Override
124 public Header authenticate(
125 final Credentials credentials,
126 final HttpRequest request,
127 final HttpContext context) throws AuthenticationException {
128 Args.notNull(request, "HTTP request");
129 switch (state) {
130 case UNINITIATED:
131 throw new AuthenticationException(getSchemeName() + " authentication has not been initiated");
132 case FAILED:
133 throw new AuthenticationException(getSchemeName() + " authentication has failed");
134 case CHALLENGE_RECEIVED:
135 try {
136 final HttpRoute route = (HttpRoute) context.getAttribute(HttpClientContext.HTTP_ROUTE);
137 if (route == null) {
138 throw new AuthenticationException("Connection route is not available");
139 }
140 HttpHost host;
141 if (isProxy()) {
142 host = route.getProxyHost();
143 if (host == null) {
144 host = route.getTargetHost();
145 }
146 } else {
147 host = route.getTargetHost();
148 }
149 String authServer;
150 if (!this.stripPort && host.getPort() > 0) {
151 authServer = host.toHostString();
152 } else {
153 authServer = host.getHostName();
154 }
155
156 if (log.isDebugEnabled()) {
157 log.debug("init " + authServer);
158 }
159 token = generateToken(token, authServer);
160 state = State.TOKEN_GENERATED;
161 } catch (final GSSException gsse) {
162 state = State.FAILED;
163 if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL
164 || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) {
165 throw new InvalidCredentialsException(gsse.getMessage(), gsse);
166 }
167 if (gsse.getMajor() == GSSException.NO_CRED ) {
168 throw new InvalidCredentialsException(gsse.getMessage(), gsse);
169 }
170 if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN
171 || gsse.getMajor() == GSSException.DUPLICATE_TOKEN
172 || gsse.getMajor() == GSSException.OLD_TOKEN) {
173 throw new AuthenticationException(gsse.getMessage(), gsse);
174 }
175
176 throw new AuthenticationException(gsse.getMessage());
177 }
178 case TOKEN_GENERATED:
179 final String tokenstr = new String(base64codec.encode(token));
180 if (log.isDebugEnabled()) {
181 log.debug("Sending response '" + tokenstr + "' back to the auth server");
182 }
183 final CharArrayBuffer buffer = new CharArrayBuffer(32);
184 if (isProxy()) {
185 buffer.append(AUTH.PROXY_AUTH_RESP);
186 } else {
187 buffer.append(AUTH.WWW_AUTH_RESP);
188 }
189 buffer.append(": Negotiate ");
190 buffer.append(tokenstr);
191 return new BufferedHeader(buffer);
192 default:
193 throw new IllegalStateException("Illegal state: " + state);
194 }
195 }
196
197 @Override
198 protected void parseChallenge(
199 final CharArrayBuffer buffer,
200 final int beginIndex, final int endIndex) throws MalformedChallengeException {
201 final String challenge = buffer.substringTrimmed(beginIndex, endIndex);
202 if (log.isDebugEnabled()) {
203 log.debug("Received challenge '" + challenge + "' from the auth server");
204 }
205 if (state == State.UNINITIATED) {
206 token = Base64.decodeBase64(challenge.getBytes());
207 state = State.CHALLENGE_RECEIVED;
208 } else {
209 log.debug("Authentication already attempted");
210 state = State.FAILED;
211 }
212 }
213
214 }