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.hc.client5.http.impl.auth;
28
29 import java.net.UnknownHostException;
30 import java.security.Principal;
31
32 import org.apache.hc.client5.http.DnsResolver;
33 import org.apache.hc.client5.http.SystemDefaultDnsResolver;
34 import org.apache.hc.client5.http.auth.AuthChallenge;
35 import org.apache.hc.client5.http.auth.AuthScheme;
36 import org.apache.hc.client5.http.auth.AuthScope;
37 import org.apache.hc.client5.http.auth.AuthenticationException;
38 import org.apache.hc.client5.http.auth.Credentials;
39 import org.apache.hc.client5.http.auth.CredentialsProvider;
40 import org.apache.hc.client5.http.auth.InvalidCredentialsException;
41 import org.apache.hc.client5.http.auth.MalformedChallengeException;
42 import org.apache.hc.client5.http.auth.StandardAuthScheme;
43 import org.apache.hc.client5.http.protocol.HttpClientContext;
44 import org.apache.hc.client5.http.utils.Base64;
45 import org.apache.hc.core5.http.HttpHost;
46 import org.apache.hc.core5.http.HttpRequest;
47 import org.apache.hc.core5.http.protocol.HttpContext;
48 import org.apache.hc.core5.util.Args;
49 import org.ietf.jgss.GSSContext;
50 import org.ietf.jgss.GSSCredential;
51 import org.ietf.jgss.GSSException;
52 import org.ietf.jgss.GSSManager;
53 import org.ietf.jgss.GSSName;
54 import org.ietf.jgss.Oid;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58
59
60
61
62
63
64
65
66 @Deprecated
67 public abstract class GGSSchemeBase implements AuthScheme {
68
69 enum State {
70 UNINITIATED,
71 CHALLENGE_RECEIVED,
72 TOKEN_GENERATED,
73 FAILED,
74 }
75
76 private static final Logger LOG = LoggerFactory.getLogger(GGSSchemeBase.class);
77 private static final String NO_TOKEN = "";
78 private static final String KERBEROS_SCHEME = "HTTP";
79 private final org.apache.hc.client5.http.auth.KerberosConfig config;
80 private final DnsResolver dnsResolver;
81
82
83 private State state;
84 private GSSCredential gssCredential;
85 private String challenge;
86 private byte[] token;
87
88 GGSSchemeBase(final org.apache.hc.client5.http.auth.KerberosConfig config, final DnsResolver dnsResolver) {
89 super();
90 this.config = config != null ? config : org.apache.hc.client5.http.auth.KerberosConfig.DEFAULT;
91 this.dnsResolver = dnsResolver != null ? dnsResolver : SystemDefaultDnsResolver.INSTANCE;
92 this.state = State.UNINITIATED;
93 }
94
95 GGSSchemeBase(final org.apache.hc.client5.http.auth.KerberosConfig config) {
96 this(config, SystemDefaultDnsResolver.INSTANCE);
97 }
98
99 GGSSchemeBase() {
100 this(org.apache.hc.client5.http.auth.KerberosConfig.DEFAULT, SystemDefaultDnsResolver.INSTANCE);
101 }
102
103 @Override
104 public String getRealm() {
105 return null;
106 }
107
108 @Override
109 public void processChallenge(
110 final AuthChallenge authChallenge,
111 final HttpContext context) throws MalformedChallengeException {
112 Args.notNull(authChallenge, "AuthChallenge");
113
114 this.challenge = authChallenge.getValue() != null ? authChallenge.getValue() : NO_TOKEN;
115
116 if (state == State.UNINITIATED) {
117 token = Base64.decodeBase64(challenge.getBytes());
118 state = State.CHALLENGE_RECEIVED;
119 } else {
120 if (LOG.isDebugEnabled()) {
121 final HttpClientContext clientContext = HttpClientContext.cast(context);
122 final String exchangeId = clientContext.getExchangeId();
123 LOG.debug("{} Authentication already attempted", exchangeId);
124 }
125 state = State.FAILED;
126 }
127 }
128
129 protected GSSManager getManager() {
130 return GSSManager.getInstance();
131 }
132
133
134
135
136 protected byte[] generateGSSToken(
137 final byte[] input, final Oid oid, final String serviceName, final String authServer) throws GSSException {
138 final GSSManager manager = getManager();
139 final GSSName serverName = manager.createName(serviceName + "@" + authServer, GSSName.NT_HOSTBASED_SERVICE);
140
141 final GSSContext gssContext = createGSSContext(manager, oid, serverName, gssCredential);
142 if (input != null) {
143 return gssContext.initSecContext(input, 0, input.length);
144 }
145 return gssContext.initSecContext(new byte[] {}, 0, 0);
146 }
147
148
149
150
151 protected GSSContext createGSSContext(
152 final GSSManager manager,
153 final Oid oid,
154 final GSSName serverName,
155 final GSSCredential gssCredential) throws GSSException {
156 final GSSContext gssContext = manager.createContext(serverName.canonicalize(oid), oid, gssCredential,
157 GSSContext.DEFAULT_LIFETIME);
158 gssContext.requestMutualAuth(true);
159 if (config.getRequestDelegCreds() != org.apache.hc.client5.http.auth.KerberosConfig.Option.DEFAULT) {
160 gssContext.requestCredDeleg(config.getRequestDelegCreds() == org.apache.hc.client5.http.auth.KerberosConfig.Option.ENABLE);
161 }
162 return gssContext;
163 }
164
165
166
167 protected abstract byte[] generateToken(byte[] input, String serviceName, String authServer) throws GSSException;
168
169 @Override
170 public boolean isChallengeComplete() {
171 return this.state == State.TOKEN_GENERATED || this.state == State.FAILED;
172 }
173
174 @Override
175 public boolean isResponseReady(
176 final HttpHost host,
177 final CredentialsProvider credentialsProvider,
178 final HttpContext context) throws AuthenticationException {
179
180 Args.notNull(host, "Auth host");
181 Args.notNull(credentialsProvider, "CredentialsProvider");
182
183 final Credentials credentials = credentialsProvider.getCredentials(
184 new AuthScope(host, null, getName()), context);
185 if (credentials instanceof org.apache.hc.client5.http.auth.KerberosCredentials) {
186 this.gssCredential = ((org.apache.hc.client5.http.auth.KerberosCredentials) credentials).getGSSCredential();
187 } else {
188 this.gssCredential = null;
189 }
190 return true;
191 }
192
193 @Override
194 public Principal getPrincipal() {
195 return null;
196 }
197
198 @Override
199 public String generateAuthResponse(
200 final HttpHost host,
201 final HttpRequest request,
202 final HttpContext context) throws AuthenticationException {
203 Args.notNull(host, "HTTP host");
204 Args.notNull(request, "HTTP request");
205 switch (state) {
206 case UNINITIATED:
207 throw new AuthenticationException(getName() + " authentication has not been initiated");
208 case FAILED:
209 throw new AuthenticationException(getName() + " authentication has failed");
210 case CHALLENGE_RECEIVED:
211 try {
212 final String authServer;
213 String hostname = host.getHostName();
214 if (config.getUseCanonicalHostname() != org.apache.hc.client5.http.auth.KerberosConfig.Option.DISABLE) {
215 try {
216 hostname = dnsResolver.resolveCanonicalHostname(host.getHostName());
217 } catch (final UnknownHostException ignore) {
218 }
219 }
220 if (config.getStripPort() != org.apache.hc.client5.http.auth.KerberosConfig.Option.DISABLE) {
221 authServer = hostname;
222 } else {
223 authServer = hostname + ":" + host.getPort();
224 }
225
226 if (LOG.isDebugEnabled()) {
227 final HttpClientContext clientContext = HttpClientContext.cast(context);
228 final String exchangeId = clientContext.getExchangeId();
229 LOG.debug("{} init {}", exchangeId, authServer);
230 }
231 token = generateToken(token, KERBEROS_SCHEME, authServer);
232 state = State.TOKEN_GENERATED;
233 } catch (final GSSException gsse) {
234 state = State.FAILED;
235 if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL
236 || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) {
237 throw new InvalidCredentialsException(gsse.getMessage(), gsse);
238 }
239 if (gsse.getMajor() == GSSException.NO_CRED ) {
240 throw new InvalidCredentialsException(gsse.getMessage(), gsse);
241 }
242 if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN
243 || gsse.getMajor() == GSSException.DUPLICATE_TOKEN
244 || gsse.getMajor() == GSSException.OLD_TOKEN) {
245 throw new AuthenticationException(gsse.getMessage(), gsse);
246 }
247
248 throw new AuthenticationException(gsse.getMessage());
249 }
250 case TOKEN_GENERATED:
251 final Base64 codec = new Base64(0);
252 final String tokenstr = new String(codec.encode(token));
253 if (LOG.isDebugEnabled()) {
254 final HttpClientContext clientContext = HttpClientContext.cast(context);
255 final String exchangeId = clientContext.getExchangeId();
256 LOG.debug("{} Sending response '{}' back to the auth server", exchangeId, tokenstr);
257 }
258 return StandardAuthScheme.SPNEGO + " " + tokenstr;
259 default:
260 throw new IllegalStateException("Illegal state: " + state);
261 }
262 }
263
264 @Override
265 public String toString() {
266 return getName() + "{" + this.state + " " + challenge + '}';
267 }
268
269 }