From c4c804e4ab0ee8ff178cef1130cf07beed3b4234 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Fri, 24 Nov 2006 23:40:38 +0000 Subject: [PATCH] SAML 2.0 SOAP client. --- saml/Makefile.am | 6 +- saml/saml.vcproj | 8 +++ saml/saml2/binding/SAML2SOAPClient.h | 90 ++++++++++++++++++++++++ saml/saml2/binding/impl/SAML2SOAPClient.cpp | 105 ++++++++++++++++++++++++++++ 4 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 saml/saml2/binding/SAML2SOAPClient.h create mode 100644 saml/saml2/binding/impl/SAML2SOAPClient.cpp diff --git a/saml/Makefile.am b/saml/Makefile.am index 5b628cb..3839885 100644 --- a/saml/Makefile.am +++ b/saml/Makefile.am @@ -90,8 +90,9 @@ saml2bindinclude_HEADERS = \ saml2/binding/SAML2RedirectDecoder.h \ saml2/binding/SAML2RedirectEncoder.h \ saml2/binding/SAML2SOAPDecoder.h \ - saml2/binding/SAML2SOAPEncoder.h - + saml2/binding/SAML2SOAPEncoder.h \ + saml2/binding/SAML2SOAPClient.h + saml2mdinclude_HEADERS = \ saml2/metadata/AbstractMetadataProvider.h \ saml2/metadata/ChainingMetadataProvider.h \ @@ -155,6 +156,7 @@ libsaml_la_SOURCES = \ saml2/binding/impl/SAML2RedirectEncoder.cpp \ saml2/binding/impl/SAML2SOAPDecoder.cpp \ saml2/binding/impl/SAML2SOAPEncoder.cpp \ + saml2/binding/impl/SAML2SOAPClient.cpp \ encryption/EncryptedKeyResolver.cpp \ signature/ContentReference.cpp \ signature/SignatureProfileValidator.cpp \ diff --git a/saml/saml.vcproj b/saml/saml.vcproj index c4664d6..3b83e31 100644 --- a/saml/saml.vcproj +++ b/saml/saml.vcproj @@ -433,6 +433,10 @@ > + + @@ -740,6 +744,10 @@ > + + diff --git a/saml/saml2/binding/SAML2SOAPClient.h b/saml/saml2/binding/SAML2SOAPClient.h new file mode 100644 index 0000000..a49af74 --- /dev/null +++ b/saml/saml2/binding/SAML2SOAPClient.h @@ -0,0 +1,90 @@ +/* + * Copyright 2001-2006 Internet2 + * + * 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. + */ + +/** + * @file saml/saml2/binding/SAML2SOAPClient.h + * + * Specialized SOAPClient for SAML 2.0 SOAP binding. + */ + +#ifndef __saml2_soap11client_h__ +#define __saml2_soap11client_h__ + +#include + +namespace opensaml { + namespace saml2p { + + class SAML_API RequestAbstractType; + class SAML_API StatusResponseType; + class SAML_API Status; + + /** + * Specialized SOAPClient for SAML 2.0 SOAP binding. + */ + class SAML_API SAML2SOAPClient : opensaml::SOAPClient + { + public: + /** + * Creates a SOAP client instance with a particular SecurityPolicy. + * + * @param policy reference to SecurityPolicy to apply + */ + SAML2SOAPClient(SecurityPolicy& policy) : opensaml::SOAPClient(policy), m_correlate(NULL) {} + + virtual ~SAML2SOAPClient() { + XMLString::release(&m_correlate); + } + + /** + * Specialized method for sending SAML 2.0 requests. The SOAP layer will be + * constructed automatically. + * + *

The request will be freed by the client object if the method succeeds. + * + * @param request SAML request to send + * @param peer peer to send message to, expressed in metadata terms + * @param endpoint URL of endpoint to recieve message + */ + virtual void sendSAML(RequestAbstractType* request, const saml2md::RoleDescriptor& peer, const char* endpoint); + + /** + * Specialized method for receiving SAML 2.0 responses. The SOAP layer will be + * evaluated automatically, and the attached policy will be applied to the Response. + * + *

The caller is responsible for freeing the response. + * + * @return SAML 2.0 response, after SecurityPolicy has been applied + */ + virtual StatusResponseType* receiveSAML(); + + protected: + /** + * Handling of SAML errors. + * + * @param status SAML Status received by client + * @return true iff the error should be treated as a fatal error + */ + virtual bool handleError(const Status& status); + + private: + XMLCh* m_correlate; + }; + + }; +}; + +#endif /* __saml2_soap11client_h__ */ diff --git a/saml/saml2/binding/impl/SAML2SOAPClient.cpp b/saml/saml2/binding/impl/SAML2SOAPClient.cpp new file mode 100644 index 0000000..db6d1f5 --- /dev/null +++ b/saml/saml2/binding/impl/SAML2SOAPClient.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2001-2006 Internet2 + * + * 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. + */ + +/** + * SAML2SOAPClient.cpp + * + * Specialized SOAPClient for SAML 2.0 SOAP binding. + */ + +#include "internal.h" +#include "exceptions.h" +#include "saml2/binding/SAML2SOAPClient.h" +#include "saml2/core/Protocols.h" +#include "saml2/metadata/Metadata.h" + +#include +#include + +using namespace opensaml::saml2p; +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace soap11; +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +void SAML2SOAPClient::sendSAML(RequestAbstractType* request, const RoleDescriptor& peer, const char* endpoint) +{ + Envelope* env = EnvelopeBuilder::buildEnvelope(); + Body* body = BodyBuilder::buildBody(); + env->setBody(body); + body->getXMLObjects().push_back(request); + try { + send(env, peer, endpoint); + m_correlate = XMLString::replicate(request->getID()); + delete env; + } + catch (XMLToolingException&) { + // A bit weird...we have to "revert" things so that the request is isolated + // so the caller can free it. + request->getParent()->detach(); + request->detach(); + throw; + } +} + +StatusResponseType* SAML2SOAPClient::receiveSAML() +{ + auto_ptr env(receive()); + if (env.get()) { + Body* body = env->getBody(); + if (body && body->hasChildren()) { + // Check for SAML Response. + StatusResponseType* response = dynamic_cast(body->getXMLObjects().front()); + if (response) { + + // Check InResponseTo. + if (m_correlate && !XMLString::equals(m_correlate, response->getInResponseTo())) + throw BindingException("InResponseTo attribute did not correlate with the Request ID."); + + // Check Status. + Status* status = response->getStatus(); + if (status) { + const XMLCh* code = status->getStatusCode() ? status->getStatusCode()->getValue() : NULL; + if (code && !XMLString::equals(code,StatusCode::SUCCESS) && handleError(*status)) + throw BindingException("SAML Response contained an error."); + } + + m_policy.evaluate(*response); + env.release(); + body->detach(); // frees Envelope + response->detach(); // frees Body + return response; + } + } + + throw BindingException("SOAP Envelope did not contain a SAML Response or a Fault."); + } + return NULL; +} + +bool SAML2SOAPClient::handleError(const Status& status) +{ + auto_ptr_char code((status.getStatusCode() ? status.getStatusCode()->getValue() : NULL)); + auto_ptr_char str((status.getStatusMessage() ? status.getStatusMessage()->getMessage() : NULL)); + Category::getInstance(SAML_LOGCAT".SOAPClient").error( + "SOAP client detected a SAML error: (%s) (%s)", + (code.get() ? code.get() : "no code"), + (str.get() ? str.get() : "no message") + ); + return true; +} -- 2.1.4