Tagging 2.4RC1 release. tags/2.4RC1 2.4
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Wed, 3 Nov 2010 02:31:45 +0000 (02:31 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Wed, 3 Nov 2010 02:31:45 +0000 (02:31 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/tags/2.4RC1@3365 cb58f699-b61c-0410-a6fe-9272a202ed29

31 files changed:
Shibboleth.sln
apache/mod_apache.cpp
configs/metagen.sh
configs/shibd-suse.in
configure.ac
doc/NOTICE.txt
schemas/shibboleth-2.0-attribute-map.xsd
schemas/shibboleth-2.0-native-sp-config.xsd
shibboleth.spec.in
shibsp/AbstractSPRequest.cpp
shibsp/GSSRequest.h [new file with mode: 0644]
shibsp/Makefile.am
shibsp/ServiceProvider.cpp
shibsp/ServiceProvider.h
shibsp/attribute/Attribute.cpp
shibsp/attribute/AttributeDecoder.h
shibsp/attribute/Base64AttributeDecoder.cpp [new file with mode: 0644]
shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp
shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp
shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp
shibsp/handler/RemotedHandler.h
shibsp/handler/impl/MetadataGenerator.cpp
shibsp/handler/impl/RemotedHandler.cpp
shibsp/handler/impl/StatusHandler.cpp
shibsp/impl/StorageServiceSessionCache.cpp
shibsp/impl/XMLRequestMapper.cpp
shibsp/impl/XMLServiceProvider.cpp
shibsp/shibsp-lite.vcxproj
shibsp/shibsp-lite.vcxproj.filters
shibsp/shibsp.vcxproj
shibsp/shibsp.vcxproj.filters

index 112d8f2..76fd0bb 100644 (file)
@@ -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
index e8b1765..0f0a770 100644 (file)
 # define _CRT_SECURE_NO_DEPRECATE 1
 #endif
 
+#include <shibsp/exceptions.h>
 #include <shibsp/AbstractSPRequest.h>
 #include <shibsp/AccessControl.h>
-#include <shibsp/exceptions.h>
+#include <shibsp/GSSRequest.h>
 #include <shibsp/RequestMapper.h>
 #include <shibsp/SPConfig.h>
 #include <shibsp/ServiceProvider.h>
 #include <shibsp/SessionCache.h>
 #include <shibsp/attribute/Attribute.h>
+
 #include <xercesc/util/XMLUniDefs.hpp>
 #include <xercesc/util/regx/RegularExpression.hpp>
 #include <xmltooling/XMLToolingConfig.h>
@@ -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"),
index 3c1efba..e460240 100755 (executable)
@@ -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
-<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="${ENTITYID}">
+<md:EntityDescriptor ${DECLS}entityID="${ENTITYID}">
   <md:SPSSODescriptor protocolSupportEnumeration="${PROTENUM}">
 EOF
 
@@ -138,7 +154,7 @@ count=1
 for h in ${HOSTS[@]}
 do
   cat << EOF
-      <DiscoveryResponse xmlns="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Binding="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Location="https://$h/Shibboleth.sso/DS" index="$count"/>
+      <disco:DiscoveryResponse Binding="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Location="https://$h/Shibboleth.sso/DS" index="$count"/>
 EOF
   let "count++"
 done
@@ -146,7 +162,7 @@ done
 for h in ${NAKEDHOSTS[@]}
 do
   cat << EOF
-      <DiscoveryResponse xmlns="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Binding="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Location="http://$h/Shibboleth.sso/DS" index="$count"/>
+      <disco:DiscoveryResponse xmlns="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Binding="urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol" Location="http://$h/Shibboleth.sso/DS" index="$count"/>
 EOF
   let "count++"
 done
@@ -286,9 +302,10 @@ if [ -n "$ORGNAME" ] ; then
 EOF
 fi
 
-for c in ${ADMIN[@]}
+count=${#ADMIN[*]}
+for (( i=0; i<count; i++ ))
 do
-  c=(${c//\// })
+  IFS="/"; declare -a c=(${ADMIN[$i]})
   cat <<EOF
   <md:ContactPerson contactType="administrative">
     <md:GivenName>${c[0]}</md:GivenName>
@@ -298,9 +315,10 @@ do
 EOF
 done
 
-for c in ${SUP[@]}
+count=${#SUP[*]}
+for (( i=0; i<count; i++ ))
 do
-  c=(${c//\// })
+  IFS="/"; declare -a c=(${SUP[$i]})
   cat <<EOF
   <md:ContactPerson contactType="support">
     <md:GivenName>${c[0]}</md:GivenName>
@@ -310,9 +328,10 @@ do
 EOF
 done
 
-for c in ${TECH[@]}
+count=${#TECH[*]}
+for (( i=0; i<count; i++ ))
 do
-  c=(${c//\// })
+  IFS="/"; declare -a c=(${TECH[$i]})
   cat <<EOF
   <md:ContactPerson contactType="technical">
     <md:GivenName>${c[0]}</md:GivenName>
@@ -324,4 +343,5 @@ done
 
 cat <<EOF 
 </md:EntityDescriptor>
+
 EOF
index 0be3acf..d644455 100644 (file)
@@ -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
 #
  
index 95b3a47..50a6327 100644 (file)
@@ -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.h>]], [[
-        time_t clock;
-        char buf[26];
-        ctime_r(&clock, buf);
-    ]])],[ac_cv_ctime_args=2],[])
-
-    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <time.h>]], [[
-        time_t clock;
-        char buf[26];
-        ctime_r(&clock, buf, 26);
-    ]])],[ac_cv_ctime_args=3],[])
+    AC_COMPILE_IFELSE(
+        [AC_LANG_PROGRAM([[#include <time.h>]], [[time_t clock; char buf[26]; ctime_r(&clock, buf);]])],
+        [ac_cv_ctime_args=2],[])
+
+    AC_COMPILE_IFELSE(
+        [AC_LANG_PROGRAM([[#include <time.h>]], [[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 <xercesc/dom/DOM.hpp>]], [[using namespace XERCES_CPP_NAMESPACE;
+AC_COMPILE_IFELSE(
+    [AC_LANG_PROGRAM([[#include <xercesc/dom/DOM.hpp>]],
+        [[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 <xercesc/dom/DOM.hpp>]], [[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 <saml/SAMLConfig.h>
-#include <saml/version.h>
-       ]], [[
-#if _OPENSAML_VERSION >= 20400
+AC_LINK_IFELSE(
+    [AC_LANG_PROGRAM([[#include <saml/SAMLConfig.h>
+#include <saml/version.h>]],
+[[#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 <sql.h>
-         #include <sqlext.h>
-         #include <stdio.h>]], [[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 <sql.h>
+ #include <sqlext.h>
+ #include <stdio.h>]],
+            [[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 <gssapi/gssapi.h>
+#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 <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+          ]],[[
+            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)
 
index 8fe5fa7..1ff0bd2 100644 (file)
@@ -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.
index bf95425..d119da3 100644 (file)
         </complexContent>
     </complexType>
 
+    <complexType name="Base64AttributeDecoder">
+        <annotation>
+            <documentation>
+                Decoder for attributes with base64-encoded string values.
+            </documentation>
+        </annotation>
+        <complexContent>
+            <extension base="am:AttributeDecoderType" />
+        </complexContent>
+    </complexType>
+
 </schema>
index 87eeab5..7c39b8e 100644 (file)
@@ -2,7 +2,7 @@
 <schema targetNamespace="urn:mace:shibboleth:2.0:native:sp:config"
        xmlns="http://www.w3.org/2001/XMLSchema"
        xmlns:conf="urn:mace:shibboleth:2.0:native:sp:config"
-    xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+  xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
        xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
        xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
        xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
     <attribute name="checkSpoofing" type="boolean"/>
     <attribute name="spoofKey" type="conf:string"/>
     <attribute name="catchAll" type="boolean"/>
+    <attribute name="extraAuthTypes" type="conf:listOfStrings"/>
     <anyAttribute namespace="##other" processContents="lax"/>
   </complexType>
 
index d510452..5888fbb 100644 (file)
@@ -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
index b6c5e80..6f9b005 100644 (file)
@@ -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 (file)
index 0000000..cb1c56f
--- /dev/null
@@ -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 <shibsp/base.h>
+#include <xmltooling/io/GenericRequest.h>
+
+#ifdef HAVE_GSSGNU
+# include <gss.h>
+#elif defined HAVE_GSSMIT
+# include <gssapi/gssapi.h>
+# include <gssapi/gssapi_generic.h>
+#else
+# include <gssapi.h>
+#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__ */
index 55072f9..1a9c00e 100644 (file)
@@ -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 \
index 30e4423..b358948 100644 (file)
@@ -163,6 +163,7 @@ void SHIBSP_API shibsp::registerServiceProviders()
 
 ServiceProvider::ServiceProvider()
 {
+    m_authTypes.insert("shibboleth");
 }
 
 ServiceProvider::~ServiceProvider()
@@ -266,16 +267,18 @@ pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handl
         pair<bool,bool> requireSession = settings.first->getBool("requireSession");
         pair<bool,const char*> 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<bool,long> 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<bool,long> ServiceProvider::doAuthorization(SPRequest& request) const
         pair<bool,bool> requireSession = settings.first->getBool("requireSession");
         pair<bool,const char*> 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) {
index 2486967..bd76b8c 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <shibsp/util/PropertySet.h>
 
+#include <set>
 #include <xmltooling/Lockable.h>
 
 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<std::string> m_authTypes;
+
     private:
         std::map<std::string,Remoted*> m_listenerMap;
     };
index 6a7da4b..89f69bf 100644 (file)
@@ -56,6 +56,7 @@ namespace shibsp {
     SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::Factory KeyInfoAttributeDecoderFactory;
     SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::Factory DOMAttributeDecoderFactory;
     SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::Factory XMLAttributeDecoderFactory;
+    SHIBSP_DLLLOCAL PluginManager<AttributeDecoder,xmltooling::QName,const DOMElement*>::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)
index 24054f2..03b7485 100644 (file)
@@ -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 (file)
index 0000000..6f1edd7
--- /dev/null
@@ -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 <saml/saml1/core/Assertions.h>
+#include <saml/saml2/core/Assertions.h>
+
+#include <xercesc/util/Base64.hpp>
+
+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<string>& 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<string>& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty
+    ) const
+{
+    auto_ptr<SimpleAttribute> simple(new SimpleAttribute(ids));
+    vector<string>& dest = simple->getValues();
+    vector<XMLObject*>::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<const opensaml::saml2::Attribute*>(xmlObject);
+        if (saml2attr) {
+            const vector<XMLObject*>& 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<const opensaml::saml1::Attribute*>(xmlObject);
+            if (saml1attr) {
+                const vector<XMLObject*>& 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<const XMLByte*>(val.get()),&x);
+                    if (decoded) {
+                        dest.push_back(reinterpret_cast<char*>(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<const NameID*>(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<const XMLByte*>(val.get()),&x);
+            if (decoded) {
+                dest.push_back(reinterpret_cast<char*>(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<const NameIdentifier*>(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<const XMLByte*>(val.get()),&x);
+                if (decoded) {
+                    dest.push_back(reinterpret_cast<char*>(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());
+}
index 18bda2f..639d7e9 100644 (file)
@@ -379,7 +379,7 @@ void XMLFilterImpl::filterAttributes(const FilteringContext& context, vector<Att
         // If no rules apply, remove the attribute entirely.
         if (rulesToRun.empty()) {
             m_log.warn(
-                "no rule found, removing attribute (%s) from (%s)",
+                "no rule found, will remove attribute (%s) from (%s)",
                 attr->getId(), issuer.get() ? issuer.get() : "unknown source"
                 );
             deletedAttributes[a] = true;
@@ -426,6 +426,10 @@ void XMLFilterImpl::filterAttributes(const FilteringContext& context, vector<Att
         Attribute* attr = attributes[a];
 
         if (deletedAttributes[a]) {
+            m_log.warn(
+                "removing filtered attribute (%s) from (%s)",
+                attr->getId(), 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, vector<Att
                 attr->getId(), issuer.get() ? issuer.get() : "unknown source"
                 );
             delete attr;
+            deletedAttributes.erase(deletedAttributes.begin() + a);
             attributes.erase(attributes.begin() + a);
             continue;
         }
index 0c6a453..85a1d88 100644 (file)
@@ -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 <saml/saml2/metadata/Metadata.h>
 #include <saml/saml2/metadata/MetadataCredentialCriteria.h>
 #include <saml/saml2/metadata/MetadataProvider.h>
+#include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/util/NDC.h>
+#include <xmltooling/util/URLEncoder.h>
 #include <xmltooling/util/XMLHelper.h>
 #include <xercesc/util/XMLUniDefs.hpp>
 
@@ -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<AttributeDesignator*> m_SAML1Designators;
         vector<saml2::Attribute*> m_SAML2Designators;
+        vector<string> 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<saml1::Assertion*>& assertions = const_cast<const saml1p::Response*>(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<saml1p::Response> wrapper(response);
     saml1::Assertion* newtoken = assertions.front();
@@ -343,7 +352,7 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const
     pair<bool,bool> 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<shibsp::Attribute>());
         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<saml2p::StatusResponseType> wrapper(srt);
@@ -492,12 +500,12 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const
     saml2p::Response* response = dynamic_cast<saml2p::Response*>(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<saml2::EncryptedAssertion*>& encassertions = const_cast<const saml2p::Response*>(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<shibsp::Attribute>());
         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);
+        }
     }
 }
index 2d27a30..573e6ec 100644 (file)
@@ -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 <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/security/TrustEngine.h>
 #include <xmltooling/util/NDC.h>
+#include <xmltooling/util/URLEncoder.h>
 #include <xmltooling/util/XMLHelper.h>
 #include <xercesc/util/XMLUniDefs.hpp>
 
@@ -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<saml2::Attribute*> m_designators;
         vector< pair<string,bool> > m_sources;
+        vector<string> 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<const AttributeAuthorityDescriptor*>(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<saml2p::StatusResponseType> wrapper(srt);
@@ -400,12 +408,12 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha
     saml2p::Response* response = dynamic_cast<saml2p::Response*>(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<const saml2p::Response*>(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<shibsp::Attribute>());
         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<Attribute> exceptWrapper(exceptAttr);
+
     // We have a master loop over all the possible sources of material.
     for (vector< pair<string,bool> >::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<string>::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<string>::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());
+    }
 }
index 3511af6..ef1a25c 100644 (file)
@@ -26,6 +26,8 @@
 #include <shibsp/handler/Handler.h>
 #include <shibsp/remoting/ListenerService.h>
 
+#include <set>
+
 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<std::string> 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
          */
index 7ae1c9d..7cafe7d 100644 (file)
@@ -89,6 +89,7 @@ namespace shibsp {
             delete m_org;
             delete m_entityAttrs;
             for_each(m_contacts.begin(), m_contacts.end(), xmltooling::cleanup<ContactPerson>());
+            for_each(m_formats.begin(), m_formats.end(), xmltooling::cleanup<NameIDFormat>());
             for_each(m_reqAttrs.begin(), m_reqAttrs.end(), xmltooling::cleanup<RequestedAttribute>());
             for_each(m_attrConsumers.begin(), m_attrConsumers.end(), xmltooling::cleanup<AttributeConsumingService>());
 #endif
@@ -114,6 +115,7 @@ namespace shibsp {
         Organization* m_org;
         EntityAttributes* m_entityAttrs;
         vector<ContactPerson*> m_contacts;
+        vector<NameIDFormat*> m_formats;
         vector<RequestedAttribute*> m_reqAttrs;
         vector<AttributeConsumingService*> 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<RequestedAttribute*>(child.get());
-                if (req) {
+                NameIDFormat* nif = dynamic_cast<NameIDFormat*>(child.get());
+                if (nif) {
                     child.release();
-                    m_reqAttrs.push_back(req);
+                    m_formats.push_back(nif);
                 }
                 else {
-                    AttributeConsumingService* acs = dynamic_cast<AttributeConsumingService*>(child.get());
-                    if (acs) {
+                    RequestedAttribute* req = dynamic_cast<RequestedAttribute*>(child.get());
+                    if (req) {
                         child.release();
-                        m_attrConsumers.push_back(acs);
+                        m_reqAttrs.push_back(req);
                     }
                     else {
-                        UIInfo* info = dynamic_cast<UIInfo*>(child.get());
-                        if (info) {
-                            if (!m_uiinfo) {
-                                child.release();
-                                m_uiinfo = info;
-                            }
-                            else {
-                                m_log.warn("skipping duplicate UIInfo element");
-                            }
+                        AttributeConsumingService* acs = dynamic_cast<AttributeConsumingService*>(child.get());
+                        if (acs) {
+                            child.release();
+                            m_attrConsumers.push_back(acs);
                         }
                         else {
-                            Organization* org = dynamic_cast<Organization*>(child.get());
-                            if (org) {
-                                if (!m_org) {
+                            UIInfo* info = dynamic_cast<UIInfo*>(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<EntityAttributes*>(child.get());
-                                if (ea) {
-                                    if (!m_entityAttrs) {
+                                Organization* org = dynamic_cast<Organization*>(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<EntityAttributes*>(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<bool,long> MetadataGenerator::processMessage(
         entity->setOrganization(m_org->cloneOrganization());
 
     for (vector<ContactPerson*>::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<bool,long> MetadataGenerator::processMessage(
         role = entity->getSPSSODescriptors().front();
     }
 
+    for (vector<NameIDFormat*>::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());
index e7ca6d7..272f667 100644 (file)
  */
 
 #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 <algorithm>
 #include <xmltooling/unicode.h>
+#include <xercesc/util/Base64.hpp>
 
 #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<XSECCryptoX509*> 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<XSECCryptoX509>());
             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<XSECCryptoX509*>& 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<XSECCryptoX509*>& 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<const XMLByte*>(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<string> 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<string>* headers, bool certs) const
 {
     DDF in = DDF(m_address.c_str()).structure();
@@ -257,13 +321,20 @@ DDF RemotedHandler::wrap(const SPRequest& request, const vector<string>* 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<string>::const_iterator h = headers->begin(); h!=headers->end(); ++h) {
-            hdr = request.getHeader(h->c_str());
+        if (headers) {
+            for (vector<string>::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<string>::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<string>* headers
 #endif
     }
 
+#ifdef HAVE_GSSAPI
+    const GSSRequest* gss = dynamic_cast<const GSSRequest*>(&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<const XMLByte*>(contextbuf.value), contextbuf.length, &len);
+                if (out) {
+                    string ctx;
+                    ctx.append(reinterpret_cast<char*>(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;
 }
 
index f2cbd7f..3ef824e 100644 (file)
 #include "handler/RemotedHandler.h"
 #include "util/CGIParser.h"
 
+#include <xmltooling/version.h>
+#include <xmltooling/util/DateTime.h>
+
+#ifdef HAVE_SYS_UTSNAME_H
+# include <sys/utsname.h>
+#endif
+
 using namespace shibsp;
 #ifndef SHIBSP_LITE
 # include "SessionCache.h"
@@ -77,6 +84,7 @@ namespace shibsp {
 
     private:
         pair<bool,long> processMessage(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
+        ostream& systemInfo(ostream& os) const;
 
         set<string> m_acl;
     };
@@ -293,16 +301,20 @@ pair<bool,long> StatusHandler::run(SPRequest& request, bool isHandler) const
         map<string,const char*> props;
         settings.first->getAll(props);
 
+        DateTime now(time(nullptr));
+        now.parseDateTime();
+        auto_ptr_char timestamp(now.getFormattedString());
         request.setContentType("text/xml");
         stringstream msg;
-        msg << "<StatusHandler>";
+        msg << "<StatusHandler time='" << timestamp.get() << "'>";
             msg << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
+                << "' XML-Tooling-C='" << XMLTOOLING_FULLVERSIONDOT
 #ifndef SHIBSP_LITE
                 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
                 << "' OpenSAML-C='" << OPENSAML_FULLVERSIONDOT
 #endif
                 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
-            msg << "<RequestSettings";
+            systemInfo(msg) << "<RequestSettings";
             for (map<string,const char*>::const_iterator p = props.begin(); p != props.end(); ++p)
                 msg << ' ' << p->first << "='" << p->second << "'";
             msg << '>' << target << "</RequestSettings>";
@@ -326,31 +338,39 @@ pair<bool,long> 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 << "<StatusHandler>";
+        msg << "<StatusHandler time='" << timestamp.get() << "'>";
             msg << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
+                << "' XML-Tooling-C='" << XMLTOOLING_FULLVERSIONDOT
 #ifndef SHIBSP_LITE
                 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
                 << "' OpenSAML-C='" << OPENSAML_FULLVERSIONDOT
 #endif
                 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
-            msg << "<Status><Exception type='" << ex.getClassName() << "'>" << ex.what() << "</Exception></Status>";
+            systemInfo(msg) << "<Status><Exception type='" << ex.getClassName() << "'>" << ex.what() << "</Exception></Status>";
         msg << "</StatusHandler>";
         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 << "<StatusHandler>";
+        msg << "<StatusHandler time='" << timestamp.get() << "'>";
             msg << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
+                << "' XML-Tooling-C='" << XMLTOOLING_FULLVERSIONDOT
 #ifndef SHIBSP_LITE
                 << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
                 << "' OpenSAML-C='" << OPENSAML_FULLVERSIONDOT
 #endif
                 << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
-            msg << "<Status><Exception type='std::exception'>" << ex.what() << "</Exception></Status>";
+            systemInfo(msg) << "<Status><Exception type='std::exception'>" << ex.what() << "</Exception></Status>";
         msg << "</StatusHandler>";
         return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
     }
@@ -387,15 +407,22 @@ pair<bool,long> 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 << "<StatusHandler>";
+    s << "<StatusHandler time='" << timestamp.get() << "'>";
     const char* status = "<OK/>";
 
     s << "<Version Xerces-C='" << XERCES_FULLVERSIONDOT
+        << "' XML-Tooling-C='" << XMLTOOLING_FULLVERSIONDOT
         << "' XML-Security-C='" << XSEC_FULLVERSIONDOT
         << "' OpenSAML-C='" << OPENSAML_FULLVERSIONDOT
         << "' Shibboleth='" << PACKAGE_VERSION << "'/>";
 
+    systemInfo(s);
+
     const char* param = nullptr;
     if (param) {
     }
@@ -488,3 +515,78 @@ pair<bool,long> 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 << "<NonWindows";
+        if (*sysinfo.sysname)
+            os << " sysname='" << sysinfo.sysname << "'";
+        if (*sysinfo.nodename)
+            os << " nodename='" << sysinfo.nodename << "'";
+        if (*sysinfo.release)
+            os << " release='" << sysinfo.release << "'";
+        if (*sysinfo.version)
+            os << " version='" << sysinfo.version << "'";
+        if (*sysinfo.machine)
+            os << " machine='" << sysinfo.machine << "'";
+        os << "/>";
+    }
+#elif defined(WIN32)
+    OSVERSIONINFOEX osvi;
+    memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
+    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+    if(GetVersionEx((OSVERSIONINFO*)&osvi)) {
+        os << "<Windows"
+           << " version='" << osvi.dwMajorVersion << "." << osvi.dwMinorVersion << "'"
+           << " build='" << osvi.dwBuildNumber << "'";
+        if (osvi.wServicePackMajor > 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;
+}
index ca3f50a..3641be9 100644 (file)
@@ -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<string>* 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<string,const char*> 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<HTTPResponse*>(&request);
-                if (response) {
-                    pair<string,const char*> 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<HTTPResponse*>(&request);
-                if (response) {
-                    pair<string,const char*> 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<string,const char*> 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<string,const char*> 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<HTTPResponse*>(&request);
+        if (response) {
+            if (!m_outboundHeader.empty())
+                response->setResponseHeader(m_outboundHeader.c_str(), nullptr);
+            pair<string,const char*> 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<HTTPResponse*>(&request);
+        if (response) {
+            if (!m_outboundHeader.empty())
+                response->setResponseHeader(m_outboundHeader.c_str(), nullptr);
+            pair<string,const char*> 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<string,const char*> 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
index 27153bd..9059491 100644 (file)
@@ -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))
index 3179637..1c1d9f4 100644 (file)
@@ -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<string,StorageService*> m_storage;
+        TransactionLog* m_tranLog;
+        map<string,StorageService*> 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<XMLConfig*>(m_outer));
-                m_outer->m_listener->regListener("get::RelayState", const_cast<XMLConfig*>(m_outer));
-                m_outer->m_listener->regListener("set::PostData", const_cast<XMLConfig*>(m_outer));
-                m_outer->m_listener->regListener("get::PostData", const_cast<XMLConfig*>(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<XMLApplication> iapp(new XMLApplication(m_outer, pp, child, defapp));
+            auto_ptr<XMLApplication> 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<bool,const char*> 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();
index 81a331c..ade8cb9 100644 (file)
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="binding\ProtocolProvider.h" />\r
+    <ClInclude Include="GSSRequest.h" />\r
     <ClInclude Include="handler\LogoutInitiator.h" />\r
     <ClInclude Include="remoting\impl\SocketListener.h" />\r
     <ClInclude Include="AbstractSPRequest.h" />\r
index 2eff898..654a364 100644 (file)
     <ClInclude Include="binding\ProtocolProvider.h">\r
       <Filter>Header Files\binding</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="GSSRequest.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="shibsp.rc">\r
index cd56309..8839b82 100644 (file)
   <ItemGroup>\r
     <ClCompile Include="AbstractSPRequest.cpp" />\r
     <ClCompile Include="Application.cpp" />\r
+    <ClCompile Include="attribute\Base64AttributeDecoder.cpp" />\r
     <ClCompile Include="binding\impl\XMLProtocolProvider.cpp" />\r
     <ClCompile Include="handler\impl\DiscoveryFeed.cpp" />\r
     <ClCompile Include="handler\impl\LogoutInitiator.cpp" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="binding\ProtocolProvider.h" />\r
+    <ClInclude Include="GSSRequest.h" />\r
     <ClInclude Include="handler\LogoutInitiator.h" />\r
     <ClInclude Include="remoting\impl\SocketListener.h" />\r
     <ClInclude Include="AbstractSPRequest.h" />\r
index f1da19e..b52f78e 100644 (file)
     <ClCompile Include="handler\impl\DiscoveryFeed.cpp">\r
       <Filter>Source Files\handler\impl</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="attribute\Base64AttributeDecoder.cpp">\r
+      <Filter>Source Files\attribute</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="remoting\impl\SocketListener.h">\r
     <ClInclude Include="binding\ProtocolProvider.h">\r
       <Filter>Header Files\binding</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="GSSRequest.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="shibsp.rc">\r