CPPXT-104 - Add exception handling to integer conversions
[shibboleth/cpp-opensaml.git] / saml / binding / impl / ArtifactMap.cpp
index 2abaecc..e4bae0e 100644 (file)
@@ -1,17 +1,21 @@
-/*
- *  Copyright 2001-2007 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
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID 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
+ * 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.
+ * 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.
  */
 
 /**
 #include "binding/ArtifactMap.h"
 #include "binding/SAMLArtifact.h"
 
-#include <log4cpp/Category.hh>
+#include <ctime>
+#include <boost/lambda/bind.hpp>
+#include <boost/lambda/lambda.hpp>
 #include <xercesc/util/XMLUniDefs.hpp>
+#include <xmltooling/logging.h>
 #include <xmltooling/XMLObjectBuilder.h>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/security/SecurityHelper.h>
 #include <xmltooling/util/NDC.h>
+#include <xmltooling/util/ParserPool.h>
+#include <xmltooling/util/StorageService.h>
 #include <xmltooling/util/XMLHelper.h>
+#include <xmltooling/util/Threads.h>
 
 using namespace opensaml;
+using namespace xmltooling::logging;
 using namespace xmltooling;
-using namespace log4cpp;
+using namespace boost::lambda;
+using namespace boost;
 using namespace std;
 
 namespace opensaml {
@@ -42,17 +56,18 @@ namespace opensaml {
     {
     public:
         ArtifactMappings() : m_lock(Mutex::create()) {}
-        ~ArtifactMappings() {
-            delete m_lock;
-            for (map<string,Mapping>::iterator i=m_artMap.begin(); i!=m_artMap.end(); ++i)
-                delete i->second.m_xml;
-        }
+        ~ArtifactMappings() {}
+
         void storeContent(XMLObject* content, const SAMLArtifact* artifact, const char* relyingParty, int TTL);
         XMLObject* retrieveContent(const SAMLArtifact* artifact, const char* relyingParty);
+        string getRelyingParty(const SAMLArtifact* artifact);
     
     private:
         struct SAML_DLLLOCAL Mapping {
-            Mapping() : m_xml(NULL), m_expires(0) {}
+            Mapping() : m_xml(nullptr), m_expires(0) {}
+            ~Mapping() {
+                delete m_xml;
+            }
             XMLObject* m_xml;
             string m_relying;
             time_t m_expires;
@@ -60,7 +75,7 @@ namespace opensaml {
 
         void removeMapping(const map<string,Mapping>::iterator& i);
         
-        Mutex* m_lock;
+        auto_ptr<Mutex> m_lock;
         map<string,Mapping> m_artMap;
         multimap<time_t,string> m_expMap;
     };
@@ -73,16 +88,19 @@ namespace opensaml {
 
 void ArtifactMappings::removeMapping(const map<string,Mapping>::iterator& i)
 {
-    // Update secondary map.
+    // All elements in the secondary map whose key matches the expiration of the removed mapping.
     pair<multimap<time_t,string>::iterator,multimap<time_t,string>::iterator> range =
         m_expMap.equal_range(i->second.m_expires);
-    for (; range.first != range.second; ++range.first) {
-        if (range.first->second == i->first) {
-            m_expMap.erase(range.first);
-            break;
-        }
+
+    // Find an element in the matching range whose value matches the input key.
+    multimap<time_t,string>::iterator el = find_if(
+        range.first, range.second,
+        (lambda::bind(&multimap<time_t,string>::value_type::second, _1) == boost::ref(i->first))
+        );
+    if (el != range.second) {
+        m_expMap.erase(el);
     }
-    delete i->second.m_xml;
+
     m_artMap.erase(i);
 }
 
@@ -91,9 +109,9 @@ void ArtifactMappings::storeContent(XMLObject* content, const SAMLArtifact* arti
     Lock wrapper(m_lock);
 
     // Garbage collect any expired artifacts.
-    time_t now=time(NULL);
-    multimap<time_t,string>::iterator stop=m_expMap.upper_bound(now);
-    for (multimap<time_t,string>::iterator i=m_expMap.begin(); i!=stop; m_expMap.erase(i++)) {
+    time_t now = time(nullptr);
+    multimap<time_t,string>::iterator stop = m_expMap.upper_bound(now);
+    for (multimap<time_t,string>::iterator i = m_expMap.begin(); i != stop; m_expMap.erase(i++)) {
         delete m_artMap[i->second].m_xml;
         m_artMap.erase(i->second);
     }
@@ -105,16 +123,16 @@ void ArtifactMappings::storeContent(XMLObject* content, const SAMLArtifact* arti
     if (relyingParty)
         m.m_relying = relyingParty;
     m.m_expires = now + TTL;
-    m_expMap.insert(make_pair(m.m_expires,hexed));
+    m_expMap.insert(pair<const time_t, string>(m.m_expires, hexed));
 }
 
 XMLObject* ArtifactMappings::retrieveContent(const SAMLArtifact* artifact, const char* relyingParty)
 {
-    Category& log=Category::getInstance(SAML_LOGCAT".ArtifactMap");
+    Category& log=Category::getInstance(SAML_LOGCAT ".ArtifactMap");
     Lock wrapper(m_lock);
 
-    map<string,Mapping>::iterator i=m_artMap.find(SAMLArtifact::toHex(artifact->getMessageHandle()));
-    if (i==m_artMap.end())
+    map<string,Mapping>::iterator i = m_artMap.find(SAMLArtifact::toHex(artifact->getMessageHandle()));
+    if (i == m_artMap.end())
         throw BindingException("Requested artifact not in map or may have expired.");
     
     if (!(i->second.m_relying.empty())) {
@@ -128,48 +146,67 @@ XMLObject* ArtifactMappings::retrieveContent(const SAMLArtifact* artifact, const
         }
     }
     
-    if (time(NULL) >= i->second.m_expires) {
+    if (time(nullptr) >= i->second.m_expires) {
         removeMapping(i);
         throw BindingException("Requested artifact has expired.");
     }
     
     log.debug("resolved artifact for (%s)", relyingParty ? relyingParty : "unknown");
     XMLObject* ret = i->second.m_xml;
-    i->second.m_xml = NULL; // clear member so it doesn't get deleted
+    i->second.m_xml = nullptr;  // clear member so it doesn't get deleted
     removeMapping(i);
     return ret;
 }
 
+string ArtifactMappings::getRelyingParty(const SAMLArtifact* artifact)
+{
+    map<string,Mapping>::iterator i = m_artMap.find(SAMLArtifact::toHex(artifact->getMessageHandle()));
+    if (i == m_artMap.end())
+        throw BindingException("Requested artifact not in map or may have expired.");
+    return i->second.m_relying;
+}
+
 ArtifactMap::ArtifactMap(xmltooling::StorageService* storage, const char* context, unsigned int artifactTTL)
-    : m_storage(storage), m_context(context ? context : "opensaml::ArtifactMap"), m_mappings(NULL), m_artifactTTL(artifactTTL)
+    : m_storage(storage), m_context((context && *context) ? context : "opensaml::ArtifactMap"), m_artifactTTL(artifactTTL)
 {
     if (!m_storage)
-        m_mappings = new ArtifactMappings();
+        m_mappings.reset(new ArtifactMappings());
 }
 
 ArtifactMap::ArtifactMap(const DOMElement* e, xmltooling::StorageService* storage)
-    : m_storage(storage), m_mappings(NULL), m_artifactTTL(180)
+    : m_storage(storage), m_artifactTTL(180)
 {
     if (e) {
-        auto_ptr_char c(e->getAttributeNS(NULL, context));
-        if (c.get() && *c.get())
+        auto_ptr_char c(e->getAttributeNS(nullptr, context));
+        if (c.get() && *c.get()) {
             m_context = c.get();
-        
-        const XMLCh* TTL = e->getAttributeNS(NULL, artifactTTL);
+            if (storage && m_context.length() > m_storage->getCapabilities().getContextSize()) {
+                throw IOException("ArtifactMap context length exceeds capacity of storage service.");
+            }
+        }
+        else {
+            m_context = "opensaml::ArtifactMap";
+        }
+
+        const XMLCh* TTL = e->getAttributeNS(nullptr, artifactTTL);
         if (TTL) {
-            m_artifactTTL = XMLString::parseInt(TTL);
+            try {
+                m_artifactTTL = XMLString::parseInt(TTL);
+            }
+            catch (XMLException&) {
+                m_artifactTTL = 0;
+            }
             if (!m_artifactTTL)
                 m_artifactTTL = 180;
         }
     }
     
     if (!m_storage)
-        m_mappings = new ArtifactMappings();
+        m_mappings.reset(new ArtifactMappings());
 }
 
 ArtifactMap::~ArtifactMap()
 {
-    delete m_mappings;
 }
 
 void ArtifactMap::storeContent(XMLObject* content, const SAMLArtifact* artifact, const char* relyingParty)
@@ -185,17 +222,30 @@ void ArtifactMap::storeContent(XMLObject* content, const SAMLArtifact* artifact,
     // Build a DOM with the same document to store the relyingParty mapping.
     if (relyingParty) {
         auto_ptr_XMLCh temp(relyingParty);
-        root = root->getOwnerDocument()->createElementNS(NULL,Mapping);
-        root->setAttributeNS(NULL,_relyingParty,temp.get());
+        root = root->getOwnerDocument()->createElementNS(nullptr,Mapping);
+        root->setAttributeNS(nullptr,_relyingParty,temp.get());
         root->appendChild(content->getDOM());
     }
     
     // Serialize the root element, whatever it is, for storage.
     string xmlbuf;
     XMLHelper::serialize(root, xmlbuf);
-    m_storage->createText(
-        m_context.c_str(), SAMLArtifact::toHex(artifact->getMessageHandle()).c_str(), xmlbuf.c_str(), time(NULL) + m_artifactTTL
-        );
+
+    // Use hex form of message handler as storage key unless it's too big.
+    string key = artifact->getMessageHandle();
+    if (key.length() > m_storage->getCapabilities().getKeySize())
+        key = SecurityHelper::doHash("SHA1", key.data(), key.length());
+    else
+        key = SAMLArtifact::toHex(key);
+
+    if (!m_storage->createText(
+        m_context.c_str(),
+        key.c_str(),
+        xmlbuf.c_str(),
+        time(nullptr) + m_artifactTTL
+        )) {
+        throw IOException("Attempt to insert duplicate artifact into map.");
+    }
         
     // Cleanup by destroying XML.
     delete content;
@@ -206,26 +256,33 @@ XMLObject* ArtifactMap::retrieveContent(const SAMLArtifact* artifact, const char
 #ifdef _DEBUG
     xmltooling::NDC ndc("retrieveContent");
 #endif
+    Category& log=Category::getInstance(SAML_LOGCAT ".ArtifactMap");
 
     if (!m_storage)
         return m_mappings->retrieveContent(artifact, relyingParty);
-    
+
+    // Use hex form of message handler as storage key unless it's too big.
+    string key = artifact->getMessageHandle();
+    if (key.length() > m_storage->getCapabilities().getKeySize())
+        key = SecurityHelper::doHash("SHA1", key.data(), key.length());
+    else
+        key = SAMLArtifact::toHex(key);
+
+    // Read the mapping and then delete it.
     string xmlbuf;
-    string key = SAMLArtifact::toHex(artifact->getMessageHandle());
     if (!m_storage->readText(m_context.c_str(), key.c_str(), &xmlbuf))
         throw BindingException("Artifact not found in mapping database.");
+    m_storage->deleteText(m_context.c_str(), key.c_str());
     
+    // Parse the data back into XML.
     istringstream is(xmlbuf);
     DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(is);
     XercesJanitor<DOMDocument> janitor(doc);
-
-    Category& log=Category::getInstance(SAML_LOGCAT".ArtifactMap");
-    m_storage->deleteText(m_context.c_str(), key.c_str());
     
     // Check the root element.
     DOMElement* messageRoot = doc->getDocumentElement();
-    if (XMLHelper::isNodeNamed(messageRoot, NULL, Mapping)) {
-        auto_ptr_char temp(messageRoot->getAttributeNS(NULL,_relyingParty));
+    if (XMLHelper::isNodeNamed(messageRoot, nullptr, Mapping)) {
+        auto_ptr_char temp(messageRoot->getAttributeNS(nullptr,_relyingParty));
         if (!relyingParty || strcmp(temp.get(),relyingParty)) {
             log.warn("request from (%s) for artifact issued to (%s)", relyingParty ? relyingParty : "unknown", temp.get());
             throw BindingException("Unauthorized artifact mapping request.");
@@ -240,3 +297,33 @@ XMLObject* ArtifactMap::retrieveContent(const SAMLArtifact* artifact, const char
     log.debug("resolved artifact for (%s)", relyingParty ? relyingParty : "unknown");
     return xmlObject;
 }
+
+string ArtifactMap::getRelyingParty(const SAMLArtifact* artifact)
+{
+    if (!m_storage)
+        return m_mappings->getRelyingParty(artifact);
+
+    // Use hex form of message handler as storage key unless it's too big.
+    string key = artifact->getMessageHandle();
+    if (key.length() > m_storage->getCapabilities().getKeySize())
+        key = SecurityHelper::doHash("SHA1", key.data(), key.length());
+    else
+        key = SAMLArtifact::toHex(key);
+
+    string xmlbuf;
+    if (!m_storage->readText(m_context.c_str(), key.c_str(), &xmlbuf))
+        throw BindingException("Artifact not found in mapping database.");
+    
+    // Parse the data back into XML.
+    istringstream is(xmlbuf);
+    DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(is);
+    XercesJanitor<DOMDocument> janitor(doc);
+    
+    // Check the root element.
+    DOMElement* messageRoot = doc->getDocumentElement();
+    if (XMLHelper::isNodeNamed(messageRoot, nullptr, Mapping)) {
+        auto_ptr_char temp(messageRoot->getAttributeNS(nullptr,_relyingParty));
+        return temp.get();
+    }
+    return string();
+}