Add signature-checking metadata filter.
[shibboleth/cpp-opensaml.git] / saml / saml2 / metadata / impl / SignatureMetadataFilter.cpp
1 /*
2  *  Copyright 2001-2006 Internet2
3  * 
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * BlacklistMetadataFilter.cpp
19  * 
20  * Removes blacklisted entities from a metadata instance
21  */
22
23 #include "internal.h"
24 #include "saml2/metadata/MetadataFilter.h"
25 #include "signature/SignatureProfileValidator.h"
26
27 #include <log4cpp/Category.hh>
28
29 #include <xmltooling/util/NDC.h>
30 #include <xmltooling/signature/SignatureValidator.h>
31
32 using namespace opensaml::saml2md;
33 using namespace opensaml;
34 using namespace xmlsignature;
35 using namespace xmltooling;
36 using namespace log4cpp;
37 using namespace std;
38
39 namespace opensaml {
40     namespace saml2md {
41                 
42         class SAML_DLLLOCAL SignatureMetadataFilter : public MetadataFilter
43         {
44         public:
45             SignatureMetadataFilter(const DOMElement* e);
46             ~SignatureMetadataFilter() {
47                 delete m_sigValidator;
48             }
49             
50             const char* getId() const { return SIGNATURE_METADATA_FILTER; }
51             void doFilter(XMLObject& xmlObject) const;
52
53         private:
54             void doFilter(EntitiesDescriptor& entities, bool rootObject=false) const;
55             void verifySignature(Signature* sig) const {
56                 if (sig) {
57                     m_profileValidator.validate(sig);
58                     m_sigValidator->validate(sig);
59                 }
60             }
61             
62             SignatureProfileValidator m_profileValidator;
63             SignatureValidator* m_sigValidator;
64         }; 
65
66         MetadataFilter* SAML_DLLLOCAL SignatureMetadataFilterFactory(const DOMElement* const & e)
67         {
68             return new SignatureMetadataFilter(e);
69         }
70
71     };
72 };
73
74 static const XMLCh GenericKeyResolver[] =   UNICODE_LITERAL_11(K,e,y,R,e,s,o,l,v,e,r);
75 static const XMLCh type[] =                 UNICODE_LITERAL_4(t,y,p,e);
76
77 SignatureMetadataFilter::SignatureMetadataFilter(const DOMElement* e) : m_sigValidator(NULL)
78 {
79     e = XMLHelper::getFirstChildElement(e, GenericKeyResolver);
80     auto_ptr_char t(e ? e->getAttributeNS(NULL,type) : NULL);
81     if (t.get()) {
82         auto_ptr<KeyResolver> kr(XMLToolingConfig::getConfig().KeyResolverManager.newPlugin(t.get(),e));
83         m_sigValidator = new SignatureValidator(kr.get());
84         kr.release();
85     }
86     else
87         throw MetadataFilterException("missing <KeyResolver> element, or no type attribute found");
88 }
89
90 void SignatureMetadataFilter::doFilter(XMLObject& xmlObject) const
91 {
92 #ifdef _DEBUG
93     NDC ndc("doFilter");
94 #endif
95     
96     try {
97         EntitiesDescriptor& entities = dynamic_cast<EntitiesDescriptor&>(xmlObject);
98         doFilter(entities, true);
99         return;
100     }
101     catch (bad_cast) {
102     }
103
104     try {
105         EntityDescriptor& entity = dynamic_cast<EntityDescriptor&>(xmlObject);
106         if (!entity.getSignature())
107             throw MetadataFilterException("Root metadata element was unsigned.");
108         verifySignature(entity.getSignature());
109     }
110     catch (bad_cast) {
111     }
112      
113     throw MetadataFilterException("SignatureMetadataFilter was given an improper metadata instance to filter.");
114 }
115
116 void SignatureMetadataFilter::doFilter(EntitiesDescriptor& entities, bool rootObject) const
117 {
118     Category& log=Category::getInstance(SAML_LOGCAT".Metadata");
119     
120     Signature* sig = entities.getSignature();
121     if (!sig && rootObject)
122         throw MetadataFilterException("Root metadata element was unsigned.");
123     verifySignature(sig);
124     
125     VectorOf(EntityDescriptor) v=entities.getEntityDescriptors();
126     for (VectorOf(EntityDescriptor)::size_type i=0; i<v.size(); ) {
127         try {
128             verifySignature(v[i]->getSignature());
129             i++;
130         }
131         catch (XMLToolingException& e) {
132             auto_ptr_char id(v[i]->getEntityID());
133             log.info("filtering out entity (%s) after failed signature check: ", id.get(), e.what());
134             v.erase(v.begin() + i);
135         }
136     }
137     
138     VectorOf(EntitiesDescriptor) w=entities.getEntitiesDescriptors();
139     for (VectorOf(EntitiesDescriptor)::size_type j=0; j<w.size(); ) {
140         try {
141             verifySignature(w[j]->getSignature());
142             j++;
143         }
144         catch (XMLToolingException& e) {
145             auto_ptr_char name(w[j]->getName());
146             log.info("filtering out group (%s) after failed signature check: ", name.get(), e.what());
147             w.erase(w.begin() + j);
148         }
149     }
150 }