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