Author: prabath Date: Tue May 11 06:28:50 2010 New Revision: 943002 URL: http://svn.apache.org/viewvc?rev=943002&view=rev Log: Fix for https://issues.apache.org/jira/browse/RAMPART-294 - thanks AmilaJ for the patch
Added: axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/ServiceNonceCache.java axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/UniqueMessageAttributeCache.java axis/axis2/java/rampart/trunk/modules/rampart-tests/src/test/java/org/apache/rampart/NonceCacheTest.java Modified: axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/RampartEngine.java axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/errors.properties axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/policy/builders/RampartConfigBuilder.java axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/policy/model/RampartConfig.java Modified: axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/RampartEngine.java URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/RampartEngine.java?rev=943002&r1=943001&r2=943002&view=diff ============================================================================== --- axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/RampartEngine.java (original) +++ axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/RampartEngine.java Tue May 11 06:28:50 2010 @@ -18,6 +18,8 @@ package org.apache.rampart; import org.apache.axiom.om.OMElement; import org.apache.axiom.soap.*; +import org.apache.axiom.soap.SOAP11Constants; +import org.apache.axiom.soap.SOAP12Constants; import org.apache.axis2.AxisFault; import org.apache.axis2.context.MessageContext; import org.apache.commons.logging.Log; @@ -30,10 +32,7 @@ import org.apache.rampart.policy.Rampart import org.apache.rampart.util.Axis2Util; import org.apache.rampart.util.RampartUtil; import org.apache.ws.secpolicy.WSSPolicyException; -import org.apache.ws.security.WSConstants; -import org.apache.ws.security.WSSecurityEngine; -import org.apache.ws.security.WSSecurityEngineResult; -import org.apache.ws.security.WSSecurityException; +import org.apache.ws.security.*; import org.apache.ws.security.components.crypto.Crypto; import org.apache.ws.security.saml.SAMLKeyInfo; import org.apache.ws.security.saml.SAMLUtil; @@ -53,7 +52,8 @@ import java.util.Vector; public class RampartEngine { private static Log log = LogFactory.getLog(RampartEngine.class); - private static Log tlog = LogFactory.getLog(RampartConstants.TIME_LOG); + private static Log tlog = LogFactory.getLog(RampartConstants.TIME_LOG); + private static ServiceNonceCache serviceNonceCache = new ServiceNonceCache(); public Vector process(MessageContext msgCtx) throws WSSPolicyException, RampartException, WSSecurityException, AxisFault { @@ -230,9 +230,42 @@ public class RampartEngine { } } else if (WSConstants.UT == actInt.intValue()) { - String username = ((Principal) wser.get(WSSecurityEngineResult.TAG_PRINCIPAL)) - .getName(); + + WSUsernameTokenPrincipal userNameTokenPrincipal = (WSUsernameTokenPrincipal)wser.get(WSSecurityEngineResult.TAG_PRINCIPAL); + + String username = userNameTokenPrincipal.getName(); msgCtx.setProperty(RampartMessageData.USERNAME, username); + + if (userNameTokenPrincipal.getNonce() != null) { + // Check whether this is a replay attack. To verify that we need to check whether nonce value + // is a repeating one + int nonceLifeTimeInSeconds = 0; + + if (rpd.getRampartConfig() != null) { + + String stringLifeTime = rpd.getRampartConfig().getNonceLifeTime(); + + try { + nonceLifeTimeInSeconds = Integer.parseInt(stringLifeTime); + + } catch (NumberFormatException e) { + log.error("Invalid value for nonceLifeTime in rampart configuration file.", e); + throw new RampartException( + "invalidNonceLifeTime", e); + + } + } + + String serviceEndpointName = msgCtx.getAxisService().getEndpointName(); + + boolean valueRepeating = serviceNonceCache.isNonceRepeatingForService(serviceEndpointName, username, userNameTokenPrincipal.getNonce()); + + if (valueRepeating){ + throw new RampartException("repeatingNonceValue", new Object[]{ userNameTokenPrincipal.getNonce(), username} ); + } + + serviceNonceCache.addNonceForService(serviceEndpointName, username, userNameTokenPrincipal.getNonce(), nonceLifeTimeInSeconds); + } } else if (WSConstants.SIGN == actInt.intValue()) { X509Certificate cert = (X509Certificate) wser.get(WSSecurityEngineResult.TAG_X509_CERTIFICATE); msgCtx.setProperty(RampartMessageData.X509_CERT, cert); Added: axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/ServiceNonceCache.java URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/ServiceNonceCache.java?rev=943002&view=auto ============================================================================== --- axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/ServiceNonceCache.java (added) +++ axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/ServiceNonceCache.java Tue May 11 06:28:50 2010 @@ -0,0 +1,69 @@ +/* + * Copyright 2004,2005 The Apache Software Foundation. + * + * Licensed 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.rampart; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * This class holds nonce information per service. + */ +public class ServiceNonceCache { + + private Map<String, UniqueMessageAttributeCache> mapServiceNonceCache = Collections.synchronizedMap(new HashMap<String, UniqueMessageAttributeCache>()); + + /** + * This method will add a nonce value for a given service. + * @param service The service url. + * @param userName Given user name. + * @param nonceValue Passed nonce value. + * @param nonceLifeTime Maximum life span of a nonce value. + */ + public void addNonceForService(String service, String userName, String nonceValue, int nonceLifeTime) { + + UniqueMessageAttributeCache nonceCache; + if (this.mapServiceNonceCache.containsKey(service)) { + nonceCache = this.mapServiceNonceCache.get(service); + } else { + nonceCache = new NonceCache(nonceLifeTime); + this.mapServiceNonceCache.put(service, nonceCache); + } + + nonceCache.addToCache(nonceValue, userName); + } + + /** + * This method will check whether the nonce value is repeating for the given service. + * @param service The service url. + * @param userName User name. + * @param nonceValue Nonce value. + * @return true if nonce value is repeating else false. + */ + public boolean isNonceRepeatingForService(String service, String userName, String nonceValue){ + + if (this.mapServiceNonceCache.containsKey(service)) { + + UniqueMessageAttributeCache nonceCache = this.mapServiceNonceCache.get(service); + return nonceCache.valueExistsInCache(nonceValue, userName); + + } + + return false; + + } + +} Added: axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/UniqueMessageAttributeCache.java URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/UniqueMessageAttributeCache.java?rev=943002&view=auto ============================================================================== --- axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/UniqueMessageAttributeCache.java (added) +++ axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/UniqueMessageAttributeCache.java Tue May 11 06:28:50 2010 @@ -0,0 +1,65 @@ +/* + * Copyright 2004,2005 The Apache Software Foundation. + * + * Licensed 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.rampart; + +/** + * An interface to cache nonce/sequence number values coming with messages. + * This mainly helps to prevent replay attacks. There are few different ways to handle replay attacks. + * 1. Cache nonce values. + * 2. Use a sequence number. + * + * "Web Services Security UsernameToken Profile 1.1 OASIS Standard Specification, 1 February 2006" specification only recommends + * to cache nonce for a period. But there can be other mechanisms like using sequence number. + * Therefore cache is implemented as an interface and later if we need to support sequence number scenario we can easily extend this. + * User: aj + * Date: Apr 30, 2010 + * Time: 12:15:52 PM + * To change this template use File | Settings | File Templates. + */ +public interface UniqueMessageAttributeCache { + + /** + * Sets the maximum life time of a message id. + * @param maxTime Maximum life time in seconds. + */ + public void setMaximumLifeTimeOfAnAttribute(int maxTime); + + /** + * Gets the maximum life time of a message id. + * @return Gets message id life time in seconds. + */ + public int getMaximumLifeTimeOfAnAttribute(); + + /** + * Add value to a cache. Value can be sequence or nonce value. + * @param id - Nonce value or sequence number. + * @param userName - User name parameter value of the UserNameToken. + */ + public void addToCache(String id, String userName); + + /** + * Checks whether value already exists in the cache for a given user name. + * @param id - Nonce or sequence id value of the newly received message. + * @param userName - User name parameter value of the UserName token. + * @return Returns true if nonce or sequence id is already received for given user name. Else false. + */ + public boolean valueExistsInCache(String id, String userName); + + /** + * Clears all recorded nonce values/sequence numbers. + */ + public void clearCache(); +} Modified: axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/errors.properties URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/errors.properties?rev=943002&r1=943001&r2=943002&view=diff ============================================================================== --- axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/errors.properties (original) +++ axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/errors.properties Tue May 11 06:28:50 2010 @@ -95,4 +95,6 @@ signedElementNotSigned = Element must be bodyNotSigned = Soap Body must be signed unexprectedSignature = Unexpected signature invalidTransport = Expected transport is "https" but incoming transport found : \"{0}\" -requiredElementsMissing = Required Elements not found in the incoming message : {0} \ No newline at end of file +requiredElementsMissing = Required Elements not found in the incoming message : {0} +repeatingNonceValue = Nonce value : {0}, already seen before for user name : {1}. Possibly this could be a replay attack. +invalidNonceLifeTime = Invalid value for nonceLifeTime in rampart configuration file. \ No newline at end of file Modified: axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/policy/builders/RampartConfigBuilder.java URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/policy/builders/RampartConfigBuilder.java?rev=943002&r1=943001&r2=943002&view=diff ============================================================================== --- axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/policy/builders/RampartConfigBuilder.java (original) +++ axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/policy/builders/RampartConfigBuilder.java Tue May 11 06:28:50 2010 @@ -126,6 +126,12 @@ public class RampartConfigBuilder implem if (childElement != null) { rampartConfig.setTimestampMaxSkew(childElement.getText().trim()); } + + childElement = element.getFirstChildWithName(new QName( + RampartConfig.NS, RampartConfig.NONCE_LIFE_TIME)); + if (childElement != null) { + rampartConfig.setNonceLifeTime(childElement.getText().trim()); + } childElement = element.getFirstChildWithName(new QName( RampartConfig.NS, RampartConfig.OPTIMISE_PARTS)); Modified: axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/policy/model/RampartConfig.java URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/policy/model/RampartConfig.java?rev=943002&r1=943001&r2=943002&view=diff ============================================================================== --- axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/policy/model/RampartConfig.java (original) +++ axis/axis2/java/rampart/trunk/modules/rampart-core/src/main/java/org/apache/rampart/policy/model/RampartConfig.java Tue May 11 06:28:50 2010 @@ -39,6 +39,7 @@ import javax.xml.stream.XMLStreamWriter; * <ramp:timestampTTL>300</ramp:timestampTTL> * <ramp:timestampMaxSkew>0</ramp:timestampMaxSkew> * <ramp:tokenStoreClass>org.apache.rahas.StorageImpl</ramp:tokenStoreClass> + * <ramp:nonceLifeTime>org.apache.rahas.StorageImpl</ramp:nonceLifeTime> * * <ramp:signatureCrypto> * <ramp:crypto provider="org.apache.ws.security.components.crypto.Merlin"> @@ -67,6 +68,8 @@ public class RampartConfig implements As public static final int DEFAULT_TIMESTAMP_MAX_SKEW = 300; + public static final int DEFAULT_NONCE_LIFE_TIME = 60 * 5; // Default life time of a nonce is 5 minutes + public final static String NS = "http://ws.apache.org/rampart/policy"; public final static String PREFIX = "rampart"; @@ -102,6 +105,8 @@ public class RampartConfig implements As public final static String TS_MAX_SKEW_LN = "timestampMaxSkew"; public final static String TOKEN_STORE_CLASS_LN = "tokenStoreClass"; + + public final static String NONCE_LIFE_TIME = "nonceLifeTime"; public final static String OPTIMISE_PARTS = "optimizeParts"; @@ -138,6 +143,8 @@ public class RampartConfig implements As private OptimizePartsConfig optimizeParts; private String tokenStoreClass; + + private String nonceLifeTime = Integer.toString(DEFAULT_NONCE_LIFE_TIME); private SSLConfig sslConfig; @@ -165,6 +172,21 @@ public class RampartConfig implements As this.tokenStoreClass = tokenStoreClass; } + /** + * @return Returns the life time of a nonce in seconds. + */ + public String getNonceLifeTime() { + return this.nonceLifeTime; + } + + /** + * @param nonceLife + * The life time of a nonce to set (in seconds). + */ + public void setNonceLifeTime(String nonceLife) { + this.nonceLifeTime = nonceLife; + } + public CryptoConfig getDecCryptoConfig() { return decCryptoConfig; } @@ -327,6 +349,12 @@ public class RampartConfig implements As writer.writeCharacters(getTokenStoreClass()); writer.writeEndElement(); } + + if (getNonceLifeTime() != null) { + writer.writeStartElement(NS, NONCE_LIFE_TIME); + writer.writeCharacters(getNonceLifeTime()); + writer.writeEndElement(); + } if (encrCryptoConfig != null) { writer.writeStartElement(NS, ENCR_CRYPTO_LN); Added: axis/axis2/java/rampart/trunk/modules/rampart-tests/src/test/java/org/apache/rampart/NonceCacheTest.java URL: http://svn.apache.org/viewvc/axis/axis2/java/rampart/trunk/modules/rampart-tests/src/test/java/org/apache/rampart/NonceCacheTest.java?rev=943002&view=auto ============================================================================== --- axis/axis2/java/rampart/trunk/modules/rampart-tests/src/test/java/org/apache/rampart/NonceCacheTest.java (added) +++ axis/axis2/java/rampart/trunk/modules/rampart-tests/src/test/java/org/apache/rampart/NonceCacheTest.java Tue May 11 06:28:50 2010 @@ -0,0 +1,61 @@ +package org.apache.rampart; + +import junit.framework.TestCase; + +/** + * Created by IntelliJ IDEA. + * User: aj + * Date: Apr 30, 2010 + * Time: 4:15:20 PM + * To change this template use File | Settings | File Templates. + */ +public class NonceCacheTest extends TestCase { + + public NonceCacheTest(String name) { + super(name); + } + + public void testAddToCache() throws Exception { + + UniqueMessageAttributeCache cache = new NonceCache(); + + cache.addToCache("j8EqKYJ/CxOZfN8CySMm0g==", "apache"); + cache.addToCache("j8EqKYJ/CxOdfN8CySMm0g==", "apache"); + cache.addToCache("j8EqKYJ/CxOhfN8CySMm0g==", "apache"); + } + + public void testValueExistsInCache() throws Exception{ + + UniqueMessageAttributeCache cache = new NonceCache(); + + cache.addToCache("j8EqKYJ/CxOZfN8CySMm0g==", "apache"); + cache.addToCache("j8EqKYJ/CxOdfN8CySMm0g==", "apache"); + cache.addToCache("j8EqKYJ/CxOhfN8CySMm0g==", "apache"); + + boolean returnValue1 = cache.valueExistsInCache("j8EqKYJ/CxOZfN8CySMm0g==", "apache"); + assertTrue("nonce - j8EqKYJ/CxOZfN8CySMm0g== and apache must exists in the cache", returnValue1); + + boolean returnValue2 = cache.valueExistsInCache("p8EqKYJ/CxOZfN8CySMm0g==", "apache"); + assertFalse("nonce - p8EqKYJ/CxOZfN8CySMm0g== and apache should not be in the cache", returnValue2); + } + + public void testValueExpiration() throws Exception{ + + UniqueMessageAttributeCache cache = new NonceCache(); + + cache.addToCache("j8EqKYJ/CxOZfN8CySMm0g==", "apache"); + cache.addToCache("j8EqKYJ/CxOdfN8CySMm0p==", "apache"); + cache.addToCache("q8EqKYJ/CxOhfN8CySMm0g==", "apache"); + + cache.setMaximumLifeTimeOfAnAttribute(1); + + boolean returnValue1 = cache.valueExistsInCache("j8EqKYJ/CxOZfN8CySMm0g==", "apache"); + assertTrue("nonce - j8EqKYJ/CxOZfN8CySMm0g== and apache must exists in the cache", returnValue1); + + Thread.sleep(2 * 1000); + + returnValue1 = cache.valueExistsInCache("j8EqKYJ/CxOZfN8CySMm0g==", "apache"); + assertFalse("nonce - j8EqKYJ/CxOZfN8CySMm0g== and apache must not exists in the cache", returnValue1); + + } +}