| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.cxf.fediz.service.idp; |
| |
| import java.security.Principal; |
| import java.security.PrivilegedActionException; |
| import java.util.List; |
| |
| import javax.security.auth.callback.CallbackHandler; |
| import javax.security.auth.login.LoginException; |
| import javax.xml.namespace.QName; |
| |
| import org.apache.cxf.Bus; |
| import org.apache.cxf.fediz.service.idp.kerberos.KerberosServiceRequestToken; |
| import org.apache.cxf.fediz.service.idp.kerberos.KerberosTokenValidator; |
| import org.apache.cxf.fediz.service.idp.kerberos.PassThroughKerberosClient; |
| import org.apache.cxf.ws.security.SecurityConstants; |
| import org.apache.cxf.ws.security.tokenstore.SecurityToken; |
| import org.apache.wss4j.common.kerberos.KerberosServiceContext; |
| import org.apache.wss4j.common.principal.SAMLTokenPrincipalImpl; |
| import org.apache.wss4j.common.saml.SamlAssertionWrapper; |
| import org.apache.wss4j.dom.WSConstants; |
| import org.ietf.jgss.GSSContext; |
| import org.ietf.jgss.GSSCredential; |
| import org.ietf.jgss.GSSException; |
| import org.ietf.jgss.GSSManager; |
| import org.ietf.jgss.GSSName; |
| import org.ietf.jgss.Oid; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.springframework.security.core.Authentication; |
| import org.springframework.security.core.GrantedAuthority; |
| |
| /** |
| * An authentication provider to authenticate a Kerberos token to the STS |
| */ |
| public class STSKrbAuthenticationProvider extends STSAuthenticationProvider { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(STSKrbAuthenticationProvider.class); |
| |
| private KerberosTokenValidator kerberosTokenValidator; |
| |
| private CallbackHandler kerberosCallbackHandler; |
| |
| private boolean kerberosUsernameServiceNameForm; |
| |
| private boolean requireDelegation; |
| |
| |
| @Override |
| public Authentication authenticate(Authentication authentication) { |
| // We only handle KerberosServiceRequestTokens |
| if (!(authentication instanceof KerberosServiceRequestToken)) { |
| return null; |
| } |
| |
| Bus cxfBus = getBus(); |
| IdpSTSClient sts = new IdpSTSClient(cxfBus); |
| sts.setAddressingNamespace("http://www.w3.org/2005/08/addressing"); |
| if (tokenType != null && tokenType.length() > 0) { |
| sts.setTokenType(tokenType); |
| } else { |
| sts.setTokenType(WSConstants.WSS_SAML2_TOKEN_TYPE); |
| } |
| sts.setKeyType(HTTP_DOCS_OASIS_OPEN_ORG_WS_SX_WS_TRUST_200512_BEARER); |
| sts.setWsdlLocation(getWsdlLocation()); |
| sts.setServiceQName(new QName(namespace, wsdlService)); |
| sts.setEndpointQName(new QName(namespace, wsdlEndpoint)); |
| |
| sts.getProperties().putAll(properties); |
| if (use200502Namespace) { |
| sts.setNamespace(HTTP_SCHEMAS_XMLSOAP_ORG_WS_2005_02_TRUST); |
| } |
| |
| if (lifetime != null) { |
| sts.setEnableLifetime(true); |
| sts.setTtl(lifetime.intValue()); |
| } |
| |
| return handleKerberos((KerberosServiceRequestToken)authentication, sts); |
| } |
| |
| private Authentication handleKerberos( |
| KerberosServiceRequestToken kerberosRequestToken, |
| IdpSTSClient sts |
| ) { |
| Principal kerberosPrincipal = null; |
| // |
| // If delegation is required then validate the received token + store the |
| // Delegated Credential so that we can retrieve a new kerberos token for the |
| // STS with it. If delegation is not required, then we just get the received |
| // token + pass it to the STS |
| // |
| if (requireDelegation) { |
| kerberosPrincipal = validateKerberosToken(kerberosRequestToken, sts); |
| if (kerberosPrincipal == null) { |
| return null; |
| } |
| } else { |
| PassThroughKerberosClient kerberosClient = new PassThroughKerberosClient(); |
| kerberosClient.setToken(kerberosRequestToken.getToken()); |
| sts.getProperties().put(SecurityConstants.KERBEROS_CLIENT, kerberosClient); |
| } |
| |
| try { |
| // Line below may be uncommented for debugging |
| // setTimeout(sts.getClient(), 3600000L); |
| |
| SecurityToken token = sts.requestSecurityToken(this.appliesTo); |
| |
| if (kerberosPrincipal == null && token.getToken() != null |
| && "Assertion".equals(token.getToken().getLocalName())) { |
| // For the pass-through Kerberos case, we don't know the Principal name... |
| kerberosPrincipal = |
| new SAMLTokenPrincipalImpl(new SamlAssertionWrapper(token.getToken())); |
| } |
| |
| if (kerberosPrincipal == null) { |
| LOG.info("Failed to authenticate user '" + kerberosRequestToken.getName()); |
| return null; |
| } |
| |
| List<GrantedAuthority> authorities = createAuthorities(token); |
| |
| KerberosServiceRequestToken ksrt = |
| new KerberosServiceRequestToken(kerberosPrincipal, authorities, kerberosRequestToken.getToken()); |
| |
| STSUserDetails details = new STSUserDetails(kerberosPrincipal.getName(), |
| "", |
| authorities, |
| token); |
| ksrt.setDetails(details); |
| |
| LOG.debug("[IDP_TOKEN={}] provided for user '{}'", token.getId(), kerberosPrincipal.getName()); |
| return ksrt; |
| } catch (Exception ex) { |
| LOG.info("Failed to authenticate user '" + kerberosRequestToken.getName() + "'", ex); |
| return null; |
| } |
| } |
| |
| private Principal validateKerberosToken( |
| KerberosServiceRequestToken token, |
| IdpSTSClient sts |
| ) { |
| if (kerberosTokenValidator == null) { |
| LOG.error("KerberosTokenValidator must be configured to support kerberos " |
| + "credential delegation"); |
| return null; |
| } |
| |
| final Principal kerberosPrincipal; |
| try { |
| KerberosServiceContext kerberosContext = kerberosTokenValidator.validate(token); |
| if (kerberosContext == null || kerberosContext.getDelegationCredential() == null) { |
| LOG.info("Kerberos Validation failure"); |
| return null; |
| } |
| GSSCredential delegatedCredential = kerberosContext.getDelegationCredential(); |
| sts.getProperties().put(SecurityConstants.DELEGATED_CREDENTIAL, |
| delegatedCredential); |
| sts.getProperties().put(SecurityConstants.KERBEROS_USE_CREDENTIAL_DELEGATION, "true"); |
| kerberosPrincipal = kerberosContext.getPrincipal(); |
| } catch (LoginException ex) { |
| LOG.info("Failed to authenticate user", ex); |
| return null; |
| } catch (PrivilegedActionException ex) { |
| LOG.info("Failed to authenticate user", ex); |
| return null; |
| } |
| |
| if (kerberosTokenValidator.getContextName() != null) { |
| sts.getProperties().put(SecurityConstants.KERBEROS_JAAS_CONTEXT_NAME, |
| kerberosTokenValidator.getContextName()); |
| } |
| if (kerberosTokenValidator.getServiceName() != null) { |
| sts.getProperties().put(SecurityConstants.KERBEROS_SPN, |
| kerberosTokenValidator.getServiceName()); |
| } |
| if (kerberosCallbackHandler != null) { |
| sts.getProperties().put(SecurityConstants.CALLBACK_HANDLER, |
| kerberosCallbackHandler); |
| } |
| if (kerberosUsernameServiceNameForm) { |
| sts.getProperties().put(SecurityConstants.KERBEROS_IS_USERNAME_IN_SERVICENAME_FORM, |
| "true"); |
| } |
| |
| return kerberosPrincipal; |
| } |
| |
| protected GSSContext createGSSContext() throws GSSException { |
| Oid oid = new Oid("1.2.840.113554.1.2.2"); |
| |
| GSSManager gssManager = GSSManager.getInstance(); |
| |
| String spn = "bob@service.ws.apache.org"; |
| GSSName gssService = gssManager.createName(spn, null); |
| |
| return gssManager.createContext(gssService.canonicalize(oid), |
| oid, null, GSSContext.DEFAULT_LIFETIME); |
| |
| } |
| |
| @Override |
| public boolean supports(Class<?> authentication) { |
| return authentication.equals(KerberosServiceRequestToken.class); |
| } |
| |
| public KerberosTokenValidator getKerberosTokenValidator() { |
| return kerberosTokenValidator; |
| } |
| |
| public void setKerberosTokenValidator(KerberosTokenValidator kerberosTokenValidator) { |
| this.kerberosTokenValidator = kerberosTokenValidator; |
| } |
| |
| public CallbackHandler getKerberosCallbackHandler() { |
| return kerberosCallbackHandler; |
| } |
| |
| public void setKerberosCallbackHandler(CallbackHandler kerberosCallbackHandler) { |
| this.kerberosCallbackHandler = kerberosCallbackHandler; |
| } |
| |
| public boolean isKerberosUsernameServiceNameForm() { |
| return kerberosUsernameServiceNameForm; |
| } |
| |
| public void setKerberosUsernameServiceNameForm(boolean kerberosUsernameServiceNameForm) { |
| this.kerberosUsernameServiceNameForm = kerberosUsernameServiceNameForm; |
| } |
| |
| public boolean isRequireDelegation() { |
| return requireDelegation; |
| } |
| |
| public void setRequireDelegation(boolean requireDelegation) { |
| this.requireDelegation = requireDelegation; |
| } |
| |
| } |