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