From a0af8ed86ab481af4605daf5cf837fdaa4544bdd Mon Sep 17 00:00:00 2001 From: cantor Date: Wed, 3 Nov 2010 02:31:45 +0000 Subject: [PATCH] Tagging 2.4RC1 release. git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/tags/2.4RC1@3365 cb58f699-b61c-0410-a6fe-9272a202ed29 --- Shibboleth.sln | 10 +- apache/mod_apache.cpp | 155 +++++++++------- configs/metagen.sh | 48 +++-- configs/shibd-suse.in | 3 +- configure.ac | 197 +++++++++++++++++---- doc/NOTICE.txt | 2 + schemas/shibboleth-2.0-attribute-map.xsd | 11 ++ schemas/shibboleth-2.0-native-sp-config.xsd | 3 +- shibboleth.spec.in | 8 +- shibsp/AbstractSPRequest.cpp | 10 ++ shibsp/GSSRequest.h | 60 +++++++ shibsp/Makefile.am | 2 + shibsp/ServiceProvider.cpp | 35 ++-- shibsp/ServiceProvider.h | 5 + shibsp/attribute/Attribute.cpp | 8 + shibsp/attribute/AttributeDecoder.h | 3 + shibsp/attribute/Base64AttributeDecoder.cpp | 191 ++++++++++++++++++++ .../filtering/impl/XMLAttributeFilter.cpp | 7 +- .../resolver/impl/QueryAttributeResolver.cpp | 102 ++++++----- .../impl/SimpleAggregationAttributeResolver.cpp | 81 ++++++--- shibsp/handler/RemotedHandler.h | 13 +- shibsp/handler/impl/MetadataGenerator.cpp | 64 ++++--- shibsp/handler/impl/RemotedHandler.cpp | 119 ++++++++++++- shibsp/handler/impl/StatusHandler.cpp | 116 +++++++++++- shibsp/impl/StorageServiceSessionCache.cpp | 122 ++++++++----- shibsp/impl/XMLRequestMapper.cpp | 2 - shibsp/impl/XMLServiceProvider.cpp | 96 ++++++---- shibsp/shibsp-lite.vcxproj | 1 + shibsp/shibsp-lite.vcxproj.filters | 3 + shibsp/shibsp.vcxproj | 2 + shibsp/shibsp.vcxproj.filters | 6 + 31 files changed, 1154 insertions(+), 331 deletions(-) create mode 100644 shibsp/GSSRequest.h create mode 100644 shibsp/attribute/Base64AttributeDecoder.cpp diff --git a/Shibboleth.sln b/Shibboleth.sln index 112d8f2..76fd0bb 100644 --- a/Shibboleth.sln +++ b/Shibboleth.sln @@ -71,17 +71,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{2543BC EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{31B171C1-E06C-464F-A541-38724AB52D79}" ProjectSection(SolutionItems) = preProject - acinclude.m4 = acinclude.m4 - acx_pthread.m4 = acx_pthread.m4 - bootstrap = bootstrap + m4\acinclude.m4 = m4\acinclude.m4 + m4\acx_pthread.m4 = m4\acx_pthread.m4 config_win32.h = config_win32.h configure.ac = configure.ac - depend = depend doxygen.am = doxygen.am doxygen.cfg = doxygen.cfg - doxygen.m4 = doxygen.m4 - libtool.m4 = libtool.m4 - ltmain.sh = ltmain.sh + m4\doxygen.m4 = m4\doxygen.m4 Makefile.am = Makefile.am pkginfo.in = pkginfo.in Portfile.in = Portfile.in diff --git a/apache/mod_apache.cpp b/apache/mod_apache.cpp index e8b1765..0f0a770 100644 --- a/apache/mod_apache.cpp +++ b/apache/mod_apache.cpp @@ -31,14 +31,16 @@ # define _CRT_SECURE_NO_DEPRECATE 1 #endif +#include #include #include -#include +#include #include #include #include #include #include + #include #include #include @@ -95,7 +97,10 @@ namespace { string g_unsetHeaderValue,g_spoofKey; bool g_checkSpoofing = true; bool g_catchAll = false; - static const char* g_UserDataKey = "_shib_check_user_"; +#ifndef SHIB_APACHE_13 + char* g_szGSSContextKey = "mod_auth_gssapi:gss_ctx"; +#endif + static const char* g_UserDataKey = "urn:mace:shibboleth:Apache:shib_check_user"; } /* Apache 2.2.x headers must be accumulated and set in the output filter. @@ -294,10 +299,11 @@ extern "C" const char* shib_table_set(cmd_parms* parms, shib_dir_config* dc, con return nullptr; } -/********************************************************************************/ -// Apache ShibTarget subclass(es) here. class ShibTargetApache : public AbstractSPRequest +#if defined(HAVE_GSSAPI) && !defined(SHIB_APACHE_13) + , public GSSRequest +#endif { bool m_handler; mutable string m_body; @@ -574,6 +580,13 @@ public: } long returnDecline(void) { return DECLINED; } long returnOK(void) { return OK; } +#if defined(HAVE_GSSAPI) && !defined(SHIB_APACHE_13) + gss_ctx_id_t getGSSContext() const { + gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; + apr_pool_userdata_get((void**)&ctx, g_szGSSContextKey, m_req->pool); + return ctx; + } +#endif }; /********************************************************************************/ @@ -1028,17 +1041,18 @@ AccessControl::aclresult_t htAccessControl::authorized(const SPRequest& request, status = true; } else if (!strcmp(w,"user") && !remote_user.empty()) { - bool regexp=false,negate=false; + bool regexp = false; while (*t) { - w=ap_getword_conf(sta->m_req->pool,&t); - if (*w=='~') { - regexp=true; + w = ap_getword_conf(sta->m_req->pool,&t); + if (*w == '~') { + regexp = true; continue; } - else if (*w=='!') { - negate=true; - if (*(w+1)=='~') - regexp=true; + else if (*w == '!') { + // A negated rule presumes success unless a match is found. + status = true; + if (*(w+1) == '~') + regexp = true; continue; } @@ -1058,87 +1072,93 @@ AccessControl::aclresult_t htAccessControl::authorized(const SPRequest& request, string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get()); } } - else if (remote_user==w) { + else if (remote_user == w) { match = true; } if (match) { - // If we matched, then we're done with this rule either way and status is set to reflect the outcome. - status = !negate; + // If we matched, then we're done with this rule either way and we flip status to reflect the outcome. + status = !status; if (request.isPriorityEnabled(SPRequest::SPDebug)) request.log(SPRequest::SPDebug, - string("htaccess: require user ") + (negate ? "rejecting (" : "accepting (") + remote_user + ")"); + string("htaccess: require user ") + (!status ? "rejecting (" : "accepting (") + remote_user + ")"); break; } } } else if (!strcmp(w,"group") && !remote_user.empty()) { - SH_AP_TABLE* grpstatus=nullptr; + SH_AP_TABLE* grpstatus = nullptr; if (sta->m_dc->szAuthGrpFile) { if (request.isPriorityEnabled(SPRequest::SPDebug)) request.log(SPRequest::SPDebug,string("htaccess plugin using groups file: ") + sta->m_dc->szAuthGrpFile); - grpstatus=groups_for_user(sta->m_req,remote_user.c_str(),sta->m_dc->szAuthGrpFile); + grpstatus = groups_for_user(sta->m_req,remote_user.c_str(),sta->m_dc->szAuthGrpFile); } - bool negate=false; while (*t) { - w=ap_getword_conf(sta->m_req->pool,&t); - if (*w=='!') { - negate=true; + w = ap_getword_conf(sta->m_req->pool,&t); + if (*w == '!') { + // A negated rule presumes success unless a match is found. + status = true; continue; } if (grpstatus && ap_table_get(grpstatus,w)) { - // If we matched, then we're done with this rule either way and status is set to reflect the outcome. - status = !negate; - request.log(SPRequest::SPDebug, string("htaccess: require group ") + (negate ? "rejecting (" : "accepting (") + w + ")"); + // If we matched, then we're done with this rule either way and we flip status to reflect the outcome. + status = !status; + request.log(SPRequest::SPDebug, string("htaccess: require group ") + (!status ? "rejecting (" : "accepting (") + w + ")"); break; } } } else if (!strcmp(w,"authnContextClassRef") || !strcmp(w,"authnContextDeclRef")) { const char* ref = !strcmp(w,"authnContextClassRef") ? session->getAuthnContextClassRef() : session->getAuthnContextDeclRef(); - bool regexp=false,negate=false; - while (ref && *t) { - w=ap_getword_conf(sta->m_req->pool,&t); - if (*w=='~') { - regexp=true; - continue; - } - else if (*w=='!') { - negate=true; - if (*(w+1)=='~') + if (ref && *ref) { + bool regexp = false; + while (ref && *t) { + w = ap_getword_conf(sta->m_req->pool,&t); + if (*w == '~') { regexp=true; - continue; - } + continue; + } + else if (*w == '!') { + // A negated rule presumes success unless a match is found. + status = true; + if (*(w+1)=='~') + regexp = true; + continue; + } - // Figure out if there's a match. - bool match = false; - if (regexp) { - try { - // To do regex matching, we have to convert from UTF-8. - RegularExpression re(w); - match = re.matches(ref); + // Figure out if there's a match. + bool match = false; + if (regexp) { + try { + // To do regex matching, we have to convert from UTF-8. + RegularExpression re(w); + match = re.matches(ref); + } + catch (XMLException& ex) { + auto_ptr_char tmp(ex.getMessage()); + request.log(SPRequest::SPError, + string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get()); + } } - catch (XMLException& ex) { - auto_ptr_char tmp(ex.getMessage()); - request.log(SPRequest::SPError, - string("htaccess plugin caught exception while parsing regular expression (") + w + "): " + tmp.get()); + else if (!strcmp(w,ref)) { + match = true; } - } - else if (!strcmp(w,ref)) { - match = true; - } - if (match) { - // If we matched, then we're done with this rule either way and status is set to reflect the outcome. - status = !negate; - if (request.isPriorityEnabled(SPRequest::SPDebug)) - request.log(SPRequest::SPDebug, - string("htaccess: require authnContext ") + (negate ? "rejecting (" : "accepting (") + ref + ")"); - break; + if (match) { + // If we matched, then we're done with this rule either way and we flip status to reflect the outcome. + status = !status; + if (request.isPriorityEnabled(SPRequest::SPDebug)) + request.log(SPRequest::SPDebug, + string("htaccess: require authnContext ") + (!status ? "rejecting (" : "accepting (") + ref + ")"); + break; + } } } + else if (request.isPriorityEnabled(SPRequest::SPDebug)) { + request.log(SPRequest::SPDebug, "htaccess: require authnContext rejecting session with no context associated"); + } } else if (!session) { request.log(SPRequest::SPError, string("htaccess: require ") + w + " not given a valid session, are you using lazy sessions?"); @@ -1439,7 +1459,7 @@ static command_rec shire_cmds[] = { OR_AUTHCFG, TAKE1, "Set Shibboleth applicationId property for content"}, {"ShibBasicHijack", (config_fn_t)ap_set_flag_slot, (void *) XtOffsetOf (shib_dir_config, bBasicHijack), - OR_AUTHCFG, FLAG, "Respond to AuthType Basic and convert to shibboleth"}, + OR_AUTHCFG, FLAG, "(DEPRECATED) Respond to AuthType Basic and convert to shibboleth"}, {"ShibRequireSession", (config_fn_t)ap_set_flag_slot, (void *) XtOffsetOf (shib_dir_config, bRequireSession), OR_AUTHCFG, FLAG, "Initiates a new session if one does not exist"}, @@ -1501,6 +1521,8 @@ module MODULE_VAR_EXPORT mod_shib = { #elif defined(SHIB_APACHE_20) || defined(SHIB_APACHE_22) +//static const char * const authnPre[] = { "mod_gss.c", nullptr }; + extern "C" void shib_register_hooks (apr_pool_t *p) { #ifdef SHIB_DEFERRED_HEADERS @@ -1511,7 +1533,14 @@ extern "C" void shib_register_hooks (apr_pool_t *p) ap_hook_post_read_request(shib_post_read, nullptr, nullptr, APR_HOOK_MIDDLE); #endif ap_hook_child_init(shib_child_init, nullptr, nullptr, APR_HOOK_MIDDLE); - ap_hook_check_user_id(shib_check_user, nullptr, nullptr, APR_HOOK_MIDDLE); + const char* prereq = getenv("SHIBSP_APACHE_PREREQ"); + if (prereq && *prereq) { + const char* const authnPre[] = { prereq, nullptr }; + ap_hook_check_user_id(shib_check_user, authnPre, nullptr, APR_HOOK_MIDDLE); + } + else { + ap_hook_check_user_id(shib_check_user, nullptr, nullptr, APR_HOOK_MIDDLE); + } ap_hook_auth_checker(shib_auth_checker, nullptr, nullptr, APR_HOOK_FIRST); ap_hook_handler(shib_handler, nullptr, nullptr, APR_HOOK_LAST); ap_hook_fixups(shib_fixups, nullptr, nullptr, APR_HOOK_MIDDLE); @@ -1527,6 +1556,8 @@ static command_rec shib_cmds[] = { RSRC_CONF, "Path to shibboleth2.xml config file"), AP_INIT_TAKE1("ShibCatalogs", (config_fn_t)ap_set_global_string_slot, &g_szSchemaDir, RSRC_CONF, "Paths of XML schema catalogs"), + AP_INIT_TAKE1("ShibGSSKey", (config_fn_t)ap_set_global_string_slot, &g_szGSSContextKey, + RSRC_CONF, "Name of user data key containing GSS context established by GSS module"), AP_INIT_TAKE1("ShibURLScheme", (config_fn_t)shib_set_server_string_slot, (void *) offsetof (shib_server_config, szScheme), @@ -1543,7 +1574,7 @@ static command_rec shib_cmds[] = { OR_AUTHCFG, "Set Shibboleth applicationId property for content"), AP_INIT_FLAG("ShibBasicHijack", (config_fn_t)ap_set_flag_slot, (void *) offsetof (shib_dir_config, bBasicHijack), - OR_AUTHCFG, "Respond to AuthType Basic and convert to shibboleth"), + OR_AUTHCFG, "(DEPRECATED) Respond to AuthType Basic and convert to shibboleth"), AP_INIT_FLAG("ShibRequireSession", (config_fn_t)ap_set_flag_slot, (void *) offsetof (shib_dir_config, bRequireSession), OR_AUTHCFG, "Initiates a new session if one does not exist"), diff --git a/configs/metagen.sh b/configs/metagen.sh index 3c1efba..e460240 100755 --- a/configs/metagen.sh +++ b/configs/metagen.sh @@ -1,5 +1,7 @@ #! /bin/sh +DECLS=1 + SAML1=0 SAML2=0 ARTIFACT=0 @@ -21,7 +23,7 @@ SAML20PAOS="urn:oasis:names:tc:SAML:2.0:bindings:PAOS" SAML1POST="urn:oasis:names:tc:SAML:1.0:profiles:browser-post" SAML1ART="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01" -while getopts a:c:e:f:h:n:o:s:t:u:12ADLN c +while getopts a:c:e:f:h:n:o:s:t:u:12ADLNO c do case $c in c) CERTS[${#CERTS[*]}]=$OPTARG;; @@ -40,7 +42,8 @@ while getopts a:c:e:f:h:n:o:s:t:u:12ADLN c D) DS=1;; L) LOGOUT=1;; N) NAMEIDMGMT=1;; - \?) echo metagen [-12ADLN] -c cert1 [-c cert2 ...] -h host1 [-h host2 ...] [-e entityID] + O) DECLS=0;; + \?) echo metagen [-12ADLNO] -c cert1 [-c cert2 ...] -h host1 [-h host2 ...] [-e entityID] exit 1;; esac done @@ -63,7 +66,11 @@ do done if [ -z $ENTITYID ] ; then - ENTITYID=https://${HOSTS[0]}/shibboleth + if [ ${#HOSTS[*]} -eq 0 ] ; then + ENTITYID=https://${NAKEDHOSTS[0]}/shibboleth + else + ENTITYID=https://${HOSTS[0]}/shibboleth + fi fi # Establish protocols and bindings. @@ -88,9 +95,9 @@ if [ $LOGOUT -eq 1 -o $NAMEIDMGMT -eq 1 ] ; then fi if [ $SAML1 -eq 1 -a $SAML2 -eq 1 ] ; then - PROTENUM="$SAML20PROT $SAML11PROT $SAML10PROT" + PROTENUM="$SAML20PROT $SAML11PROT" elif [ $SAML1 -eq 1 ] ; then - PROTENUM="$SAML11PROT $SAML10PROT" + PROTENUM="$SAML11PROT" else PROTENUM="$SAML20PROT" fi @@ -122,8 +129,17 @@ if [ $SAML2 -eq 1 ] ; then ACSLOC[${#ACSLOC[*]}]="SAML2/ECP" fi +if [ $DECLS -eq 1 ] ; then + DECLS="xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" " + if [ $DS -eq 1 ] ; then + DECLS="${DECLS}xmlns:disco=\"urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol\" " + fi +else + DECLS="" +fi + cat < + EOF @@ -138,7 +154,7 @@ count=1 for h in ${HOSTS[@]} do cat << EOF - + EOF let "count++" done @@ -146,7 +162,7 @@ done for h in ${NAKEDHOSTS[@]} do cat << EOF - + EOF let "count++" done @@ -286,9 +302,10 @@ if [ -n "$ORGNAME" ] ; then EOF fi -for c in ${ADMIN[@]} +count=${#ADMIN[*]} +for (( i=0; i ${c[0]} @@ -298,9 +315,10 @@ do EOF done -for c in ${SUP[@]} +count=${#SUP[*]} +for (( i=0; i ${c[0]} @@ -310,9 +328,10 @@ do EOF done -for c in ${TECH[@]} +count=${#TECH[*]} +for (( i=0; i ${c[0]} @@ -324,4 +343,5 @@ done cat < + EOF diff --git a/configs/shibd-suse.in b/configs/shibd-suse.in index 0be3acf..d644455 100644 --- a/configs/shibd-suse.in +++ b/configs/shibd-suse.in @@ -14,8 +14,7 @@ # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: Shibboleth 2.x Service Provider Daemon -# Description: Starts the separate daemon used by the Shibboleth -# Apache module to manage state and SAML interactions. +# Description: Starts the separate daemon used by the Shibboleth Apache module to manage state and SAML interactions. ### END INIT INFO # diff --git a/configure.ac b/configure.ac index 95b3a47..50a6327 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,11 @@ -AC_PREREQ([2.67]) +AC_PREREQ([2.50]) AC_INIT([shibboleth],[2.4],[https://bugs.internet2.edu/],[shibboleth]) AC_CONFIG_SRCDIR(shibsp) AC_CONFIG_AUX_DIR(build-aux) AC_CONFIG_MACRO_DIR(m4) AM_INIT_AUTOMAKE -LT_INIT +AC_DISABLE_STATIC +AC_PROG_LIBTOOL # Docygen features DX_HTML_FEATURE(ON) @@ -35,7 +36,6 @@ AC_CONFIG_FILES([shibboleth.spec pkginfo Portfile]) AC_PROG_CC([gcc gcc3 cc]) AC_PROG_CXX([g++ g++3 c++ CC]) -AC_DISABLE_STATIC AC_CANONICAL_HOST if test "$GCC" = "yes" ; then @@ -72,6 +72,7 @@ AC_STRUCT_TM # Checks for library functions. AC_FUNC_STRFTIME AC_FUNC_STRERROR_R +AC_CHECK_HEADERS([sys/utsname.h]) AC_CHECK_FUNCS([strchr strdup strstr timegm gmtime_r strtok_r strcasecmp]) # checks for pthreads @@ -118,25 +119,21 @@ AC_CXX_REQUIRE_STL # Thank you Solaris, really. AC_MSG_CHECKING(for ctime_r) if test -z "$ac_cv_ctime_args"; then - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ - time_t clock; - char buf[26]; - ctime_r(&clock, buf); - ]])],[ac_cv_ctime_args=2],[]) - - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[ - time_t clock; - char buf[26]; - ctime_r(&clock, buf, 26); - ]])],[ac_cv_ctime_args=3],[]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include ]], [[time_t clock; char buf[26]; ctime_r(&clock, buf);]])], + [ac_cv_ctime_args=2],[]) + + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include ]], [[time_t clock; char buf[26]; ctime_r(&clock, buf, 26);]])], + [ac_cv_ctime_args=3],[]) fi if test -z "$ac_cv_ctime_args"; then AC_MSG_RESULT(no) else if test "$ac_cv_ctime_args" = 2; then - AC_DEFINE(HAVE_CTIME_R_2,1,[Define if ctime_r is present with 2 parameters.]) + AC_DEFINE([HAVE_CTIME_R_2],[1],[Define if ctime_r is present with 2 parameters.]) elif test "$ac_cv_ctime_args" = 3; then - AC_DEFINE(HAVE_CTIME_R_3,1,[Define if ctime_r is present with 3 parameters.]) + AC_DEFINE([HAVE_CTIME_R_3],[1],[Define if ctime_r is present with 3 parameters.]) fi AC_MSG_RESULT([yes, and it takes $ac_cv_ctime_args arguments]) fi @@ -222,7 +219,9 @@ AC_COMPILE_IFELSE( [AC_MSG_RESULT([no])]) AC_MSG_CHECKING([whether Xerces DOMNodeFilter API returns a short]) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[using namespace XERCES_CPP_NAMESPACE; +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include ]], + [[using namespace XERCES_CPP_NAMESPACE; class Blocker : public DOMNodeFilter { public: short acceptNode(const DOMNode* node) const { @@ -230,8 +229,9 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[using n } }; static Blocker g_Blocker; - ]])],[AC_MSG_RESULT([yes]) - AC_DEFINE([SHIBSP_XERCESC_SHORT_ACCEPTNODE],[1],[Define to 1 if Xerces DOMNodeFilter API returns a short.])],[AC_MSG_RESULT([no])]) + ]])], + [AC_MSG_RESULT([yes])AC_DEFINE([SHIBSP_XERCESC_SHORT_ACCEPTNODE],[1],[Define to 1 if Xerces DOMNodeFilter API returns a short.])], + [AC_MSG_RESULT([no])]) #XML-Tooling settings AC_ARG_WITH(xmltooling, @@ -336,16 +336,15 @@ save_LIBS="$LIBS" LIBS="$XMLSEC_LIBS $LIBS" AC_CHECK_HEADER([saml/saml2/metadata/Metadata.h],,AC_MSG_ERROR([unable to find OpenSAML header files])) -AC_LINK_IFELSE([AC_LANG_PROGRAM([[ -#include -#include - ]], [[ -#if _OPENSAML_VERSION >= 20400 +AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[#include +#include ]], +[[#if _OPENSAML_VERSION >= 20400 opensaml::SAMLConfig::getConfig(); #else #error Need OpenSAML version 2.4 or higher -#endif - ]])],[AC_DEFINE(HAVE_SAML,1,[Define if saml library was found])],[AC_MSG_ERROR([unable to link with OpenSAML, or version was too old]) +#endif]])], + ,[AC_MSG_ERROR([unable to link with OpenSAML, or version was too old]) ]) # restore master libs @@ -926,11 +925,13 @@ if test "$build_odbc" = "yes" ; then save_LIBS="$LIBS" LIBS="$LIBS $ODBC_LIBS" AC_MSG_CHECKING(if we can link againt ODBC) - AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include - #include - #include ]], [[SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, (void*)SQL_CP_ONE_PER_HENV, 0)]])],[have_odbc_libs=yes],[have_odbc_libs=no]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[#include + #include + #include ]], + [[SQLSetEnvAttr(SQL_NULL_HANDLE, SQL_ATTR_CONNECTION_POOLING, (void*)SQL_CP_ONE_PER_HENV, 0)]])], + [have_odbc_libs=yes],[have_odbc_libs=no]) LIBS="$save_LIBS" - if test "$have_odbc_libs" = no ; then if test "$odbc_enabled" = "yes" ; then AC_MSG_ERROR([unable to link with ODBC Library]) @@ -952,6 +953,142 @@ if test "$have_odbc_libs" = yes ; then AC_SUBST(ODBC_LIBS) fi +# GSS-API checking + +GSSAPI_ROOT="/usr" +AC_ARG_WITH(gssapi-includes, + AS_HELP_STRING([--with-gssapi-includes=DIR],[Specify location of GSSAPI header]), + [ GSSAPI_INCS="-I$withval" + want_gss="yes" ] +) + +AC_ARG_WITH(gssapi-libs, + AS_HELP_STRING([--with-gssapi-libs=DIR],[Specify location of GSSAPI libs]), + [ GSSAPI_LIB_DIR="-L$withval" + want_gss="yes" ] +) + +AC_ARG_WITH(gssapi, + AS_HELP_STRING([--with-gssapi=DIR],[Where to look for GSSAPI]), + [ GSSAPI_ROOT="$withval" + if test x"$GSSAPI_ROOT" != xno; then + want_gss="yes" + if test x"$GSSAPI_ROOT" = xyes; then + dnl if yes, then use default root + GSSAPI_ROOT="/usr" + fi + fi +]) + +save_CPPFLAGS="$CPPFLAGS" +AC_MSG_CHECKING([if GSSAPI support is requested]) +if test x"$want_gss" = xyes; then + AC_MSG_RESULT(yes) + + if test -z "$GSSAPI_INCS"; then + if test -f "$GSSAPI_ROOT/bin/krb5-config"; then + GSSAPI_INCS=`$GSSAPI_ROOT/bin/krb5-config --cflags gssapi` + elif test "$GSSAPI_ROOT" != "yes"; then + GSSAPI_INCS="-I$GSSAPI_ROOT/include" + fi + fi + + CPPFLAGS="$CPPFLAGS $GSSAPI_INCS" + + AC_CHECK_HEADER(gss.h, + [ + dnl found in the given dirs + AC_DEFINE([HAVE_GSSGNU],[1],[if you have the GNU gssapi libraries]) + gnu_gss=yes + ], + [ + dnl not found, check Heimdal or MIT + AC_CHECK_HEADERS([gssapi/gssapi.h], [], [not_mit=1]) + AC_CHECK_HEADERS( + [gssapi/gssapi_generic.h gssapi/gssapi_krb5.h], + [], + [not_mit=1], + [ +AC_INCLUDES_DEFAULT +#ifdef HAVE_GSSAPI_GSSAPI_H +#include +#endif + ]) + if test "x$not_mit" = "x1"; then + dnl MIT not found, check for Heimdal + AC_CHECK_HEADER([gssapi.h], + [ + dnl found + AC_DEFINE([HAVE_GSSHEIMDAL],[1],[if you have the Heimdal gssapi libraries]) + ], + [ + dnl no header found, disabling GSS + want_gss=no + AC_MSG_WARN([disabling GSSAPI since no header files was found]) + ] + ) + else + dnl MIT found + AC_DEFINE([HAVE_GSSMIT],[1],[if you have the MIT gssapi libraries]) + dnl check if we have a really old MIT kerberos (<= 1.2) + AC_MSG_CHECKING([if gssapi headers declare GSS_C_NT_HOSTBASED_SERVICE]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ +#include +#include +#include + ]],[[ + gss_import_name( + (OM_uint32 *)0, + (gss_buffer_t)0, + GSS_C_NT_HOSTBASED_SERVICE, + (gss_name_t *)0); + ]]) + ],[ + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + AC_DEFINE([HAVE_OLD_GSSMIT],[1],[if you have an old MIT gssapi library, lacking GSS_C_NT_HOSTBASED_SERVICE]) + ]) + fi + ] + ) +else + AC_MSG_RESULT(no) +fi +if test x"$want_gss" = xyes; then + AC_DEFINE([HAVE_GSSAPI],[1],[if you have the gssapi libraries]) + + if test -n "$gnu_gss"; then + LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR" + LIBS="$LIBS -lgss" + elif test -z "$GSSAPI_LIB_DIR"; then + case $host in + *-*-darwin*) + LIBS="$LIBS -lgssapi_krb5 -lresolv" + ;; + *) + if test -f "$GSSAPI_ROOT/bin/krb5-config"; then + dnl krb5-config doesn't have --libs-only-L or similar, put everything + dnl into LIBS + gss_libs=`$GSSAPI_ROOT/bin/krb5-config --libs gssapi` + LIBS="$LIBS $gss_libs" + elif test "$GSSAPI_ROOT" != "yes"; then + LDFLAGS="$LDFLAGS -L$GSSAPI_ROOT/lib$libsuff" + LIBS="$LIBS -lgssapi" + else + LIBS="$LIBS -lgssapi" + fi + ;; + esac + else + LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR" + LIBS="$LIBS -lgssapi" + fi +else + CPPFLAGS="$save_CPPFLAGS" +fi + AC_SUBST(WANT_SUBDIRS) diff --git a/doc/NOTICE.txt b/doc/NOTICE.txt index 8fe5fa7..1ff0bd2 100644 --- a/doc/NOTICE.txt +++ b/doc/NOTICE.txt @@ -12,3 +12,5 @@ Source code for these libraries is available on request. This project includes software developed by the National Research Council of Canada. + +This project includes software developed by the Danish CLARIN Consortium. diff --git a/schemas/shibboleth-2.0-attribute-map.xsd b/schemas/shibboleth-2.0-attribute-map.xsd index bf95425..d119da3 100644 --- a/schemas/shibboleth-2.0-attribute-map.xsd +++ b/schemas/shibboleth-2.0-attribute-map.xsd @@ -281,4 +281,15 @@ + + + + Decoder for attributes with base64-encoded string values. + + + + + + + diff --git a/schemas/shibboleth-2.0-native-sp-config.xsd b/schemas/shibboleth-2.0-native-sp-config.xsd index 87eeab5..7c39b8e 100644 --- a/schemas/shibboleth-2.0-native-sp-config.xsd +++ b/schemas/shibboleth-2.0-native-sp-config.xsd @@ -2,7 +2,7 @@ + diff --git a/shibboleth.spec.in b/shibboleth.spec.in index d510452..5888fbb 100644 --- a/shibboleth.spec.in +++ b/shibboleth.spec.in @@ -2,14 +2,14 @@ Name: @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ Release: 1 Summary: Open source system for attribute-based Web SSO -Group: System Environment/Libraries +Group: Productivity/Networking/Security Vendor: Internet2 License: Apache 2.0 URL: http://shibboleth.internet2.edu/ Source: %{name}-sp-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-root PreReq: openssl, xmltooling-schemas, opensaml-schemas -%if 0%{?suse_version} > 1030 +%if 0%{?suse_version} > 1030 && 0%{?suse_version} < 1130 PreReq: %{insserv_prereq} BuildRequires: libXerces-c-devel >= 2.8.0 %else @@ -48,9 +48,9 @@ and Apache module(s). %package devel Summary: Shibboleth development Headers -Group: Development/Libraries +Group: Development/Libraries/C and C++ Requires: %{name} = %{version}-%{release} -%if 0%{?suse_version} > 1030 +%if 0%{?suse_version} > 1030 && 0%{?suse_version} < 1130 Requires: libXerces-c-devel >= 2.8.0 %else Requires: libxerces-c-devel >= 2.8.0 diff --git a/shibsp/AbstractSPRequest.cpp b/shibsp/AbstractSPRequest.cpp index b6c5e80..6f9b005 100644 --- a/shibsp/AbstractSPRequest.cpp +++ b/shibsp/AbstractSPRequest.cpp @@ -50,6 +50,16 @@ void SPRequest::setAuthType(const char* authtype) { } +#ifdef HAVE_GSSAPI +GSSRequest::GSSRequest() +{ +} + +GSSRequest::~GSSRequest() +{ +} +#endif + AbstractSPRequest::AbstractSPRequest(const char* category) : m_sp(nullptr), m_mapper(nullptr), m_app(nullptr), m_sessionTried(false), m_session(nullptr), m_log(&Category::getInstance(category)), m_parser(nullptr) diff --git a/shibsp/GSSRequest.h b/shibsp/GSSRequest.h new file mode 100644 index 0000000..cb1c56f --- /dev/null +++ b/shibsp/GSSRequest.h @@ -0,0 +1,60 @@ +/* + * Copyright 2010 Internet2 + * + * 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 + * + * 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. + */ + +/** + * @file shibsp/GSSRequest.h + * + * Interface to a GSS-authenticated request. + */ + +#if !defined(__shibsp_gssreq_h__) && defined(HAVE_GSSAPI) +#define __shibsp_gssreq_h__ + +#include +#include + +#ifdef HAVE_GSSGNU +# include +#elif defined HAVE_GSSMIT +# include +# include +#else +# include +#endif + +namespace shibsp { + + /** + * Interface to a GSS-authenticated request. + */ + class SHIBSP_API GSSRequest : public virtual xmltooling::GenericRequest + { + protected: + GSSRequest(); + public: + virtual ~GSSRequest(); + + /** + * Returns the GSS-API context established for this request, or + * GSS_C_NO_CONTEXT if none is available. + * + * @return a GSS-API context handle, or GSS_C_NO_CONTEXT + */ + virtual gss_ctx_id_t getGSSContext() const=0; + }; +}; + +#endif /* __shibsp_gssreq_h__ */ diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am index 55072f9..1a9c00e 100644 --- a/shibsp/Makefile.am +++ b/shibsp/Makefile.am @@ -31,6 +31,7 @@ libshibspinclude_HEADERS = \ base.h \ exceptions.h \ paths.h \ + GSSRequest.h \ RequestMapper.h \ ServiceProvider.h \ SessionCache.h \ @@ -164,6 +165,7 @@ libshibsp_lite_la_SOURCES = \ libshibsp_la_SOURCES = \ ${common_sources} \ + attribute/Base64AttributeDecoder.cpp \ attribute/DOMAttributeDecoder.cpp \ attribute/KeyInfoAttributeDecoder.cpp \ attribute/NameIDAttributeDecoder.cpp \ diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp index 30e4423..b358948 100644 --- a/shibsp/ServiceProvider.cpp +++ b/shibsp/ServiceProvider.cpp @@ -163,6 +163,7 @@ void SHIBSP_API shibsp::registerServiceProviders() ServiceProvider::ServiceProvider() { + m_authTypes.insert("shibboleth"); } ServiceProvider::~ServiceProvider() @@ -266,16 +267,18 @@ pair ServiceProvider::doAuthentication(SPRequest& request, bool handl pair requireSession = settings.first->getBool("requireSession"); pair requireSessionWith = settings.first->getString("requireSessionWith"); - // If no session is required AND the AuthType (an Apache-derived concept) isn't shibboleth, + string lcAuthType; + if (authType.first) { + while (*authType.second) + lcAuthType += tolower(*authType.second++); + } + + // If no session is required AND the AuthType (an Apache-derived concept) isn't recognized, // then we ignore this request and consider it unprotected. Apache might lie to us if // ShibBasicHijack is on, but that's up to it. if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first && -#ifdef HAVE_STRCASECMP - (!authType.first || strcasecmp(authType.second,"shibboleth"))) -#else - (!authType.first || _stricmp(authType.second,"shibboleth"))) -#endif - return make_pair(true,request.returnDecline()); + (!authType.first || m_authTypes.find(lcAuthType) == m_authTypes.end())) + return make_pair(true, request.returnDecline()); // Fix for secadv 20050901 clearHeaders(request); @@ -315,7 +318,7 @@ pair ServiceProvider::doAuthentication(SPRequest& request, bool handl return initiator->run(request,false); } - request.setAuthType("shibboleth"); + request.setAuthType(lcAuthType.c_str()); // We're done. Everything is okay. Nothing to report. Nothing to do.. // Let the caller decide how to proceed. @@ -349,16 +352,18 @@ pair ServiceProvider::doAuthorization(SPRequest& request) const pair requireSession = settings.first->getBool("requireSession"); pair requireSessionWith = settings.first->getString("requireSessionWith"); - // If no session is required AND the AuthType (an Apache-derived concept) isn't shibboleth, + string lcAuthType; + if (authType.first) { + while (*authType.second) + lcAuthType += tolower(*authType.second++); + } + + // If no session is required AND the AuthType (an Apache-derived concept) isn't recognized, // then we ignore this request and consider it unprotected. Apache might lie to us if // ShibBasicHijack is on, but that's up to it. if ((!requireSession.first || !requireSession.second) && !requireSessionWith.first && -#ifdef HAVE_STRCASECMP - (!authType.first || strcasecmp(authType.second,"shibboleth"))) -#else - (!authType.first || _stricmp(authType.second,"shibboleth"))) -#endif - return make_pair(true,request.returnDecline()); + (!authType.first || m_authTypes.find(lcAuthType) == m_authTypes.end())) + return make_pair(true, request.returnDecline()); // Do we have an access control plugin? if (settings.second) { diff --git a/shibsp/ServiceProvider.h b/shibsp/ServiceProvider.h index 2486967..bd76b8c 100644 --- a/shibsp/ServiceProvider.h +++ b/shibsp/ServiceProvider.h @@ -25,6 +25,7 @@ #include +#include #include namespace xmltooling { @@ -239,6 +240,10 @@ namespace shibsp { */ virtual Remoted* lookupListener(const char* address) const; + protected: + /** The AuthTypes to "recognize" (defaults to "shibboleth"). */ + std::set m_authTypes; + private: std::map m_listenerMap; }; diff --git a/shibsp/attribute/Attribute.cpp b/shibsp/attribute/Attribute.cpp index 6a7da4b..89f69bf 100644 --- a/shibsp/attribute/Attribute.cpp +++ b/shibsp/attribute/Attribute.cpp @@ -56,6 +56,7 @@ namespace shibsp { SHIBSP_DLLLOCAL PluginManager::Factory KeyInfoAttributeDecoderFactory; SHIBSP_DLLLOCAL PluginManager::Factory DOMAttributeDecoderFactory; SHIBSP_DLLLOCAL PluginManager::Factory XMLAttributeDecoderFactory; + SHIBSP_DLLLOCAL PluginManager::Factory Base64AttributeDecoderFactory; static const XMLCh _StringAttributeDecoder[] = UNICODE_LITERAL_22(S,t,r,i,n,g,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); static const XMLCh _ScopedAttributeDecoder[] = UNICODE_LITERAL_22(S,c,o,p,e,d,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); @@ -64,6 +65,11 @@ namespace shibsp { static const XMLCh _KeyInfoAttributeDecoder[] =UNICODE_LITERAL_23(K,e,y,I,n,f,o,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); static const XMLCh _DOMAttributeDecoder[] = UNICODE_LITERAL_19(D,O,M,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); static const XMLCh _XMLAttributeDecoder[] = UNICODE_LITERAL_19(X,M,L,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); + static const XMLCh _Base64AttributeDecoder[] = { + chLatin_B, chLatin_a, chLatin_s, chLatin_e, chDigit_6, chDigit_4, + chLatin_A, chLatin_t, chLatin_t, chLatin_r, chLatin_i, chLatin_b, chLatin_u, chLatin_t, chLatin_e, + chLatin_D, chLatin_e, chLatin_c, chLatin_o, chLatin_d, chLatin_e, chLatin_r, chNull + }; static const XMLCh caseSensitive[] = UNICODE_LITERAL_13(c,a,s,e,S,e,n,s,i,t,i,v,e); static const XMLCh hashAlg[] = UNICODE_LITERAL_7(h,a,s,h,A,l,g); @@ -79,6 +85,7 @@ xmltooling::QName shibsp::NameIDFromScopedAttributeDecoderType(shibspconstants:: xmltooling::QName shibsp::KeyInfoAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _KeyInfoAttributeDecoder); xmltooling::QName shibsp::DOMAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _DOMAttributeDecoder); xmltooling::QName shibsp::XMLAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _XMLAttributeDecoder); +xmltooling::QName shibsp::Base64AttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _Base64AttributeDecoder); void shibsp::registerAttributeDecoders() { @@ -90,6 +97,7 @@ void shibsp::registerAttributeDecoders() conf.AttributeDecoderManager.registerFactory(KeyInfoAttributeDecoderType, KeyInfoAttributeDecoderFactory); conf.AttributeDecoderManager.registerFactory(DOMAttributeDecoderType, DOMAttributeDecoderFactory); conf.AttributeDecoderManager.registerFactory(XMLAttributeDecoderType, XMLAttributeDecoderFactory); + conf.AttributeDecoderManager.registerFactory(Base64AttributeDecoderType, Base64AttributeDecoderFactory); } AttributeDecoder::AttributeDecoder(const DOMElement *e) diff --git a/shibsp/attribute/AttributeDecoder.h b/shibsp/attribute/AttributeDecoder.h index 24054f2..03b7485 100644 --- a/shibsp/attribute/AttributeDecoder.h +++ b/shibsp/attribute/AttributeDecoder.h @@ -110,6 +110,9 @@ namespace shibsp { /** Decodes arbitrary XML into an XMLAttribute. */ extern SHIBSP_API xmltooling::QName XMLAttributeDecoderType; + /** Decodes base64-encoded data into a SimpleAttribute. */ + extern SHIBSP_API xmltooling::QName Base64AttributeDecoderType; + /** Registers built-in AttributeDecoders into the runtime. */ void registerAttributeDecoders(); }; diff --git a/shibsp/attribute/Base64AttributeDecoder.cpp b/shibsp/attribute/Base64AttributeDecoder.cpp new file mode 100644 index 0000000..6f1edd7 --- /dev/null +++ b/shibsp/attribute/Base64AttributeDecoder.cpp @@ -0,0 +1,191 @@ +/* + * Copyright 2010 The Danish CLARIN Consortium + * + * 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 + * + * 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. + */ + +/** + * Base64AttributeDecoder.cpp + * + * Decodes SAML containing base64-encoded values into SimpleAttributes. + */ + +#include "internal.h" +#include "attribute/AttributeDecoder.h" +#include "attribute/SimpleAttribute.h" + +#include +#include + +#include + +using namespace shibsp; +using namespace opensaml::saml1; +using namespace opensaml::saml2; +using namespace xmltooling::logging; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + class SHIBSP_DLLLOCAL Base64AttributeDecoder : virtual public AttributeDecoder + { + public: + Base64AttributeDecoder(const DOMElement* e) : AttributeDecoder(e) {} + ~Base64AttributeDecoder() {} + + shibsp::Attribute* decode( + const vector& ids, const XMLObject* xmlObject, const char* assertingParty=nullptr, const char* relyingParty=nullptr + ) const; + }; + + AttributeDecoder* SHIBSP_DLLLOCAL Base64AttributeDecoderFactory(const DOMElement* const & e) + { + return new Base64AttributeDecoder(e); + } +}; + +shibsp::Attribute* Base64AttributeDecoder::decode( + const vector& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty + ) const +{ + auto_ptr simple(new SimpleAttribute(ids)); + vector& dest = simple->getValues(); + vector::const_iterator v,stop; + + Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder.Base64"); + + if (xmlObject && XMLString::equals(opensaml::saml1::Attribute::LOCAL_NAME,xmlObject->getElementQName().getLocalPart())) { + const opensaml::saml2::Attribute* saml2attr = dynamic_cast(xmlObject); + if (saml2attr) { + const vector& values = saml2attr->getAttributeValues(); + v = values.begin(); + stop = values.end(); + if (log.isDebugEnabled()) { + auto_ptr_char n(saml2attr->getName()); + log.debug( + "decoding SimpleAttribute (%s) from SAML 2 Attribute (%s) with %lu base64-encoded value(s)", + ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size() + ); + } + } + else { + const opensaml::saml1::Attribute* saml1attr = dynamic_cast(xmlObject); + if (saml1attr) { + const vector& values = saml1attr->getAttributeValues(); + v = values.begin(); + stop = values.end(); + if (log.isDebugEnabled()) { + auto_ptr_char n(saml1attr->getAttributeName()); + log.debug( + "decoding SimpleAttribute (%s) from SAML 1 Attribute (%s) with %lu base64-encoded value(s)", + ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size() + ); + } + } + else { + log.warn("XMLObject type not recognized by Base64AttributeDecoder, no values returned"); + return nullptr; + } + } + + for (; v!=stop; ++v) { + if (!(*v)->hasChildren()) { + auto_ptr_char val((*v)->getTextContent()); + if (val.get() && *val.get()) { + xsecsize_t x; + XMLByte* decoded=Base64::decode(reinterpret_cast(val.get()),&x); + if (decoded) { + dest.push_back(reinterpret_cast(decoded)); +#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&decoded); +#else + XMLString::release((char**)&decoded); +#endif + } + else { + log.warn("skipping AttributeValue, unable to base64-decode"); + } + } + else + log.warn("skipping empty AttributeValue"); + } + else { + log.warn("skipping complex AttributeValue"); + } + } + + return dest.empty() ? nullptr : _decode(simple.release()); + } + + const NameID* saml2name = dynamic_cast(xmlObject); + if (saml2name) { + if (log.isDebugEnabled()) { + auto_ptr_char f(saml2name->getFormat()); + log.debug("decoding SimpleAttribute (%s) from SAML 2 NameID with Format (%s)", ids.front().c_str(), f.get() ? f.get() : "unspecified"); + } + auto_ptr_char val(saml2name->getName()); + if (val.get() && *val.get()) { + xsecsize_t x; + XMLByte* decoded=Base64::decode(reinterpret_cast(val.get()),&x); + if (decoded) { + dest.push_back(reinterpret_cast(decoded)); +#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&decoded); +#else + XMLString::release((char**)&decoded); +#endif + } + else { + log.warn("ignoring NameID, unable to base64-decode"); + } + } + else + log.warn("ignoring empty NameID"); + } + else { + const NameIdentifier* saml1name = dynamic_cast(xmlObject); + if (saml1name) { + if (log.isDebugEnabled()) { + auto_ptr_char f(saml1name->getFormat()); + log.debug( + "decoding SimpleAttribute (%s) from SAML 1 NameIdentifier with Format (%s)", + ids.front().c_str(), f.get() ? f.get() : "unspecified" + ); + } + auto_ptr_char val(saml1name->getName()); + if (val.get() && *val.get()) { + xsecsize_t x; + XMLByte* decoded=Base64::decode(reinterpret_cast(val.get()),&x); + if (decoded) { + dest.push_back(reinterpret_cast(decoded)); + #ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&decoded); + #else + XMLString::release((char**)&decoded); + #endif + } + else { + log.warn("ignoring NameIdentifier, unable to base64-decode"); + } + } + else + log.warn("ignoring empty NameIdentifier"); + } + else { + log.warn("XMLObject type not recognized by Base64AttributeDecoder, no values returned"); + return nullptr; + } + } + + return dest.empty() ? nullptr : _decode(simple.release()); +} diff --git a/shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp b/shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp index 18bda2f..639d7e9 100644 --- a/shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp +++ b/shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp @@ -379,7 +379,7 @@ void XMLFilterImpl::filterAttributes(const FilteringContext& context, vectorgetId(), issuer.get() ? issuer.get() : "unknown source" ); deletedAttributes[a] = true; @@ -426,6 +426,10 @@ void XMLFilterImpl::filterAttributes(const FilteringContext& context, vectorgetId(), issuer.get() ? issuer.get() : "unknown source" + ); delete attr; deletedAttributes.erase(deletedAttributes.begin() + a); attributes.erase(attributes.begin() + a); @@ -449,6 +453,7 @@ void XMLFilterImpl::filterAttributes(const FilteringContext& context, vectorgetId(), issuer.get() ? issuer.get() : "unknown source" ); delete attr; + deletedAttributes.erase(deletedAttributes.begin() + a); attributes.erase(attributes.begin() + a); continue; } diff --git a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp index 0c6a453..85a1d88 100644 --- a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp +++ b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp @@ -24,7 +24,7 @@ #include "Application.h" #include "ServiceProvider.h" #include "SessionCache.h" -#include "attribute/Attribute.h" +#include "attribute/SimpleAttribute.h" #include "attribute/filtering/AttributeFilter.h" #include "attribute/filtering/BasicFilteringContext.h" #include "attribute/resolver/AttributeExtractor.h" @@ -45,7 +45,9 @@ #include #include #include +#include #include +#include #include #include @@ -201,14 +203,15 @@ namespace shibsp { } private: - bool SAML1Query(QueryContext& ctx) const; - bool SAML2Query(QueryContext& ctx) const; + void SAML1Query(QueryContext& ctx) const; + void SAML2Query(QueryContext& ctx) const; Category& m_log; string m_policyId; bool m_subjectMatch; vector m_SAML1Designators; vector m_SAML2Designators; + vector m_exceptionId; }; AttributeResolver* SHIBSP_DLLLOCAL QueryResolverFactory(const DOMElement* const & e) @@ -216,6 +219,7 @@ namespace shibsp { return new QueryResolver(e); } + static const XMLCh exceptionId[] = UNICODE_LITERAL_11(e,x,c,e,p,t,i,o,n,I,d); static const XMLCh policyId[] = UNICODE_LITERAL_8(p,o,l,i,c,y,I,d); static const XMLCh subjectMatch[] = UNICODE_LITERAL_12(s,u,b,j,e,c,t,M,a,t,c,h); }; @@ -254,9 +258,13 @@ QueryResolver::QueryResolver(const DOMElement* e) } child = XMLHelper::getNextSiblingElement(child); } + + string exid(XMLHelper::getAttrString(e, nullptr, exceptionId)); + if (!exid.empty()) + m_exceptionId.push_back(exid); } -bool QueryResolver::SAML1Query(QueryContext& ctx) const +void QueryResolver::SAML1Query(QueryContext& ctx) const { #ifdef _DEBUG xmltooling::NDC ndc("query"); @@ -267,7 +275,7 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const find_if(ctx.getEntityDescriptor()->getAttributeAuthorityDescriptors(), isValidForProtocol(ctx.getProtocol())); if (!AA) { m_log.warn("no SAML 1.%d AttributeAuthority role found in metadata", version); - return false; + return; } const Application& application = ctx.getApplication(); @@ -319,23 +327,24 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const if (!response) { m_log.error("unable to obtain a SAML response from attribute authority"); - return false; + throw BindingException("Unable to obtain a SAML response from attribute authority."); } else if (!response->getStatus() || !response->getStatus()->getStatusCode() || response->getStatus()->getStatusCode()->getValue()==nullptr || *(response->getStatus()->getStatusCode()->getValue()) != saml1p::StatusCode::SUCCESS) { delete response; m_log.error("attribute authority returned a SAML error"); - return true; + throw FatalProfileException("Attribute authority returned a SAML error."); } const vector& assertions = const_cast(response)->getAssertions(); if (assertions.empty()) { delete response; m_log.warn("response from attribute authority was empty"); - return true; + return; } - else if (assertions.size()>1) + else if (assertions.size()>1) { m_log.warn("simple resolver only supports one assertion in the query response"); + } auto_ptr wrapper(response); saml1::Assertion* newtoken = assertions.front(); @@ -343,7 +352,7 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const pair signedAssertions = relyingParty->getBool("requireSignedAssertions"); if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) { m_log.error("assertion unsigned, rejecting it based on signedAssertions policy"); - return true; + throw SecurityPolicyException("Rejected unsigned assertion based on local policy."); } try { @@ -361,7 +370,7 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const } catch (exception& ex) { m_log.error("assertion failed policy validation: %s", ex.what()); - return true; + throw; } newtoken->detach(); @@ -404,12 +413,11 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const m_log.error("caught exception extracting/filtering attributes from query result: %s", ex.what()); for_each(ctx.getResolvedAttributes().begin(), ctx.getResolvedAttributes().end(), xmltooling::cleanup()); ctx.getResolvedAttributes().clear(); + throw; } - - return true; } -bool QueryResolver::SAML2Query(QueryContext& ctx) const +void QueryResolver::SAML2Query(QueryContext& ctx) const { #ifdef _DEBUG xmltooling::NDC ndc("query"); @@ -419,7 +427,7 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const find_if(ctx.getEntityDescriptor()->getAttributeAuthorityDescriptors(), isValidForProtocol(samlconstants::SAML20P_NS)); if (!AA) { m_log.warn("no SAML 2 AttributeAuthority role found in metadata"); - return false; + return; } const Application& application = ctx.getApplication(); @@ -484,7 +492,7 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const if (!srt) { m_log.error("unable to obtain a SAML response from attribute authority"); - return false; + throw BindingException("Unable to obtain a SAML response from attribute authority."); } auto_ptr wrapper(srt); @@ -492,12 +500,12 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const saml2p::Response* response = dynamic_cast(srt); if (!response) { m_log.error("message was not a samlp:Response"); - return true; + throw FatalProfileException("Attribute authority returned an unrecognized message."); } else if (!response->getStatus() || !response->getStatus()->getStatusCode() || !XMLString::equals(response->getStatus()->getStatusCode()->getValue(), saml2p::StatusCode::SUCCESS)) { m_log.error("attribute authority returned a SAML error"); - return true; + throw FatalProfileException("Attribute authority returned a SAML error."); } saml2::Assertion* newtoken = nullptr; @@ -507,7 +515,7 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const const vector& encassertions = const_cast(response)->getEncryptedAssertions(); if (encassertions.empty()) { m_log.warn("response from attribute authority was empty"); - return true; + return; } else if (encassertions.size() > 1) { m_log.warn("simple resolver only supports one assertion in the query response"); @@ -516,7 +524,7 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const CredentialResolver* cr=application.getCredentialResolver(); if (!cr) { m_log.warn("found encrypted assertion, but no CredentialResolver was available"); - return true; + throw FatalProfileException("Assertion was encrypted, but no decryption credentials are available."); } // Attempt to decrypt it. @@ -528,18 +536,13 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const tokenwrapper.release(); if (m_log.isDebugEnabled()) m_log.debugStream() << "decrypted Assertion: " << *newtoken << logging::eol; + // Free the Response now, so we know this is a stand-alone token later. + delete wrapper.release(); } } catch (exception& ex) { m_log.error(ex.what()); - } - if (newtoken) { - // Free the Response now, so we know this is a stand-alone token later. - delete wrapper.release(); - } - else { - // Nothing decrypted, should already be logged. - return true; + throw; } } else { @@ -552,7 +555,7 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const m_log.error("assertion unsigned, rejecting it based on signedAssertions policy"); if (!wrapper.get()) delete newtoken; - return true; + throw SecurityPolicyException("Rejected unsigned assertion based on local policy."); } try { @@ -606,7 +609,7 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const m_log.warn("ignoring Assertion without NameID in Subject"); if (!wrapper.get()) delete newtoken; - return true; + return; } } } @@ -614,7 +617,7 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const m_log.error("assertion failed policy validation: %s", ex.what()); if (!wrapper.get()) delete newtoken; - return true; + throw; } if (wrapper.get()) { @@ -642,9 +645,8 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const m_log.error("caught exception extracting/filtering attributes from query result: %s", ex.what()); for_each(ctx.getResolvedAttributes().begin(), ctx.getResolvedAttributes().end(), xmltooling::cleanup()); ctx.getResolvedAttributes().clear(); + throw; } - - return true; } void QueryResolver::resolveAttributes(ResolutionContext& ctx) const @@ -659,21 +661,31 @@ void QueryResolver::resolveAttributes(ResolutionContext& ctx) const return; } - if (qctx.getNameID() && qctx.getEntityDescriptor()) { - if (XMLString::equals(qctx.getProtocol(), samlconstants::SAML20P_NS)) { - m_log.debug("attempting SAML 2.0 attribute query"); - SAML2Query(qctx); - } - else if (XMLString::equals(qctx.getProtocol(), samlconstants::SAML11_PROTOCOL_ENUM) || - XMLString::equals(qctx.getProtocol(), samlconstants::SAML10_PROTOCOL_ENUM)) { - m_log.debug("attempting SAML 1.x attribute query"); - SAML1Query(qctx); + try { + if (qctx.getNameID() && qctx.getEntityDescriptor()) { + if (XMLString::equals(qctx.getProtocol(), samlconstants::SAML20P_NS)) { + m_log.debug("attempting SAML 2.0 attribute query"); + SAML2Query(qctx); + } + else if (XMLString::equals(qctx.getProtocol(), samlconstants::SAML11_PROTOCOL_ENUM) || + XMLString::equals(qctx.getProtocol(), samlconstants::SAML10_PROTOCOL_ENUM)) { + m_log.debug("attempting SAML 1.x attribute query"); + SAML1Query(qctx); + } + else { + m_log.info("SSO protocol does not allow for attribute query"); + } } else { - m_log.info("SSO protocol does not allow for attribute query"); + m_log.warn("can't attempt attribute query, either no NameID or no metadata to use"); } } - else { - m_log.warn("can't attempt attribute query, either no NameID or no metadata to use"); + catch (exception& ex) { + // Already logged. + if (!m_exceptionId.empty()) { + SimpleAttribute* attr = new SimpleAttribute(m_exceptionId); + attr->getValues().push_back(XMLToolingConfig::getConfig().getURLEncoder()->encode(ex.what())); + qctx.getResolvedAttributes().push_back(attr); + } } } diff --git a/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp b/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp index 2d27a30..573e6ec 100644 --- a/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp +++ b/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp @@ -26,6 +26,7 @@ #include "ServiceProvider.h" #include "SessionCache.h" #include "attribute/NameIDAttribute.h" +#include "attribute/SimpleAttribute.h" #include "attribute/filtering/AttributeFilter.h" #include "attribute/filtering/BasicFilteringContext.h" #include "attribute/resolver/AttributeExtractor.h" @@ -47,6 +48,7 @@ #include #include #include +#include #include #include @@ -185,7 +187,7 @@ namespace shibsp { } private: - bool doQuery(SimpleAggregationContext& ctx, const char* entityID, const NameID* name) const; + void doQuery(SimpleAggregationContext& ctx, const char* entityID, const NameID* name) const; Category& m_log; string m_policyId; @@ -196,6 +198,7 @@ namespace shibsp { TrustEngine* m_trust; vector m_designators; vector< pair > m_sources; + vector m_exceptionId; }; AttributeResolver* SHIBSP_DLLLOCAL SimpleAggregationResolverFactory(const DOMElement* const & e) @@ -206,6 +209,7 @@ namespace shibsp { static const XMLCh attributeId[] = UNICODE_LITERAL_11(a,t,t,r,i,b,u,t,e,I,d); static const XMLCh Entity[] = UNICODE_LITERAL_6(E,n,t,i,t,y); static const XMLCh EntityReference[] = UNICODE_LITERAL_15(E,n,t,i,t,y,R,e,f,e,r,e,n,c,e); + static const XMLCh exceptionId[] = UNICODE_LITERAL_11(e,x,c,e,p,t,i,o,n,I,d); static const XMLCh format[] = UNICODE_LITERAL_6(f,o,r,m,a,t); static const XMLCh _MetadataProvider[] = UNICODE_LITERAL_16(M,e,t,a,d,a,t,a,P,r,o,v,i,d,e,r); static const XMLCh policyId[] = UNICODE_LITERAL_8(p,o,l,i,c,y,I,d); @@ -247,6 +251,10 @@ SimpleAggregationResolver::SimpleAggregationResolver(const DOMElement* e) m_format = aid; } + string exid(XMLHelper::getAttrString(e, nullptr, exceptionId)); + if (!exid.empty()) + m_exceptionId.push_back(exid); + DOMElement* child = XMLHelper::getFirstChildElement(e, _MetadataProvider); if (child) { string t(XMLHelper::getAttrString(child, nullptr, _type)); @@ -306,7 +314,7 @@ SimpleAggregationResolver::SimpleAggregationResolver(const DOMElement* e) } } -bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const char* entityID, const NameID* name) const +void SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const char* entityID, const NameID* name) const { #ifdef _DEBUG xmltooling::NDC ndc("doQuery"); @@ -319,11 +327,11 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha (m_metadata ? m_metadata : application.getMetadataProvider())->getEntityDescriptor(mc); if (!mdresult.first) { m_log.warn("unable to locate metadata for provider (%s)", entityID); - return false; + return; } else if (!(AA=dynamic_cast(mdresult.second))) { m_log.warn("no SAML 2 AttributeAuthority role found in metadata for (%s)", entityID); - return false; + return; } const PropertySet* relyingParty = application.getRelyingParty(mdresult.first); @@ -392,7 +400,7 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha if (!srt) { m_log.error("unable to obtain a SAML response from attribute authority (%s)", entityID); - return false; + throw BindingException("Unable to obtain a SAML response from attribute authority."); } auto_ptr wrapper(srt); @@ -400,12 +408,12 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha saml2p::Response* response = dynamic_cast(srt); if (!response) { m_log.error("message was not a samlp:Response"); - return true; + throw FatalProfileException("Attribute authority returned an unrecognized message."); } else if (!response->getStatus() || !response->getStatus()->getStatusCode() || !XMLString::equals(response->getStatus()->getStatusCode()->getValue(), saml2p::StatusCode::SUCCESS)) { m_log.error("attribute authority (%s) returned a SAML error", entityID); - return true; + throw FatalProfileException("Attribute authority returned a SAML error."); } saml2::Assertion* newtoken = nullptr; @@ -416,7 +424,7 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha const_cast(response)->getEncryptedAssertions(); if (encassertions.empty()) { m_log.warn("response from attribute authority was empty"); - return true; + return; } else if (encassertions.size() > 1) { m_log.warn("simple resolver only supports one assertion in the query response"); @@ -425,7 +433,7 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha CredentialResolver* cr=application.getCredentialResolver(); if (!cr) { m_log.warn("found encrypted assertion, but no CredentialResolver was available"); - return true; + throw FatalProfileException("Assertion was encrypted, but no decryption credentials are available."); } // Attempt to decrypt it. @@ -437,18 +445,13 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha tokenwrapper.release(); if (m_log.isDebugEnabled()) m_log.debugStream() << "decrypted Assertion: " << *newtoken << logging::eol; + // Free the Response now, so we know this is a stand-alone token later. + delete wrapper.release(); } } catch (exception& ex) { m_log.error(ex.what()); - } - if (newtoken) { - // Free the Response now, so we know this is a stand-alone token later. - delete wrapper.release(); - } - else { - // Nothing decrypted, should already be logged. - return true; + throw; } } else { @@ -461,7 +464,7 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha m_log.error("assertion unsigned, rejecting it based on signedAssertions policy"); if (!wrapper.get()) delete newtoken; - return true; + throw SecurityPolicyException("Rejected unsigned assertion based on local policy."); } try { @@ -515,7 +518,7 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha m_log.warn("ignoring Assertion without NameID in Subject"); if (!wrapper.get()) delete newtoken; - return true; + return; } } } @@ -523,7 +526,7 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha m_log.error("assertion failed policy validation: %s", ex.what()); if (!wrapper.get()) delete newtoken; - return true; + throw; } if (wrapper.get()) { @@ -551,9 +554,8 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha m_log.error("caught exception extracting/filtering attributes from query result: %s", ex.what()); for_each(ctx.getResolvedAttributes().begin(), ctx.getResolvedAttributes().end(), xmltooling::cleanup()); ctx.getResolvedAttributes().clear(); + throw; } - - return true; } void SimpleAggregationResolver::resolveAttributes(ResolutionContext& ctx) const @@ -646,13 +648,26 @@ void SimpleAggregationResolver::resolveAttributes(ResolutionContext& ctx) const if (qctx.getEntityID()) history.insert(qctx.getEntityID()); + // Prepare to track exceptions. + SimpleAttribute* exceptAttr = nullptr; + if (!m_exceptionId.empty()) { + exceptAttr = new SimpleAttribute(m_exceptionId); + } + auto_ptr exceptWrapper(exceptAttr); + // We have a master loop over all the possible sources of material. for (vector< pair >::const_iterator source = m_sources.begin(); source != m_sources.end(); ++source) { if (source->second) { // A literal entityID to query. if (history.count(source->first) == 0) { m_log.debug("issuing SAML query to (%s)", source->first.c_str()); - doQuery(qctx, source->first.c_str(), n ? n : qctx.getNameID()); + try { + doQuery(qctx, source->first.c_str(), n ? n : qctx.getNameID()); + } + catch (exception& ex) { + if (exceptAttr) + exceptAttr->getValues().push_back(XMLToolingConfig::getConfig().getURLEncoder()->encode(ex.what())); + } history.insert(source->first); } else { @@ -670,7 +685,13 @@ void SimpleAggregationResolver::resolveAttributes(ResolutionContext& ctx) const for (vector::const_iterator link = links.begin(); link != links.end(); ++link) { if (history.count(*link) == 0) { m_log.debug("issuing SAML query to (%s)", link->c_str()); - doQuery(qctx, link->c_str(), n ? n : qctx.getNameID()); + try { + doQuery(qctx, link->c_str(), n ? n : qctx.getNameID()); + } + catch (exception& ex) { + if (exceptAttr) + exceptAttr->getValues().push_back(XMLToolingConfig::getConfig().getURLEncoder()->encode(ex.what())); + } history.insert(*link); } else { @@ -688,7 +709,13 @@ void SimpleAggregationResolver::resolveAttributes(ResolutionContext& ctx) const for (vector::const_iterator link = links.begin(); link != links.end(); ++link) { if (history.count(*link) == 0) { m_log.debug("issuing SAML query to (%s)", link->c_str()); - doQuery(qctx, link->c_str(), n ? n : qctx.getNameID()); + try { + doQuery(qctx, link->c_str(), n ? n : qctx.getNameID()); + } + catch (exception& ex) { + if (exceptAttr) + exceptAttr->getValues().push_back(XMLToolingConfig::getConfig().getURLEncoder()->encode(ex.what())); + } history.insert(*link); } else { @@ -700,4 +727,8 @@ void SimpleAggregationResolver::resolveAttributes(ResolutionContext& ctx) const } } } + + if (exceptAttr) { + qctx.getResolvedAttributes().push_back(exceptWrapper.release()); + } } diff --git a/shibsp/handler/RemotedHandler.h b/shibsp/handler/RemotedHandler.h index 3511af6..ef1a25c 100644 --- a/shibsp/handler/RemotedHandler.h +++ b/shibsp/handler/RemotedHandler.h @@ -26,6 +26,8 @@ #include #include +#include + namespace xmltooling { class XMLTOOL_API HTTPRequest; class XMLTOOL_API HTTPResponse; @@ -38,9 +40,18 @@ namespace shibsp { */ class SHIBSP_API RemotedHandler : public virtual Handler, public Remoted { + static std::set m_remotedHeaders; + public: virtual ~RemotedHandler(); + /** + * Ensures that a request header will be remoted. + * + * @param header name of request header to remote + */ + static void addRemotedHeader(const char* header); + protected: RemotedHandler(); @@ -56,7 +67,7 @@ namespace shibsp { * to remote the request information. * * @param request an SPRequest to remote - * @param headers array of request headers to copy to remote request + * @param headers array of additional request headers to copy to remote request * @param certs true iff client certificates should be available for the remote request * @return the input dataflow object */ diff --git a/shibsp/handler/impl/MetadataGenerator.cpp b/shibsp/handler/impl/MetadataGenerator.cpp index 7ae1c9d..7cafe7d 100644 --- a/shibsp/handler/impl/MetadataGenerator.cpp +++ b/shibsp/handler/impl/MetadataGenerator.cpp @@ -89,6 +89,7 @@ namespace shibsp { delete m_org; delete m_entityAttrs; for_each(m_contacts.begin(), m_contacts.end(), xmltooling::cleanup()); + for_each(m_formats.begin(), m_formats.end(), xmltooling::cleanup()); for_each(m_reqAttrs.begin(), m_reqAttrs.end(), xmltooling::cleanup()); for_each(m_attrConsumers.begin(), m_attrConsumers.end(), xmltooling::cleanup()); #endif @@ -114,6 +115,7 @@ namespace shibsp { Organization* m_org; EntityAttributes* m_entityAttrs; vector m_contacts; + vector m_formats; vector m_reqAttrs; vector m_attrConsumers; #endif @@ -184,48 +186,55 @@ MetadataGenerator::MetadataGenerator(const DOMElement* e, const char* appId) m_contacts.push_back(cp); } else { - RequestedAttribute* req = dynamic_cast(child.get()); - if (req) { + NameIDFormat* nif = dynamic_cast(child.get()); + if (nif) { child.release(); - m_reqAttrs.push_back(req); + m_formats.push_back(nif); } else { - AttributeConsumingService* acs = dynamic_cast(child.get()); - if (acs) { + RequestedAttribute* req = dynamic_cast(child.get()); + if (req) { child.release(); - m_attrConsumers.push_back(acs); + m_reqAttrs.push_back(req); } else { - UIInfo* info = dynamic_cast(child.get()); - if (info) { - if (!m_uiinfo) { - child.release(); - m_uiinfo = info; - } - else { - m_log.warn("skipping duplicate UIInfo element"); - } + AttributeConsumingService* acs = dynamic_cast(child.get()); + if (acs) { + child.release(); + m_attrConsumers.push_back(acs); } else { - Organization* org = dynamic_cast(child.get()); - if (org) { - if (!m_org) { + UIInfo* info = dynamic_cast(child.get()); + if (info) { + if (!m_uiinfo) { child.release(); - m_org = org; + m_uiinfo = info; } else { - m_log.warn("skipping duplicate Organization element"); + m_log.warn("skipping duplicate UIInfo element"); } } else { - EntityAttributes* ea = dynamic_cast(child.get()); - if (ea) { - if (!m_entityAttrs) { + Organization* org = dynamic_cast(child.get()); + if (org) { + if (!m_org) { child.release(); - m_entityAttrs = ea; + m_org = org; } else { - m_log.warn("skipping duplicate EntityAttributes element"); + m_log.warn("skipping duplicate Organization element"); + } + } + else { + EntityAttributes* ea = dynamic_cast(child.get()); + if (ea) { + if (!m_entityAttrs) { + child.release(); + m_entityAttrs = ea; + } + else { + m_log.warn("skipping duplicate EntityAttributes element"); + } } } } @@ -363,7 +372,7 @@ pair MetadataGenerator::processMessage( entity->setOrganization(m_org->cloneOrganization()); for (vector::const_iterator cp = m_contacts.begin(); cp != m_contacts.end(); ++cp) - entity->getContactPersons().push_back((*cp)->cloneContactPerson()); + entity->getContactPersons().push_back((*cp)->cloneContactPerson()); if (m_entityAttrs) { if (!entity->getExtensions()) @@ -380,6 +389,9 @@ pair MetadataGenerator::processMessage( role = entity->getSPSSODescriptors().front(); } + for (vector::const_iterator nif = m_formats.begin(); nif != m_formats.end(); ++nif) + role->getNameIDFormats().push_back((*nif)->cloneNameIDFormat()); + if (m_uiinfo) { if (!role->getExtensions()) role->setExtensions(ExtensionsBuilder::buildExtensions()); diff --git a/shibsp/handler/impl/RemotedHandler.cpp b/shibsp/handler/impl/RemotedHandler.cpp index e7ca6d7..272f667 100644 --- a/shibsp/handler/impl/RemotedHandler.cpp +++ b/shibsp/handler/impl/RemotedHandler.cpp @@ -21,14 +21,16 @@ */ #include "internal.h" -#include "Application.h" #include "exceptions.h" +#include "Application.h" +#include "GSSRequest.h" #include "ServiceProvider.h" #include "SPRequest.h" #include "handler/RemotedHandler.h" #include #include +#include #ifndef SHIBSP_LITE # include "util/CGIParser.h" @@ -46,22 +48,44 @@ using namespace std; #ifndef SHIBSP_LITE namespace shibsp { - class SHIBSP_DLLLOCAL RemotedRequest : public virtual HTTPRequest + class SHIBSP_DLLLOCAL RemotedRequest : +#ifdef HAVE_GSSAPI + public GSSRequest, +#endif + public HTTPRequest { DDF& m_input; mutable CGIParser* m_parser; mutable vector m_certs; +#ifdef HAVE_GSSAPI + mutable gss_ctx_id_t m_gss; +#endif public: - RemotedRequest(DDF& input) : m_input(input), m_parser(nullptr) {} + RemotedRequest(DDF& input) : m_input(input), m_parser(nullptr) +#ifdef HAVE_GSSAPI + , m_gss(GSS_C_NO_CONTEXT) +#endif + { + } + virtual ~RemotedRequest() { for_each(m_certs.begin(), m_certs.end(), xmltooling::cleanup()); delete m_parser; +#ifdef HAVE_GSSAPI + if (m_gss != GSS_C_NO_CONTEXT) { + OM_uint32 minor; + gss_delete_sec_context(&minor, &m_gss, GSS_C_NO_BUFFER); + } +#endif } // GenericRequest const char* getScheme() const { return m_input["scheme"].string(); } + bool isSecure() const { + return HTTPRequest::isSecure(); + } const char* getHostname() const { return m_input["hostname"].string(); } @@ -93,6 +117,11 @@ namespace shibsp { const std::vector& getClientCertificates() const; +#ifdef HAVE_GSSAPI + // GSSRequest + gss_ctx_id_t getGSSContext() const; +#endif + // HTTPRequest const char* getMethod() const { return m_input["method"].string(); @@ -177,6 +206,34 @@ const std::vector& RemotedRequest::getClientCertificates() cons return m_certs; } +#ifdef HAVE_GSSAPI +gss_ctx_id_t RemotedRequest::getGSSContext() const +{ + if (m_gss == GSS_C_NO_CONTEXT) { + const char* encoded = m_input["gss_context"].string(); + if (encoded) { + xsecsize_t x; + XMLByte* decoded=Base64::decode(reinterpret_cast(encoded), &x); + if (decoded) { + gss_buffer_desc importbuf; + importbuf.length = x; + importbuf.value = decoded; + OM_uint32 minor; + OM_uint32 major = gss_import_sec_context(&minor, &importbuf, &m_gss); + if (major != GSS_S_COMPLETE) + m_gss = GSS_C_NO_CONTEXT; +#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&decoded); +#else + XMLString::release((char**)&decoded); +#endif + } + } + } + return m_gss; +} +#endif + long RemotedResponse::sendResponse(std::istream& in, long status) { string msg; @@ -228,6 +285,8 @@ void RemotedHandler::setAddress(const char* address) } } +set RemotedHandler::m_remotedHeaders; + RemotedHandler::RemotedHandler() { } @@ -240,6 +299,11 @@ RemotedHandler::~RemotedHandler() listener->unregListener(m_address.c_str(),this); } +void RemotedHandler::addRemotedHeader(const char* header) +{ + m_remotedHeaders.insert(header); +} + DDF RemotedHandler::wrap(const SPRequest& request, const vector* headers, bool certs) const { DDF in = DDF(m_address.c_str()).structure(); @@ -257,13 +321,20 @@ DDF RemotedHandler::wrap(const SPRequest& request, const vector* headers in.addmember("url").unsafe_string(request.getRequestURL()); in.addmember("query").string(request.getQueryString()); - if (headers) { + if (headers || !m_remotedHeaders.empty()) { string hdr; DDF hin = in.addmember("headers").structure(); - for (vector::const_iterator h = headers->begin(); h!=headers->end(); ++h) { - hdr = request.getHeader(h->c_str()); + if (headers) { + for (vector::const_iterator h = headers->begin(); h!=headers->end(); ++h) { + hdr = request.getHeader(h->c_str()); + if (!hdr.empty()) + hin.addmember(h->c_str()).unsafe_string(hdr.c_str()); + } + } + for (set::const_iterator hh = m_remotedHeaders.begin(); hh != m_remotedHeaders.end(); ++hh) { + hdr = request.getHeader(hh->c_str()); if (!hdr.empty()) - hin.addmember(h->c_str()).unsafe_string(hdr.c_str()); + hin.addmember(hh->c_str()).unsafe_string(hdr.c_str()); } } @@ -289,6 +360,40 @@ DDF RemotedHandler::wrap(const SPRequest& request, const vector* headers #endif } +#ifdef HAVE_GSSAPI + const GSSRequest* gss = dynamic_cast(&request); + if (gss) { + gss_ctx_id_t ctx = gss->getGSSContext(); + if (ctx != GSS_C_NO_CONTEXT) { + OM_uint32 minor; + gss_buffer_desc contextbuf; + contextbuf.length = 0; + contextbuf.value = nullptr; + OM_uint32 major = gss_export_sec_context(&minor, &ctx, &contextbuf); + if (major == GSS_S_COMPLETE) { + xsecsize_t len=0; + XMLByte* out=Base64::encode(reinterpret_cast(contextbuf.value), contextbuf.length, &len); + if (out) { + string ctx; + ctx.append(reinterpret_cast(out), len); +#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&out); +#else + XMLString::release((char**)&out); +#endif + in.addmember("gss_context").string(ctx.c_str()); + } + else { + request.log(SPRequest::SPError, "error while base64-encoding GSS context"); + } + } + else { + request.log(SPRequest::SPError, "error while exporting GSS context"); + } + } + } +#endif + return in; } diff --git a/shibsp/handler/impl/StatusHandler.cpp b/shibsp/handler/impl/StatusHandler.cpp index f2cbd7f..3ef824e 100644 --- a/shibsp/handler/impl/StatusHandler.cpp +++ b/shibsp/handler/impl/StatusHandler.cpp @@ -29,6 +29,13 @@ #include "handler/RemotedHandler.h" #include "util/CGIParser.h" +#include +#include + +#ifdef HAVE_SYS_UTSNAME_H +# include +#endif + using namespace shibsp; #ifndef SHIBSP_LITE # include "SessionCache.h" @@ -77,6 +84,7 @@ namespace shibsp { private: pair processMessage(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse) const; + ostream& systemInfo(ostream& os) const; set m_acl; }; @@ -293,16 +301,20 @@ pair StatusHandler::run(SPRequest& request, bool isHandler) const map props; settings.first->getAll(props); + DateTime now(time(nullptr)); + now.parseDateTime(); + auto_ptr_char timestamp(now.getFormattedString()); request.setContentType("text/xml"); stringstream msg; - msg << ""; + msg << ""; msg << ""; - msg << "::const_iterator p = props.begin(); p != props.end(); ++p) msg << ' ' << p->first << "='" << p->second << "'"; msg << '>' << target << ""; @@ -326,31 +338,39 @@ pair StatusHandler::run(SPRequest& request, bool isHandler) const } catch (XMLToolingException& ex) { m_log.error("error while processing request: %s", ex.what()); + DateTime now(time(nullptr)); + now.parseDateTime(); + auto_ptr_char timestamp(now.getFormattedString()); request.setContentType("text/xml"); stringstream msg; - msg << ""; + msg << ""; msg << ""; - msg << "" << ex.what() << ""; + systemInfo(msg) << "" << ex.what() << ""; msg << ""; return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR)); } catch (exception& ex) { m_log.error("error while processing request: %s", ex.what()); + DateTime now(time(nullptr)); + now.parseDateTime(); + auto_ptr_char timestamp(now.getFormattedString()); request.setContentType("text/xml"); stringstream msg; - msg << ""; + msg << ""; msg << ""; - msg << "" << ex.what() << ""; + systemInfo(msg) << "" << ex.what() << ""; msg << ""; return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR)); } @@ -387,15 +407,22 @@ pair StatusHandler::processMessage( #ifndef SHIBSP_LITE m_log.debug("processing status request"); + DateTime now(time(nullptr)); + now.parseDateTime(); + auto_ptr_char timestamp(now.getFormattedString()); + stringstream s; - s << ""; + s << ""; const char* status = ""; s << ""; + systemInfo(s); + const char* param = nullptr; if (param) { } @@ -488,3 +515,78 @@ pair StatusHandler::processMessage( return make_pair(false,0L); #endif } + +#ifdef WIN32 +typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); +#endif + +ostream& StatusHandler::systemInfo(ostream& os) const +{ +#if defined(HAVE_SYS_UTSNAME_H) + struct utsname sysinfo; + if (uname(&sysinfo) == 0) { + os << ""; + } +#elif defined(WIN32) + OSVERSIONINFOEX osvi; + memset(&osvi, 0, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if(GetVersionEx((OSVERSIONINFO*)&osvi)) { + os << " 0) + os << " servicepack='" << osvi.wServicePackMajor << "." << osvi.wServicePackMinor << "'"; + switch (osvi.wProductType) { + case VER_NT_WORKSTATION: + os << " producttype='Workstation'"; + break; + case VER_NT_SERVER: + case VER_NT_DOMAIN_CONTROLLER: + os << " producttype='Server'"; + break; + } + + SYSTEM_INFO si; + memset(&si, 0, sizeof(SYSTEM_INFO)); + PGNSI pGNSI = (PGNSI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); + if(pGNSI) + pGNSI(&si); + else + GetSystemInfo(&si); + switch (si.dwProcessorType) { + case PROCESSOR_ARCHITECTURE_INTEL: + os << " arch='i386'"; + break; + case PROCESSOR_ARCHITECTURE_AMD64: + os << " arch='x86_64'"; + break; + case PROCESSOR_ARCHITECTURE_IA64: + os << " arch='IA64'"; + break; + } + os << " cpucount='" << si.dwNumberOfProcessors << "'"; + + MEMORYSTATUSEX ms; + memset(&ms, 0, sizeof(MEMORYSTATUSEX)); + ms.dwLength = sizeof(MEMORYSTATUSEX); + if (GlobalMemoryStatusEx(&ms)) { + os << " memory='" << (ms.ullTotalPhys / (1024 * 1024)) << "M'"; + } + + os << "/>"; + } +#endif + return os; +} diff --git a/shibsp/impl/StorageServiceSessionCache.cpp b/shibsp/impl/StorageServiceSessionCache.cpp index ca3f50a..3641be9 100644 --- a/shibsp/impl/StorageServiceSessionCache.cpp +++ b/shibsp/impl/StorageServiceSessionCache.cpp @@ -35,6 +35,7 @@ #include "SessionCacheEx.h" #include "TransactionLog.h" #include "attribute/Attribute.h" +#include "handler/RemotedHandler.h" #include "remoting/ListenerService.h" #include "util/SPConstants.h" @@ -106,7 +107,7 @@ namespace shibsp { ); bool matches( const Application& app, - const xmltooling::HTTPRequest& request, + const HTTPRequest& request, const saml2md::EntityDescriptor* issuer, const saml2::NameID& nameid, const set* indexes @@ -116,7 +117,12 @@ namespace shibsp { void remove(const Application& app, const char* key); void test(); - string active(const Application& app, const xmltooling::HTTPRequest& request) { + string active(const Application& app, const HTTPRequest& request) { + if (!m_inboundHeader.empty()) { + string session_id = request.getHeader(m_inboundHeader.c_str()); + if (!session_id.empty()) + return session_id; + } pair shib_cookie = app.getCookieNameProps("_shibsession_"); const char* session_id = request.getCookie(shib_cookie.first.c_str()); return (session_id ? session_id : ""); @@ -129,47 +135,8 @@ namespace shibsp { return nullptr; } - Session* find(const Application& app, HTTPRequest& request, const char* client_addr=nullptr, time_t* timeout=nullptr) { - string id = active(app, request); - if (id.empty()) - return nullptr; - try { - Session* session = find(app, id.c_str(), client_addr, timeout); - if (session) - return session; - HTTPResponse* response = dynamic_cast(&request); - if (response) { - pair shib_cookie = app.getCookieNameProps("_shibsession_"); - string exp(shib_cookie.second); - exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT"; - response->setCookie(shib_cookie.first.c_str(), exp.c_str()); - } - } - catch (exception&) { - HTTPResponse* response = dynamic_cast(&request); - if (response) { - pair shib_cookie = app.getCookieNameProps("_shibsession_"); - string exp(shib_cookie.second); - exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT"; - response->setCookie(shib_cookie.first.c_str(), exp.c_str()); - } - throw; - } - return nullptr; - } - - void remove(const Application& app, const HTTPRequest& request, HTTPResponse* response=nullptr) { - pair shib_cookie = app.getCookieNameProps("_shibsession_"); - const char* session_id = request.getCookie(shib_cookie.first.c_str()); - if (session_id && *session_id) { - if (response) { - string exp(shib_cookie.second); - exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT"; - response->setCookie(shib_cookie.first.c_str(), exp.c_str()); - } - remove(app, session_id); - } - } + Session* find(const Application& app, HTTPRequest& request, const char* client_addr=nullptr, time_t* timeout=nullptr); + void remove(const Application& app, const HTTPRequest& request, HTTPResponse* response=nullptr); unsigned long getCacheTimeout(const Application& app) { // Computes offset for adjusting expiration of sessions. @@ -203,6 +170,7 @@ namespace shibsp { #endif const DOMElement* m_root; // Only valid during initialization unsigned long m_inprocTimeout,m_cacheTimeout,m_cacheAllowance; + string m_inboundHeader,m_outboundHeader; // inproc means we buffer sessions in memory RWLock* m_lock; @@ -775,6 +743,8 @@ SSCache::SSCache(const DOMElement* e) static const XMLCh cacheAssertions[] = UNICODE_LITERAL_15(c,a,c,h,e,A,s,s,e,r,t,i,o,n,s); static const XMLCh cacheTimeout[] = UNICODE_LITERAL_12(c,a,c,h,e,T,i,m,e,o,u,t); static const XMLCh inprocTimeout[] = UNICODE_LITERAL_13(i,n,p,r,o,c,T,i,m,e,o,u,t); + static const XMLCh inboundHeader[] = UNICODE_LITERAL_13(i,n,b,o,u,n,d,H,e,a,d,e,r); + static const XMLCh outboundHeader[] = UNICODE_LITERAL_14(o,u,t,b,o,u,n,d,H,e,a,d,e,r); static const XMLCh _StorageService[] = UNICODE_LITERAL_14(S,t,o,r,a,g,e,S,e,r,v,i,c,e); static const XMLCh _StorageServiceLite[] = UNICODE_LITERAL_18(S,t,o,r,a,g,e,S,e,r,v,i,c,e,L,i,t,e); @@ -782,6 +752,10 @@ SSCache::SSCache(const DOMElement* e) m_cacheAllowance = XMLHelper::getAttrInt(e, 0, cacheAllowance); if (inproc) m_inprocTimeout = XMLHelper::getAttrInt(e, 900, inprocTimeout); + m_inboundHeader = XMLHelper::getAttrString(e, nullptr, inboundHeader); + if (!m_inboundHeader.empty()) + RemotedHandler::addRemotedHeader(m_inboundHeader.c_str()); + m_outboundHeader = XMLHelper::getAttrString(e, nullptr, outboundHeader); #ifndef SHIBSP_LITE if (conf.isEnabled(SPConfig::OutOfProcess)) { @@ -1127,6 +1101,9 @@ void SSCache::insert( xlog->log.info("}"); } + if (!m_outboundHeader.empty()) + httpResponse.setResponseHeader(m_outboundHeader.c_str(), key.get()); + time_t cookieLifetime = 0; pair shib_cookie = app.getCookieNameProps("_shibsession_", &cookieLifetime); string k(key.get()); @@ -1525,6 +1502,65 @@ Session* SSCache::find(const Application& app, const char* key, const char* clie return session; } +Session* SSCache::find(const Application& app, HTTPRequest& request, const char* client_addr, time_t* timeout) +{ + string id = active(app, request); + if (id.empty()) + return nullptr; + try { + Session* session = find(app, id.c_str(), client_addr, timeout); + if (session) + return session; + HTTPResponse* response = dynamic_cast(&request); + if (response) { + if (!m_outboundHeader.empty()) + response->setResponseHeader(m_outboundHeader.c_str(), nullptr); + pair shib_cookie = app.getCookieNameProps("_shibsession_"); + string exp(shib_cookie.second); + exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT"; + response->setCookie(shib_cookie.first.c_str(), exp.c_str()); + } + } + catch (exception&) { + HTTPResponse* response = dynamic_cast(&request); + if (response) { + if (!m_outboundHeader.empty()) + response->setResponseHeader(m_outboundHeader.c_str(), nullptr); + pair shib_cookie = app.getCookieNameProps("_shibsession_"); + string exp(shib_cookie.second); + exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT"; + response->setCookie(shib_cookie.first.c_str(), exp.c_str()); + } + throw; + } + return nullptr; +} + +void SSCache::remove(const Application& app, const HTTPRequest& request, HTTPResponse* response) +{ + string session_id; + pair shib_cookie = app.getCookieNameProps("_shibsession_"); + + if (!m_inboundHeader.empty()) + session_id = request.getHeader(m_inboundHeader.c_str()); + if (session_id.empty()) { + const char* c = request.getCookie(shib_cookie.first.c_str()); + if (c && *c) + session_id = c; + } + + if (!session_id.empty()) { + if (response) { + if (!m_outboundHeader.empty()) + response->setResponseHeader(m_outboundHeader.c_str(), nullptr); + string exp(shib_cookie.second); + exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT"; + response->setCookie(shib_cookie.first.c_str(), exp.c_str()); + } + remove(app, session_id.c_str()); + } +} + void SSCache::remove(const Application& app, const char* key) { #ifdef _DEBUG diff --git a/shibsp/impl/XMLRequestMapper.cpp b/shibsp/impl/XMLRequestMapper.cpp index 27153bd..9059491 100644 --- a/shibsp/impl/XMLRequestMapper.cpp +++ b/shibsp/impl/XMLRequestMapper.cpp @@ -479,8 +479,6 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : #ifdef _DEBUG xmltooling::NDC ndc("XMLRequestMapperImpl"); #endif - static const XMLCh _default[] = UNICODE_LITERAL_7(d,e,f,a,u,l,t); - static const XMLCh _id[] = UNICODE_LITERAL_2(i,d); static const XMLCh _RequestMap[] = UNICODE_LITERAL_10(R,e,q,u,e,s,t,M,a,p); if (e && !XMLHelper::isNodeNamed(e, SHIB2SPCONFIG_NS, _RequestMap)) diff --git a/shibsp/impl/XMLServiceProvider.cpp b/shibsp/impl/XMLServiceProvider.cpp index 3179637..1c1d9f4 100644 --- a/shibsp/impl/XMLServiceProvider.cpp +++ b/shibsp/impl/XMLServiceProvider.cpp @@ -270,7 +270,7 @@ namespace { class SHIBSP_DLLLOCAL XMLConfigImpl : public DOMPropertySet, public DOMNodeFilter { public: - XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer, Category& log); + XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer, Category& log); ~XMLConfigImpl(); RequestMapper* m_requestMapper; @@ -293,12 +293,11 @@ namespace { } private: - void doExtensions(const DOMElement* e, const char* label, Category& log); - void doListener(const DOMElement* e, Category& log); - void doCaching(const DOMElement* e, Category& log); + void doExtensions(const DOMElement*, const char*, Category&); + void doListener(const DOMElement*, XMLConfig*, Category&); + void doCaching(const DOMElement*, XMLConfig*, Category&); void cleanup(); - const XMLConfig* m_outer; DOMDocument* m_document; }; @@ -440,11 +439,11 @@ namespace { private: friend class XMLConfigImpl; XMLConfigImpl* m_impl; - mutable ListenerService* m_listener; - mutable SessionCache* m_sessionCache; + ListenerService* m_listener; + SessionCache* m_sessionCache; #ifndef SHIBSP_LITE - mutable TransactionLog* m_tranLog; - mutable map m_storage; + TransactionLog* m_tranLog; + map m_storage; #endif }; @@ -1698,7 +1697,7 @@ void XMLConfigImpl::doExtensions(const DOMElement* e, const char* label, Categor } } -void XMLConfigImpl::doListener(const DOMElement* e, Category& log) +void XMLConfigImpl::doListener(const DOMElement* e, XMLConfig* conf, Category& log) { #ifdef WIN32 string plugtype(TCP_LISTENER_SERVICE); @@ -1723,19 +1722,19 @@ void XMLConfigImpl::doListener(const DOMElement* e, Category& log) } log.info("building ListenerService of type %s...", plugtype.c_str()); - m_outer->m_listener = SPConfig::getConfig().ListenerServiceManager.newPlugin(plugtype.c_str(), child); + conf->m_listener = SPConfig::getConfig().ListenerServiceManager.newPlugin(plugtype.c_str(), child); } -void XMLConfigImpl::doCaching(const DOMElement* e, Category& log) +void XMLConfigImpl::doCaching(const DOMElement* e, XMLConfig* conf, Category& log) { - SPConfig& conf = SPConfig::getConfig(); + SPConfig& spConf = SPConfig::getConfig(); #ifndef SHIBSP_LITE SAMLConfig& samlConf = SAMLConfig::getConfig(); #endif DOMElement* child; #ifndef SHIBSP_LITE - if (conf.isEnabled(SPConfig::OutOfProcess)) { + if (spConf.isEnabled(SPConfig::OutOfProcess)) { XMLToolingConfig& xmlConf = XMLToolingConfig::getConfig(); // First build any StorageServices. child = XMLHelper::getFirstChildElement(e, _StorageService); @@ -1745,7 +1744,7 @@ void XMLConfigImpl::doCaching(const DOMElement* e, Category& log) if (!t.empty()) { try { log.info("building StorageService (%s) of type %s...", id.c_str(), t.c_str()); - m_outer->m_storage[id] = xmlConf.StorageServiceManager.newPlugin(t.c_str(), child); + conf->m_storage[id] = xmlConf.StorageServiceManager.newPlugin(t.c_str(), child); } catch (exception& ex) { log.crit("failed to instantiate StorageService (%s): %s", id.c_str(), ex.what()); @@ -1754,9 +1753,9 @@ void XMLConfigImpl::doCaching(const DOMElement* e, Category& log) child = XMLHelper::getNextSiblingElement(child, _StorageService); } - if (m_outer->m_storage.empty()) { + if (conf->m_storage.empty()) { log.info("no StorageService plugin(s) installed, using (mem) in-memory instance"); - m_outer->m_storage["mem"] = xmlConf.StorageServiceManager.newPlugin(MEMORY_STORAGE_SERVICE, nullptr); + conf->m_storage["mem"] = xmlConf.StorageServiceManager.newPlugin(MEMORY_STORAGE_SERVICE, nullptr); } // Replay cache. @@ -1765,23 +1764,23 @@ void XMLConfigImpl::doCaching(const DOMElement* e, Category& log) if (child) { string ssid(XMLHelper::getAttrString(child, nullptr, _StorageService)); if (!ssid.empty()) { - if (m_outer->m_storage.count(ssid)) { + if (conf->m_storage.count(ssid)) { log.info("building ReplayCache on top of StorageService (%s)...", ssid.c_str()); - replaySS = m_outer->m_storage[ssid]; + replaySS = conf->m_storage[ssid]; } else { log.error("unable to locate StorageService (%s), using arbitrary instance for ReplayCache", ssid.c_str()); - replaySS = m_outer->m_storage.begin()->second; + replaySS = conf->m_storage.begin()->second; } } else { log.info("no StorageService specified for ReplayCache, using arbitrary instance"); - replaySS = m_outer->m_storage.begin()->second; + replaySS = conf->m_storage.begin()->second; } } else { log.info("no ReplayCache specified, using arbitrary StorageService instance"); - replaySS = m_outer->m_storage.begin()->second; + replaySS = conf->m_storage.begin()->second; } xmlConf.setReplayCache(new ReplayCache(replaySS)); @@ -1790,9 +1789,9 @@ void XMLConfigImpl::doCaching(const DOMElement* e, Category& log) if (child) { string ssid(XMLHelper::getAttrString(child, nullptr, _StorageService)); if (!ssid.empty()) { - if (m_outer->m_storage.count(ssid)) { + if (conf->m_storage.count(ssid)) { log.info("building ArtifactMap on top of StorageService (%s)...", ssid.c_str()); - samlConf.setArtifactMap(new ArtifactMap(child, m_outer->m_storage[ssid])); + samlConf.setArtifactMap(new ArtifactMap(child, conf->m_storage[ssid])); } else { log.error("unable to locate StorageService (%s), using in-memory ArtifactMap", ssid.c_str()); @@ -1816,21 +1815,21 @@ void XMLConfigImpl::doCaching(const DOMElement* e, Category& log) string t(XMLHelper::getAttrString(child, nullptr, _type)); if (!t.empty()) { log.info("building SessionCache of type %s...", t.c_str()); - m_outer->m_sessionCache = conf.SessionCacheManager.newPlugin(t.c_str(), child); + conf->m_sessionCache = spConf.SessionCacheManager.newPlugin(t.c_str(), child); } } - if (!m_outer->m_sessionCache) { + if (!conf->m_sessionCache) { log.info("no SessionCache specified, using StorageService-backed instance"); - m_outer->m_sessionCache = conf.SessionCacheManager.newPlugin(STORAGESERVICE_SESSION_CACHE, nullptr); + conf->m_sessionCache = spConf.SessionCacheManager.newPlugin(STORAGESERVICE_SESSION_CACHE, nullptr); } } -XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer, Category& log) +XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer, Category& log) : m_requestMapper(nullptr), #ifndef SHIBSP_LITE m_policy(nullptr), #endif - m_outer(outer), m_document(nullptr) + m_document(nullptr) { #ifdef _DEBUG xmltooling::NDC ndc("XMLConfigImpl"); @@ -1868,7 +1867,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o #ifndef SHIBSP_LITE if (first) - m_outer->m_tranLog = new TransactionLog(); + outer->m_tranLog = new TransactionLog(); #endif } @@ -1940,18 +1939,18 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o // Instantiate the ListenerService and SessionCache objects. if (conf.isEnabled(SPConfig::Listener)) - doListener(e, log); + doListener(e, outer, log); #ifndef SHIBSP_LITE - if (m_outer->m_listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess)) { - m_outer->m_listener->regListener("set::RelayState", const_cast(m_outer)); - m_outer->m_listener->regListener("get::RelayState", const_cast(m_outer)); - m_outer->m_listener->regListener("set::PostData", const_cast(m_outer)); - m_outer->m_listener->regListener("get::PostData", const_cast(m_outer)); + if (outer->m_listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess)) { + outer->m_listener->regListener("set::RelayState", outer); + outer->m_listener->regListener("get::RelayState", outer); + outer->m_listener->regListener("set::PostData", outer); + outer->m_listener->regListener("get::PostData", outer); } #endif if (conf.isEnabled(SPConfig::Caching)) - doCaching(e, log); + doCaching(e, outer, log); } // end of first-time-only stuff // Back to the fully dynamic stuff...next up is the RequestMapper. @@ -2050,13 +2049,13 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o log.fatal("can't build default Application object, missing conf:ApplicationDefaults element?"); throw ConfigurationException("can't build default Application object, missing conf:ApplicationDefaults element?"); } - XMLApplication* defapp = new XMLApplication(m_outer, pp, child); + XMLApplication* defapp = new XMLApplication(outer, pp, child); m_appmap[defapp->getId()] = defapp; // Load any overrides. child = XMLHelper::getFirstChildElement(child, ApplicationOverride); while (child) { - auto_ptr iapp(new XMLApplication(m_outer, pp, child, defapp)); + auto_ptr iapp(new XMLApplication(outer, pp, child, defapp)); if (m_appmap.count(iapp->getId())) log.crit("found conf:ApplicationOverride element with duplicate id attribute (%s), skipping it", iapp->getId()); else { @@ -2066,6 +2065,25 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o child = XMLHelper::getNextSiblingElement(child, ApplicationOverride); } + + // Check for extra AuthTypes to recognize. + if (conf.isEnabled(SPConfig::InProcess)) { + const PropertySet* inprocs = getPropertySet("InProcess"); + if (inprocs) { + pair extraAuthTypes = inprocs->getString("extraAuthTypes"); + if (extraAuthTypes.first) { + string types=extraAuthTypes.second; + unsigned int j_types=0; + for (unsigned int i_types=0; i_types < types.length(); i_types++) { + if (types.at(i_types) == ' ') { + outer->m_authTypes.insert(types.substr(j_types, i_types - j_types)); + j_types = i_types + 1; + } + } + outer->m_authTypes.insert(types.substr(j_types, types.length() - j_types)); + } + } + } } catch (exception&) { cleanup(); diff --git a/shibsp/shibsp-lite.vcxproj b/shibsp/shibsp-lite.vcxproj index 81a331c..ade8cb9 100644 --- a/shibsp/shibsp-lite.vcxproj +++ b/shibsp/shibsp-lite.vcxproj @@ -241,6 +241,7 @@ + diff --git a/shibsp/shibsp-lite.vcxproj.filters b/shibsp/shibsp-lite.vcxproj.filters index 2eff898..654a364 100644 --- a/shibsp/shibsp-lite.vcxproj.filters +++ b/shibsp/shibsp-lite.vcxproj.filters @@ -338,6 +338,9 @@ Header Files\binding + + Header Files + diff --git a/shibsp/shibsp.vcxproj b/shibsp/shibsp.vcxproj index cd56309..8839b82 100644 --- a/shibsp/shibsp.vcxproj +++ b/shibsp/shibsp.vcxproj @@ -181,6 +181,7 @@ + @@ -279,6 +280,7 @@ + diff --git a/shibsp/shibsp.vcxproj.filters b/shibsp/shibsp.vcxproj.filters index f1da19e..b52f78e 100644 --- a/shibsp/shibsp.vcxproj.filters +++ b/shibsp/shibsp.vcxproj.filters @@ -378,6 +378,9 @@ Source Files\handler\impl + + Source Files\attribute + @@ -539,6 +542,9 @@ Header Files\binding + + Header Files + -- 2.1.4