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 } else {
145 return gssContext.initSecContext(new byte[] {}, 0, 0);
146 }
147 }
148
149
150
151
152 protected GSSContext createGSSContext(
153 final GSSManager manager,
154 final Oid oid,
155 final GSSName serverName,
156 final GSSCredential gssCredential) throws GSSException {
157 final GSSContext gssContext = manager.createContext(serverName.canonicalize(oid), oid, gssCredential,
158 GSSContext.DEFAULT_LIFETIME);
159 gssContext.requestMutualAuth(true);
160 if (config.getRequestDelegCreds() != org.apache.hc.client5.http.auth.KerberosConfig.Option.DEFAULT) {
161 gssContext.requestCredDeleg(config.getRequestDelegCreds() == org.apache.hc.client5.http.auth.KerberosConfig.Option.ENABLE);
162 }
163 return gssContext;
164 }
165
166
167
168 protected abstract byte[] generateToken(byte[] input, String serviceName, String authServer) throws GSSException;
169
170 @Override
171 public boolean isChallengeComplete() {
172 return this.state == State.TOKEN_GENERATED || this.state == State.FAILED;
173 }
174
175 @Override
176 public boolean isResponseReady(
177 final HttpHost host,
178 final CredentialsProvider credentialsProvider,
179 final HttpContext context) throws AuthenticationException {
180
181 Args.notNull(host, "Auth host");
182 Args.notNull(credentialsProvider, "CredentialsProvider");
183
184 final Credentials credentials = credentialsProvider.getCredentials(
185 new AuthScope(host, null, getName()), context);
186 if (credentials instanceof org.apache.hc.client5.http.auth.KerberosCredentials) {
187 this.gssCredential = ((org.apache.hc.client5.http.auth.KerberosCredentials) credentials).getGSSCredential();
188 } else {
189 this.gssCredential = null;
190 }
191 return true;
192 }
193
194 @Override
195 public Principal getPrincipal() {
196 return null;
197 }
198
199 @Override
200 public String generateAuthResponse(
201 final HttpHost host,
202 final HttpRequest request,
203 final HttpContext context) throws AuthenticationException {
204 Args.notNull(host, "HTTP host");
205 Args.notNull(request, "HTTP request");
206 switch (state) {
207 case UNINITIATED:
208 throw new AuthenticationException(getName() + " authentication has not been initiated");
209 case FAILED:
210 throw new AuthenticationException(getName() + " authentication has failed");
211 case CHALLENGE_RECEIVED:
212 try {
213 final String authServer;
214 String hostname = host.getHostName();
215 if (config.getUseCanonicalHostname() != org.apache.hc.client5.http.auth.KerberosConfig.Option.DISABLE){
216 try {
217 hostname = dnsResolver.resolveCanonicalHostname(host.getHostName());
218 } catch (final UnknownHostException ignore){
219 }
220 }
221 if (config.getStripPort() != org.apache.hc.client5.http.auth.KerberosConfig.Option.DISABLE) {
222 authServer = hostname;
223 } else {
224 authServer = hostname + ":" + host.getPort();
225 }
226
227 if (LOG.isDebugEnabled()) {
228 final HttpClientContext clientContext = HttpClientContext.cast(context);
229 final String exchangeId = clientContext.getExchangeId();
230 LOG.debug("{} init {}", exchangeId, authServer);
231 }
232 token = generateToken(token, KERBEROS_SCHEME, authServer);
233 state = State.TOKEN_GENERATED;
234 } catch (final GSSException gsse) {
235 state = State.FAILED;
236 if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL
237 || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) {
238 throw new InvalidCredentialsException(gsse.getMessage(), gsse);
239 }
240 if (gsse.getMajor() == GSSException.NO_CRED ) {
241 throw new InvalidCredentialsException(gsse.getMessage(), gsse);
242 }
243 if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN
244 || gsse.getMajor() == GSSException.DUPLICATE_TOKEN
245 || gsse.getMajor() == GSSException.OLD_TOKEN) {
246 throw new AuthenticationException(gsse.getMessage(), gsse);
247 }
248
249 throw new AuthenticationException(gsse.getMessage());
250 }
251 case TOKEN_GENERATED:
252 final Base64 codec = new Base64(0);
253 final String tokenstr = new String(codec.encode(token));
254 if (LOG.isDebugEnabled()) {
255 final HttpClientContext clientContext = HttpClientContext.cast(context);
256 final String exchangeId = clientContext.getExchangeId();
257 LOG.debug("{} Sending response '{}' back to the auth server", exchangeId, tokenstr);
258 }
259 return StandardAuthScheme.SPNEGO + " " + tokenstr;
260 default:
261 throw new IllegalStateException("Illegal state: " + state);
262 }
263 }
264
265 @Override
266 public String toString() {
267 return getName() + "{" + this.state + " " + challenge + '}';
268 }
269
270 }