2 * The Shibboleth License, Version 1.
4 * University Corporation for Advanced Internet Development, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution, if any, must include
17 * the following acknowledgment: "This product includes software developed by
18 * the University Corporation for Advanced Internet Development
19 * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20 * may appear in the software itself, if and wherever such third-party
21 * acknowledgments normally appear.
23 * Neither the name of Shibboleth nor the names of its contributors, nor
24 * Internet2, nor the University Corporation for Advanced Internet Development,
25 * Inc., nor UCAID may be used to endorse or promote products derived from this
26 * software without specific prior written permission. For written permission,
27 * please contact shibboleth@shibboleth.org
29 * Products derived from this software may not be called Shibboleth, Internet2,
30 * UCAID, or the University Corporation for Advanced Internet Development, nor
31 * may Shibboleth appear in their name, without prior written permission of the
32 * University Corporation for Advanced Internet Development.
35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38 * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39 * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40 * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41 * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 /* ShibPOSTProfile.cpp - Shibboleth-specific wrapper around SAML POST profile
62 #include <openssl/x509v3.h>
64 using namespace shibboleth;
66 using namespace log4cpp;
69 ShibPOSTProfile::ShibPOSTProfile(
70 const Iterator<IMetadata*>& metadatas,
71 const Iterator<IRevocation*>& revocations,
72 const Iterator<ITrust*>& trusts,
73 const Iterator<ICredentials*>& creds
74 ) : m_metadatas(metadatas), m_revocations(revocations), m_trusts(trusts), m_creds(creds) {}
76 const SAMLAssertion* ShibPOSTProfile::getSSOAssertion(const SAMLResponse& r, const Iterator<const XMLCh*>& audiences)
78 return SAMLPOSTProfile::getSSOAssertion(r,audiences);
81 const SAMLAuthenticationStatement* ShibPOSTProfile::getSSOStatement(const SAMLAssertion& a)
83 return SAMLPOSTProfile::getSSOStatement(a);
86 const XMLCh* ShibPOSTProfile::getProviderId(const saml::SAMLResponse& r)
88 // Favor an AuthnStatement Subject NameQualifier, but use Issuer if need be.
89 const XMLCh* ret=NULL;
90 Iterator<SAMLAssertion*> ia=r.getAssertions();
91 while (ia.hasNext()) {
92 SAMLAssertion* a=ia.next();
94 Iterator<SAMLStatement*> is=a->getStatements();
95 while (is.hasNext()) {
96 SAMLAuthenticationStatement* as=dynamic_cast<SAMLAuthenticationStatement*>(is.next());
97 if (as && as->getSubject()->getNameQualifier())
98 return as->getSubject()->getNameQualifier();
104 SAMLResponse* ShibPOSTProfile::accept(
106 const XMLCh* recipient,
108 const saml::Iterator<const XMLCh*>& audiences,
112 Category& log=Category::getInstance(SHIB_LOGCAT".ShibPOSTProfile");
114 // The built-in SAML functionality will do most of the basic non-crypto checks.
115 // Note that if the response only contains a status error, it gets tossed out
117 auto_ptr<SAMLResponse> r(SAMLPOSTProfile::accept(buf, recipient, ttlSeconds, false));
119 // Now we do some more non-crypto (ie. cheap) work to match up the origin site
120 // with its associated data.
121 const SAMLAssertion* assertion = NULL;
122 const SAMLAuthenticationStatement* sso = NULL;
125 assertion = getSSOAssertion(*(r.get()),audiences);
126 sso = getSSOStatement(*assertion);
129 // We want to try our best to locate an origin site name so we can fill it in.
131 *pproviderId=XMLString::replicate(getProviderId(*(r.get())));
135 // Finish SAML processing.
136 SAMLPOSTProfile::process(*(r.get()), recipient, ttlSeconds);
138 // Try and locate metadata for the IdP. With this new version, we try Issuer first.
139 log.debug("searching metadata for assertion issuer...");
140 Metadata m(m_metadatas);
141 const IProvider* provider=m.lookup(assertion->getIssuer());
144 *pproviderId=XMLString::replicate(assertion->getIssuer());
145 log.debug("matched assertion issuer against metadata");
148 // Might be a down-level origin.
149 provider=m.lookup(sso->getSubject()->getNameQualifier());
152 *pproviderId=XMLString::replicate(sso->getSubject()->getNameQualifier());
153 log.debug("matched subject name qualifier against metadata");
157 // No metadata at all.
159 auto_ptr_char issuer(assertion->getIssuer());
160 auto_ptr_char nq(sso->getSubject()->getNameQualifier());
161 log.error("assertion issuer not found in metadata (Issuer='%s', NameQualifier='%s'",
162 issuer.get(), (nq.get() ? nq.get() : "null"));
163 throw MetadataException("ShibPOSTProfile::accept() metadata lookup failed, unable to process assertion");
166 // Is this provider an IdP?
167 Iterator<const IProviderRole*> roles=provider->getRoles();
168 while (roles.hasNext()) {
169 const IProviderRole* role=roles.next();
170 if (dynamic_cast<const IIDPProviderRole*>(role)) {
171 // Check for Shibboleth 1.x protocol support.
172 if (role->hasSupport(Constants::SHIB_NS)) {
173 log.debug("passing response to trust layer");
175 // Use this role to evaluate the signature.
177 if (!t.validate(m_revocations,role,*r))
178 throw TrustException("ShibPOSTProfile::accept() unable to verify signed response");
180 // Assertion(s) signed?
181 Iterator<SAMLAssertion*> itera=r->getAssertions();
182 while (itera.hasNext()) {
183 SAMLAssertion* _a=itera.next();
184 if (_a->isSigned()) {
185 log.debug("passing signed assertion to trust layer");
186 if (!t.validate(m_revocations,role,*_a))
187 throw TrustException("ShibPOSTProfile::accept() unable to verify signed assertion");
195 auto_ptr_char issuer(assertion->getIssuer());
196 auto_ptr_char nq(sso->getSubject()->getNameQualifier());
197 log.error("metadata for assertion issuer indicates no SAML 1.x identity provider role (Issuer='%s', NameQualifier='%s'",
198 issuer.get(), (nq.get() ? nq.get() : "null"));
199 throw MetadataException("ShibPOSTProfile::accept() metadata lookup failed, issuer not registered as SAML identity provider");
202 SAMLResponse* ShibPOSTProfile::prepare(
203 const IIDPProviderRole* role,
204 const char* credResolverId,
205 const XMLCh* recipient,
206 const XMLCh* authMethod,
210 const XMLCh* nameQualifier,
211 const XMLCh* subjectIP,
212 const saml::Iterator<const XMLCh*>& audiences,
213 const saml::Iterator<saml::SAMLAuthorityBinding*>& bindings)
216 struct tm* ptime=gmtime(&authInstant);
219 struct tm* ptime=gmtime_r(&authInstant,&res);
222 strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime);
223 auto_ptr_XMLCh timeptr(timebuf);
224 XMLDateTime authDateTime(timeptr.get());
225 authDateTime.parseDateTime();
227 SAMLResponse* r = SAMLPOSTProfile::prepare(
229 role->getProvider()->getId(),
240 Credentials c(m_creds);
241 const ICredResolver* cr=c.lookup(credResolverId);
244 throw CredentialException("ShibPOSTProfile::prepare() unable to access credential resolver");
246 XSECCryptoKey* key=cr->getKey();
249 throw CredentialException("ShibPOSTProfile::prepare() unable to resolve signing key");
252 r->sign(SIGNATURE_RSA,key,cr->getCertificates());
256 bool ShibPOSTProfile::checkReplayCache(const SAMLAssertion& a)
258 // Default implementation uses the basic replay cache implementation.
259 return SAMLPOSTProfile::checkReplayCache(a);