X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=shibsp%2Fhandler%2Fimpl%2FSAML1Consumer.cpp;h=310c1e244c1490c53e3cbceebe1a08a176f983ec;hb=c51bfd77603cf0ddb0b5e374c35586a8435895d6;hp=30c059d005f4ef99985b2817a29feea56facb2b8;hpb=755adf7e70c2a059ca74e6b8e670e455f502bb45;p=shibboleth%2Fcpp-sp.git diff --git a/shibsp/handler/impl/SAML1Consumer.cpp b/shibsp/handler/impl/SAML1Consumer.cpp index 30c059d..310c1e2 100644 --- a/shibsp/handler/impl/SAML1Consumer.cpp +++ b/shibsp/handler/impl/SAML1Consumer.cpp @@ -1,41 +1,53 @@ -/* - * Copyright 2001-2009 Internet2 +/** + * Licensed to the University Corporation for Advanced Internet + * Development, Inc. (UCAID) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * UCAID licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the + * License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. */ /** * SAML1Consumer.cpp * - * SAML 1.x assertion consumer service + * SAML 1.x assertion consumer service. */ #include "internal.h" #include "handler/AssertionConsumerService.h" #ifndef SHIBSP_LITE -# include "exceptions.h" # include "Application.h" # include "ServiceProvider.h" # include "SessionCache.h" +# include "TransactionLog.h" # include "attribute/resolver/ResolutionContext.h" +# include +# include # include +# include +# include # include # include # include +# include +# include +# include using namespace opensaml::saml1; using namespace opensaml::saml1p; using namespace opensaml; +using namespace boost; using saml2::NameID; using saml2::NameIDBuilder; using saml2md::EntityDescriptor; @@ -60,19 +72,14 @@ namespace shibsp { { public: SAML1Consumer(const DOMElement* e, const char* appId) - : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".SSO.SAML1")) { + : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT ".SSO.SAML1")) { #ifndef SHIBSP_LITE - m_ssoRule = NULL; m_post = XMLString::equals(getString("Binding").second, samlconstants::SAML1_PROFILE_BROWSER_POST); if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) - m_ssoRule = SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(SAML1BROWSERSSO_POLICY_RULE, e); -#endif - } - virtual ~SAML1Consumer() { -#ifndef SHIBSP_LITE - delete m_ssoRule; + m_ssoRule.reset(SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(SAML1BROWSERSSO_POLICY_RULE, e)); #endif } + virtual ~SAML1Consumer() {} #ifndef SHIBSP_LITE void generateMetadata(SPSSODescriptor& role, const char* handlerURL) const { @@ -87,12 +94,16 @@ namespace shibsp { const HTTPRequest& httpRequest, HTTPResponse& httpResponse, SecurityPolicy& policy, - const PropertySet* settings, + const PropertySet*, const XMLObject& xmlObject ) const; bool m_post; - SecurityPolicyRule* m_ssoRule; + scoped_ptr m_ssoRule; +#else + const XMLCh* getProtocolFamily() const { + return samlconstants::SAML11_PROTOCOL_ENUM; + } #endif }; @@ -126,7 +137,7 @@ void SAML1Consumer::implementProtocol( const HTTPRequest& httpRequest, HTTPResponse& httpResponse, SecurityPolicy& policy, - const PropertySet* settings, + const PropertySet*, const XMLObject& xmlObject ) const { @@ -134,7 +145,7 @@ void SAML1Consumer::implementProtocol( m_log.debug("processing message against SAML 1.x SSO profile"); // Check for errors...this will throw if it's not a successful message. - checkError(&xmlObject); + checkError(&xmlObject, policy.getIssuerMetadata()); // With the binding aspects now moved out to the MessageDecoder, // the focus here is on the assertion content. For SAML 1.x POST, @@ -161,7 +172,7 @@ void SAML1Consumer::implementProtocol( pair minor = response->getMinorVersion(); // Maintain list of "legit" tokens to feed to SP subsystems. - const AuthenticationStatement* ssoStatement=NULL; + const AuthenticationStatement* ssoStatement=nullptr; vector tokens; // Also track "bad" tokens that we'll cache but not use. @@ -169,7 +180,7 @@ void SAML1Consumer::implementProtocol( vector badtokens; // With this flag on, we ignore any unsigned assertions. - const EntityDescriptor* entity = policy.getIssuerMetadata() ? dynamic_cast(policy.getIssuerMetadata()->getParent()) : NULL; + const EntityDescriptor* entity = policy.getIssuerMetadata() ? dynamic_cast(policy.getIssuerMetadata()->getParent()) : nullptr; pair flag = application.getRelyingParty(entity)->getBool("requireSignedAssertions"); // authnskew allows rejection of SSO if AuthnInstant is too old. @@ -180,17 +191,18 @@ void SAML1Consumer::implementProtocol( string contextualError; // Ensure the BrowserSSO rule is in the policy set. - if (find_if(policy.getRules(), _rulenamed(SAML1BROWSERSSO_POLICY_RULE)) == NULL) - policy.getRules().push_back(m_ssoRule); + if (find_if(policy.getRules(), _rulenamed(SAML1BROWSERSSO_POLICY_RULE)) == nullptr) + policy.getRules().push_back(m_ssoRule.get()); // Populate recipient as audience. policy.getAudiences().push_back(application.getRelyingParty(entity)->getXMLString("entityID").second); - time_t now = time(NULL); - for (vector::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) { + time_t now = time(nullptr); + for (indirect_iterator::const_iterator> a = make_indirect_iterator(assertions.begin()); + a != make_indirect_iterator(assertions.end()); ++a) { try { // Skip unsigned assertion? - if (!(*a)->getSignature() && flag.first && flag.second) + if (!a->getSignature() && flag.first && flag.second) throw SecurityPolicyException("The incoming assertion was unsigned, violating local security policy."); // We clear the security flag, so we can tell whether the token was secured on its own. @@ -199,38 +211,48 @@ void SAML1Consumer::implementProtocol( // Extract message bits and re-verify Issuer information. extractMessageDetails( - *(*a), (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM, policy + *a, (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM, policy ); // Run the policy over the assertion. Handles replay, freshness, and // signature verification, assuming the relevant rules are configured, // along with condition and profile enforcement. - policy.evaluate(*(*a)); + policy.evaluate(*a, &httpRequest); // If no security is in place now, we kick it. if (!alreadySecured && !policy.isAuthenticated()) throw SecurityPolicyException("Unable to establish security of incoming assertion."); // Track it as a valid token. - tokens.push_back(*a); + tokens.push_back(&(*a)); // Save off the first valid SSO statement. - const vector& statements = const_cast(*a)->getAuthenticationStatements(); - for (vector::const_iterator s = statements.begin(); s!=statements.end(); ++s) { - if (authnskew.first && authnskew.second && - (*s)->getAuthenticationInstant() && (now - (*s)->getAuthenticationInstantEpoch() > authnskew.second)) - contextualError = "The gap between now and the time you logged into your identity provider exceeds the limit."; + const vector& statements = + const_cast(*a).getAuthenticationStatements(); + for (indirect_iterator::const_iterator> s = make_indirect_iterator(statements.begin()); + s != make_indirect_iterator(statements.end()); ++s) { + if (s->getAuthenticationInstant() && + s->getAuthenticationInstantEpoch() - XMLToolingConfig::getConfig().clock_skew_secs > now) { + contextualError = "The login time at your identity provider was future-dated."; + } + else if (authnskew.first && authnskew.second && s->getAuthenticationInstant() && + s->getAuthenticationInstantEpoch() <= now && (now - s->getAuthenticationInstantEpoch() > authnskew.second)) { + contextualError = "The gap between now and the time you logged into your identity provider exceeds the allowed limit."; + } + else if (authnskew.first && authnskew.second && s->getAuthenticationInstant() == nullptr) { + contextualError = "Your identity provider did not supply a time of login, violating local policy."; + } else if (!ssoStatement) { - ssoStatement = *s; + ssoStatement = &(*s); break; } } } - catch (exception& ex) { + catch (std::exception& ex) { m_log.warn("detected a problem with assertion: %s", ex.what()); if (!ssoStatement) contextualError = ex.what(); - badtokens.push_back(*a); + badtokens.push_back(&(*a)); } } @@ -262,7 +284,7 @@ void SAML1Consumer::implementProtocol( // To complete processing, we need to extract and resolve attributes and then create the session. // Normalize the SAML 1.x NameIdentifier... - auto_ptr nameid(n ? NameIDBuilder::buildNameID() : NULL); + scoped_ptr nameid(n ? NameIDBuilder::buildNameID() : nullptr); if (n) { nameid->setName(n->getName()); nameid->setFormat(n->getFormat()); @@ -270,21 +292,25 @@ void SAML1Consumer::implementProtocol( } // The context will handle deleting attributes and new tokens. - auto_ptr ctx( + scoped_ptr ctx( resolveAttributes( application, + &httpRequest, policy.getIssuerMetadata(), (!response->getMinorVersion().first || response->getMinorVersion().second==1) ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM, + response, n, + ssoStatement, nameid.get(), + nullptr, ssoStatement->getAuthenticationMethod(), - NULL, + nullptr, &tokens ) ); - if (ctx.get()) { + if (ctx) { // Copy over any new tokens, but leave them in the context for cleanup. tokens.insert(tokens.end(), ctx->getResolvedAssertions().begin(), ctx->getResolvedAssertions().end()); } @@ -292,7 +318,9 @@ void SAML1Consumer::implementProtocol( // Now merge in bad tokens for caching. tokens.insert(tokens.end(), badtokens.begin(), badtokens.end()); + string session_id; application.getServiceProvider().getSessionCache()->insert( + session_id, application, httpRequest, httpResponse, @@ -301,13 +329,30 @@ void SAML1Consumer::implementProtocol( (!response->getMinorVersion().first || response->getMinorVersion().second==1) ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM, nameid.get(), - ssoStatement->getAuthenticationInstant() ? ssoStatement->getAuthenticationInstant()->getRawData() : NULL, - NULL, + ssoStatement->getAuthenticationInstant() ? ssoStatement->getAuthenticationInstant()->getRawData() : nullptr, + nullptr, ssoStatement->getAuthenticationMethod(), - NULL, + nullptr, &tokens, - ctx.get() ? &ctx->getResolvedAttributes() : NULL + ctx ? &ctx->getResolvedAttributes() : nullptr ); + + scoped_ptr login_event(newLoginEvent(application, httpRequest)); + if (login_event) { + login_event->m_sessionID = session_id.c_str(); + login_event->m_peer = entity; + auto_ptr_char prot( + (!response->getMinorVersion().first || response->getMinorVersion().second==1) ? + samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM + ); + login_event->m_protocol = prot.get(); + login_event->m_nameID = nameid.get(); + login_event->m_saml1AuthnStatement = ssoStatement; + login_event->m_saml1Response = response; + if (ctx) + login_event->m_attributes = &ctx->getResolvedAttributes(); + application.getServiceProvider().getTransactionLog()->write(*login_event); + } } #endif