e572d2be4fc0bbf5542a682c52ba13a1fa9bbece
[shibboleth/cpp-opensaml.git] / saml / profile / impl / ConditionsRule.cpp
1 /*
2  *  Copyright 2009 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  * ConditionsRule.cpp
19  *
20  * SAML Conditions SecurityPolicyRule.
21  */
22
23 #include "internal.h"
24 #include "exceptions.h"
25 #include "binding/SecurityPolicy.h"
26 #include "binding/SecurityPolicyRule.h"
27 #include "saml1/core/Assertions.h"
28 #include "saml2/core/Assertions.h"
29
30 #include <xercesc/util/XMLUniDefs.hpp>
31 #include <xmltooling/logging.h>
32 #include <xmltooling/XMLToolingConfig.h>
33 #include <xmltooling/util/ParserPool.h>
34
35 using namespace opensaml;
36 using namespace xmltooling::logging;
37 using namespace xmltooling;
38 using namespace std;
39
40 namespace opensaml {
41     class SAML_DLLLOCAL ConditionsRule : public SecurityPolicyRule
42     {
43     public:
44         ConditionsRule(const DOMElement* e);
45
46         virtual ~ConditionsRule() {
47             for_each(m_rules.begin(), m_rules.end(), xmltooling::cleanup<SecurityPolicyRule>());
48             if (m_doc)
49                 m_doc->release();
50         }
51         const char* getType() const {
52             return CONDITIONS_POLICY_RULE;
53         }
54         bool evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const;
55
56     private:
57         DOMDocument* m_doc;
58         vector<SecurityPolicyRule*> m_rules;
59     };
60
61     SecurityPolicyRule* SAML_DLLLOCAL ConditionsRuleFactory(const DOMElement* const & e)
62     {
63         return new ConditionsRule(e);
64     }
65
66     static const XMLCh Rule[] =     UNICODE_LITERAL_10(P,o,l,i,c,y,R,u,l,e);
67     static const XMLCh type[] =     UNICODE_LITERAL_4(t,y,p,e);
68
69     const char config[] =
70         "<PolicyRule type=\"Conditions\" xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\">"
71             "<PolicyRule type=\"Audience\"/>"
72             "<PolicyRule type=\"Ignore\">saml:DoNotCacheCondition</PolicyRule>"
73             "<PolicyRule type=\"Ignore\">saml2:OneTimeUse</PolicyRule>"
74             "<PolicyRule type=\"Ignore\">saml2:ProxyRestriction</PolicyRule>"
75         "</PolicyRule>";
76 };
77
78 ConditionsRule::ConditionsRule(const DOMElement* e) : m_doc(NULL)
79 {
80     Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.Conditions");
81
82     if (!e || !e->hasChildNodes()) {
83         // Default the configuration.
84         istringstream in(config);
85         m_doc = XMLToolingConfig::getConfig().getParser().parse(in);
86         e = m_doc->getDocumentElement();
87     }
88
89     e = XMLHelper::getFirstChildElement(e, Rule);
90     while (e) {
91         auto_ptr_char temp(e->getAttributeNS(NULL, type));
92         if (temp.get() && *temp.get()) {
93             try {
94                 log.info("building SecurityPolicyRule of type %s", temp.get());
95                 m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(temp.get(),e));
96             }
97             catch (exception& ex) {
98                 log.crit("error building SecurityPolicyRule: %s", ex.what());
99             }
100         }
101         e = XMLHelper::getNextSiblingElement(e, Rule);
102     }
103 }
104
105 bool ConditionsRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const
106 {
107     const saml2::Assertion* a2=dynamic_cast<const saml2::Assertion*>(&message);
108     if (a2) {
109         const saml2::Conditions* conds = a2->getConditions();
110         if (!conds)
111             return true;
112
113         // First verify the time conditions, using the specified timestamp.
114         time_t now = policy.getTime();
115         unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs;
116         time_t t = conds->getNotBeforeEpoch();
117         if (now + skew < t)
118             throw SecurityPolicyException("Assertion is not yet valid.");
119         t = conds->getNotOnOrAfterEpoch();
120         if (t <= now - skew)
121             throw SecurityPolicyException("Assertion is no longer valid.");
122
123         // Now we process conditions, starting with the known types and then extensions.
124
125         bool valid;
126
127         const vector<saml2::AudienceRestriction*>& acvec = conds->getAudienceRestrictions();
128         for (vector<saml2::AudienceRestriction*>::const_iterator ac = acvec.begin(); ac != acvec.end(); ++ac) {
129             valid = false;
130             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
131                 valid = (*r)->evaluate(*(*ac), request, policy);
132             if (!valid)
133                 throw SecurityPolicyException("AudienceRestriction condition not successfully validated by policy.");
134         }
135
136         const vector<saml2::OneTimeUse*>& otvec = conds->getOneTimeUses();
137         for (vector<saml2::OneTimeUse*>::const_iterator ot = otvec.begin(); ot!=otvec.end(); ++ot) {
138             valid = false;
139             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
140                 valid = (*r)->evaluate(*(*ot), request, policy);
141             if (!valid)
142                 throw SecurityPolicyException("OneTimeUse condition not successfully validated by policy.");
143         }
144
145         const vector<saml2::ProxyRestriction*> pvec = conds->getProxyRestrictions();
146         for (vector<saml2::ProxyRestriction*>::const_iterator p = pvec.begin(); p != pvec.end(); ++p) {
147             valid = false;
148             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
149                 valid = (*r)->evaluate(*(*p), request, policy);
150             if (!valid)
151                 throw SecurityPolicyException("ProxyRestriction condition not successfully validated by policy.");
152         }
153
154         const vector<saml2::Condition*>& convec = conds->getConditions();
155         for (vector<saml2::Condition*>::const_iterator c = convec.begin(); c != convec.end(); ++c) {
156             valid = false;
157             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
158                 valid = (*r)->evaluate(*(*c), request, policy);
159             if (!valid) {
160                 throw SecurityPolicyException(
161                     "Extension condition ($1) not successfully validated by policy.",
162                     params(1,((*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : "Unknown Type"))
163                     );
164             }
165         }
166
167         return true;
168     }
169
170     const saml1::Assertion* a1=dynamic_cast<const saml1::Assertion*>(&message);
171     if (a1) {
172         const saml1::Conditions* conds = a1->getConditions();
173         if (!conds)
174             return true;
175
176         // First verify the time conditions, using the specified timestamp.
177         time_t now = policy.getTime();
178         unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs;
179         time_t t = conds->getNotBeforeEpoch();
180         if (now + skew < t)
181             throw SecurityPolicyException("Assertion is not yet valid.");
182         t = conds->getNotOnOrAfterEpoch();
183         if (t <= now - skew)
184             throw SecurityPolicyException("Assertion is no longer valid.");
185
186         // Now we process conditions, starting with the known types and then extensions.
187
188         bool valid;
189
190         const vector<saml1::AudienceRestrictionCondition*>& acvec = conds->getAudienceRestrictionConditions();
191         for (vector<saml1::AudienceRestrictionCondition*>::const_iterator ac = acvec.begin(); ac != acvec.end(); ++ac) {
192             valid = false;
193             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
194                 valid = (*r)->evaluate(*(*ac), request, policy);
195             if (!valid)
196                 throw SecurityPolicyException("AudienceRestrictionCondition not successfully validated by policy.");
197         }
198
199         const vector<saml1::DoNotCacheCondition*>& dncvec = conds->getDoNotCacheConditions();
200         for (vector<saml1::DoNotCacheCondition*>::const_iterator dnc = dncvec.begin(); dnc != dncvec.end(); ++dnc) {
201             valid = false;
202             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
203                 valid = (*r)->evaluate(*(*dnc), request, policy);
204             if (!valid)
205                 throw SecurityPolicyException("DoNotCacheCondition not successfully validated by policy.");
206         }
207
208         const vector<saml1::Condition*>& convec = conds->getConditions();
209         for (vector<saml1::Condition*>::const_iterator c = convec.begin(); c != convec.end(); ++c) {
210             valid = false;
211             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
212                 valid = (*r)->evaluate(*(*c), request, policy);
213             if (!valid) {
214                 throw SecurityPolicyException(
215                     "Extension condition ($1) not successfully validated by policy.",
216                     params(1,((*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : (*c)->getElementQName().toString().c_str()))
217                     );
218             }
219         }
220
221         return true;
222     }
223
224     return false;
225 }