/**
* 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.ws.security.policy.interceptors;
import java.security.Key;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.crypto.SecretKey;
import javax.xml.namespace.QName;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageUtils;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.ws.policy.AbstractPolicyInterceptorProvider;
import org.apache.cxf.ws.policy.AssertionInfo;
import org.apache.cxf.ws.policy.AssertionInfoMap;
import org.apache.cxf.ws.security.SecurityConstants;
import org.apache.cxf.ws.security.kerberos.KerberosClient;
import org.apache.cxf.ws.security.kerberos.KerberosUtils;
import org.apache.cxf.ws.security.policy.PolicyUtils;
import org.apache.cxf.ws.security.tokenstore.SecurityToken;
import org.apache.cxf.ws.security.tokenstore.TokenStoreUtils;
import org.apache.cxf.ws.security.wss4j.KerberosTokenInterceptor;
import org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JInInterceptor;
import org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JStaxInInterceptor;
import org.apache.cxf.ws.security.wss4j.PolicyBasedWSS4JStaxOutInterceptor;
import org.apache.cxf.ws.security.wss4j.StaxSecurityContextInInterceptor;
import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
import org.apache.cxf.ws.security.wss4j.policyvalidators.PolicyValidatorParameters;
import org.apache.cxf.ws.security.wss4j.policyvalidators.SecurityPolicyValidator;
import org.apache.cxf.ws.security.wss4j.policyvalidators.ValidatorUtils;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.util.KeyUtils;
import org.apache.wss4j.dom.handler.WSHandlerConstants;
import org.apache.wss4j.dom.handler.WSHandlerResult;
import org.apache.wss4j.policy.SP11Constants;
import org.apache.wss4j.policy.SP12Constants;
import org.apache.wss4j.policy.SPConstants;
import org.apache.wss4j.stax.securityEvent.KerberosTokenSecurityEvent;
import org.apache.wss4j.stax.securityEvent.WSSecurityEventConstants;
import org.apache.wss4j.stax.securityToken.KerberosServiceSecurityToken;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.stax.securityEvent.SecurityEvent;
/**
*
*/
public class KerberosTokenInterceptorProvider extends AbstractPolicyInterceptorProvider {
private static final long serialVersionUID = 5922028830873137490L;
public KerberosTokenInterceptorProvider() {
super(Arrays.asList(SP11Constants.KERBEROS_TOKEN, SP12Constants.KERBEROS_TOKEN));
this.getOutInterceptors().add(new KerberosTokenOutInterceptor());
this.getOutFaultInterceptors().add(new KerberosTokenOutInterceptor());
this.getInInterceptors().add(new KerberosTokenDOMInInterceptor());
this.getInFaultInterceptors().add(new KerberosTokenDOMInInterceptor());
this.getInInterceptors().add(new KerberosTokenStaxInInterceptor());
this.getInFaultInterceptors().add(new KerberosTokenStaxInInterceptor());
this.getOutInterceptors().add(new KerberosTokenInterceptor());
this.getInInterceptors().add(new KerberosTokenInterceptor());
PolicyBasedWSS4JStaxOutInterceptor so = new PolicyBasedWSS4JStaxOutInterceptor();
PolicyBasedWSS4JStaxInInterceptor si = new PolicyBasedWSS4JStaxInInterceptor();
this.getOutInterceptors().add(so);
this.getOutFaultInterceptors().add(so);
this.getInInterceptors().add(si);
this.getInFaultInterceptors().add(si);
}
static class KerberosTokenOutInterceptor extends AbstractPhaseInterceptor<Message> {
KerberosTokenOutInterceptor() {
super(Phase.PREPARE_SEND);
}
public void handleMessage(Message message) throws Fault {
AssertionInfoMap aim = message.get(AssertionInfoMap.class);
// extract Assertion information
if (aim != null) {
Collection<AssertionInfo> ais =
PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.KERBEROS_TOKEN);
if (ais.isEmpty()) {
return;
}
if (isRequestor(message)) {
SecurityToken tok = null;
try {
KerberosClient client = KerberosUtils.getClient(message, "kerberos");
synchronized (client) {
tok = client.requestSecurityToken();
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new Fault(e);
}
if (tok != null) {
for (AssertionInfo ai : ais) {
ai.setAsserted(true);
}
message.getExchange().getEndpoint().put(SecurityConstants.TOKEN_ID,
tok.getId());
message.getExchange().put(SecurityConstants.TOKEN_ID,
tok.getId());
TokenStoreUtils.getTokenStore(message).add(tok);
// Create another cache entry with the SHA1 Identifier as the key for easy retrieval
if (tok.getSHA1() != null) {
TokenStoreUtils.getTokenStore(message).add(tok.getSHA1(), tok);
}
}
} else {
//server side should be checked on the way in
for (AssertionInfo ai : ais) {
ai.setAsserted(true);
}
}
PolicyUtils.assertPolicy(aim, "WssKerberosV5ApReqToken11");
PolicyUtils.assertPolicy(aim, "WssGssKerberosV5ApReqToken11");
}
}
}
static class KerberosTokenDOMInInterceptor extends AbstractPhaseInterceptor<Message> {
KerberosTokenDOMInInterceptor() {
super(Phase.PRE_PROTOCOL);
addAfter(WSS4JInInterceptor.class.getName());
addAfter(PolicyBasedWSS4JInInterceptor.class.getName());
}
public void handleMessage(Message message) throws Fault {
AssertionInfoMap aim = message.get(AssertionInfoMap.class);
// extract Assertion information
boolean enableStax =
MessageUtils.isTrue(message.getContextualProperty(SecurityConstants.ENABLE_STREAMING_SECURITY));
if (aim != null && !enableStax) {
Collection<AssertionInfo> ais =
PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.KERBEROS_TOKEN);
if (ais.isEmpty()) {
return;
}
if (!isRequestor(message)) {
List<WSHandlerResult> results =
CastUtils.cast((List<?>)message.get(WSHandlerConstants.RECV_RESULTS));
if (results != null && !results.isEmpty()) {
parseHandlerResults(results.get(0), message, aim, ais);
}
} else {
//client side should be checked on the way out
for (AssertionInfo ai : ais) {
ai.setAsserted(true);
}
}
PolicyUtils.assertPolicy(aim, "WssKerberosV5ApReqToken11");
PolicyUtils.assertPolicy(aim, "WssGssKerberosV5ApReqToken11");
}
}
private void parseHandlerResults(
WSHandlerResult rResult,
Message message,
AssertionInfoMap aim,
Collection<AssertionInfo> ais
) {
PolicyValidatorParameters parameters = new PolicyValidatorParameters();
parameters.setAssertionInfoMap(message.get(AssertionInfoMap.class));
parameters.setMessage(message);
parameters.setResults(rResult);
QName qName = ais.iterator().next().getAssertion().getName();
Map<QName, SecurityPolicyValidator> validators =
ValidatorUtils.getSecurityPolicyValidators(message);
if (validators.containsKey(qName)) {
validators.get(qName).validatePolicies(parameters, ais);
}
}
}
static class KerberosTokenStaxInInterceptor extends AbstractPhaseInterceptor<Message> {
private static final Logger LOG =
LogUtils.getL7dLogger(KerberosTokenStaxInInterceptor.class);
KerberosTokenStaxInInterceptor() {
super(Phase.PRE_PROTOCOL);
getBefore().add(StaxSecurityContextInInterceptor.class.getName());
}
public void handleMessage(Message message) throws Fault {
AssertionInfoMap aim = message.get(AssertionInfoMap.class);
// extract Assertion information
boolean enableStax =
MessageUtils.isTrue(message.getContextualProperty(SecurityConstants.ENABLE_STREAMING_SECURITY));
if (aim != null && enableStax) {
Collection<AssertionInfo> ais =
PolicyUtils.getAllAssertionsByLocalname(aim, SPConstants.KERBEROS_TOKEN);
if (ais.isEmpty()) {
return;
}
if (!isRequestor(message)) {
SecurityEvent event = findKerberosEvent(message);
if (event != null) {
for (AssertionInfo ai : ais) {
ai.setAsserted(true);
}
KerberosServiceSecurityToken kerberosToken =
((KerberosTokenSecurityEvent)event).getSecurityToken();
if (kerberosToken != null) {
storeKerberosToken(message, kerberosToken);
}
}
} else {
//client side should be checked on the way out
for (AssertionInfo ai : ais) {
ai.setAsserted(true);
}
}
PolicyUtils.assertPolicy(aim, "WssKerberosV5ApReqToken11");
PolicyUtils.assertPolicy(aim, "WssGssKerberosV5ApReqToken11");
}
}
private void storeKerberosToken(Message message, KerberosServiceSecurityToken kerberosToken) {
SecurityToken token = new SecurityToken(kerberosToken.getId());
token.setTokenType(kerberosToken.getKerberosTokenValueType());
SecretKey secretKey = getSecretKeyFromToken(kerberosToken);
token.setKey(secretKey);
if (secretKey != null) {
token.setSecret(secretKey.getEncoded());
}
byte[] ticket = kerberosToken.getBinaryContent();
try {
token.setSHA1(Base64.getMimeEncoder().encodeToString(KeyUtils.generateDigest(ticket)));
} catch (WSSecurityException e) {
// Just consume this for now as it isn't critical...
}
TokenStoreUtils.getTokenStore(message).add(token);
message.getExchange().put(SecurityConstants.TOKEN_ID, token.getId());
}
private SecurityEvent findKerberosEvent(Message message) {
@SuppressWarnings("unchecked")
final List<SecurityEvent> incomingEventList =
(List<SecurityEvent>)message.get(SecurityEvent.class.getName() + ".in");
if (incomingEventList != null) {
for (SecurityEvent incomingEvent : incomingEventList) {
if (WSSecurityEventConstants.KERBEROS_TOKEN
== incomingEvent.getSecurityEventType()) {
return incomingEvent;
}
}
}
return null;
}
private SecretKey getSecretKeyFromToken(KerberosServiceSecurityToken kerberosToken) {
try {
Map<String, Key> secretKeys = kerberosToken.getSecretKey();
if (secretKeys != null) {
SecretKey foundKey = null;
for (String key : kerberosToken.getSecretKey().keySet()) {
if (secretKeys.get(key) instanceof SecretKey) {
SecretKey secretKey = (SecretKey)secretKeys.get(key);
if (foundKey == null
|| secretKey.getEncoded().length > foundKey.getEncoded().length) {
foundKey = secretKey;
}
}
}
return foundKey;
}
} catch (XMLSecurityException e) {
LOG.fine(e.getMessage());
}
return null;
}
}
}