Change license header, remove stale pkg files.
[shibboleth/cpp-opensaml.git] / saml / profile / impl / ConditionsRule.cpp
1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20
21 /**
22  * ConditionsRule.cpp
23  *
24  * SAML Conditions SecurityPolicyRule.
25  */
26
27 #include "internal.h"
28 #include "exceptions.h"
29 #include "binding/SecurityPolicy.h"
30 #include "binding/SecurityPolicyRule.h"
31 #include "saml1/core/Assertions.h"
32 #include "saml2/core/Assertions.h"
33
34 #include <xercesc/util/XMLUniDefs.hpp>
35 #include <xmltooling/logging.h>
36 #include <xmltooling/XMLToolingConfig.h>
37 #include <xmltooling/util/ParserPool.h>
38
39 using namespace opensaml;
40 using namespace xmltooling::logging;
41 using namespace xmltooling;
42 using namespace std;
43
44 namespace opensaml {
45     class SAML_DLLLOCAL ConditionsRule : public SecurityPolicyRule
46     {
47     public:
48         ConditionsRule(const DOMElement* e);
49
50         virtual ~ConditionsRule() {
51             for_each(m_rules.begin(), m_rules.end(), xmltooling::cleanup<SecurityPolicyRule>());
52             if (m_doc)
53                 m_doc->release();
54         }
55         const char* getType() const {
56             return CONDITIONS_POLICY_RULE;
57         }
58         bool evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const;
59
60     private:
61         DOMDocument* m_doc;
62         vector<SecurityPolicyRule*> m_rules;
63     };
64
65     SecurityPolicyRule* SAML_DLLLOCAL ConditionsRuleFactory(const DOMElement* const & e)
66     {
67         return new ConditionsRule(e);
68     }
69
70     static const XMLCh Rule[] =     UNICODE_LITERAL_10(P,o,l,i,c,y,R,u,l,e);
71     static const XMLCh type[] =     UNICODE_LITERAL_4(t,y,p,e);
72
73     const char config[] =
74         "<PolicyRule type=\"Conditions\" xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\">"
75             "<PolicyRule type=\"Audience\"/>"
76             "<PolicyRule type=\"Ignore\">saml:DoNotCacheCondition</PolicyRule>"
77             "<PolicyRule type=\"Ignore\">saml2:OneTimeUse</PolicyRule>"
78             "<PolicyRule type=\"Ignore\">saml2:ProxyRestriction</PolicyRule>"
79         "</PolicyRule>";
80 };
81
82 ConditionsRule::ConditionsRule(const DOMElement* e) : m_doc(nullptr)
83 {
84     Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.Conditions");
85
86     if (!e || !e->hasChildNodes()) {
87         // Default the configuration.
88         istringstream in(config);
89         m_doc = XMLToolingConfig::getConfig().getParser().parse(in);
90         e = m_doc->getDocumentElement();
91     }
92
93     e = XMLHelper::getFirstChildElement(e, Rule);
94     while (e) {
95         string t = XMLHelper::getAttrString(e, nullptr, type);
96         if (!t.empty()) {
97             try {
98                 log.info("building SecurityPolicyRule of type %s", t.c_str());
99                 m_rules.push_back(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(t.c_str(), e));
100             }
101             catch (exception& ex) {
102                 log.crit("error building SecurityPolicyRule: %s", ex.what());
103             }
104         }
105         e = XMLHelper::getNextSiblingElement(e, Rule);
106     }
107 }
108
109 bool ConditionsRule::evaluate(const XMLObject& message, const GenericRequest* request, SecurityPolicy& policy) const
110 {
111     const saml2::Assertion* a2=dynamic_cast<const saml2::Assertion*>(&message);
112     if (a2) {
113         const saml2::Conditions* conds = a2->getConditions();
114         if (!conds)
115             return true;
116
117         // First verify the time conditions, using the specified timestamp.
118         time_t now = policy.getTime();
119         unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs;
120         time_t t = conds->getNotBeforeEpoch();
121         if (now + skew < t)
122             throw SecurityPolicyException("Assertion is not yet valid.");
123         t = conds->getNotOnOrAfterEpoch();
124         if (t <= now - skew)
125             throw SecurityPolicyException("Assertion is no longer valid.");
126
127         // Now we process conditions, starting with the known types and then extensions.
128
129         bool valid;
130
131         const vector<saml2::AudienceRestriction*>& acvec = conds->getAudienceRestrictions();
132         for (vector<saml2::AudienceRestriction*>::const_iterator ac = acvec.begin(); ac != acvec.end(); ++ac) {
133             valid = false;
134             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
135                 valid = (*r)->evaluate(*(*ac), request, policy);
136             if (!valid)
137                 throw SecurityPolicyException("AudienceRestriction condition not successfully validated by policy.");
138         }
139
140         const vector<saml2::OneTimeUse*>& otvec = conds->getOneTimeUses();
141         for (vector<saml2::OneTimeUse*>::const_iterator ot = otvec.begin(); ot!=otvec.end(); ++ot) {
142             valid = false;
143             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
144                 valid = (*r)->evaluate(*(*ot), request, policy);
145             if (!valid)
146                 throw SecurityPolicyException("OneTimeUse condition not successfully validated by policy.");
147         }
148
149         const vector<saml2::ProxyRestriction*> pvec = conds->getProxyRestrictions();
150         for (vector<saml2::ProxyRestriction*>::const_iterator p = pvec.begin(); p != pvec.end(); ++p) {
151             valid = false;
152             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
153                 valid = (*r)->evaluate(*(*p), request, policy);
154             if (!valid)
155                 throw SecurityPolicyException("ProxyRestriction condition not successfully validated by policy.");
156         }
157
158         const vector<saml2::Condition*>& convec = conds->getConditions();
159         for (vector<saml2::Condition*>::const_iterator c = convec.begin(); c != convec.end(); ++c) {
160             valid = false;
161             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
162                 valid = (*r)->evaluate(*(*c), request, policy);
163             if (!valid) {
164                 throw SecurityPolicyException(
165                     "Extension condition ($1) not successfully validated by policy.",
166                     params(1,((*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : "Unknown Type"))
167                     );
168             }
169         }
170
171         return true;
172     }
173
174     const saml1::Assertion* a1=dynamic_cast<const saml1::Assertion*>(&message);
175     if (a1) {
176         const saml1::Conditions* conds = a1->getConditions();
177         if (!conds)
178             return true;
179
180         // First verify the time conditions, using the specified timestamp.
181         time_t now = policy.getTime();
182         unsigned int skew = XMLToolingConfig::getConfig().clock_skew_secs;
183         time_t t = conds->getNotBeforeEpoch();
184         if (now + skew < t)
185             throw SecurityPolicyException("Assertion is not yet valid.");
186         t = conds->getNotOnOrAfterEpoch();
187         if (t <= now - skew)
188             throw SecurityPolicyException("Assertion is no longer valid.");
189
190         // Now we process conditions, starting with the known types and then extensions.
191
192         bool valid;
193
194         const vector<saml1::AudienceRestrictionCondition*>& acvec = conds->getAudienceRestrictionConditions();
195         for (vector<saml1::AudienceRestrictionCondition*>::const_iterator ac = acvec.begin(); ac != acvec.end(); ++ac) {
196             valid = false;
197             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
198                 valid = (*r)->evaluate(*(*ac), request, policy);
199             if (!valid)
200                 throw SecurityPolicyException("AudienceRestrictionCondition not successfully validated by policy.");
201         }
202
203         const vector<saml1::DoNotCacheCondition*>& dncvec = conds->getDoNotCacheConditions();
204         for (vector<saml1::DoNotCacheCondition*>::const_iterator dnc = dncvec.begin(); dnc != dncvec.end(); ++dnc) {
205             valid = false;
206             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
207                 valid = (*r)->evaluate(*(*dnc), request, policy);
208             if (!valid)
209                 throw SecurityPolicyException("DoNotCacheCondition not successfully validated by policy.");
210         }
211
212         const vector<saml1::Condition*>& convec = conds->getConditions();
213         for (vector<saml1::Condition*>::const_iterator c = convec.begin(); c != convec.end(); ++c) {
214             valid = false;
215             for (vector<SecurityPolicyRule*>::const_iterator r = m_rules.begin(); !valid && r != m_rules.end(); ++r)
216                 valid = (*r)->evaluate(*(*c), request, policy);
217             if (!valid) {
218                 throw SecurityPolicyException(
219                     "Extension condition ($1) not successfully validated by policy.",
220                     params(1,((*c)->getSchemaType() ? (*c)->getSchemaType()->toString().c_str() : (*c)->getElementQName().toString().c_str()))
221                     );
222             }
223         }
224
225         return true;
226     }
227
228     return false;
229 }