2 * Copyright 2001-2005 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 /* ShibBrowserProfile.cpp - Shibboleth-specific wrapper around SAML browser profile
28 #include <openssl/x509v3.h>
29 #include <saml/saml1/core/Protocols.h>
30 #include <xmltooling/XMLToolingConfig.h>
31 #include <xmltooling/util/NDC.h>
33 using namespace shibboleth;
35 using namespace opensaml::saml1p;
36 using namespace opensaml::saml2md;
37 using namespace xmltooling;
38 using namespace log4cpp;
41 ShibBrowserProfile::ShibBrowserProfile(
42 const ITokenValidator* validator, MetadataProvider* metadata, TrustEngine* trust
43 ) : m_validator(validator), m_metadata(metadata), m_trust(trust)
45 m_profile=SAMLBrowserProfile::getInstance();
48 ShibBrowserProfile::~ShibBrowserProfile()
53 SAMLBrowserProfile::BrowserProfileResponse ShibBrowserProfile::receive(
54 const char* samlResponse,
55 const XMLCh* recipient,
56 saml::IReplayCache* replayCache,
61 xmltooling::NDC("recieve");
63 Category& log=Category::getInstance(SHIB_LOGCAT".ShibBrowserProfile");
65 // The built-in SAML functionality will do most of the basic non-crypto checks.
66 // Note that if the response only contains a status error, it gets tossed out
68 SAMLBrowserProfile::BrowserProfileResponse bpr=m_profile->receive(samlResponse, recipient, replayCache, minorVersion);
71 postprocess(bpr,minorVersion);
80 SAMLBrowserProfile::BrowserProfileResponse ShibBrowserProfile::receive(
81 Iterator<const char*> artifacts,
82 const XMLCh* recipient,
83 SAMLBrowserProfile::ArtifactMapper* artifactMapper,
84 IReplayCache* replayCache,
88 // The built-in SAML functionality will do most of the basic non-crypto checks.
89 // Note that if the response only contains a status error, it gets tossed out
91 SAMLBrowserProfile::BrowserProfileResponse bpr=m_profile->receive(artifacts, recipient, artifactMapper, replayCache, minorVersion);
94 postprocess(bpr,minorVersion);
103 void ShibBrowserProfile::postprocess(SAMLBrowserProfile::BrowserProfileResponse& bpr, int minorVersion) const
106 xmltooling::NDC("postprocess");
108 Category& log=Category::getInstance(SHIB_LOGCAT".ShibBrowserProfile");
111 throw MetadataException("No metadata found, unable to process assertion.");
113 // Try and locate metadata for the IdP. We try Issuer first.
114 log.debug("searching metadata for assertion issuer...");
115 xmltooling::Locker locker(m_metadata);
116 const EntityDescriptor* provider=m_metadata->getEntityDescriptor(bpr.assertion->getIssuer());
118 log.debug("matched assertion issuer against metadata");
119 else if (bpr.authnStatement->getSubject()->getNameIdentifier() &&
120 bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier()) {
121 // Might be a down-level origin.
122 provider=m_metadata->getEntityDescriptor(bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier());
124 log.debug("matched subject name qualifier against metadata");
127 // No metadata at all.
129 xmltooling::auto_ptr_char issuer(bpr.assertion->getIssuer());
130 xmltooling::auto_ptr_char nq(bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier());
131 log.error("assertion issuer not found in metadata (Issuer='%s', NameQualifier='%s')",
132 issuer.get(), (nq.get() ? nq.get() : "none"));
134 // Try a non-strict lookup for more contact info.
135 const EntityDescriptor* provider=m_metadata->getEntityDescriptor(bpr.assertion->getIssuer(),false);
137 log.debug("found invalid metadata for assertion issuer, using for contact info");
138 MetadataException ex("metadata lookup failed, unable to process assertion");
139 annotateException(&ex,provider); // throws it
141 throw MetadataException("Metadata lookup failed, unable to process assertion",xmltooling::namedparams(1,"issuer",issuer.get()));
144 // Is this provider an IdP?
145 const IDPSSODescriptor* role=provider->getIDPSSODescriptor(
146 minorVersion==1 ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM
149 xmltooling::auto_ptr_char issuer(bpr.assertion->getIssuer());
150 xmltooling::auto_ptr_char nq(bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier());
151 log.error("metadata for assertion issuer indicates no SAML 1.%d identity provider role (Issuer='%s', NameQualifier='%s'",
152 minorVersion, issuer.get(), (nq.get() ? nq.get() : "none"));
153 MetadataException ex("Metadata lookup failed, issuer not registered as SAML 1.x identity provider");
154 annotateException(&ex,provider); // throws it
157 // Use this role to evaluate the signature(s). If the response is unsigned, we know
158 // it was an artifact profile run.
159 if (bpr.response->isSigned()) {
160 log.debug("passing signed response to trust layer");
162 XMLSecurityException ex("No trust provider, unable to verify signed profile response.");
163 annotateException(&ex,role); // throws it
166 // This will all change, but for fun, we'll port the object from OS1->OS2 for validation.
169 DOMDocument* doc = XMLToolingConfig::getConfig().getValidatingParser().parse(s);
170 XercesJanitor<DOMDocument> jdoc(doc);
171 auto_ptr<Response> os2resp(ResponseBuilder::buildResponse());
172 os2resp->unmarshall(doc->getDocumentElement(),true);
175 if (!m_trust->validate(*(os2resp->getSignature()),*role,m_metadata->getKeyResolver())) {
176 log.error("unable to verify signed profile response");
177 XMLSecurityException ex("Unable to verify signed profile response.");
178 annotateException(&ex,role); // throws it
182 time_t now=time(NULL);
183 Iterator<SAMLAssertion*> assertions=bpr.response->getAssertions();
184 for (unsigned int a=0; a<assertions.size();) {
185 // Discard any assertions not issued by the same entity that issued the authn.
186 if (bpr.assertion!=assertions[a] && XMLString::compareString(bpr.assertion->getIssuer(),assertions[a]->getIssuer())) {
187 xmltooling::auto_ptr_char bad(assertions[a]->getIssuer());
188 log.warn("discarding assertion not issued by authenticating IdP, instead by (%s)",bad.get());
189 bpr.response->removeAssertion(a);
193 // Validate the token.
195 m_validator->validateToken(assertions[a],now,role,m_trust);
198 catch (SAMLException&) {
199 if (assertions[a]==bpr.assertion) {
200 // If the authn token fails, we have to fail the whole profile run.
201 log.error("authentication assertion failed to validate");
202 //annotateException(&e,role,false);
205 log.warn("token failed to validate, removing it from response");
206 bpr.response->removeAssertion(a);