Move Shib constants to new lib, fixed symbol conflicts.
[shibboleth/cpp-sp.git] / xmlproviders / XMLMetadata.cpp
index ad2e3e3..093cc52 100644 (file)
@@ -1,52 +1,20 @@
-/* 
- * The Shibboleth License, Version 1. 
- * Copyright (c) 2002 
- * University Corporation for Advanced Internet Development, Inc. 
- * All rights reserved
+/*
+ *  Copyright 2001-2005 Internet2
  * 
- * 
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions are met:
- * 
- * Redistributions of source code must retain the above copyright notice, this 
- * list of conditions and the following disclaimer.
- * 
- * Redistributions in binary form must reproduce the above copyright notice, 
- * this list of conditions and the following disclaimer in the documentation 
- * and/or other materials provided with the distribution, if any, must include 
- * the following acknowledgment: "This product includes software developed by 
- * the University Corporation for Advanced Internet Development 
- * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
- * may appear in the software itself, if and wherever such third-party 
- * acknowledgments normally appear.
- * 
- * Neither the name of Shibboleth nor the names of its contributors, nor 
- * Internet2, nor the University Corporation for Advanced Internet Development, 
- * Inc., nor UCAID may be used to endorse or promote products derived from this 
- * software without specific prior written permission. For written permission, 
- * please contact shibboleth@shibboleth.org
- * 
- * Products derived from this software may not be called Shibboleth, Internet2, 
- * UCAID, or the University Corporation for Advanced Internet Development, nor 
- * may Shibboleth appear in their name, without prior written permission of the 
- * University Corporation for Advanced Internet Development.
- * 
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
- * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
- * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
- * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
- * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
- * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 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.
  */
 
