308631027f5ce22dbbc7a9c3df0d7808576ed673
[shibboleth/cpp-sp.git] / shib / ShibBrowserProfile.cpp
1 /*
2  *  Copyright 2001-2005 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 /* ShibBrowserProfile.cpp - Shibboleth-specific wrapper around SAML browser profile
18
19    Scott Cantor
20    2/6/05
21
22    $History:$
23 */
24
25 #include "internal.h"
26
27 #include <ctime>
28
29 #include <openssl/x509v3.h>
30
31 using namespace shibboleth::logging;
32 using namespace shibboleth;
33 using namespace saml;
34 using namespace std;
35
36 ShibBrowserProfile::ShibBrowserProfile(const Iterator<IMetadata*>& metadatas, const Iterator<ITrust*>& trusts)
37     : m_metadatas(metadatas), m_trusts(trusts)
38 {
39     m_profile=SAMLBrowserProfile::getInstance();
40 }
41
42 ShibBrowserProfile::~ShibBrowserProfile()
43 {
44     delete m_profile;
45 }
46
47 SAMLBrowserProfile::BrowserProfileResponse ShibBrowserProfile::receive(
48     const char* packet,
49     const XMLCh* recipient,
50     int supportedProfiles,
51     IReplayCache* replayCache,
52     SAMLBrowserProfile::ArtifactMapper* callback,
53     int minorVersion
54     ) const
55 {
56 #ifdef _DEBUG
57     saml::NDC("recieve");
58 #endif
59     Category& log=Category::getInstance(SHIB_LOGCAT".ShibBrowserProfile");
60  
61     // The built-in SAML functionality will do most of the basic non-crypto checks.
62     // Note that if the response only contains a status error, it gets tossed out
63     // as an exception.
64     SAMLBrowserProfile::BrowserProfileResponse bpr;
65     try {
66         bpr=m_profile->receive(packet, recipient, supportedProfiles, replayCache, callback, minorVersion);
67     }
68     catch (SAMLException& e) {
69         // Try our best to attach additional information.
70         if (e.getProperty("issuer")) {
71             Metadata m(m_metadatas);
72             const IEntityDescriptor* provider=m.lookup(e.getProperty("issuer"),false);
73             if (provider) {
74                 const IIDPSSODescriptor* role=provider->getIDPSSODescriptor(
75                     minorVersion==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
76                     );
77                 if (role) annotateException(&e,role); // throws it
78                 annotateException(&e,provider);  // throws it
79             }
80         }
81         throw;
82     }
83     
84     // Try and locate metadata for the IdP. We try Issuer first.
85     log.debug("searching metadata for assertion issuer...");
86     Metadata m(m_metadatas);
87     const IEntityDescriptor* provider=m.lookup(bpr.assertion->getIssuer());
88     if (provider)
89         log.debug("matched assertion issuer against metadata");
90     else if (bpr.authnStatement->getSubject()->getNameIdentifier() &&
91              bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier()) {
92         // Might be a down-level origin.
93         provider=m.lookup(bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier());
94         if (provider)
95             log.debug("matched subject name qualifier against metadata");
96     }
97
98     // No metadata at all.
99     if (!provider) {
100         auto_ptr_char issuer(bpr.assertion->getIssuer());
101         auto_ptr_char nq(bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier());
102         log.error("assertion issuer not found in metadata (Issuer='%s', NameQualifier='%s')",
103             issuer.get(), (nq.get() ? nq.get() : "none"));
104         
105         // Try a non-strict lookup for more contact info.
106         const IEntityDescriptor* provider=m.lookup(bpr.assertion->getIssuer(),false);
107         if (provider) {
108                 log.debug("found invalid metadata for assertion issuer, using for contact info");
109             bpr.clear();
110             MetadataException ex("metadata lookup failed, unable to process assertion");
111             annotateException(&ex,provider);  // throws it
112         }
113         bpr.clear();
114         throw MetadataException("metadata lookup failed, unable to process assertion",namedparams(1,"issuer",issuer.get()));
115     }
116
117     // Is this provider an IdP?
118     const IIDPSSODescriptor* role=provider->getIDPSSODescriptor(
119         minorVersion==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
120         );
121     if (role) {
122         // Use this role to evaluate the signature(s). If the response is unsigned, we know
123         // it was an artifact profile run.
124         Trust t(m_trusts);
125         if (bpr.response->isSigned()) {        
126             log.debug("passing signed response to trust layer");
127             if (!t.validate(*bpr.response,role)) {
128                 bpr.clear();
129                 log.error("unable to verify signed profile response");
130                 TrustException ex("unable to verify signed profile response");
131                 annotateException(&ex,role); // throws it
132             }
133         }    
134         // SSO assertion signed?
135         if (bpr.assertion->isSigned()) {
136             log.debug("passing signed authentication assertion to trust layer"); 
137             if (!t.validate(*bpr.assertion,role)) {
138                 bpr.clear();
139                 log.error("unable to verify signed authentication assertion");
140                 TrustException ex("unable to verify signed authentication assertion");
141                 annotateException(&ex,role); // throws it
142             }
143         }
144         
145         // Finally, discard any assertions not issued by the same entity that issued the authn.
146         Iterator<SAMLAssertion*> assertions=bpr.response->getAssertions();
147         for (unsigned long a=0; a<assertions.size();) {
148             if (XMLString::compareString(bpr.assertion->getIssuer(),assertions[a]->getIssuer())) {
149                 auto_ptr_char bad(assertions[a]->getIssuer());
150                 log.warn("discarding assertion not issued by authenticating IdP, instead by (%s)",bad.get());
151                 bpr.response->removeAssertion(a);
152                 continue;
153             }
154             a++;
155         }
156         
157         return bpr;
158     }
159
160     auto_ptr_char issuer(bpr.assertion->getIssuer());
161     auto_ptr_char nq(bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier());
162     log.error("metadata for assertion issuer indicates no SAML 1.%d identity provider role (Issuer='%s', NameQualifier='%s'",
163         minorVersion, issuer.get(), (nq.get() ? nq.get() : "none"));
164     bpr.clear();
165     MetadataException ex("metadata lookup failed, issuer not registered as SAML 1.x identity provider");
166     annotateException(&ex,provider,false);
167     throw ex;
168 }