From 6555ae4f975641398a3e93e2145d4534feffaf8d Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Tue, 27 Sep 2011 21:49:18 +0000 Subject: [PATCH] Extend attribute resolution to include authn statement. --- adfs/adfs.cpp | 42 ++++++++++++---------- shibsp/handler/AssertionConsumerService.h | 33 +++++++++++++++++ shibsp/handler/impl/AssertionConsumerService.cpp | 45 +++++++++++++++++++++--- shibsp/handler/impl/SAML1Consumer.cpp | 2 ++ shibsp/handler/impl/SAML2Consumer.cpp | 2 ++ 5 files changed, 101 insertions(+), 23 deletions(-) diff --git a/adfs/adfs.cpp b/adfs/adfs.cpp index c49ec99..e14330c 100644 --- a/adfs/adfs.cpp +++ b/adfs/adfs.cpp @@ -639,8 +639,10 @@ void ADFSConsumer::implementProtocol( if (!policy.isAuthenticated()) throw SecurityPolicyException("Unable to establish security of incoming assertion."); - saml1::NameIdentifier* saml1name=nullptr; + const saml1::NameIdentifier* saml1name=nullptr; + const saml1::AuthenticationStatement* saml1statement=nullptr; saml2::NameID* saml2name=nullptr; + const saml2::AuthnStatement* saml2statement=nullptr; const XMLCh* authMethod=nullptr; const XMLCh* authInstant=nullptr; time_t now = time(nullptr), sessionExp = 0; @@ -657,13 +659,13 @@ void ADFSConsumer::implementProtocol( // authnskew allows rejection of SSO if AuthnInstant is too old. pair authnskew = sessionProps ? sessionProps->getUnsignedInt("maxTimeSinceAuthn") : pair(false,0); - const saml1::AuthenticationStatement* ssoStatement=saml1token->getAuthenticationStatements().front(); - if (ssoStatement->getAuthenticationInstant()) { - if (ssoStatement->getAuthenticationInstantEpoch() - XMLToolingConfig::getConfig().clock_skew_secs > now) { + saml1statement = saml1token->getAuthenticationStatements().front(); + if (saml1statement->getAuthenticationInstant()) { + if (saml1statement->getAuthenticationInstantEpoch() - XMLToolingConfig::getConfig().clock_skew_secs > now) { throw FatalProfileException("The login time at your identity provider was future-dated."); } - else if (authnskew.first && authnskew.second && ssoStatement->getAuthenticationInstantEpoch() <= now && - (now - ssoStatement->getAuthenticationInstantEpoch() > authnskew.second)) { + else if (authnskew.first && authnskew.second && saml1statement->getAuthenticationInstantEpoch() <= now && + (now - saml1statement->getAuthenticationInstantEpoch() > authnskew.second)) { throw FatalProfileException("The gap between now and the time you logged into your identity provider exceeds the allowed limit."); } } @@ -672,16 +674,16 @@ void ADFSConsumer::implementProtocol( } // Address checking. - saml1::SubjectLocality* locality = ssoStatement->getSubjectLocality(); + saml1::SubjectLocality* locality = saml1statement->getSubjectLocality(); if (locality && locality->getIPAddress()) { auto_ptr_char ip(locality->getIPAddress()); checkAddress(application, httpRequest, ip.get()); } - saml1name = ssoStatement->getSubject()->getNameIdentifier(); - authMethod = ssoStatement->getAuthenticationMethod(); - if (ssoStatement->getAuthenticationInstant()) - authInstant = ssoStatement->getAuthenticationInstant()->getRawData(); + saml1name = saml1statement->getSubject()->getNameIdentifier(); + authMethod = saml1statement->getAuthenticationMethod(); + if (saml1statement->getAuthenticationInstant()) + authInstant = saml1statement->getAuthenticationInstant()->getRawData(); // Session expiration. pair lifetime = sessionProps ? sessionProps->getUnsignedInt("lifetime") : pair(true,28800); @@ -703,26 +705,26 @@ void ADFSConsumer::implementProtocol( // authnskew allows rejection of SSO if AuthnInstant is too old. pair authnskew = sessionProps ? sessionProps->getUnsignedInt("maxTimeSinceAuthn") : pair(false,0); - const saml2::AuthnStatement* ssoStatement=saml2token->getAuthnStatements().front(); + saml2statement = saml2token->getAuthnStatements().front(); if (authnskew.first && authnskew.second && - ssoStatement->getAuthnInstant() && (now - ssoStatement->getAuthnInstantEpoch() > authnskew.second)) + saml2statement->getAuthnInstant() && (now - saml2statement->getAuthnInstantEpoch() > authnskew.second)) throw FatalProfileException("The gap between now and the time you logged into your identity provider exceeds the limit."); // Address checking. - saml2::SubjectLocality* locality = ssoStatement->getSubjectLocality(); + saml2::SubjectLocality* locality = saml2statement->getSubjectLocality(); if (locality && locality->getAddress()) { auto_ptr_char ip(locality->getAddress()); checkAddress(application, httpRequest, ip.get()); } saml2name = saml2token->getSubject() ? saml2token->getSubject()->getNameID() : nullptr; - if (ssoStatement->getAuthnContext() && ssoStatement->getAuthnContext()->getAuthnContextClassRef()) - authMethod = ssoStatement->getAuthnContext()->getAuthnContextClassRef()->getReference(); - if (ssoStatement->getAuthnInstant()) - authInstant = ssoStatement->getAuthnInstant()->getRawData(); + if (saml2statement->getAuthnContext() && saml2statement->getAuthnContext()->getAuthnContextClassRef()) + authMethod = saml2statement->getAuthnContext()->getAuthnContextClassRef()->getReference(); + if (saml2statement->getAuthnInstant()) + authInstant = saml2statement->getAuthnInstant()->getRawData(); // Session expiration for SAML 2.0 is jointly IdP- and SP-driven. - sessionExp = ssoStatement->getSessionNotOnOrAfter() ? ssoStatement->getSessionNotOnOrAfterEpoch() : 0; + sessionExp = saml2statement->getSessionNotOnOrAfter() ? saml2statement->getSessionNotOnOrAfterEpoch() : 0; pair lifetime = sessionProps ? sessionProps->getUnsignedInt("lifetime") : pair(true,28800); if (!lifetime.first || lifetime.second == 0) lifetime.second = 28800; @@ -753,7 +755,9 @@ void ADFSConsumer::implementProtocol( policy.getIssuerMetadata(), m_protocol.get(), saml1name, + saml1statement, (saml1name ? nameid.get() : saml2name), + saml2statement, authMethod, nullptr, &tokens diff --git a/shibsp/handler/AssertionConsumerService.h b/shibsp/handler/AssertionConsumerService.h index 3a32393..3d452b9 100644 --- a/shibsp/handler/AssertionConsumerService.h +++ b/shibsp/handler/AssertionConsumerService.h @@ -35,9 +35,11 @@ namespace opensaml { class SAML_API Assertion; class SAML_API MessageDecoder; namespace saml1 { + class SAML_API AuthenticationStatement; class SAML_API NameIdentifier; }; namespace saml2 { + class SAML_API AuthnStatement; class SAML_API NameID; }; namespace saml2md { @@ -150,6 +152,35 @@ namespace shibsp { ) const; /** + * @deprecated + * Attempt SSO-initiated attribute resolution using the supplied information, + * including NameID and token extraction and filtering followed by + * secondary resolution. + * + *

The caller must free the returned context handle. + * + * @param application reference to application receiving message + * @param issuer source of SSO tokens + * @param protocol SSO protocol used + * @param v1nameid identifier of principal in SAML 1.x form, if any + * @param nameid identifier of principal in SAML 2.0 form + * @param authncontext_class method/category of authentication event, if known + * @param authncontext_decl specifics of authentication event, if known + * @param tokens available assertions, if any + */ + ResolutionContext* resolveAttributes( + const Application& application, + const opensaml::saml2md::RoleDescriptor* issuer=nullptr, + const XMLCh* protocol=nullptr, + const opensaml::saml1::NameIdentifier* v1nameid=nullptr, + const opensaml::saml2::NameID* nameid=nullptr, + const XMLCh* authncontext_class=nullptr, + const XMLCh* authncontext_decl=nullptr, + const std::vector* tokens=nullptr + ) const; + + /** + * @deprecated * Attempt SSO-initiated attribute resolution using the supplied information, * including NameID and token extraction and filtering followed by * secondary resolution. @@ -170,7 +201,9 @@ namespace shibsp { const opensaml::saml2md::RoleDescriptor* issuer=nullptr, const XMLCh* protocol=nullptr, const opensaml::saml1::NameIdentifier* v1nameid=nullptr, + const opensaml::saml1::AuthenticationStatement* v1statement=nullptr, const opensaml::saml2::NameID* nameid=nullptr, + const opensaml::saml2::AuthnStatement* statement=nullptr, const XMLCh* authncontext_class=nullptr, const XMLCh* authncontext_decl=nullptr, const std::vector* tokens=nullptr diff --git a/shibsp/handler/impl/AssertionConsumerService.cpp b/shibsp/handler/impl/AssertionConsumerService.cpp index e2b4c49..81807a3 100644 --- a/shibsp/handler/impl/AssertionConsumerService.cpp +++ b/shibsp/handler/impl/AssertionConsumerService.cpp @@ -314,6 +314,33 @@ ResolutionContext* AssertionConsumerService::resolveAttributes( const vector* tokens ) const { + return resolveAttributes( + application, + issuer, + protocol, + v1nameid, + nullptr, + nameid, + nullptr, + authncontext_class, + authncontext_decl, + tokens + ); +} + +ResolutionContext* AssertionConsumerService::resolveAttributes( + const Application& application, + const saml2md::RoleDescriptor* issuer, + const XMLCh* protocol, + const saml1::NameIdentifier* v1nameid, + const saml1::AuthenticationStatement* v1statement, + const saml2::NameID* nameid, + const saml2::AuthnStatement* statement, + const XMLCh* authncontext_class, + const XMLCh* authncontext_decl, + const vector* tokens + ) const +{ // First we do the extraction of any pushed information, including from metadata. vector resolvedAttributes; AttributeExtractor* extractor = application.getAttributeExtractor(); @@ -337,23 +364,33 @@ ResolutionContext* AssertionConsumerService::resolveAttributes( } } } + m_log.debug("extracting pushed attributes..."); - if (v1nameid) { + + if (v1nameid || nameid) { try { - extractor->extractAttributes(application, issuer, *v1nameid, resolvedAttributes); + if (v1nameid) + extractor->extractAttributes(application, issuer, *v1nameid, resolvedAttributes); + else + extractor->extractAttributes(application, issuer, *nameid, resolvedAttributes); } catch (exception& ex) { m_log.error("caught exception extracting attributes: %s", ex.what()); } } - else if (nameid) { + + if (v1statement || statement) { try { - extractor->extractAttributes(application, issuer, *nameid, resolvedAttributes); + if (v1statement) + extractor->extractAttributes(application, issuer, *v1statement, resolvedAttributes); + else + extractor->extractAttributes(application, issuer, *statement, resolvedAttributes); } catch (exception& ex) { m_log.error("caught exception extracting attributes: %s", ex.what()); } } + if (tokens) { for (vector::const_iterator t = tokens->begin(); t!=tokens->end(); ++t) { try { diff --git a/shibsp/handler/impl/SAML1Consumer.cpp b/shibsp/handler/impl/SAML1Consumer.cpp index 46ef55c..adeebf3 100644 --- a/shibsp/handler/impl/SAML1Consumer.cpp +++ b/shibsp/handler/impl/SAML1Consumer.cpp @@ -300,7 +300,9 @@ void SAML1Consumer::implementProtocol( (!response->getMinorVersion().first || response->getMinorVersion().second==1) ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM, n, + ssoStatement, nameid.get(), + nullptr, ssoStatement->getAuthenticationMethod(), nullptr, &tokens diff --git a/shibsp/handler/impl/SAML2Consumer.cpp b/shibsp/handler/impl/SAML2Consumer.cpp index 6af77e7..ad8fcdc 100644 --- a/shibsp/handler/impl/SAML2Consumer.cpp +++ b/shibsp/handler/impl/SAML2Consumer.cpp @@ -419,7 +419,9 @@ void SAML2Consumer::implementProtocol( policy.getIssuerMetadata(), samlconstants::SAML20P_NS, nullptr, + nullptr, ssoName, + ssoStatement, (authnContext && authnContext->getAuthnContextClassRef()) ? authnContext->getAuthnContextClassRef()->getReference() : nullptr, (authnContext && authnContext->getAuthnContextDeclRef()) ? authnContext->getAuthnContextDeclRef()->getReference() : nullptr, &tokens -- 2.1.4