+
 /* XMLMetadata.cpp - a metadata implementation that uses an XML-based registry
 
    Scott Cantor
 
 #include "internal.h"
 
+#include <algorithm>
 #include <sys/types.h>
 #include <sys/stat.h>
 
 #include <log4cpp/Category.hh>
 #include <xercesc/util/XMLChar.hpp>
+#include <xsec/dsig/DSIGTransformC14n.hpp>
+#include <xsec/dsig/DSIGReference.hpp>
+#include <xsec/dsig/DSIGTransformList.hpp>
 #include <xsec/enc/XSECCryptoException.hpp>
 #include <xsec/enc/XSECKeyInfoResolverDefault.hpp>
 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
+#include <xsec/framework/XSECException.hpp>
+#include <xsec/framework/XSECProvider.hpp>
+
+#include <shibsp/SPConstants.h>
 
 using namespace shibboleth;
 using namespace saml;
@@ -73,6 +49,7 @@ using namespace std;
 
 namespace {
 
+    class XMLMetadata;
     class XMLMetadataImpl : public ReloadableXMLFileImpl
     {
     public:
@@ -304,17 +281,7 @@ namespace {
             vector<const XMLCh*> m_formats;
         };
 
-        class ScopedRole : public virtual IScopedRoleDescriptor
-        {
-        public:
-            ScopedRole(const DOMElement* e);
-            saml::Iterator<std::pair<const XMLCh*,bool> > getScopes() const {return m_scopes;}
-
-        private:
-            vector<pair<const XMLCh*,bool> > m_scopes;
-        };
-        
-        class IDPRole : public SSORole, public ScopedRole, public virtual IIDPSSODescriptor
+        class IDPRole : public SSORole, public virtual IIDPSSODescriptor
         {
         public:
             IDPRole(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e);
@@ -335,7 +302,7 @@ namespace {
             friend class EntityDescriptor;
         };
 
-        class AARole : public Role, public ScopedRole, public virtual IAttributeAuthorityDescriptor
+        class AARole : public Role, public virtual IAttributeAuthorityDescriptor
         {
         public:
             AARole(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e);
@@ -378,6 +345,7 @@ namespace {
             Iterator<pair<const XMLCh*,const XMLCh*> > getAdditionalMetadataLocations() const {return m_locs;}
             const IEntitiesDescriptor* getEntitiesDescriptor() const {return m_parent;}
             Iterator<const IKeyAuthority*> getKeyAuthorities() const {return m_keyauths;}
+            saml::Iterator<std::pair<const XMLCh*,bool> > getScopes() const {return m_scopes;}
             const DOMElement* getElement() const {return m_root;}
 
             // Used internally
@@ -393,6 +361,7 @@ namespace {
             vector<const IRoleDescriptor*> m_roles;
             vector<pair<const XMLCh*,const XMLCh*> > m_locs;
             vector<const IKeyAuthority*> m_keyauths;
+            vector<pair<const XMLCh*,bool> > m_scopes;
             time_t m_validUntil;
         };
 
@@ -427,8 +396,10 @@ namespace {
             time_t m_validUntil;
         };
 
-        XMLMetadataImpl(const char* pathname) : ReloadableXMLFileImpl(pathname), m_rootProvider(NULL), m_rootGroup(NULL) { init(); }
-        XMLMetadataImpl(const DOMElement* e) : ReloadableXMLFileImpl(e), m_rootProvider(NULL), m_rootGroup(NULL) { init(); }
+        XMLMetadataImpl(const char* pathname, const XMLMetadata* wrapper)
+            : ReloadableXMLFileImpl(pathname), m_outer(wrapper), m_rootProvider(NULL), m_rootGroup(NULL) { init(); }
+        XMLMetadataImpl(const DOMElement* e, const XMLMetadata* wrapper)
+            : ReloadableXMLFileImpl(e), m_outer(wrapper), m_rootProvider(NULL), m_rootGroup(NULL) { init(); }
         void init();
         ~XMLMetadataImpl();
 
@@ -439,39 +410,14 @@ namespace {
         groupmap_t m_groups;
         EntityDescriptor* m_rootProvider;
         EntitiesDescriptor* m_rootGroup;
+        const XMLMetadata* m_outer;
     };
 
     class XMLMetadata : public IMetadata, public ReloadableXMLFile
     {
     public:
-        XMLMetadata(const DOMElement* e) : ReloadableXMLFile(e), m_exclusions(true) {
-            static const XMLCh uri[] = { chLatin_u, chLatin_r, chLatin_i, chNull };
-            if (e->hasAttributeNS(NULL,uri)) {
-                // First check for explicit enablement of entities.
-                DOMNodeList* nlist=e->getElementsByTagName(SHIB_L(Include));
-                for (int i=0; nlist && i<nlist->getLength(); i++) {
-                    if (nlist->item(i)->hasChildNodes()) {
-                        auto_ptr_char temp(nlist->item(i)->getFirstChild()->getNodeValue());
-                        if (temp.get()) {
-                            m_set.insert(temp.get());
-                            m_exclusions=false;
-                        }
-                    }
-                }
-                // If there was no explicit enablement, build a set of exclusions.
-                if (m_exclusions) {
-                    nlist=e->getElementsByTagName(SHIB_L(Exclude));
-                    for (int j=0; nlist && j<nlist->getLength(); j++) {
-                        if (nlist->item(j)->hasChildNodes()) {
-                            auto_ptr_char temp(nlist->item(j)->getFirstChild()->getNodeValue());
-                            if (temp.get())
-                                m_set.insert(temp.get());
-                        }
-                    }
-                }
-            }
-        }
-        ~XMLMetadata() {}
+        XMLMetadata(const DOMElement* e);
+        ~XMLMetadata() {delete m_credResolver;}
 
         const IEntityDescriptor* lookup(const char* providerId, bool strict=true) const;
         const IEntityDescriptor* lookup(const XMLCh* providerId, bool strict=true) const;
@@ -480,13 +426,16 @@ namespace {
         const IEntitiesDescriptor* lookupGroup(const XMLCh* name, bool strict=true) const;
         pair<const IEntitiesDescriptor*,const IEntityDescriptor*> getRoot() const;
         
+        bool verifySignature(DOMDocument* doc, const DOMElement* parent, bool failUnsigned) const;
+        
     protected:
         virtual ReloadableXMLFileImpl* newImplementation(const char* pathname, bool first=true) const;
         virtual ReloadableXMLFileImpl* newImplementation(const DOMElement* e, bool first=true) const;
         
     private:
-        bool m_exclusions;
+        bool m_exclusions,m_verify;
         set<string> m_set;
+        ICredResolver* m_credResolver;
     };
 }
 
@@ -499,12 +448,12 @@ IPlugIn* XMLMetadataFactory(const DOMElement* e)
 
 ReloadableXMLFileImpl* XMLMetadata::newImplementation(const DOMElement* e, bool first) const
 {
-    return new XMLMetadataImpl(e);
+    return new XMLMetadataImpl(e,this);
 }
 
 ReloadableXMLFileImpl* XMLMetadata::newImplementation(const char* pathname, bool first) const
 {
-    return new XMLMetadataImpl(pathname);
+    return new XMLMetadataImpl(pathname,this);
 }
 
 XMLMetadataImpl::ContactPerson::ContactPerson(const DOMElement* e) : m_root(e)
@@ -514,7 +463,8 @@ XMLMetadataImpl::ContactPerson::ContactPerson(const DOMElement* e) : m_root(e)
     // Old metadata or new?
     if (saml::XML::isElementNamed(e,::XML::SHIB_NS,SHIB_L(Contact))) {
         type=e->getAttributeNS(NULL,SHIB_L(Type));
-        m_surName=auto_ptr<char>(toUTF8(e->getAttributeNS(NULL,SHIB_L(Name))));
+        auto_ptr<char> wrapper(toUTF8(e->getAttributeNS(NULL,SHIB_L(Name))));
+        m_surName=wrapper;
         if (e->hasAttributeNS(NULL,SHIB_L(Email))) {
             auto_ptr<char> temp(toUTF8(e->getAttributeNS(NULL,SHIB_L(Email))));
             if (temp.get())
@@ -523,34 +473,27 @@ XMLMetadataImpl::ContactPerson::ContactPerson(const DOMElement* e) : m_root(e)
     }
     else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(ContactPerson))) {
         type=e->getAttributeNS(NULL,SHIB_L(contactType));
-        DOMNode* n=NULL;
         e=saml::XML::getFirstChildElement(e);
         while (e) {
-            if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(Company))) {
-                n=e->getFirstChild();
-                if (n) m_company=auto_ptr<char>(toUTF8(n->getNodeValue()));
+            if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(Company)) && e->hasChildNodes()) {
+                auto_ptr<char> wrapper(toUTF8(e->getFirstChild()->getNodeValue()));
+                m_company=wrapper;
             }
-            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(GivenName))) {
-                n=e->getFirstChild();
-                if (n) m_givenName=auto_ptr<char>(toUTF8(n->getNodeValue()));
+            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(GivenName)) && e->hasChildNodes()) {
+                auto_ptr<char> wrapper(toUTF8(e->getFirstChild()->getNodeValue()));
+                m_givenName=wrapper;
             }
-            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(SurName))) {
-                n=e->getFirstChild();
-                if (n) m_surName=auto_ptr<char>(toUTF8(n->getNodeValue()));
+            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(SurName)) && e->hasChildNodes()) {
+                auto_ptr<char> wrapper(toUTF8(e->getFirstChild()->getNodeValue()));
+                m_surName=wrapper;
             }
-            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(EmailAddress))) {
-                n=e->getFirstChild();
-                if (n) {
-                    auto_ptr<char> temp(toUTF8(n->getNodeValue()));
-                    if (temp.get()) m_emails.push_back(temp.get());
-                }
+            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(EmailAddress)) && e->hasChildNodes()) {
+                auto_ptr<char> temp(toUTF8(e->getFirstChild()->getNodeValue()));
+                if (temp.get()) m_emails.push_back(temp.get());
             }
-            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(TelephoneNumber))) {
-                n=e->getFirstChild();
-                if (n) {
-                    auto_ptr<char> temp(toUTF8(n->getNodeValue()));
-                    if (temp.get()) m_phones.push_back(temp.get());
-                }
+            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(TelephoneNumber)) && e->hasChildNodes()) {
+                auto_ptr<char> temp(toUTF8(e->getFirstChild()->getNodeValue()));
+                if (temp.get()) m_phones.push_back(temp.get());
             }
             e=saml::XML::getNextSiblingElement(e);
         }
@@ -657,8 +600,7 @@ XMLMetadataImpl::KeyDescriptor::KeyDescriptor(const DOMElement* e) : m_root(e),
 
 XMLMetadataImpl::KeyDescriptor::~KeyDescriptor()
 {
-    for (vector<const XENCEncryptionMethod*>::iterator i=m_methods.begin(); i!=m_methods.end(); i++)
-        delete const_cast<XENCEncryptionMethod*>(*i);
+    for_each(m_methods.begin(),m_methods.end(),xmltooling::cleanup<XENCEncryptionMethod>());
     delete m_klist;
 }
 
@@ -703,8 +645,7 @@ XMLMetadataImpl::KeyAuthority::KeyAuthority(const DOMElement* e) : m_depth(1)
 
 XMLMetadataImpl::KeyAuthority::~KeyAuthority()
 {
-    for (vector<DSIGKeyInfoList*>::iterator i=m_klists.begin(); i!=m_klists.end(); i++)
-        delete (*i);
+    for_each(m_klists.begin(),m_klists.end(),xmltooling::cleanup<DSIGKeyInfoList>());
 }
 
 XMLMetadataImpl::Role::Role(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e)
@@ -757,10 +698,8 @@ XMLMetadataImpl::Role::~Role()
     delete m_org;
     delete m_errorURL;
     if (m_protocolEnumCopy) XMLString::release(&m_protocolEnumCopy);
-    for (vector<const IKeyDescriptor*>::iterator i=m_keys.begin(); i!=m_keys.end(); i++)
-        delete const_cast<IKeyDescriptor*>(*i);
-    for (vector<const IContactPerson*>::iterator j=m_contacts.begin(); j!=m_contacts.end(); j++)
-        delete const_cast<IContactPerson*>(*j);
+    for_each(m_keys.begin(),m_keys.end(),xmltooling::cleanup<IKeyDescriptor>());
+    for_each(m_contacts.begin(),m_contacts.end(),xmltooling::cleanup<IContactPerson>());
 }
 
 bool XMLMetadataImpl::Role::hasSupport(const XMLCh* protocol) const
@@ -778,7 +717,7 @@ XMLMetadataImpl::SSORole::SSORole(const EntityDescriptor* provider, time_t valid
 {
     // Check the root element namespace. If SAML2, assume it's the std schema.
     if (!XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
-        int i;
+        unsigned int i;
         DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(ArtifactResolutionService));
         for (i=0; nlist && i<nlist->getLength(); i++)
             m_artifact.add(new IndexedEndpoint(static_cast<DOMElement*>(nlist->item(i))));
@@ -800,35 +739,12 @@ XMLMetadataImpl::SSORole::SSORole(const EntityDescriptor* provider, time_t valid
     else {
         // For old style, we just do SAML 1.1 compatibility with Shib handles.
         m_protocolEnum.push_back(saml::XML::SAML11_PROTOCOL_ENUM);
-        m_formats.push_back(shibboleth::Constants::SHIB_NAMEID_FORMAT_URI);
-    }
-}
-
-XMLMetadataImpl::ScopedRole::ScopedRole(const DOMElement* e)
-{
-    // Check the root element namespace. If SAML2, assume it's the std schema.
-    DOMNodeList* nlist=NULL;
-    if (!XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
-        e=saml::XML::getFirstChildElement(e,::XML::SAML2META_NS,SHIB_L(Extensions));
-        nlist=e->getElementsByTagNameNS(::XML::SHIBMETA_NS,SHIB_L(Scope));
-    }
-    else {
-        nlist=e->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(Domain));
-    }
-    
-    for (int i=0; nlist && i < nlist->getLength(); i++) {
-        const XMLCh* dom=(nlist->item(i)->hasChildNodes()) ? nlist->item(i)->getFirstChild()->getNodeValue() : NULL;
-        if (dom && *dom) {
-            const XMLCh* regexp=static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,SHIB_L(regexp));
-            m_scopes.push_back(
-                pair<const XMLCh*,bool>(dom,(regexp && (*regexp==chLatin_t || *regexp==chDigit_1)))
-                );
-        }
+        m_formats.push_back(shibspconstants::SHIB1_NAMEID_FORMAT_URI);
     }
 }
 
 XMLMetadataImpl::IDPRole::IDPRole(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e)
-    : SSORole(provider,validUntil,e), ScopedRole(e), m_wantAuthnRequestsSigned(false), m_sourceId(NULL)
+    : SSORole(provider,validUntil,e), m_wantAuthnRequestsSigned(false), m_sourceId(NULL)
 {
     // Check the root element namespace. If SAML2, assume it's the std schema.
     if (!XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
@@ -843,7 +759,7 @@ XMLMetadataImpl::IDPRole::IDPRole(const EntityDescriptor* provider, time_t valid
                 m_sourceId=ext->getFirstChild()->getNodeValue();
         }
         
-        int i;
+        unsigned int i;
         DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(SingleSignOnService));
         for (i=0; nlist && i<nlist->getLength(); i++)
             m_sso.add(new Endpoint(static_cast<DOMElement*>(nlist->item(i))));
@@ -874,7 +790,7 @@ XMLMetadataImpl::IDPRole::IDPRole(const EntityDescriptor* provider, time_t valid
                 src=saml::XML::getNextSiblingElement(src,::XML::SAML2ASSERT_NS,L(AttributeValue));
                 DOMElement* val=e->getOwnerDocument()->createElementNS(saml::XML::SAML_NS,L(AttributeValue));
                 DOMNamedNodeMap* attrs = src->getAttributes();
-                for (int j=0; j<attrs->getLength(); j++)
+                for (unsigned int j=0; j<attrs->getLength(); j++)
                     val->setAttributeNodeNS(static_cast<DOMAttr*>(e->getOwnerDocument()->importNode(attrs->item(j),true)));
                 while (src->hasChildNodes())
                     val->appendChild(src->getFirstChild());
@@ -884,13 +800,14 @@ XMLMetadataImpl::IDPRole::IDPRole(const EntityDescriptor* provider, time_t valid
         }
     }
     else {
-        m_attrprofs.push_back(Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
-        int i;
+        m_protocolEnum.push_back(::XML::SHIB_NS);
+        m_attrprofs.push_back(shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI);
+        unsigned int i;
         DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(HandleService));
         for (i=0; nlist && i<nlist->getLength(); i++) {
             // Manufacture an endpoint for the "Shib" binding.
             m_sso.add(
-                new Endpoint(Constants::SHIB_AUTHNREQUEST_PROFILE_URI,static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,L(Location)))
+                new Endpoint(shibspconstants::SHIB1_AUTHNREQUEST_PROFILE_URI,static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,L(Location)))
                 );
 
             // We're going to "mock up" a KeyDescriptor that contains the specified Name as a ds:KeyName.
@@ -912,16 +829,15 @@ XMLMetadataImpl::IDPRole::IDPRole(const EntityDescriptor* provider, time_t valid
 
 XMLMetadataImpl::IDPRole::~IDPRole()
 {
-    for (vector<const SAMLAttribute*>::iterator i=m_attrs.begin(); i!=m_attrs.end(); i++)
-        delete const_cast<SAMLAttribute*>(*i);
+    for_each(m_attrs.begin(),m_attrs.end(),xmltooling::cleanup<SAMLAttribute>());
 }
 
 XMLMetadataImpl::AARole::AARole(const EntityDescriptor* provider, time_t validUntil, const DOMElement* e)
-    : Role(provider,validUntil,e), ScopedRole(e)
+    : Role(provider,validUntil,e)
 {
     // Check the root element namespace. If SAML2, assume it's the std schema.
     if (!XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
-        int i;
+        unsigned int i;
         DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::SAML2META_NS,SHIB_L(AttributeService));
         for (i=0; nlist && i<nlist->getLength(); i++)
             m_query.add(new Endpoint(static_cast<DOMElement*>(nlist->item(i))));
@@ -954,7 +870,7 @@ XMLMetadataImpl::AARole::AARole(const EntityDescriptor* provider, time_t validUn
                 src=saml::XML::getNextSiblingElement(src,::XML::SAML2ASSERT_NS,L(AttributeValue));
                 DOMElement* val=e->getOwnerDocument()->createElementNS(saml::XML::SAML_NS,L(AttributeValue));
                 DOMNamedNodeMap* attrs = src->getAttributes();
-                for (int j=0; j<attrs->getLength(); j++)
+                for (unsigned int j=0; j<attrs->getLength(); j++)
                     val->setAttributeNodeNS(static_cast<DOMAttr*>(e->getOwnerDocument()->importNode(attrs->item(j),true)));
                 while (src->hasChildNodes())
                     val->appendChild(src->getFirstChild());
@@ -966,9 +882,9 @@ XMLMetadataImpl::AARole::AARole(const EntityDescriptor* provider, time_t validUn
     else {
         // For old style, we just do SAML 1.1 compatibility with Shib handles.
         m_protocolEnum.push_back(saml::XML::SAML11_PROTOCOL_ENUM);
-        m_formats.push_back(Constants::SHIB_NAMEID_FORMAT_URI);
-        m_attrprofs.push_back(Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
-        int i;
+        m_formats.push_back(shibspconstants::SHIB1_NAMEID_FORMAT_URI);
+        m_attrprofs.push_back(shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI);
+        unsigned int i;
         DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(AttributeAuthority));
         for (i=0; nlist && i<nlist->getLength(); i++) {
             // Manufacture an endpoint for the SOAP binding.
@@ -997,8 +913,7 @@ XMLMetadataImpl::AARole::AARole(const EntityDescriptor* provider, time_t validUn
 
 XMLMetadataImpl::AARole::~AARole()
 {
-    for (vector<const SAMLAttribute*>::iterator i=m_attrs.begin(); i!=m_attrs.end(); i++)
-        delete const_cast<SAMLAttribute*>(*i);
+    for_each(m_attrs.begin(),m_attrs.end(),xmltooling::cleanup<SAMLAttribute>());
 }
 
 XMLMetadataImpl::EntityDescriptor::EntityDescriptor(
@@ -1006,6 +921,7 @@ XMLMetadataImpl::EntityDescriptor::EntityDescriptor(
     ) : m_root(e), m_parent(parent), m_org(NULL), m_validUntil(validUntil)
 {
     // Check the root element namespace. If SAML2, assume it's the std schema.
+    DOMNodeList* scopes=NULL;
     if (!XMLString::compareString(e->getNamespaceURI(),::XML::SAML2META_NS)) {
         m_id=e->getAttributeNS(NULL,SHIB_L(entityID));
 
@@ -1039,17 +955,23 @@ XMLMetadataImpl::EntityDescriptor::EntityDescriptor(
                         );
             }
             else if (saml::XML::isElementNamed(child,::XML::SAML2META_NS,SHIB_L(IDPSSODescriptor))) {
-                m_roles.push_back(new IDPRole(this,m_validUntil,child));
+                if (wrapper->m_outer->verifySignature(child->getOwnerDocument(),child,false))
+                    m_roles.push_back(new IDPRole(this,m_validUntil,child));
             }
             else if (saml::XML::isElementNamed(child,::XML::SAML2META_NS,SHIB_L(AttributeAuthorityDescriptor))) {
-                m_roles.push_back(new AARole(this,m_validUntil,child));
+                if (wrapper->m_outer->verifySignature(child->getOwnerDocument(),child,false))
+                    m_roles.push_back(new AARole(this,m_validUntil,child));
             }
             child = saml::XML::getNextSiblingElement(child);
         }
+
+        // Grab all the shibmd:Scope elements here and at the role level.
+        scopes=e->getElementsByTagNameNS(::XML::SHIBMETA_NS,SHIB_L(Scope));
     }
     else {
         m_id=e->getAttributeNS(NULL,SHIB_L(Name));
-        m_errorURL=auto_ptr<char>(toUTF8(e->getAttributeNS(NULL,SHIB_L(ErrorURL))));
+        auto_ptr<char> wrapper(toUTF8(e->getAttributeNS(NULL,SHIB_L(ErrorURL))));
+        m_errorURL=wrapper;
         
         bool idp=false,aa=false;    // only want to build a role once
         DOMElement* child=saml::XML::getFirstChildElement(e);
@@ -1070,10 +992,24 @@ XMLMetadataImpl::EntityDescriptor::EntityDescriptor(
             }
             child = saml::XML::getNextSiblingElement(child);
         }
+
+        // Grab all the shib:Domain elements.
+        scopes=e->getElementsByTagNameNS(::XML::SHIB_NS,SHIB_L(Domain));
+    }
+
+    // Process scopes.
+    for (unsigned int i=0; scopes && i < scopes->getLength(); i++) {
+        const XMLCh* dom=(scopes->item(i)->hasChildNodes()) ? scopes->item(i)->getFirstChild()->getNodeValue() : NULL;
+        if (dom && *dom) {
+            const XMLCh* regexp=static_cast<DOMElement*>(scopes->item(i))->getAttributeNS(NULL,SHIB_L(regexp));
+            m_scopes.push_back(
+                pair<const XMLCh*,bool>(dom,(regexp && (*regexp==chLatin_t || *regexp==chDigit_1)))
+                );
+        }
     }
 
     auto_ptr_char id(m_id);
-    wrapper->m_sites.insert(pair<string,const EntityDescriptor*>(id.get(),this));
+    wrapper->m_sites.insert(pair<const string,const EntityDescriptor*>(id.get(),this));
     
     // Look for an IdP role, and register the artifact source ID and endpoints.
     const IDPRole* idp=NULL;
@@ -1081,16 +1017,16 @@ XMLMetadataImpl::EntityDescriptor::EntityDescriptor(
         if (idp=dynamic_cast<const IDPRole*>(*r)) {
             if (idp->m_sourceId) {
                 auto_ptr_char sourceid(idp->m_sourceId);
-                wrapper->m_sources.insert(pair<string,const EntityDescriptor*>(sourceid.get(),this));
+                wrapper->m_sources.insert(pair<const string,const EntityDescriptor*>(sourceid.get(),this));
             }
             else {
                 string sourceid=SAMLArtifact::toHex(SAMLArtifactType0001::generateSourceId(id.get()));
-                wrapper->m_sources.insert(pair<string,const EntityDescriptor*>(sourceid,this));
+                wrapper->m_sources.insert(pair<const string,const EntityDescriptor*>(sourceid,this));
             }
             Iterator<const IEndpoint*> locs=idp->getArtifactResolutionServiceManager()->getEndpoints();
             while (locs.hasNext()) {
                 auto_ptr_char loc(locs.next()->getLocation());
-                wrapper->m_sources.insert(pair<string,const EntityDescriptor*>(loc.get(),this));
+                wrapper->m_sources.insert(pair<const string,const EntityDescriptor*>(loc.get(),this));
             }
         }
     }
@@ -1119,12 +1055,9 @@ const IAttributeAuthorityDescriptor* XMLMetadataImpl::EntityDescriptor::getAttri
 XMLMetadataImpl::EntityDescriptor::~EntityDescriptor()
 {
     delete m_org;
-    for (vector<const IContactPerson*>::iterator i=m_contacts.begin(); i!=m_contacts.end(); i++)
-        delete const_cast<IContactPerson*>(*i);
-    for (vector<const IRoleDescriptor*>::iterator j=m_roles.begin(); j!=m_roles.end(); j++)
-        delete const_cast<IRoleDescriptor*>(*j);
-    for (vector<const IKeyAuthority*>::iterator k=m_keyauths.begin(); k!=m_keyauths.end(); k++)
-        delete const_cast<IKeyAuthority*>(*k);
+    for_each(m_contacts.begin(),m_contacts.end(),xmltooling::cleanup<IContactPerson>());
+    for_each(m_roles.begin(),m_roles.end(),xmltooling::cleanup<IRoleDescriptor>());
+    for_each(m_keyauths.begin(),m_keyauths.end(),xmltooling::cleanup<IKeyAuthority>());
 }
 
 XMLMetadataImpl::EntitiesDescriptor::EntitiesDescriptor(
@@ -1149,36 +1082,43 @@ XMLMetadataImpl::EntitiesDescriptor::EntitiesDescriptor(
                     ext = saml::XML::getNextSiblingElement(ext,::XML::SHIBMETA_NS,SHIB_L(KeyAuthority));
                 }
             }
-            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(EntitiesDescriptor)))
-                m_groups.push_back(new EntitiesDescriptor(e,wrapper,m_validUntil,this));
-            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(EntityDescriptor)))
-                m_providers.push_back(new EntityDescriptor(e,wrapper,m_validUntil,this));
+            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(EntitiesDescriptor))) {
+                if (wrapper->m_outer->verifySignature(e->getOwnerDocument(),e,false))
+                    m_groups.push_back(new EntitiesDescriptor(e,wrapper,m_validUntil,this));
+            }
+            else if (saml::XML::isElementNamed(e,::XML::SAML2META_NS,SHIB_L(EntityDescriptor))) {
+                if (wrapper->m_outer->verifySignature(e->getOwnerDocument(),e,false))
+                    m_providers.push_back(new EntityDescriptor(e,wrapper,m_validUntil,this));
+            }
             e=saml::XML::getNextSiblingElement(e);
         }
     }
     else {
         e=saml::XML::getFirstChildElement(e);
         while (e) {
-            if (saml::XML::isElementNamed(e,::XML::SHIB_NS,SHIB_L(SiteGroup)))
-                m_groups.push_back(new EntitiesDescriptor(e,wrapper,m_validUntil,this));
+            if (saml::XML::isElementNamed(e,::XML::SHIB_NS,SHIB_L(SiteGroup))) {
+                if (wrapper->m_outer->verifySignature(e->getOwnerDocument(),e,false))
+                    m_groups.push_back(new EntitiesDescriptor(e,wrapper,m_validUntil,this));
+            }
             else if (saml::XML::isElementNamed(e,::XML::SHIB_NS,SHIB_L(OriginSite)))
                 m_providers.push_back(new EntityDescriptor(e,wrapper,m_validUntil,this));
             e=saml::XML::getNextSiblingElement(e);
         }
     }
 
-    auto_ptr_char n(m_name);
-    wrapper->m_groups.insert(pair<string,const EntitiesDescriptor*>(n.get(),this));
+    if (!saml::XML::isEmpty(m_name)) {
+        auto_ptr_char n(m_name);
+        wrapper->m_groups.insert(pair<const string,const EntitiesDescriptor*>(n.get(),this));
+    }
+    else
+        m_name=NULL;
 }
 
 XMLMetadataImpl::EntitiesDescriptor::~EntitiesDescriptor()
 {
-    for (vector<const IEntityDescriptor*>::iterator i=m_providers.begin(); i!=m_providers.end(); i++)
-        delete const_cast<IEntityDescriptor*>(*i);
-    for (vector<const IEntitiesDescriptor*>::iterator j=m_groups.begin(); j!=m_groups.end(); j++)
-        delete const_cast<IEntitiesDescriptor*>(*j);
-    for (vector<const IKeyAuthority*>::iterator k=m_keyauths.begin(); k!=m_keyauths.end(); k++)
-        delete const_cast<IKeyAuthority*>(*k);
+    for_each(m_providers.begin(),m_providers.end(),xmltooling::cleanup<IEntityDescriptor>());
+    for_each(m_groups.begin(),m_groups.end(),xmltooling::cleanup<IEntitiesDescriptor>());
+    for_each(m_keyauths.begin(),m_keyauths.end(),xmltooling::cleanup<IKeyAuthority>());
 }
 
 void XMLMetadataImpl::init()
@@ -1190,14 +1130,22 @@ void XMLMetadataImpl::init()
 
     try
     {
-        if (saml::XML::isElementNamed(m_root,::XML::SAML2META_NS,SHIB_L(EntitiesDescriptor)))
-            m_rootGroup=new EntitiesDescriptor(m_root,this);
-        else if (saml::XML::isElementNamed(m_root,::XML::SAML2META_NS,SHIB_L(EntityDescriptor)))
-            m_rootProvider=new EntityDescriptor(m_root,this);
-        else if (saml::XML::isElementNamed(m_root,::XML::SHIB_NS,SHIB_L(SiteGroup)))
-            m_rootGroup=new EntitiesDescriptor(m_root,this);
-        else if (saml::XML::isElementNamed(m_root,::XML::SHIB_NS,SHIB_L(OriginSite)))
-            m_rootProvider=new EntityDescriptor(m_root,this);
+        if (saml::XML::isElementNamed(m_root,::XML::SAML2META_NS,SHIB_L(EntitiesDescriptor))) {
+            if (m_outer->verifySignature(m_root->getOwnerDocument(),m_root,true))
+                m_rootGroup=new EntitiesDescriptor(m_root,this);
+        }
+        else if (saml::XML::isElementNamed(m_root,::XML::SAML2META_NS,SHIB_L(EntityDescriptor))) {
+            if (m_outer->verifySignature(m_root->getOwnerDocument(),m_root,true))
+                m_rootProvider=new EntityDescriptor(m_root,this);
+        }
+        else if (saml::XML::isElementNamed(m_root,::XML::SHIB_NS,SHIB_L(SiteGroup))) {
+            if (m_outer->verifySignature(m_root->getOwnerDocument(),m_root,true))
+                m_rootGroup=new EntitiesDescriptor(m_root,this);
+        }
+        else if (saml::XML::isElementNamed(m_root,::XML::SHIB_NS,SHIB_L(OriginSite))) {
+            if (m_outer->verifySignature(m_root->getOwnerDocument(),m_root,true))
+                m_rootProvider=new EntityDescriptor(m_root,this);
+        }
         else {
             log.error("Construction requires a valid SAML metadata file");
             throw MetadataException("Construction requires a valid SAML metadata file");
@@ -1217,6 +1165,11 @@ void XMLMetadataImpl::init()
         throw;
     }
 #endif
+
+    if (!m_rootGroup && !m_rootProvider) {
+        log.error("Metadata file contained no valid information");
+        throw MetadataException("Metadata file contained no valid information");
+    }
 }
 
 XMLMetadataImpl::~XMLMetadataImpl()
@@ -1225,6 +1178,173 @@ XMLMetadataImpl::~XMLMetadataImpl()
     delete m_rootProvider;
 }
 
+XMLMetadata::XMLMetadata(const DOMElement* e) : ReloadableXMLFile(e), m_exclusions(true), m_verify(false), m_credResolver(NULL)
+{
+    static const XMLCh uri[] = { chLatin_u, chLatin_r, chLatin_i, chNull };
+    if (e->hasAttributeNS(NULL,uri)) {
+        // First check for explicit enablement of entities.
+        DOMNodeList* nlist=e->getElementsByTagName(SHIB_L(Include));
+        for (unsigned int i=0; nlist && i<nlist->getLength(); i++) {
+            if (nlist->item(i)->hasChildNodes()) {
+                auto_ptr_char temp(nlist->item(i)->getFirstChild()->getNodeValue());
+                if (temp.get()) {
+                    m_set.insert(temp.get());
+                    m_exclusions=false;
+                }
+            }
+        }
+        // If there was no explicit enablement, build a set of exclusions.
+        if (m_exclusions) {
+            nlist=e->getElementsByTagName(SHIB_L(Exclude));
+            for (unsigned int j=0; nlist && j<nlist->getLength(); j++) {
+                if (nlist->item(j)->hasChildNodes()) {
+                    auto_ptr_char temp(nlist->item(j)->getFirstChild()->getNodeValue());
+                    if (temp.get())
+                        m_set.insert(temp.get());
+                }
+            }
+        }
+    }
+    
+    const XMLCh* v=e->getAttributeNS(NULL,SHIB_L(verify));
+    m_verify=(v && (*v==chLatin_t || *v==chDigit_1));
+
+    string cr_type;
+    DOMElement* r=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(FileResolver));
+    if (r)
+        cr_type="edu.internet2.middleware.shibboleth.common.Credentials.FileCredentialResolver";            
+    else {
+        r=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(CustomResolver));
+        if (r) {
+            auto_ptr_char c(r->getAttributeNS(NULL,SHIB_L(Class)));
+            cr_type=c.get();
+        }
+    }
+    
+    if (!cr_type.empty()) {
+        try {
+            IPlugIn* plugin=SAMLConfig::getConfig().getPlugMgr().newPlugin(cr_type.c_str(),r);
+            ICredResolver* cr=dynamic_cast<ICredResolver*>(plugin);
+            if (cr)
+                m_credResolver=cr;
+            else {
+                Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata").error("plugin was not a credential resolver");
+                delete plugin;
+                throw UnsupportedExtensionException("plugin was not a credential resolver");
+            }
+        }
+        catch (SAMLException& e) {
+            Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata").error("failed to instantiate credential resolver: %s", e.what());
+            throw;
+        }
+    }
+    
+    if (m_verify && !m_credResolver) {
+        delete m_credResolver;
+        throw MalformedException("Metadata provider told to verify signatures, but a verification key is not available.");
+    }
+}
+
+bool XMLMetadata::verifySignature(DOMDocument* doc, const DOMElement* parent, bool failUnsigned) const
+{
+    if (!m_verify)
+        return true;
+
+#ifdef _DEBUG
+    saml::NDC ndc("verifySignature");
+#endif
+    Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".Metadata");
+    
+    DOMElement* sigNode=saml::XML::getFirstChildElement(parent,saml::XML::XMLSIG_NS,L(Signature));
+    if (!sigNode) {
+        if (failUnsigned) {
+            log.error("rejecting unsigned element");
+            return false;
+        }
+        return true;
+    }
+    
+    XSECCryptoX509* cert=NULL;
+    Iterator<XSECCryptoX509*> certs=m_credResolver->getCertificates();
+    if (certs.hasNext())
+        cert=certs.next();
+    else {
+        log.error("unable to find any certificates to use in verifying signature");
+        return false;
+    }
+
+    static const XMLCh ID[]={chLatin_I, chLatin_D, chNull};
+    static const XMLCh null[]={chDoubleQuote, chDoubleQuote, chNull};
+
+    // Load the signature.
+    XSECProvider prov;
+    DSIGSignature* sig=NULL;
+    try {
+        sig=prov.newSignatureFromDOM(doc,sigNode);
+        sig->load();
+
+        bool valid=false;
+        const XMLCh* URI=NULL;
+
+        // Verify the signature coverage.
+        DSIGReferenceList* refs=sig->getReferenceList();
+        if (sig->getSignatureMethod()==SIGNATURE_RSA && refs && refs->getSize()==1) {
+            DSIGReference* ref=refs->item(0);
+            if (ref) {
+                URI=ref->getURI();
+                if (!URI || !*URI || (*URI==chPound &&
+                        !XMLString::compareString(&URI[1],static_cast<DOMElement*>(sigNode->getParentNode())->getAttributeNS(NULL,ID)))) {
+                    DSIGTransformList* tlist=ref->getTransforms();
+                    for (unsigned int i=0; tlist && i<tlist->getSize(); i++) {
+                        if (tlist->item(i)->getTransformType()==TRANSFORM_ENVELOPED_SIGNATURE)
+                            valid=true;
+                        else if (tlist->item(i)->getTransformType()!=TRANSFORM_EXC_C14N &&
+                                 tlist->item(i)->getTransformType()!=TRANSFORM_C14N) {
+                            valid=false;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    
+        if (!valid) {
+            auto_ptr_char temp((URI && *URI) ? URI : null);
+            log.error("detected an invalid signature profile (Reference URI was %s)",temp.get());
+            return false;
+        }
+        
+        sig->setSigningKey(cert->clonePublicKey());
+        if (!sig->verify()) {
+            auto_ptr_char temp((URI && *URI) ? URI : null);
+            log.error("detected an invalid signature value (Reference URI was %s)",temp.get());
+            return false;
+        }
+        
+        prov.releaseSignature(sig);
+    }
+    catch(XSECException& e) {
+        auto_ptr_char msg(e.getMsg());
+        log.errorStream() << "caught XMLSec exception while verifying metadata signature: " << msg.get() << CategoryStream::ENDLINE;
+        if (sig)
+            prov.releaseSignature(sig);
+        return false;
+    }
+    catch(XSECCryptoException& e) {
+        log.errorStream() << "caught XMLSecCrypto exception while verifying metadata signature: " << e.getMsg() << CategoryStream::ENDLINE;
+        if (sig)
+            prov.releaseSignature(sig);
+        return false;
+    }
+    catch(...) {
+        if (sig)
+            prov.releaseSignature(sig);
+        log.error("caught unknown exception while verifying metadata signature");
+        throw;
+    }
+    return true;
+}
+
 const IEntityDescriptor* XMLMetadata::lookup(const char* providerId, bool strict) const
 {
     if (strict && m_exclusions && m_set.find(providerId)!=m_set.end())
@@ -1233,7 +1353,7 @@ const IEntityDescriptor* XMLMetadata::lookup(const char* providerId, bool strict
         return NULL;
         
     XMLMetadataImpl* impl=dynamic_cast<XMLMetadataImpl*>(getImplementation());
-    pair<XMLMetadataImpl::sitemap_t::const_iterator,XMLMetadataImpl::sitemap_t::const_iterator> range=
+    pair<XMLMetadataImpl::sitemap_t::iterator,XMLMetadataImpl::sitemap_t::iterator> range=
         impl->m_sites.equal_range(providerId);
 
     time_t now=time(NULL);
@@ -1257,7 +1377,7 @@ const IEntityDescriptor* XMLMetadata::lookup(const SAMLArtifact* artifact) const
 {
     time_t now=time(NULL);
     XMLMetadataImpl* impl=dynamic_cast<XMLMetadataImpl*>(getImplementation());
-    pair<XMLMetadataImpl::sitemap_t::const_iterator,XMLMetadataImpl::sitemap_t::const_iterator> range;
+    pair<XMLMetadataImpl::sitemap_t::iterator,XMLMetadataImpl::sitemap_t::iterator> range;
     
     // Depends on type of artifact.
     const SAMLArtifactType0001* type1=dynamic_cast<const SAMLArtifactType0001*>(artifact);
@@ -1281,7 +1401,7 @@ const IEntityDescriptor* XMLMetadata::lookup(const SAMLArtifact* artifact) const
         else if (!m_exclusions && m_set.find(id.get())==m_set.end())
             return NULL;
 
-        for (XMLMetadataImpl::sitemap_t::const_iterator i=range.first; i!=range.second; i++)
+        for (XMLMetadataImpl::sitemap_t::iterator i=range.first; i!=range.second; i++)
             if (now < i->second->getValidUntil())
                 return i->second;
     }
@@ -1297,11 +1417,11 @@ const IEntitiesDescriptor* XMLMetadata::lookupGroup(const char* name, bool stric
         return NULL;
         
     XMLMetadataImpl* impl=dynamic_cast<XMLMetadataImpl*>(getImplementation());
-    pair<XMLMetadataImpl::groupmap_t::const_iterator,XMLMetadataImpl::groupmap_t::const_iterator> range=
+    pair<XMLMetadataImpl::groupmap_t::iterator,XMLMetadataImpl::groupmap_t::iterator> range=
         impl->m_groups.equal_range(name);
 
     time_t now=time(NULL);
-    for (XMLMetadataImpl::groupmap_t::const_iterator i=range.first; i!=range.second; i++)
+    for (XMLMetadataImpl::groupmap_t::iterator i=range.first; i!=range.second; i++)
         if (now < i->second->getValidUntil())
             return i->second;
     
@@ -1320,6 +1440,6 @@ const IEntitiesDescriptor* XMLMetadata::lookupGroup(const XMLCh* name, bool stri
 pair<const IEntitiesDescriptor*,const IEntityDescriptor*> XMLMetadata::getRoot() const
 {
     XMLMetadataImpl* impl=dynamic_cast<XMLMetadataImpl*>(getImplementation());
-    return make_pair(impl->m_rootGroup,impl->m_rootProvider);
+    return pair<const IEntitiesDescriptor*,const IEntityDescriptor*>(impl->m_rootGroup,impl->m_rootProvider);
 }