diff -Nru eucalyptus-2.0.1+bzr1256/debian/changelog eucalyptus-2.0.1+bzr1256/debian/changelog --- eucalyptus-2.0.1+bzr1256/debian/changelog 2011-05-18 13:10:16.000000000 +0000 +++ eucalyptus-2.0.1+bzr1256/debian/changelog 2011-05-26 15:33:10.000000000 +0000 @@ -1,3 +1,13 @@ +eucalyptus (2.0.1+bzr1256-0ubuntu6) oneiric; urgency=low + + [ Dave Walker ] + * SECURITY UPDATE: SOAP signature replay vulnerability. + - add debian/patches/27-soap-security.patch, thanks to upstream. + - CVE-2011-0730 + - LP: #746101 + + -- Jamie Strandboge Thu, 26 May 2011 10:21:56 -0500 + eucalyptus (2.0.1+bzr1256-0ubuntu5) oneiric; urgency=low * Fix FTBFS against libgoogle-collections-java 1.0 (LP: #784491). diff -Nru eucalyptus-2.0.1+bzr1256/debian/patches/27-soap-security.patch eucalyptus-2.0.1+bzr1256/debian/patches/27-soap-security.patch --- eucalyptus-2.0.1+bzr1256/debian/patches/27-soap-security.patch 1970-01-01 00:00:00.000000000 +0000 +++ eucalyptus-2.0.1+bzr1256/debian/patches/27-soap-security.patch 2011-05-26 15:20:25.000000000 +0000 @@ -0,0 +1,824 @@ +=== modified file 'clc/modules/msgs/src/main/java/com/eucalyptus/auth/login/SecurityContext.java' +--- a/clc/modules/msgs/src/main/java/com/eucalyptus/auth/login/SecurityContext.java ++++ b/clc/modules/msgs/src/main/java/com/eucalyptus/auth/login/SecurityContext.java +@@ -1,6 +1,7 @@ + package com.eucalyptus.auth.login; + + import java.lang.reflect.Modifier; ++import java.util.Date; + import java.util.HashMap; + import java.util.List; + import java.util.Map; +@@ -11,19 +12,32 @@ + import javax.security.auth.login.LoginException; + import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag; + import javax.security.auth.spi.LoginModule; ++import org.apache.log4j.Logger; + import com.eucalyptus.bootstrap.ServiceJarDiscovery; + import com.eucalyptus.util.TimedEvictionSet; + import com.google.common.collect.Lists; + + public class SecurityContext extends Configuration { + private static SecurityContext singleton = new SecurityContext( ); +- private static TimedEvictionSet replayQueue = new TimedEvictionSet( 10*1000l ); ++ private static Logger LOG = Logger.getLogger( SecurityContext.class ); ++ // Note: According WS-Security spec, WS-Security requests need ++ // to be cached for at least 5 min for timestamps to expire ++ // Since a typical timestamp duration is 5 mins, caching for ++ // 6 mins (or more?) gives some buffer for clock drifts ++ private static TimedEvictionSet replayQueue = new TimedEvictionSet(6 * 60 * 1000l); + private List loginModules = Lists.newArrayList( ); + private SecurityContext( ) {} + + public static void enqueueSignature( String signature ) throws AuthenticationException { + if( !SecurityContext.replayQueue.add( signature ) ) { +- throw new AuthenticationException( "Message replay detected. Same signature was used within the last 15 minutes." ); ++ // TODO: GRZE what kind of exception should be thrown here so that ++ // it was propagated back to the user ++ // For example, if euca2ools are used and a command is issued within ++ // the same second, it will result in the same signature and will ++ // be considered a replay. With the currently thrown exception, euca2ools ++ // don't see any response and hang on the user side. ++ LOG.info("Replay detected for " + signature); ++ throw new AuthenticationException( "Message replay detected. Same signature was used within the last 5 minutes"); + } + } + +@@ -31,7 +45,23 @@ + return new LoginContext( "eucalyptus" , new Subject( ), credentials, singleton ); + } + +- ++ /** ++ * Makes sure that the difference between the expiration time ++ * does not exceed replay detection caching ++ * ++ * @param createdMillis ++ * @param expiresMillis ++ * @return ++ */ ++ public static boolean validateTimestampPeriod(Date expires) { ++ Long nanoLimit = replayQueue.getEvictionNanos(); ++ ++ Date currentDate = new Date(); ++ if((currentDate.getTime() + (nanoLimit / 1000000)) < expires.getTime()) ++ return false; ++ ++ return true; ++ } + + public static void registerLoginModule( Class loginModuleClass ) { + singleton.loginModules.add( loginModuleClass.getName( ) ); +--- a/clc/modules/msgs/src/main/java/com/eucalyptus/auth/login/WsSecLoginModule.java ++++ b/clc/modules/msgs/src/main/java/com/eucalyptus/auth/login/WsSecLoginModule.java +@@ -29,7 +29,11 @@ + try { + final Element secNode = WSSecurity.getSecurityElement( wrappedCredentials.getLoginData( ) ); + final XMLSignature sig = WSSecurity.getXMLSignature( secNode ); +- SecurityContext.enqueueSignature( sig.getTextFromTextChild( ) ); ++ // this enqueues an empty string ++ //SecurityContext.enqueueSignature( sig.getTextFromTextChild( ) ); ++ String sigValue = new String(sig.getSignatureValue()); ++ SecurityContext.enqueueSignature( sigValue ); ++ + final X509Certificate cert = WSSecurity.verifySignature( secNode, sig ); + try { + final User user = Users.lookupCertificate( cert ); +@@ -44,7 +48,8 @@ + throw e; + } + } catch ( Exception ex ) { +- throw e; ++ // TODO: GRZE should it be "throw ex" instead? ++ throw e; + } + } + } finally { +--- a/clc/modules/msgs/src/main/java/com/eucalyptus/auth/util/WSSecurity.java ++++ b/clc/modules/msgs/src/main/java/com/eucalyptus/auth/util/WSSecurity.java +@@ -68,6 +68,8 @@ + import java.security.cert.CertificateException; + import java.security.cert.CertificateFactory; + import java.security.cert.X509Certificate; ++import java.util.Date; ++import java.util.Vector; + import org.apache.axiom.om.OMElement; + import org.apache.axiom.om.impl.builder.StAXOMBuilder; + import org.apache.axiom.soap.SOAPEnvelope; +@@ -75,36 +77,42 @@ + import org.apache.ws.security.SOAPConstants; + import org.apache.ws.security.WSConstants; + import org.apache.ws.security.WSSConfig; ++import org.apache.ws.security.WSSecurityEngineResult; + import org.apache.ws.security.WSSecurityException; + import org.apache.ws.security.message.EnvelopeIdResolver; + import org.apache.ws.security.message.token.BinarySecurity; + import org.apache.ws.security.message.token.Reference; + import org.apache.ws.security.message.token.SecurityTokenReference; ++import org.apache.ws.security.message.token.Timestamp; + import org.apache.ws.security.message.token.X509Security; ++import org.apache.ws.security.processor.TimestampProcessor; + import org.apache.ws.security.util.WSSecurityUtil; + import org.apache.xml.security.exceptions.XMLSecurityException; + import org.apache.xml.security.keys.KeyInfo; ++import org.apache.xml.security.signature.SignedInfo; + import org.apache.xml.security.signature.XMLSignature; + import org.apache.xml.security.signature.XMLSignatureException; ++import org.apache.xml.security.signature.XMLSignatureInput; + import org.bouncycastle.jce.provider.BouncyCastleProvider; + import org.bouncycastle.openssl.PEMReader; + import org.w3c.dom.Element; + import org.w3c.dom.Node; + import org.w3c.dom.Text; +-import com.eucalyptus.auth.login.AuthenticationException; + import com.eucalyptus.auth.login.SecurityContext; + import com.eucalyptus.binding.HoldMe; + ++ + public class WSSecurity { + private static Logger LOG = Logger.getLogger( WSSecurity.class ); + private static CertificateFactory factory; ++ + static { + org.apache.xml.security.Init.init( ); + WSSConfig.getDefaultWSConfig( ).addJceProvider( "BC", BouncyCastleProvider.class.getCanonicalName( ) ); + WSSConfig.getDefaultWSConfig( ).setTimeStampStrict( true ); + WSSConfig.getDefaultWSConfig( ).setEnableSignatureConfirmation( true ); + } +- ++ + public static CertificateFactory getCertificateFactory( ) { + if ( factory == null ) { + try { +@@ -119,8 +127,10 @@ + } + + private static boolean useBc = false; ++ + +- public static X509Certificate verifySignature( final Element securityNode, final XMLSignature sig ) throws WSSecurityException, XMLSignatureException { ++ public static X509Certificate verifySignature( final Element securityNode, final XMLSignature sig ) ++ throws WSSecurityException, XMLSignatureException, XMLSecurityException { + final SecurityTokenReference secRef = WSSecurity.getSecurityTokenReference( sig.getKeyInfo( ) ); + final Reference tokenRef = secRef.getReference( ); + Element bstDirect = WSSecurityUtil.getElementByWsuId( securityNode.getOwnerDocument( ), tokenRef.getURI( ) ); +@@ -156,10 +166,133 @@ + } + if ( !sig.checkSignatureValue( cert ) ) { + throw new WSSecurityException( WSSecurityException.FAILED_CHECK ); +- } ++ } ++ ++ verifyReferences(sig); + return cert; + } + ++ /** ++ * Verifies that signed Timestamp is not expired and that signed Body ++ * is subelement of root SOAP element (that's what expected by ++ * subsequent request processing logic) ++ * ++ * @param sig ++ * @return ++ * @throws WSSecurityException ++ * @throws XMLSignatureException ++ */ ++ private static boolean verifyReferences(XMLSignature sig) ++ throws WSSecurityException, XMLSignatureException, XMLSecurityException { ++ ++ if ( sig.getSignedInfo() == null ) throw new WSSecurityException( WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "SignedInfo" ); ++ SignedInfo si = sig.getSignedInfo(); ++ ++ boolean tsSigned = false; ++ boolean bdSigned = false; ++ ++ for(int i = 0; i < si.getLength(); i++) { ++ ++ org.apache.xml.security.signature.Reference ref = si.item(i); ++ String uri = ref.getURI(); ++ ++ if("".compareTo(uri) == 0) { ++ throw new XMLSignatureException("signature.Transform.NotYetImplemented", new Object[]{"XPath"}); ++ ++ } ++ ++ XMLSignatureInput xmlInput = ref.getContentsBeforeTransformation(); ++ ++ if(xmlInput.isElement()) { ++ Node subNode = xmlInput.getSubNode(); ++ String name = subNode.getLocalName(); ++ LOG.debug("Reference: name = " + subNode.getNodeName() + ", type = " + subNode.getNodeType()); ++ ++ if(WSConstants.TIMESTAMP_TOKEN_LN.compareTo(name) == 0) { ++ verifyTimestamp(subNode); ++ tsSigned = true; ++ } else if(WSConstants.ELEM_BODY.compareTo(name) == 0) { ++ verifyBodyLocation(subNode); ++ bdSigned = true; ++ } ++ ++ } else { ++ throw new XMLSignatureException("generic.NotYetImplemented", ++ new Object[]{"References to multiple elements"}); ++ } ++ ++ } ++ ++ if(!tsSigned) ++ throw new WSSecurityException(WSSecurityException.FAILED_CHECK, ++ "requiredElementNotSigned", ++ new Object[]{WSConstants.TIMESTAMP_TOKEN_LN}); ++ if(!bdSigned) ++ throw new WSSecurityException(WSSecurityException.FAILED_CHECK, ++ "requiredElementNotSigned", ++ new Object[]{WSConstants.ELEM_BODY}); ++ ++ return true; ++ } ++ ++ private static void verifyTimestamp(Node node) throws WSSecurityException { ++ TimestampProcessor tsProc = new TimestampProcessor(); ++ ++ LOG.debug("Timestamp: " + node); ++ ++ // can't call handleTimestamp() directly because ++ // the config will not be set in that case ++ // all null-params are not used by handleToken() ++ Vector retResults = new Vector(); ++ tsProc.handleToken((Element)node, null, null, null, null, retResults, WSSConfig.getDefaultWSConfig()); ++ ++ // need to make sure that the timestamp's valid period ++ // is at least as long as the request caching time ++ Timestamp ts = (Timestamp)((WSSecurityEngineResult)retResults.get(0)).get(WSSecurityEngineResult.TAG_TIMESTAMP); ++ LOG.debug("timestamp: " + ts); ++ ++ Date expires = ts.getExpires().getTime(); ++ ++ // TODO: GRZE throw an exception that will result in ++ // an error response sent back to the user ++ if(!SecurityContext.validateTimestampPeriod(expires)) ++ throw new WSSecurityException("Timestamp expiration is too far in the future"); ++ ++ } ++ ++ /** ++ * Checks that Body element is located inside ++ * the main element ++ * ++ * @param node ++ * @throws WSSecurityException ++ */ ++ private static void verifyBodyLocation(Node node) throws WSSecurityException { ++ ++ Node parent = node.getParentNode(); ++ LOG.debug("processing " + node.getNodeName() + ", parent = " + parent.getNodeName()); ++ ++ if(parent == null || (WSConstants.ELEM_ENVELOPE.compareTo(parent.getLocalName()) != 0)) ++ throw new WSSecurityException("Unexpected parent element for signed "); ++ /* ++ throw new WSSecurityException(WSSecurityException.FAILED_CHECK, ++ "badElement", ++ new Object[] {WSConstants.ELEM_ENVELOPE, parent.getNodeName()}); ++ */ ++ int depth = 0; ++ while(parent != null) { ++ depth++; ++ parent = parent.getParentNode(); ++ } ++ ++ if(depth != 2) { ++ LOG.debug("depth of " + node.getNodeName() + " is " + depth); ++ throw new WSSecurityException("Unexpected location of signed "); ++ } ++ /*throw new WSSecurityException(WSSecurityException.FAILED_CHECK, "badElement", ++ new Object[]{" at depth 2", " at depth " + depth});*/ ++ } ++ + public static XMLSignature getXmlSignature( final Element signatureNode ) throws WSSecurityException { + XMLSignature sig = null; + try { +@@ -205,6 +338,7 @@ + } + + public static Element getSecurityElement( SOAPEnvelope envelope ) { ++ // get security header + final StAXOMBuilder doomBuilder = HoldMe.getStAXOMBuilder( HoldMe.getDOOMFactory( ), envelope.getXMLStreamReader( ) ); + final OMElement elem = doomBuilder.getDocumentElement( ); + elem.build( ); +--- a/clc/modules/msgs/src/main/java/com/eucalyptus/util/TimedEvictionSet.java ++++ b/clc/modules/msgs/src/main/java/com/eucalyptus/util/TimedEvictionSet.java +@@ -15,7 +15,8 @@ + private static Logger LOG = Logger.getLogger( TimedEvictionSet.class ); + private NavigableSet entries = new ConcurrentSkipListSet(); + private NavigableSet timestamps = new ConcurrentSkipListSet(); +- private Long evictionMillis = 15*1000*60l; ++ //private Long evictionMillis = 15*1000*60l; ++ private Long evictionNanos = 15*1000*60*1000*1000l; + private AtomicBoolean busy = new AtomicBoolean( false ); + + class TimestampedElement implements Comparable { +@@ -58,8 +59,9 @@ + return this.timeNanos.compareTo( that.timeNanos ); + } + } ++ + public boolean isExpired( ) { +- return System.nanoTime( ) > ( this.timeNanos + TimedEvictionSet.this.evictionMillis ); ++ return System.nanoTime( ) > ( this.timeNanos + TimedEvictionSet.this.evictionNanos); + } + public E get( ) { + return this.element; +@@ -70,6 +72,7 @@ + } + private boolean timestamp( E e ) { + this.scavenge( ); ++ + if( this.entries.add( e ) ) { + TimestampedElement elem = new TimestampedElement( e ); + this.timestamps.add( elem ); +@@ -83,9 +86,10 @@ + } + } + } ++ + public TimedEvictionSet( Long evictionMillis ) { + super( ); +- this.evictionMillis = evictionMillis; ++ this.evictionNanos = evictionMillis * 1000 * 1000; + } + + private void scavenge() { +@@ -102,6 +106,11 @@ + } + } + ++ ++ public Long getEvictionNanos() { ++ return evictionNanos; ++ } ++ + public boolean add( E e ) { + return timestamp( e ); + } +--- a/clc/modules/wsstack/src/main/java/com/eucalyptus/ws/handlers/MessageStackHandler.java ++++ b/clc/modules/wsstack/src/main/java/com/eucalyptus/ws/handlers/MessageStackHandler.java +@@ -74,9 +74,11 @@ + import org.jboss.netty.channel.Channels; + import org.jboss.netty.channel.ExceptionEvent; + import org.jboss.netty.channel.MessageEvent; ++import org.jboss.netty.handler.codec.http.HttpResponseStatus; + + import com.eucalyptus.system.LogLevels; + import com.eucalyptus.util.LogUtil; ++import com.eucalyptus.ws.WebServicesException; + + public abstract class MessageStackHandler implements ChannelDownstreamHandler, ChannelUpstreamHandler { + private static Logger LOG = Logger.getLogger( MessageStackHandler.class ); +@@ -127,8 +129,9 @@ + this.incomingMessage( channelHandlerContext, msgEvent ); + } catch ( Throwable e ) { + LOG.error( e, e ); +- Channels.fireExceptionCaught( channelHandlerContext, e ); +- return; ++ /*Channels.fireExceptionCaught( channelHandlerContext, e ); ++ return;*/ ++ throw new WebServicesException( e.getMessage( ), HttpResponseStatus.BAD_REQUEST ); + } + } + channelHandlerContext.sendUpstream( channelEvent ); +--- a/clc/modules/wsstack/src/main/java/com/eucalyptus/ws/handlers/wssecurity/ClusterWsSecHandler.java ++++ b/clc/modules/wsstack/src/main/java/com/eucalyptus/ws/handlers/wssecurity/ClusterWsSecHandler.java +@@ -69,6 +69,9 @@ + import com.eucalyptus.bootstrap.Component; + import com.eucalyptus.ws.util.CredentialProxy; + import com.google.common.collect.Lists; ++import org.apache.axiom.soap.SOAP11Constants; ++import org.apache.axiom.soap.SOAPConstants; ++import org.apache.ws.security.WSConstants; + + public class ClusterWsSecHandler extends WsSecHandler { + private static final String WSA_NAMESPACE = "http://www.w3.org/2005/08/addressing"; +@@ -79,14 +82,15 @@ + + @Override + public Collection getSignatureParts() { +- return Lists.newArrayList( new WSEncryptionPart( "To", WSA_NAMESPACE, "Content" ),new WSEncryptionPart( "MessageID", WSA_NAMESPACE, "Content" ), new WSEncryptionPart( "Action", WSA_NAMESPACE, "Content" ) ); ++ return Lists.newArrayList(new WSEncryptionPart( "To", WSA_NAMESPACE, "Content" ), ++ new WSEncryptionPart( "MessageID", WSA_NAMESPACE, "Content" ), new WSEncryptionPart( "Action", WSA_NAMESPACE, "Content" ), new WSEncryptionPart(WSConstants.TIMESTAMP_TOKEN_LN, WSConstants.WSU_NS, "Content" ), new WSEncryptionPart( SOAPConstants.BODY_LOCAL_NAME, SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI, "Content" ) ); + } + + + + @Override + public boolean shouldTimeStamp() { +- return false; ++ return true; + } + + } +--- a/clc/modules/wsstack/src/main/java/com/eucalyptus/ws/handlers/wssecurity/InternalWsSecHandler.java ++++ b/clc/modules/wsstack/src/main/java/com/eucalyptus/ws/handlers/wssecurity/InternalWsSecHandler.java +@@ -116,7 +116,9 @@ + final SOAPEnvelope envelope = httpRequest.getSoapEnvelope( ); + final Element secNode = WSSecurity.getSecurityElement( envelope ); + final XMLSignature sig = WSSecurity.getXMLSignature( secNode ); +- SecurityContext.enqueueSignature( sig.getTextFromTextChild( ) ); ++ String sigValue = new String(sig.getSignatureValue()); ++ SecurityContext.enqueueSignature( sigValue ); ++ //SecurityContext.enqueueSignature( sig.getTextFromTextChild( ) ); + final X509Certificate cert = WSSecurity.verifySignature( secNode, sig ); + if(cert != null) { + if( !cert.equals( SystemCredentialProvider.getCredentialProvider( Component.eucalyptus ).getCertificate( ) ) ) { +--- a/storage/storage.c ++++ b/storage/storage.c +@@ -373,13 +373,13 @@ + + static long long init_cache (const char * cache_path) + { +- long long total_size = 0; ++ long long total_size_b = 0; + + logprintfl (EUCAINFO, "checking the integrity of the cache directory (%s)\n", cache_path); + + if (cache_path==NULL) { + logprintfl (EUCAINFO, "no cache directory yet\n"); +- return total_size; ++ return total_size_b; + } + + struct stat mystat; +@@ -387,7 +387,7 @@ + logprintfl (EUCAFATAL, "error: could not stat %s\n", cache_path); + return -1; + } +- total_size += mystat.st_size; ++ total_size_b += mystat.st_size; + + DIR * cache_dir; + if ((cache_dir=opendir(cache_path))==NULL) { +@@ -399,7 +399,7 @@ + while ((cache_dir_entry=readdir(cache_dir))!=NULL) { + char * image_name = cache_dir_entry->d_name; + char image_path [BUFSIZE]; +- int image_size = 0; ++ long long image_size_b = 0; + int image_files = 0; + + if (!strcmp(".", image_name) || +@@ -418,7 +418,7 @@ + closedir(image_dir); + continue; + } +- image_size += mystat.st_size; ++ image_size_b += mystat.st_size; + + /* make sure that image directory contains only two files: one + * named X and another X-digest, also add up their sizes */ +@@ -445,7 +445,7 @@ + logprintfl (EUCAERROR, "error: empty file among cached images in %s\n", filepath); + break; + } +- image_size += mystat.st_size; ++ image_size_b += mystat.st_size; + + char * suffix; + if ((suffix=strstr (name, "-digest"))==NULL) { +@@ -470,10 +470,10 @@ + } else { + char filepath [BUFSIZE]; + snprintf (filepath, BUFSIZE, "%s/%s", image_path, X); +- if (image_size>0) { +- logprintfl (EUCAINFO, "- cached image %s directory, size=%d\n", image_name, image_size); +- total_size += image_size; +- add_to_cache (filepath, image_size); ++ if (image_size_b>0) { ++ logprintfl (EUCAINFO, "- cached image %s directory, size=%lld\n", image_name, image_size_b); ++ total_size_b += image_size_b; ++ add_to_cache (filepath, image_size_b); + } else { + logprintfl (EUCAWARN, "warning: empty cached image directory %s\n", image_path); + } +@@ -482,7 +482,7 @@ + } + closedir (cache_dir); + +- return total_size; ++ return total_size_b; + } + + #define F1 "/tmp/improbable-cache-file-1" +--- a/tools/client-policy-template.xml ++++ b/tools/client-policy-template.xml +@@ -36,9 +36,8 @@ + + + +- ++ + +- + + + +@@ -50,6 +49,7 @@ + + + ++ + + + +--- a/tools/service-policy-template.xml ++++ b/tools/service-policy-template.xml +@@ -5,7 +5,7 @@ + + + +- ++ a + + + +@@ -36,7 +36,7 @@ + + + +- ++ + + + +@@ -46,10 +46,12 @@ + + + ++ + + + + ++ + + + +@@ -58,13 +60,6 @@ + EUCALYPTUS_HOME/var/lib/eucalyptus/keys/SERVER-CERT + EUCALYPTUS_HOME/var/lib/eucalyptus/keys/SERVER-KEY + +- + + + +--- a/util/euca_axis.c ++++ b/util/euca_axis.c +@@ -69,6 +69,12 @@ + wsu:Id="CertId-469">[..snip..] + [..snip..] + ++ ++ ++ + + +@@ -90,6 +96,10 @@ + #include "rampart_error.h" + #include "axis2_op_ctx.h" + #include "rampart_context.h" ++#include "rampart_constants.h" ++#include "axis2_addr.h" ++#include "axiom_util.h" ++#include "rampart_timestamp_token.h" + + #include + #include +@@ -220,6 +230,10 @@ + AXIS2_LOG_CRITICAL(env->log,AXIS2_LOG_SI," ---------------------------------------------------" ); + NO_U_FAIL("The certificate specified is invalid!"); + } ++ if(verify_references(sig_node, env, out_msg_ctx, soap_envelope) == AXIS2_FAILURE) { ++ return AXIS2_FAILURE; ++ } ++ + } + else + { +@@ -228,10 +242,197 @@ + } + oxs_x509_cert_free(_cert, env); + oxs_x509_cert_free(recv_cert, env); ++ + return AXIS2_SUCCESS; + + } + ++/** ++ * Verifes that Body, Timestamp, To, Action, and MessageId elements are signed and located ++ * where expected by the application logic. Timestamp is checked for expiration regardless ++ * of its actual location. ++ */ ++axis2_status_t verify_references(axiom_node_t *sig_node, const axutil_env_t *env, axis2_msg_ctx_t *msg_ctx, axiom_soap_envelope_t *envelope) { ++ axiom_node_t *si_node = NULL; ++ axiom_node_t *ref_node = NULL; ++ axis2_status_t status = AXIS2_SUCCESS; ++ ++ si_node = oxs_axiom_get_first_child_node_by_name(env,sig_node, OXS_NODE_SIGNEDINFO, OXS_DSIG_NS, OXS_DS); ++ ++ if(!si_node) { ++ axis2_char_t *tmp = axiom_node_to_string(sig_node, env); ++ AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "[euca-rampart]sig = %s", tmp); ++ NO_U_FAIL("Couldn't find SignedInfo!"); ++ } ++ ++ axutil_qname_t *qname = NULL; ++ axiom_element_t *parent_elem = NULL; ++ axiom_children_qname_iterator_t *qname_iter = NULL; ++ ++ parent_elem = axiom_node_get_data_element(si_node, env); ++ if(!parent_elem) ++ { ++ NO_U_FAIL("Could not get Reference elem"); ++ } ++ ++ axis2_char_t *ref = NULL; ++ axis2_char_t *ref_id = NULL; ++ axiom_node_t *signed_node = NULL; ++ axiom_node_t *envelope_node = NULL; ++ ++ short signed_elems[5] = {0,0,0,0,0}; ++ ++ envelope_node = axiom_soap_envelope_get_base_node(envelope, env); ++ ++ qname = axutil_qname_create(env, OXS_NODE_REFERENCE, OXS_DSIG_NS, NULL); ++ qname_iter = axiom_element_get_children_with_qname(parent_elem, env, qname, si_node); ++ while (axiom_children_qname_iterator_has_next(qname_iter , env)) { ++ ref_node = axiom_children_qname_iterator_next(qname_iter, env); ++ axis2_char_t *txt = axiom_node_to_string(ref_node, env); ++ ++ /* get reference to a signed element */ ++ ref = oxs_token_get_reference(env, ref_node); ++ if(ref == NULL || strlen(ref) == 0 || ref[0] != '#') { ++ oxs_error(env, OXS_ERROR_LOCATION, OXS_ERROR_ELEMENT_FAILED, "Unsupported reference ID in %s", txt); ++ status = AXIS2_FAILURE; ++ break; ++ } ++ ++ AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "[euca-rampart] %s, ref = %s", txt, ref); ++ ++ /* get rid of '#' */ ++ ref_id = axutil_string_substring_starting_at(axutil_strdup(env, ref), 1); ++ signed_node = oxs_axiom_get_node_by_id(env, envelope_node, OXS_ATTR_ID, ref_id, OXS_WSU_XMLNS); ++ if(!signed_node) { ++ oxs_error(env, OXS_ERROR_LOCATION, OXS_ERROR_ELEMENT_FAILED, "Error retrieving elementwith ID=%s", ref_id); ++ status = AXIS2_FAILURE; ++ break; ++ } ++ if(verify_node(signed_node, env, msg_ctx, ref, signed_elems)) { ++ status = AXIS2_FAILURE; ++ break; ++ } ++ } ++ ++ ++ axutil_qname_free(qname, env); ++ qname = NULL; ++ ++ if(status == AXIS2_FAILURE) { ++ NO_U_FAIL("Failed to verify location of signed elements!"); ++ } ++ ++ /* This is needed to make sure that all security-critical elements are signed */ ++ for(int i = 0; i < 5; i++) { ++ if(signed_elems[i] == 0) { ++ NO_U_FAIL("Not all required elements are signed"); ++ } ++ } ++ ++ return status; ++ ++} ++ ++/** ++ * Verifies XPath location of signed elements. ++ */ ++int verify_node(axiom_node_t *signed_node, const axutil_env_t *env, axis2_msg_ctx_t *msg_ctx, axis2_char_t *ref, short *signed_elems) { ++ ++ if(!axutil_strcmp(OXS_NODE_BODY, axiom_util_get_localname(signed_node, env))) { ++ AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "[euca-rampart] node %s is Body", ref); ++ signed_elems[0] = 1; ++ ++ axiom_node_t *parent = axiom_node_get_parent(signed_node,env); ++ if(axutil_strcmp(OXS_NODE_ENVELOPE, axiom_util_get_localname(parent, env))) { ++ oxs_error(env, OXS_ERROR_LOCATION, OXS_ERROR_ELEMENT_FAILED, "Unexpected parent element for Body with ID = %s", ref); ++ return 1; ++ } ++ ++ parent = axiom_node_get_parent(parent,env); ++ if(parent) { ++ AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "[euca-rampart] parent of Envelope = %s", axiom_node_to_string(parent, env)); ++ oxs_error(env, OXS_ERROR_LOCATION, OXS_ERROR_ELEMENT_FAILED, "Unexpected location of signed Body with ID = %s", ref); ++ return 1; ++ } ++ ++ } else if(!axutil_strcmp(RAMPART_SECURITY_TIMESTAMP, axiom_util_get_localname(signed_node, env))) { ++ AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "[euca-rampart] node %s is Timestamp", ref); ++ signed_elems[1] = 1; ++ ++ /* Regardless of the location of the Timestamp, verify the one that is signed */ ++ if(AXIS2_FAILURE == rampart_timestamp_token_validate(env, msg_ctx, signed_node, 0)) { ++ oxs_error(env, OXS_ERROR_LOCATION, OXS_ERROR_ELEMENT_FAILED, "Validation failed for Timestamp with ID = %s", ref); ++ return 1; ++ } ++ ++ } else if(!axutil_strcmp(AXIS2_WSA_ACTION, axiom_util_get_localname(signed_node, env))) { ++ AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "[euca-rampart] node %s is Action", ref); ++ signed_elems[2] = 1; ++ ++ if(verify_addr_hdr_elem_loc(signed_node, env, ref)) { ++ oxs_error(env, OXS_ERROR_LOCATION, OXS_ERROR_ELEMENT_FAILED, "Validation failed for Action with ID = %s", ref); ++ return 1; ++ } ++ ++ } else if(!axutil_strcmp(AXIS2_WSA_TO, axiom_util_get_localname(signed_node, env))) { ++ AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "[euca-rampart] node %s is To", ref); ++ signed_elems[3] = 1; ++ ++ if(verify_addr_hdr_elem_loc(signed_node, env, ref)) { ++ oxs_error(env, OXS_ERROR_LOCATION, OXS_ERROR_ELEMENT_FAILED, "Validation failed for To with ID = %s", ref); ++ return 1; ++ } ++ ++ ++ } else if(!axutil_strcmp(AXIS2_WSA_MESSAGE_ID, axiom_util_get_localname(signed_node, env))) { ++ AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "[euca-rampart] node %s is MessageId", ref); ++ signed_elems[4] = 1; ++ ++ if(verify_addr_hdr_elem_loc(signed_node, env, ref)) { ++ oxs_error(env, OXS_ERROR_LOCATION, OXS_ERROR_ELEMENT_FAILED, "Validation failed for MessageId with ID = %s", ref); ++ return 1; ++ } ++ ++ } else { ++ AXIS2_LOG_WARNING(env->log, AXIS2_LOG_SI, "[euca-rampart] node %s is UNKNOWN", ref); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Verify that an addressing element is located in /
++ */ ++int verify_addr_hdr_elem_loc(axiom_node_t *signed_node, const axutil_env_t *env, axis2_char_t *ref) { ++ ++ axiom_node_t *parent = axiom_node_get_parent(signed_node,env); ++ ++ if(axutil_strcmp(OXS_NODE_HEADER, axiom_util_get_localname(parent, env))) { ++ AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "[euca-rampart] parent of addressing elem is %s", axiom_node_to_string(parent, env)); ++ oxs_error(env, OXS_ERROR_LOCATION, OXS_ERROR_ELEMENT_FAILED, "Unexpected location of signed addressing elem with ID = %s", ref); ++ return 1; ++ ++ } ++ parent = axiom_node_get_parent(parent,env); ++ ++ if(axutil_strcmp(OXS_NODE_ENVELOPE, axiom_util_get_localname(parent, env))) { ++ AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "[euca-rampart] second parent of addressing elem is %s", axiom_node_to_string(parent, env)); ++ oxs_error(env, OXS_ERROR_LOCATION, OXS_ERROR_ELEMENT_FAILED, "Unexpected location of signed addressing elem with ID = %s", ref); ++ return 1; ++ ++ } ++ ++ parent = axiom_node_get_parent(parent,env); ++ if(parent) { ++ AXIS2_LOG_DEBUG(env->log, AXIS2_LOG_SI, "[euca-rampart] parent of Envelope = %s", axiom_node_to_string(parent, env)); ++ oxs_error(env, OXS_ERROR_LOCATION, OXS_ERROR_ELEMENT_FAILED, "Unexpected location of signed Body with ID = %s", ref); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++ + int InitWSSEC(axutil_env_t *env, axis2_stub_t *stub, char *policyFile) { + axis2_svc_client_t *svc_client = NULL; + neethi_policy_t *policy = NULL; +--- a/util/euca_axis.h ++++ b/util/euca_axis.h +@@ -78,6 +78,9 @@ + int InitWSSEC(axutil_env_t *env, axis2_stub_t *stub, char *policyFile); + + axis2_status_t __euca_authenticate(const axutil_env_t *env,axis2_msg_ctx_t *out_msg_ctx, axis2_op_ctx_t *op_ctx); ++axis2_status_t verify_references(axiom_node_t *sig_node, const axutil_env_t *env, axis2_msg_ctx_t *msg_ctx, axiom_soap_envelope_t *soap_envelope); ++int verify_node( axiom_node_t *signed_node, const axutil_env_t *env, axis2_msg_ctx_t *msg_ctx, axis2_char_t *ref, short *signed_elems); ++int verify_addr_hdr_elem_loc(axiom_node_t *signed_node, const axutil_env_t *env, axis2_char_t *ref); + + #define euca_authenticate(a,b,c) do{ if( __euca_authenticate(a,b,c) == AXIS2_FAILURE ) return NULL; }while(0) + diff -Nru eucalyptus-2.0.1+bzr1256/debian/patches/series eucalyptus-2.0.1+bzr1256/debian/patches/series --- eucalyptus-2.0.1+bzr1256/debian/patches/series 2011-05-18 13:07:34.000000000 +0000 +++ eucalyptus-2.0.1+bzr1256/debian/patches/series 2011-05-26 15:21:16.000000000 +0000 @@ -14,3 +14,4 @@ 24-label-metadata-ip.patch 25-uec-logo-png.patch 26-google-collections-1.0-ftbfs.patch +27-soap-security.patch