PortSystem 1.0
name shibboleth
-version 2.3
+version 2.3.1
categories security www shibboleth
maintainers scantor snc
description Shibboleth Native Service Provider
eval file delete [glob ${destroot}${prefix}/lib/${name}/*.la]
}
+post-activate {
+ # Make sure initial conf files are present and set up correctly
+ set confDir ${prefix}/etc/${name}
+ foreach f [glob -tails -directory ${confDir} *.dist] {
+ regexp {(.+)\.dist} $f ign destname
+ if {![file exists ${confDir}/${destname}]} {
+ file copy ${confDir}/${f} ${confDir}/${destname}
+ }
+ }
+ system "cd ${prefix}/etc/${name} && ./keygen.sh -b"
+}
+
startupitem.create yes
startupitem.name shibd
startupitem.executable ${prefix}/sbin/shibd -F -f -p ${prefix}/var/run/${name}/shibd.pid
eval file delete [glob ${destroot}${prefix}/lib/${name}/*.la]
}
+post-activate {
+ # Make sure initial conf files are present and set up correctly
+ set confDir ${prefix}/etc/${name}
+ foreach f [glob -tails -directory ${confDir} *.dist] {
+ regexp {(.+)\.dist} $f ign destname
+ if {![file exists ${confDir}/${destname}]} {
+ file copy ${confDir}/${f} ${confDir}/${destname}
+ }
+ }
+ system "cd ${prefix}/etc/${name} && ./keygen.sh -b"
+}
+
startupitem.create yes
startupitem.name shibd
startupitem.executable ${prefix}/sbin/shibd -F -f -p ${prefix}/var/run/${name}/shibd.pid
//\r
\r
VS_VERSION_INFO VERSIONINFO\r
- FILEVERSION 2,3,0,0\r
- PRODUCTVERSION 2,3,0,0\r
+ FILEVERSION 2,3,1,0\r
+ PRODUCTVERSION 2,3,1,0\r
FILEFLAGSMASK 0x3fL\r
#ifdef _DEBUG\r
FILEFLAGS 0x1L\r
VALUE "Comments", "\0"\r
VALUE "CompanyName", "Internet2\0"\r
VALUE "FileDescription", "Shibboleth ADFSv1 Plugin\0"\r
- VALUE "FileVersion", "2, 3, 0, 0\0"\r
+ VALUE "FileVersion", "2, 3, 1, 0\0"\r
#ifdef SHIBSP_LITE\r
VALUE "InternalName", "adfs-lite\0"\r
#else\r
VALUE "OriginalFilename", "adfs.so\0"\r
#endif\r
VALUE "PrivateBuild", "\0"\r
- VALUE "ProductName", "Shibboleth 2.3\0"\r
- VALUE "ProductVersion", "2, 3, 0, 0\0"\r
+ VALUE "ProductName", "Shibboleth 2.3.1\0"\r
+ VALUE "ProductVersion", "2, 3, 1, 0\0"\r
VALUE "SpecialBuild", "\0"\r
END\r
END\r
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,3,0,0
- PRODUCTVERSION 2,3,0,0
+ FILEVERSION 2,3,1,0
+ PRODUCTVERSION 2,3,1,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
VALUE "Comments", "\0"
VALUE "CompanyName", "Internet2\0"
VALUE "FileDescription", "Shibboleth Apache 1.3 Module\0"
- VALUE "FileVersion", "2, 3, 0, 0\0"
+ VALUE "FileVersion", "2, 3, 1, 0\0"
VALUE "InternalName", "mod_shib_13\0"
VALUE "LegalCopyright", "Copyright © 2009 Internet2\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "mod_shib_13.so\0"
VALUE "PrivateBuild", "\0"
- VALUE "ProductName", "Shibboleth 2.3\0"
- VALUE "ProductVersion", "2, 3, 0, 0\0"
+ VALUE "ProductName", "Shibboleth 2.3.1\0"
+ VALUE "ProductVersion", "2, 3, 1, 0\0"
VALUE "SpecialBuild", "\0"
END
END
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,3,0,0
- PRODUCTVERSION 2,3,0,0
+ FILEVERSION 2,3,1,0
+ PRODUCTVERSION 2,3,1,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
VALUE "Comments", "\0"
VALUE "CompanyName", "Internet2\0"
VALUE "FileDescription", "Shibboleth Apache 2.0 Module\0"
- VALUE "FileVersion", "2, 3, 0, 0\0"
+ VALUE "FileVersion", "2, 3, 1, 0\0"
VALUE "InternalName", "mod_shib_20\0"
VALUE "LegalCopyright", "Copyright © 2009 Internet2\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "mod_shib_20.so\0"
VALUE "PrivateBuild", "\0"
- VALUE "ProductName", "Shibboleth 2.3\0"
- VALUE "ProductVersion", "2, 3, 0, 0\0"
+ VALUE "ProductName", "Shibboleth 2.3.1\0"
+ VALUE "ProductVersion", "2, 3, 1, 0\0"
VALUE "SpecialBuild", "\0"
END
END
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,3,0,0
- PRODUCTVERSION 2,3,0,0
+ FILEVERSION 2,3,1,0
+ PRODUCTVERSION 2,3,1,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
VALUE "Comments", "\0"
VALUE "CompanyName", "Internet2\0"
VALUE "FileDescription", "Shibboleth Apache 2.2 Module\0"
- VALUE "FileVersion", "2, 3, 0, 0\0"
+ VALUE "FileVersion", "2, 3, 1, 0\0"
VALUE "InternalName", "mod_shib_22\0"
VALUE "LegalCopyright", "Copyright © 2009 Internet2\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "mod_shib_22.so\0"
VALUE "PrivateBuild", "\0"
- VALUE "ProductName", "Shibboleth 2.3\0"
- VALUE "ProductVersion", "2, 3, 0, 0\0"
+ VALUE "ProductName", "Shibboleth 2.3.1\0"
+ VALUE "ProductVersion", "2, 3, 1, 0\0"
VALUE "SpecialBuild", "\0"
END
END
#define PACKAGE_NAME "shibboleth"
/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "shibboleth 2.3"
+#define PACKAGE_STRING "shibboleth 2.3.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "shibboleth"
/* Define to the version of this package. */
-#define PACKAGE_VERSION "2.3"
+#define PACKAGE_VERSION "2.3.1"
/* Define to the necessary symbol if this constant uses a non-standard name on
your system. */
/* #undef TM_IN_SYS_TIME */
/* Version number of package */
-#define VERSION "2.3"
+#define VERSION "2.3.1"
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
<AttributeExtractor type="XML" validate="true" path="attribute-map.xml"/>
<!-- Use a SAML query if no attributes are supplied during SSO. -->
- <AttributeResolver type="Query"/>
+ <AttributeResolver type="Query" subjectMatch="true"/>
<!-- Default filtering policy for recognized attributes, lets other data pass. -->
<AttributeFilter type="XML" validate="true" path="attribute-policy.xml"/>
SHIBD_USER=root
pidfile=@-PKGRUNDIR-@/shibd.pid
prog=shibd
-VER=`cat /etc/redhat-release | awk '{print $3}' | awk -F . '{print $1}'`
+#VER=`cat /etc/redhat-release | awk '{print $3}' | awk -F . '{print $1}'`
RETVAL=0
start() {
if [ -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then
/sbin/restorecon $pidfile
fi
- if [ 5 -le $VER ] ; then
- daemon --user $SHIBD_USER --pidfile $pidfile $shibd -p $pidfile -f -w 30
- else
- daemon --user $SHIBD_USER $shibd -p $pidfile -f -w 30
- fi
+ daemon --user $SHIBD_USER $shibd -p $pidfile -f -w 30
RETVAL=$?
echo
stop() {
echo -n $"Stopping $prog: "
- if [ -f $pidfile -a 5 -le $VER ] ; then
- killproc -p $pidfile shibd
- else
- killproc shibd
- fi
+ killproc shibd
RETVAL=$?
echo
#
### BEGIN INIT INFO
# Provides: shibd
-# Required-Start: network
-# Required-Stop: $null
+# Required-Start: $local_fs $remote_fs $network
+# Should-Start: $time
+# Should-Stop: $time
+# Required-Stop: $local_fs $remote_fs $network
# 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.
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.59 for shibboleth 2.3.
+# Generated by GNU Autoconf 2.59 for shibboleth 2.3.1.
#
# Report bugs to <shibboleth-users@internet2.edu>.
#
# Identity of this package.
PACKAGE_NAME='shibboleth'
PACKAGE_TARNAME='shibboleth'
-PACKAGE_VERSION='2.3'
-PACKAGE_STRING='shibboleth 2.3'
+PACKAGE_VERSION='2.3.1'
+PACKAGE_STRING='shibboleth 2.3.1'
PACKAGE_BUGREPORT='shibboleth-users@internet2.edu'
# Factoring default headers for most tests.
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures shibboleth 2.3 to adapt to many kinds of systems.
+\`configure' configures shibboleth 2.3.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of shibboleth 2.3:";;
+ short | recursive ) echo "Configuration of shibboleth 2.3.1:";;
esac
cat <<\_ACEOF
test -n "$ac_init_help" && exit 0
if $ac_init_version; then
cat <<\_ACEOF
-shibboleth configure 2.3
+shibboleth configure 2.3.1
generated by GNU Autoconf 2.59
Copyright (C) 2003 Free Software Foundation, Inc.
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by shibboleth $as_me 2.3, which was
+It was created by shibboleth $as_me 2.3.1, which was
generated by GNU Autoconf 2.59. Invocation command line was
$ $0 $@
# Define the identity of the package.
PACKAGE=shibboleth
- VERSION=2.3
+ VERSION=2.3.1
cat >>confdefs.h <<_ACEOF
} >&5
cat >&5 <<_CSEOF
-This file was extended by shibboleth $as_me 2.3, which was
+This file was extended by shibboleth $as_me 2.3.1, which was
generated by GNU Autoconf 2.59. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
-shibboleth config.status 2.3
+shibboleth config.status 2.3.1
configured by $0, generated by GNU Autoconf 2.59,
with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
AC_PREREQ([2.50])
-AC_INIT([shibboleth], [2.3], [shibboleth-users@internet2.edu], [shibboleth])
+AC_INIT([shibboleth], [2.3.1], [shibboleth-users@internet2.edu], [shibboleth])
AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE([shibboleth],[2.3])
+AM_INIT_AUTOMAKE([shibboleth],[2.3.1])
sinclude(doxygen.m4)
sinclude(acx_pthread.m4)
-Version 2.3
+Version 2.3.1
Welcome to Internet2's Shibboleth
Release Notes
Shibboleth Native SP
-2.3
+2.3.1
NOTE: The shibboleth2.xml configuration format in this release
-is fully compatible with the 2.1 release, but there are some small
+is fully compatible with the 2.1 and 2.2 releases, but there are some small
changes required to eliminate various warnings about deprecated options.
List of issues addressed by this release:
-https://bugs.internet2.edu/jira/browse/SSPCPP/fixforversion/10261
+https://bugs.internet2.edu/jira/browse/SSPCPP/fixforversion/10271
Fully Supported
//\r
\r
VS_VERSION_INFO VERSIONINFO\r
- FILEVERSION 2,3,0,0\r
- PRODUCTVERSION 2,3,0,0\r
+ FILEVERSION 2,3,1,0\r
+ PRODUCTVERSION 2,3,1,0\r
FILEFLAGSMASK 0x3fL\r
#ifdef _DEBUG\r
FILEFLAGS 0x1L\r
VALUE "Comments", "\0"\r
VALUE "CompanyName", "Internet2\0"\r
VALUE "FileDescription", "Shibboleth FastCGI Authorizer\0"\r
- VALUE "FileVersion", "2, 3, 0, 0\0"\r
+ VALUE "FileVersion", "2, 3, 1, 0\0"\r
VALUE "InternalName", "shibauthorizer\0"\r
VALUE "LegalCopyright", "Copyright © 2009 Internet2\0"\r
VALUE "LegalTrademarks", "\0"\r
VALUE "OriginalFilename", "shibauthorizer.exe\0"\r
VALUE "PrivateBuild", "\0"\r
- VALUE "ProductName", "Shibboleth 2.3\0"\r
- VALUE "ProductVersion", "2, 3, 0, 0\0"\r
+ VALUE "ProductName", "Shibboleth 2.3.1\0"\r
+ VALUE "ProductVersion", "2, 3, 1, 0\0"\r
VALUE "SpecialBuild", "\0"\r
END\r
END\r
//\r
\r
VS_VERSION_INFO VERSIONINFO\r
- FILEVERSION 2,3,0,0\r
- PRODUCTVERSION 2,3,0,0\r
+ FILEVERSION 2,3,1,0\r
+ PRODUCTVERSION 2,3,1,0\r
FILEFLAGSMASK 0x3fL\r
#ifdef _DEBUG\r
FILEFLAGS 0x1L\r
VALUE "Comments", "\0"\r
VALUE "CompanyName", "Internet2\0"\r
VALUE "FileDescription", "Shibboleth FastCGI Responder\0"\r
- VALUE "FileVersion", "2, 3, 0, 0\0"\r
+ VALUE "FileVersion", "2, 3, 1, 0\0"\r
VALUE "InternalName", "shibresponder\0"\r
VALUE "LegalCopyright", "Copyright © 2009 Internet2\0"\r
VALUE "LegalTrademarks", "\0"\r
VALUE "OriginalFilename", "shibresponder.exe\0"\r
VALUE "PrivateBuild", "\0"\r
- VALUE "ProductName", "Shibboleth 2.3\0"\r
- VALUE "ProductVersion", "2, 3, 0, 0\0"\r
+ VALUE "ProductName", "Shibboleth 2.3.1\0"\r
+ VALUE "ProductVersion", "2, 3, 1, 0\0"\r
VALUE "SpecialBuild", "\0"\r
END\r
END\r
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,3,0,0
- PRODUCTVERSION 2,3,0,0
+ FILEVERSION 2,3,1,0
+ PRODUCTVERSION 2,3,1,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
VALUE "Comments", "\0"
VALUE "CompanyName", "Internet2\0"
VALUE "FileDescription", "Shibboleth ISAPI Filter / Extension\0"
- VALUE "FileVersion", "2, 3, 0, 0\0"
+ VALUE "FileVersion", "2, 3, 1, 0\0"
VALUE "InternalName", "isapi_shib\0"
VALUE "LegalCopyright", "Copyright © 2009 Internet2\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "isapi_shib.dll\0"
VALUE "PrivateBuild", "\0"
- VALUE "ProductName", "Shibboleth 2.3\0"
- VALUE "ProductVersion", "2, 3, 0, 0\0"
+ VALUE "ProductName", "Shibboleth 2.3.1\0"
+ VALUE "ProductVersion", "2, 3, 1, 0\0"
VALUE "SpecialBuild", "\0"
END
END
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,3,0,0
- PRODUCTVERSION 2,3,0,0
+ FILEVERSION 2,3,1,0
+ PRODUCTVERSION 2,3,1,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
BEGIN
VALUE "CompanyName", "Internet2\0"
VALUE "FileDescription", "Shibboleth Memcache Storage Service Plugin\0"
- VALUE "FileVersion", "2, 3, 0, 0\0"
+ VALUE "FileVersion", "2, 3, 1, 0\0"
VALUE "InternalName", "memcache-store\0"
VALUE "LegalCopyright", "Copyright © 2009 Internet2\0"
VALUE "OriginalFilename", "memcache-store.so\0"
- VALUE "ProductName", "Shibboleth 2.3\0"
- VALUE "ProductVersion", "2, 3, 0, 0\0"
+ VALUE "ProductName", "Shibboleth 2.3.1\0"
+ VALUE "ProductVersion", "2, 3, 1, 0\0"
END
END
BLOCK "VarFileInfo"
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,3,0,0
- PRODUCTVERSION 2,3,0,0
+ FILEVERSION 2,3,1,0
+ PRODUCTVERSION 2,3,1,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
VALUE "Comments", "\0"
VALUE "CompanyName", "Internet2\0"
VALUE "FileDescription", "Shibboleth NSAPI Extension\0"
- VALUE "FileVersion", "2, 3, 0, 0\0"
+ VALUE "FileVersion", "2, 3, 1, 0\0"
VALUE "InternalName", "nsapi_shib\0"
VALUE "LegalCopyright", "Copyright © 2009 Internet2\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "nsapi_shib.dll\0"
VALUE "PrivateBuild", "\0"
- VALUE "ProductName", "Shibboleth 2.3\0"
- VALUE "ProductVersion", "2, 3, 0, 0\0"
+ VALUE "ProductName", "Shibboleth 2.3.1\0"
+ VALUE "ProductVersion", "2, 3, 1, 0\0"
VALUE "SpecialBuild", "\0"
END
END
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 2,3,0,0
- PRODUCTVERSION 2,3,0,0
+ FILEVERSION 2,3,1,0
+ PRODUCTVERSION 2,3,1,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
BEGIN
VALUE "CompanyName", "Internet2\0"
VALUE "FileDescription", "Shibboleth ODBC Storage Service Plugin\0"
- VALUE "FileVersion", "2, 3, 0, 0\0"
+ VALUE "FileVersion", "2, 3, 1, 0\0"
VALUE "InternalName", "odbc-store\0"
VALUE "LegalCopyright", "Copyright © 2009 Internet2\0"
VALUE "OriginalFilename", "odbc-store.so\0"
- VALUE "ProductName", "Shibboleth 2.3\0"
- VALUE "ProductVersion", "2, 3, 0, 0\0"
+ VALUE "ProductName", "Shibboleth 2.3.1\0"
+ VALUE "ProductVersion", "2, 3, 1, 0\0"
END
END
BLOCK "VarFileInfo"
PKG=SHIBsp
-VERSION=2.3
+VERSION=2.3.1
BASEDIR=/
NAME=shibboleth-sp
CATEGORY=application,security
-<?xml version="1.0" encoding="US-ASCII"?>\r
-<schema targetNamespace="urn:mace:shibboleth:2.0:native:sp:config"\r
- xmlns="http://www.w3.org/2001/XMLSchema"\r
- xmlns:conf="urn:mace:shibboleth:2.0:native:sp:config"\r
- xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"\r
- xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"\r
- xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"\r
- elementFormDefault="qualified"\r
- attributeFormDefault="unqualified"\r
- blockDefault="substitution"\r
- version="2.3">\r
-\r
- <import namespace="urn:oasis:names:tc:SAML:2.0:assertion" schemaLocation="saml-schema-assertion-2.0.xsd"/>\r
- <import namespace="urn:oasis:names:tc:SAML:2.0:protocol" schemaLocation="saml-schema-protocol-2.0.xsd"/>\r
- <import namespace="urn:oasis:names:tc:SAML:2.0:metadata" schemaLocation="saml-schema-metadata-2.0.xsd"/>\r
-\r
- <annotation>\r
- <documentation>\r
- 2.0 schema for XML-based configuration of Shibboleth Native SP instances.\r
- First appearing in Shibboleth 2.0 release.\r
- </documentation>\r
- </annotation>\r
-\r
- <simpleType name="string">\r
- <restriction base="string">\r
- <minLength value="1"/>\r
- </restriction>\r
- </simpleType>\r
-\r
- <simpleType name="listOfStrings">\r
- <list itemType="conf:string"/>\r
- </simpleType>\r
-\r
- <simpleType name="listOfURIs">\r
- <list itemType="anyURI"/>\r
- </simpleType>\r
-\r
- <simpleType name="bindingBoolean">\r
- <restriction base="string">\r
- <enumeration value="true"/>\r
- <enumeration value="false"/>\r
- <enumeration value="front"/>\r
- <enumeration value="back"/>\r
- </restriction>\r
- </simpleType>\r
- \r
- <complexType name="PluggableType">\r
- <sequence>\r
- <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="type" type="conf:string" use="required"/>\r
- <anyAttribute namespace="##any" processContents="skip"/>\r
- </complexType>\r
-\r
- <element name="SPConfig">\r
- <complexType>\r
- <annotation>\r
- <documentation>Root of configuration</documentation>\r
- </annotation>\r
- <sequence>\r
- <element ref="conf:Extensions" minOccurs="0"/>\r
- <element ref="conf:OutOfProcess"/>\r
- <element ref="conf:InProcess"/>\r
- <choice minOccurs="0">\r
- <element name="UnixListener">\r
- <complexType>\r
- <attribute name="address" type="conf:string" use="required"/>\r
- </complexType>\r
- </element>\r
- <element name="TCPListener">\r
- <complexType>\r
- <attribute name="address" type="conf:string" use="required"/>\r
- <attribute name="port" type="unsignedInt" use="required"/>\r
- <attribute name="acl" type="conf:listOfStrings"/>\r
- </complexType>\r
- </element>\r
- <element name="Listener" type="conf:PluggableType"/>\r
- </choice>\r
- <element ref="conf:StorageService" minOccurs="0" maxOccurs="unbounded"/>\r
- <element ref="conf:SessionCache" minOccurs="0"/>\r
- <element ref="conf:ReplayCache" minOccurs="0"/>\r
- <element ref="conf:ArtifactMap" minOccurs="0"/>\r
- <element name="RequestMapper" type="conf:PluggableType" minOccurs="0"/>\r
- <element ref="conf:ApplicationDefaults"/>\r
- <element ref="conf:SecurityPolicies"/>\r
- <element ref="conf:TransportOption" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="logger" type="anyURI"/>\r
- <attribute name="clockSkew" type="unsignedInt"/>\r
- <attribute name="unsafeChars" type="conf:string"/>\r
- <attribute name="allowedSchemes" type="conf:listOfStrings"/>\r
- <anyAttribute namespace="##other" processContents="lax"/>\r
- </complexType>\r
- </element>\r
-\r
- <element name="Extensions">\r
- <annotation>\r
- <documentation>Container for extension libraries and custom configuration</documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence>\r
- <element name="Library" minOccurs="0" maxOccurs="unbounded">\r
- <complexType>\r
- <sequence>\r
- <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="path" type="anyURI" use="required"/>\r
- <attribute name="fatal" type="boolean"/>\r
- <anyAttribute namespace="##any" processContents="skip"/>\r
- </complexType>\r
- </element>\r
- <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- </complexType>\r
- </element>\r
-\r
- <element name="StorageService">\r
- <annotation>\r
- <documentation>References StorageService plugins</documentation>\r
- </annotation>\r
- <complexType>\r
- <complexContent>\r
- <restriction base="conf:PluggableType">\r
- <sequence>\r
- <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="id" type="ID" use="required"/>\r
- <attribute name="cleanupInterval" type="unsignedInt"/>\r
- <anyAttribute namespace="##any" processContents="skip"/>\r
- </restriction>\r
- </complexContent>\r
- </complexType>\r
- </element>\r
-\r
- <element name="SessionCache">\r
- <annotation>\r
- <documentation>References SessionCache plugins</documentation>\r
- </annotation>\r
- <complexType>\r
- <complexContent>\r
- <restriction base="conf:PluggableType">\r
- <sequence>\r
- <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="StorageService" type="IDREF"/>\r
- <attribute name="cacheTimeout" type="unsignedInt"/>\r
- <anyAttribute namespace="##any" processContents="skip"/>\r
- </restriction>\r
- </complexContent>\r
- </complexType>\r
- </element>\r
- \r
- <element name="ReplayCache">\r
- <annotation>\r
- <documentation>Ties ReplayCache to a custom StorageService</documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence/>\r
- <attribute name="StorageService" type="IDREF" use="required"/>\r
- </complexType>\r
- </element>\r
- \r
- <element name="ArtifactMap">\r
- <annotation>\r
- <documentation>Customizes an ArtifactMap</documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence/>\r
- <attribute name="StorageService" type="IDREF"/>\r
- <attribute name="context" type="conf:string"/>\r
- <attribute name="artifactTTL" type="unsignedInt"/>\r
- </complexType>\r
- </element>\r
- \r
- <element name="OutOfProcess">\r
- <annotation>\r
- <documentation>Container for out-of-process (shibd) configuration</documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence>\r
- <element ref="conf:Extensions" minOccurs="0"/>\r
- <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="logger" type="anyURI"/>\r
- <attribute name="catchAll" type="boolean"/>\r
- <anyAttribute namespace="##other" processContents="lax"/>\r
- </complexType>\r
- </element>\r
-\r
- <element name="InProcess">\r
- <annotation>\r
- <documentation>\r
- Container for configuration of locally integrated or platform-specific\r
- features (e.g. web server filters)\r
- </documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence>\r
- <element ref="conf:Extensions" minOccurs="0"/>\r
- <element ref="conf:ISAPI" minOccurs="0"/>\r
- <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="logger" type="anyURI"/>\r
- <attribute name="unsetHeaderValue" type="conf:string"/>\r
- <attribute name="checkSpoofing" type="boolean"/>\r
- <attribute name="spoofKey" type="conf:string"/>\r
- <attribute name="catchAll" type="boolean"/>\r
- <anyAttribute namespace="##other" processContents="lax"/>\r
- </complexType>\r
- </element>\r
- \r
- <element name="ISAPI">\r
- <complexType>\r
- <sequence>\r
- <element name="Site" maxOccurs="unbounded">\r
- <complexType>\r
- <sequence>\r
- <element name="Alias" type="string" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="id" type="unsignedInt" use="required"/>\r
- <attribute name="name" type="conf:string" use="required"/>\r
- <attribute name="port" type="unsignedInt"/>\r
- <attribute name="sslport" type="unsignedInt"/>\r
- <attribute name="scheme" type="conf:string"/>\r
- </complexType>\r
- </element>\r
- <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="normalizeRequest" type="boolean"/>\r
- <attribute name="safeHeaderNames" type="boolean"/>\r
- <anyAttribute namespace="##other" processContents="lax"/>\r
- </complexType>\r
- </element>\r
-\r
- <element name="AccessControl" type="conf:UniOperatorType">\r
- <annotation>\r
- <documentation>\r
- A simple example access policy language extension that supersedes Apache .htaccess\r
- </documentation>\r
- </annotation>\r
- </element>\r
- <element name="OR" type="conf:MultiOperatorType"/>\r
- <element name="AND" type="conf:MultiOperatorType"/>\r
- <element name="NOT" type="conf:UniOperatorType"/>\r
- <complexType name="UniOperatorType">\r
- <choice>\r
- <element ref="conf:AND"/>\r
- <element ref="conf:OR"/>\r
- <element ref="conf:NOT"/>\r
- <element ref="conf:Rule"/>\r
- <element ref="conf:RuleRegex"/>\r
- </choice>\r
- </complexType>\r
- <complexType name="MultiOperatorType">\r
- <choice minOccurs="2" maxOccurs="unbounded">\r
- <element ref="conf:AND"/>\r
- <element ref="conf:OR"/>\r
- <element ref="conf:NOT"/>\r
- <element ref="conf:Rule"/>\r
- <element ref="conf:RuleRegex"/>\r
- </choice>\r
- </complexType>\r
- <element name="Rule">\r
- <complexType>\r
- <simpleContent>\r
- <extension base="conf:listOfStrings">\r
- <attribute name="require" type="conf:string" use="required"/>\r
- <attribute name="list" type="boolean"/>\r
- </extension>\r
- </simpleContent>\r
- </complexType>\r
- </element>\r
- <element name="RuleRegex">\r
- <complexType>\r
- <simpleContent>\r
- <extension base="conf:string">\r
- <attribute name="require" type="conf:string" use="required"/>\r
- <attribute name="ignoreCase" type="boolean"/>\r
- </extension>\r
- </simpleContent>\r
- </complexType>\r
- </element>\r
- \r
- <attributeGroup name="ContentSettings">\r
- <attribute name="authType" type="conf:string"/>\r
- <attribute name="requireSession" type="boolean"/>\r
- <attribute name="requireSessionWith" type="conf:string"/>\r
- <attribute name="exportAssertion" type="boolean"/>\r
- <attribute name="redirectToSSL" type="unsignedInt"/>\r
- <attribute name="entityID" type="anyURI"/>\r
- <attribute name="discoveryURL" type="anyURI"/>\r
- <attribute name="isPassive" type="boolean"/>\r
- <attribute name="forceAuthn" type="boolean"/>\r
- <attribute name="authnContextClassRef" type="anyURI"/>\r
- <attribute name="authnContextComparison" type="samlp:AuthnContextComparisonType"/>\r
- <attribute name="NameIDFormat" type="anyURI"/>\r
- <attribute name="SPNameQualifier" type="conf:string"/>\r
- <attribute name="redirectErrors" type="anyURI"/>\r
- <attribute name="sessionError" type="anyURI"/>\r
- <attribute name="metadataError" type="anyURI"/>\r
- <attribute name="accessError" type="anyURI"/>\r
- <attribute name="sslError" type="anyURI"/>\r
- <attribute name="REMOTE_ADDR" type="conf:string"/>\r
- <anyAttribute namespace="##other" processContents="lax"/>\r
- </attributeGroup>\r
- <element name="AccessControlProvider" type="conf:PluggableType"/>\r
- <element name="htaccess" type="conf:PluggableType"/>\r
-\r
- <element name="RequestMap">\r
- <annotation>\r
- <documentation>\r
- Built-in request mapping syntax, decomposes URLs into Host/Path/Path/...\r
- </documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence>\r
- <choice minOccurs="0">\r
- <element ref="conf:htaccess"/>\r
- <element ref="conf:AccessControl"/>\r
- <element ref="conf:AccessControlProvider"/>\r
- </choice>\r
- <choice minOccurs="0" maxOccurs="unbounded">\r
- <element ref="conf:Host"/>\r
- <element ref="conf:HostRegex"/>\r
- </choice>\r
- </sequence>\r
- <attribute name="applicationId" type="conf:string" fixed="default"/>\r
- <attributeGroup ref="conf:ContentSettings"/>\r
- </complexType>\r
- </element>\r
-\r
- <element name="Host">\r
- <complexType>\r
- <sequence>\r
- <choice minOccurs="0">\r
- <element ref="conf:htaccess"/>\r
- <element ref="conf:AccessControl"/>\r
- <element ref="conf:AccessControlProvider"/>\r
- </choice>\r
- <choice minOccurs="0" maxOccurs="unbounded">\r
- <element ref="conf:Path"/>\r
- <element ref="conf:PathRegex"/>\r
- <element ref="conf:Query"/>\r
- </choice>\r
- </sequence>\r
- <attribute name="scheme">\r
- <simpleType>\r
- <restriction base="conf:string">\r
- <enumeration value="http"/>\r
- <enumeration value="https"/>\r
- <enumeration value="ftp"/>\r
- <enumeration value="ldap"/>\r
- <enumeration value="ldaps"/>\r
- </restriction>\r
- </simpleType>\r
- </attribute>\r
- <attribute name="name" type="conf:string" use="required"/>\r
- <attribute name="port" type="unsignedInt"/>\r
- <attribute name="applicationId" type="conf:string"/>\r
- <attributeGroup ref="conf:ContentSettings"/>\r
- </complexType>\r
- </element>\r
- \r
- <element name="HostRegex">\r
- <complexType>\r
- <sequence>\r
- <choice minOccurs="0">\r
- <element ref="conf:htaccess"/>\r
- <element ref="conf:AccessControl"/>\r
- <element ref="conf:AccessControlProvider"/>\r
- </choice>\r
- <choice minOccurs="0" maxOccurs="unbounded">\r
- <element ref="conf:Path"/>\r
- <element ref="conf:PathRegex"/>\r
- <element ref="conf:Query"/>\r
- </choice>\r
- </sequence>\r
- <attribute name="regex" type="conf:string" use="required"/>\r
- <attribute name="ignoreCase" type="boolean"/>\r
- <attribute name="applicationId" type="conf:string"/>\r
- <attributeGroup ref="conf:ContentSettings"/>\r
- </complexType>\r
- </element>\r
-\r
- <element name="Path">\r
- <complexType>\r
- <sequence>\r
- <choice minOccurs="0">\r
- <element ref="conf:htaccess"/>\r
- <element ref="conf:AccessControl"/>\r
- <element ref="conf:AccessControlProvider"/>\r
- </choice>\r
- <choice minOccurs="0" maxOccurs="unbounded">\r
- <element ref="conf:Path"/>\r
- <element ref="conf:PathRegex"/>\r
- <element ref="conf:Query"/>\r
- </choice>\r
- </sequence>\r
- <attribute name="name" type="conf:string" use="required"/>\r
- <attribute name="applicationId" type="conf:string"/>\r
- <attributeGroup ref="conf:ContentSettings"/>\r
- </complexType>\r
- </element>\r
-\r
- <element name="PathRegex">\r
- <complexType>\r
- <sequence>\r
- <choice minOccurs="0">\r
- <element ref="conf:htaccess"/>\r
- <element ref="conf:AccessControl"/>\r
- <element ref="conf:AccessControlProvider"/>\r
- </choice>\r
- <element ref="conf:Query" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="regex" type="conf:string" use="required"/>\r
- <attribute name="ignoreCase" type="boolean"/>\r
- <attribute name="applicationId" type="conf:string"/>\r
- <attributeGroup ref="conf:ContentSettings"/>\r
- </complexType>\r
- </element>\r
- \r
- <element name="Query">\r
- <complexType>\r
- <sequence>\r
- <choice minOccurs="0">\r
- <element ref="conf:htaccess"/>\r
- <element ref="conf:AccessControl"/>\r
- <element ref="conf:AccessControlProvider"/>\r
- </choice>\r
- <element ref="conf:Query" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="name" type="conf:string" use="required"/>\r
- <attribute name="regex" type="conf:string"/>\r
- <attributeGroup ref="conf:ContentSettings"/>\r
- </complexType>\r
- </element>\r
- \r
- <element name="ApplicationDefaults">\r
- <annotation>\r
- <documentation>Container for default settings and application-specific overrides</documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence>\r
- <element ref="conf:Sessions"/>\r
- <element ref="conf:Errors" minOccurs="0"/>\r
- <element ref="conf:RelyingParty" minOccurs="0" maxOccurs="unbounded"/>\r
- <element ref="conf:Notify" minOccurs="0" maxOccurs="unbounded"/>\r
- <element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>\r
- <element name="MetadataProvider" type="conf:PluggableType"/>\r
- <element name="TrustEngine" type="conf:PluggableType"/>\r
- <element name="AttributeExtractor" type="conf:PluggableType" minOccurs="0"/>\r
- <element name="AttributeResolver" type="conf:PluggableType" minOccurs="0"/>\r
- <element name="AttributeFilter" type="conf:PluggableType" minOccurs="0"/>\r
- <element name="CredentialResolver" type="conf:PluggableType" minOccurs="0"/>\r
- <element ref="conf:ApplicationOverride" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="id" type="conf:string" fixed="default"/>\r
- <attribute name="entityID" type="anyURI" use="required"/>\r
- <attribute name="policyId" type="conf:string" use="required"/>\r
- <attributeGroup ref="conf:ApplicationGroup"/>\r
- <attributeGroup ref="conf:RelyingPartyGroup"/>\r
- <anyAttribute namespace="##other" processContents="lax"/>\r
- </complexType>\r
- </element>\r
- \r
- <element name="ApplicationOverride">\r
- <annotation>\r
- <documentation>Container for application-specific overrides</documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence>\r
- <element ref="conf:Sessions" minOccurs="0"/>\r
- <element ref="conf:Errors" minOccurs="0"/>\r
- <element ref="conf:RelyingParty" minOccurs="0" maxOccurs="unbounded"/>\r
- <element ref="conf:Notify" minOccurs="0" maxOccurs="unbounded"/>\r
- <element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>\r
- <element name="MetadataProvider" type="conf:PluggableType" minOccurs="0"/>\r
- <element name="TrustEngine" type="conf:PluggableType" minOccurs="0"/>\r
- <element name="AttributeExtractor" type="conf:PluggableType" minOccurs="0"/>\r
- <element name="AttributeResolver" type="conf:PluggableType" minOccurs="0"/>\r
- <element name="AttributeFilter" type="conf:PluggableType" minOccurs="0"/>\r
- <element name="CredentialResolver" type="conf:PluggableType" minOccurs="0"/>\r
- </sequence>\r
- <attribute name="id" type="conf:string" use="required"/>\r
- <attribute name="entityID" type="anyURI"/>\r
- <attribute name="policyId" type="conf:string"/>\r
- <attributeGroup ref="conf:ApplicationGroup"/>\r
- <attributeGroup ref="conf:RelyingPartyGroup"/>\r
- <anyAttribute namespace="##other" processContents="lax"/>\r
- </complexType>\r
- </element>\r
-\r
- <attributeGroup name="ApplicationGroup">\r
- <attribute name="homeURL" type="anyURI"/>\r
- <attribute name="REMOTE_USER" type="conf:listOfStrings"/>\r
- <attribute name="unsetHeaders" type="conf:listOfStrings"/>\r
- <attribute name="metadataAttributePrefix" type="conf:string"/>\r
- <attribute name="attributePrefix" type="conf:string"/>\r
- </attributeGroup>\r
-\r
- <attributeGroup name="RelyingPartyGroup">\r
- <attribute name="authType" type="conf:string"/>\r
- <attribute name="authUsername" type="conf:string"/>\r
- <attribute name="authPassword" type="conf:string"/>\r
- <attribute name="signing" type="conf:bindingBoolean"/>\r
- <attribute name="signingAlg" type="anyURI"/>\r
- <attribute name="digestAlg" type="anyURI"/>\r
- <attribute name="encryption" type="conf:bindingBoolean"/>\r
- <attribute name="encryptionAlg" type="anyURI"/>\r
- <attribute name="keyName" type="conf:string"/>\r
- <attribute name="artifactEndpointIndex" type="unsignedShort"/>\r
- <attribute name="chunkedEncoding" type="boolean"/>\r
- <attribute name="connectTimeout" type="unsignedShort"/>\r
- <attribute name="timeout" type="unsignedShort"/>\r
- <attribute name="requireConfidentiality" type="boolean"/>\r
- <attribute name="requireTransportAuth" type="boolean"/>\r
- <attribute name="requireSignedAssertions" type="boolean"/>\r
- </attributeGroup>\r
- \r
- <element name="Sessions">\r
- <annotation>\r
- <documentation>Container for specifying protocol handlers and session policy</documentation>\r
- </annotation>\r
- <complexType>\r
- <choice minOccurs="0" maxOccurs="unbounded">\r
- <element ref="conf:SessionInitiator"/>\r
- <element ref="conf:LogoutInitiator"/>\r
- <element ref="md:AssertionConsumerService"/>\r
- <element ref="md:ArtifactResolutionService"/>\r
- <element ref="md:SingleLogoutService"/>\r
- <element ref="md:ManageNameIDService"/>\r
- <element name="Handler">\r
- <complexType>\r
- <complexContent>\r
- <restriction base="conf:PluggableType">\r
- <sequence>\r
- <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="Location" type="anyURI" use="required"/>\r
- <attribute name="acl" type="conf:listOfStrings"/>\r
- <anyAttribute namespace="##any" processContents="skip"/>\r
- </restriction>\r
- </complexContent>\r
- </complexType>\r
- </element>\r
- </choice>\r
- <attribute name="handlerURL" type="anyURI" use="required"/>\r
- <attribute name="handlerSSL" type="boolean"/>\r
- <attribute name="exportLocation" type="conf:string"/>\r
- <attribute name="exportACL" type="conf:listOfStrings"/>\r
- <attribute name="cookieName" type="conf:string"/>\r
- <attribute name="cookieProps" type="conf:string"/>\r
- <attribute name="cookieLifetime" type="unsignedInt"/>\r
- <attribute name="idpHistory" type="boolean"/>\r
- <attribute name="idpHistoryDays" type="unsignedInt"/>\r
- <attribute name="lifetime" type="unsignedInt"/>\r
- <attribute name="timeout" type="unsignedInt"/>\r
- <attribute name="maxTimeSinceAuthn" type="unsignedInt"/>\r
- <attribute name="checkAddress" type="boolean"/>\r
- <attribute name="consistentAddress" type="boolean"/>\r
- <attribute name="postData" type="conf:string"/>\r
- <attribute name="postLimit" type="positiveInteger"/>\r
- <attribute name="postTemplate" type="conf:string"/>\r
- <attribute name="postExpire" type="boolean"/>\r
- <anyAttribute namespace="##other" processContents="lax"/>\r
- </complexType>\r
- </element>\r
-\r
- <attribute name="policyId" type="conf:string">\r
- <annotation>\r
- <documentation>Used to reference Policy elements from profile endpoints.</documentation>\r
- </annotation>\r
- </attribute>\r
-\r
- <element name="SessionInitiator">\r
- <annotation>\r
- <documentation>Used to specify handlers that can issue AuthnRequests or perform discovery</documentation>\r
- </annotation>\r
- <complexType>\r
- <complexContent>\r
- <restriction base="conf:PluggableType">\r
- <sequence>\r
- <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="Location" type="anyURI"/>\r
- <attribute name="id" type="conf:string"/>\r
- <attribute name="isDefault" type="boolean"/>\r
- <attribute name="relayState" type="conf:string"/>\r
- <attribute name="entityIDParam" type="conf:string"/>\r
- <attribute name="entityID" type="anyURI"/>\r
- <attribute name="URL" type="anyURI"/>\r
- <attribute name="outgoingBindings" type="conf:listOfURIs"/>\r
- <attribute name="template" type="anyURI"/>\r
- <attribute name="postArtifact" type="boolean"/>\r
- <attribute name="acsByIndex" type="boolean"/>\r
- <attribute name="acsIndex" type="unsignedShort"/>\r
- <attribute name="defaultACSIndex" type="unsignedShort"/> <!-- deprecated -->\r
- <attribute name="isPassive" type="boolean"/>\r
- <attribute name="forceAuthn" type="boolean"/>\r
- <attribute name="authnContextClassRef" type="anyURI"/>\r
- <attribute name="authnContextComparison" type="samlp:AuthnContextComparisonType"/>\r
- <attribute name="NameIDFormat" type="anyURI"/>\r
- <attribute name="SPNameQualifier" type="conf:string"/>\r
- <attribute name="requestDelegation" type="boolean"/>\r
- <anyAttribute namespace="##any" processContents="skip"/>\r
- </restriction>\r
- </complexContent>\r
- </complexType>\r
- </element>\r
-\r
- <element name="LogoutInitiator">\r
- <annotation>\r
- <documentation>Used to specify handlers that can issue LogoutRequests</documentation>\r
- </annotation>\r
- <complexType>\r
- <complexContent>\r
- <restriction base="conf:PluggableType">\r
- <sequence>\r
- <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="Location" type="anyURI"/>\r
- <attribute name="relayState" type="conf:string"/>\r
- <attribute name="outgoingBindings" type="conf:listOfURIs"/>\r
- <attribute name="template" type="anyURI"/>\r
- <attribute name="postArtifact" type="boolean"/>\r
- <anyAttribute namespace="##any" processContents="skip"/>\r
- </restriction>\r
- </complexContent>\r
- </complexType>\r
- </element>\r
- \r
- <element name="Errors">\r
- <annotation>\r
- <documentation>Container for error templates and associated details</documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence>\r
- <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>\r
- </sequence>\r
- <attribute name="redirectErrors" type="anyURI"/>\r
- <attribute name="session" type="anyURI"/>\r
- <attribute name="metadata" type="anyURI"/>\r
- <attribute name="access" type="anyURI"/>\r
- <attribute name="ssl" type="anyURI"/>\r
- <attribute name="localLogout" type="anyURI"/>\r
- <attribute name="globalLogout" type="anyURI"/>\r
- <attribute name="partialLogout" type="anyURI"/>\r
- <attribute name="supportContact" type="conf:string"/>\r
- <attribute name="logoLocation" type="anyURI"/>\r
- <attribute name="styleSheet" type="anyURI"/>\r
- <anyAttribute namespace="##any" processContents="skip"/>\r
- </complexType>\r
- </element>\r
-\r
- <element name="RelyingParty">\r
- <annotation>\r
- <documentation>Container for specifying settings to use with particular peers</documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence/>\r
- <attribute name="Name" type="conf:string" use="required"/>\r
- <attributeGroup ref="conf:RelyingPartyGroup"/>\r
- <attribute name="entityID" type="anyURI"/>\r
- <anyAttribute namespace="##other" processContents="lax"/>\r
- </complexType>\r
- </element>\r
- \r
- <element name="Notify">\r
- <annotation>\r
- <documentation>Used to specify locations to receive application notifications</documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence/>\r
- <attribute name="Channel" use="required">\r
- <simpleType>\r
- <restriction base="string">\r
- <enumeration value="front"/>\r
- <enumeration value="back"/>\r
- </restriction>\r
- </simpleType>\r
- </attribute>\r
- <attribute name="Location" type="anyURI" use="required"/>\r
- <anyAttribute namespace="##any" processContents="skip"/>\r
- </complexType>\r
- </element>\r
- \r
- <element name="SecurityPolicies">\r
- <annotation>\r
- <documentation>Container for specifying sets of policy rules to apply to incoming messages</documentation>\r
- </annotation>\r
- <complexType>\r
- <sequence>\r
- <element name="Policy" minOccurs="1" maxOccurs="unbounded">\r
- <annotation>\r
- <documentation>Specifies a set of SecurityPolicyRule plugins</documentation>\r
- </annotation>\r
- <complexType>\r
- <choice>\r
- <element name="Rule" type="conf:PluggableType" minOccurs="1" maxOccurs="unbounded"/>\r
- <element name="PolicyRule" type="conf:PluggableType" minOccurs="1" maxOccurs="unbounded"/>\r
- </choice>\r
- <attribute name="id" type="conf:string" use="required"/>\r
- <attribute name="validate" type="boolean"/>\r
- <anyAttribute namespace="##any" processContents="skip"/>\r
- </complexType>\r
- </element>\r
- </sequence>\r
- </complexType>\r
- </element>\r
-\r
- <element name="TransportOption">\r
- <annotation>\r
- <documentation>Implementation-specific option to pass to SOAPTransport provider.</documentation>\r
- </annotation>\r
- <complexType>\r
- <simpleContent>\r
- <extension base="anySimpleType">\r
- <attribute name="provider" type="conf:string" use="required"/>\r
- <attribute name="option" type="conf:string" use="required"/>\r
- </extension>\r
- </simpleContent>\r
- </complexType>\r
- </element>\r
- \r
-</schema>\r
+<?xml version="1.0" encoding="US-ASCII"?>
+<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: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"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ blockDefault="substitution"
+ version="2.3">
+
+ <import namespace="urn:oasis:names:tc:SAML:2.0:assertion" schemaLocation="saml-schema-assertion-2.0.xsd"/>
+ <import namespace="urn:oasis:names:tc:SAML:2.0:protocol" schemaLocation="saml-schema-protocol-2.0.xsd"/>
+ <import namespace="urn:oasis:names:tc:SAML:2.0:metadata" schemaLocation="saml-schema-metadata-2.0.xsd"/>
+
+ <annotation>
+ <documentation>
+ 2.0 schema for XML-based configuration of Shibboleth Native SP instances.
+ First appearing in Shibboleth 2.0 release.
+ </documentation>
+ </annotation>
+
+ <simpleType name="string">
+ <restriction base="string">
+ <minLength value="1"/>
+ </restriction>
+ </simpleType>
+
+ <simpleType name="listOfStrings">
+ <list itemType="conf:string"/>
+ </simpleType>
+
+ <simpleType name="listOfURIs">
+ <list itemType="anyURI"/>
+ </simpleType>
+
+ <simpleType name="bindingBoolean">
+ <restriction base="string">
+ <enumeration value="true"/>
+ <enumeration value="false"/>
+ <enumeration value="front"/>
+ <enumeration value="back"/>
+ </restriction>
+ </simpleType>
+
+ <complexType name="PluggableType">
+ <sequence>
+ <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="type" type="conf:string" use="required"/>
+ <anyAttribute namespace="##any" processContents="skip"/>
+ </complexType>
+
+ <element name="SPConfig">
+ <complexType>
+ <annotation>
+ <documentation>Root of configuration</documentation>
+ </annotation>
+ <sequence>
+ <element ref="conf:Extensions" minOccurs="0"/>
+ <element ref="conf:OutOfProcess"/>
+ <element ref="conf:InProcess"/>
+ <choice minOccurs="0">
+ <element name="UnixListener">
+ <complexType>
+ <attribute name="address" type="conf:string" use="required"/>
+ </complexType>
+ </element>
+ <element name="TCPListener">
+ <complexType>
+ <attribute name="address" type="conf:string" use="required"/>
+ <attribute name="port" type="unsignedInt" use="required"/>
+ <attribute name="acl" type="conf:listOfStrings"/>
+ </complexType>
+ </element>
+ <element name="Listener" type="conf:PluggableType"/>
+ </choice>
+ <element ref="conf:StorageService" minOccurs="0" maxOccurs="unbounded"/>
+ <element ref="conf:SessionCache" minOccurs="0"/>
+ <element ref="conf:ReplayCache" minOccurs="0"/>
+ <element ref="conf:ArtifactMap" minOccurs="0"/>
+ <element name="RequestMapper" type="conf:PluggableType" minOccurs="0"/>
+ <element ref="conf:ApplicationDefaults"/>
+ <element ref="conf:SecurityPolicies"/>
+ <element ref="conf:TransportOption" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="logger" type="anyURI"/>
+ <attribute name="clockSkew" type="unsignedInt"/>
+ <attribute name="unsafeChars" type="conf:string"/>
+ <attribute name="allowedSchemes" type="conf:listOfStrings"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ </element>
+
+ <element name="Extensions">
+ <annotation>
+ <documentation>Container for extension libraries and custom configuration</documentation>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element name="Library" minOccurs="0" maxOccurs="unbounded">
+ <complexType>
+ <sequence>
+ <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="path" type="anyURI" use="required"/>
+ <attribute name="fatal" type="boolean"/>
+ <anyAttribute namespace="##any" processContents="skip"/>
+ </complexType>
+ </element>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ </complexType>
+ </element>
+
+ <element name="StorageService">
+ <annotation>
+ <documentation>References StorageService plugins</documentation>
+ </annotation>
+ <complexType>
+ <complexContent>
+ <restriction base="conf:PluggableType">
+ <sequence>
+ <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="id" type="ID" use="required"/>
+ <attribute name="cleanupInterval" type="unsignedInt"/>
+ <anyAttribute namespace="##any" processContents="skip"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ </element>
+
+ <element name="SessionCache">
+ <annotation>
+ <documentation>References SessionCache plugins</documentation>
+ </annotation>
+ <complexType>
+ <complexContent>
+ <restriction base="conf:PluggableType">
+ <sequence>
+ <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="StorageService" type="IDREF"/>
+ <attribute name="cacheTimeout" type="unsignedInt"/>
+ <anyAttribute namespace="##any" processContents="skip"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ </element>
+
+ <element name="ReplayCache">
+ <annotation>
+ <documentation>Ties ReplayCache to a custom StorageService</documentation>
+ </annotation>
+ <complexType>
+ <sequence/>
+ <attribute name="StorageService" type="IDREF" use="required"/>
+ </complexType>
+ </element>
+
+ <element name="ArtifactMap">
+ <annotation>
+ <documentation>Customizes an ArtifactMap</documentation>
+ </annotation>
+ <complexType>
+ <sequence/>
+ <attribute name="StorageService" type="IDREF"/>
+ <attribute name="context" type="conf:string"/>
+ <attribute name="artifactTTL" type="unsignedInt"/>
+ </complexType>
+ </element>
+
+ <element name="OutOfProcess">
+ <annotation>
+ <documentation>Container for out-of-process (shibd) configuration</documentation>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="conf:Extensions" minOccurs="0"/>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="logger" type="anyURI"/>
+ <attribute name="catchAll" type="boolean"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ </element>
+
+ <element name="InProcess">
+ <annotation>
+ <documentation>
+ Container for configuration of locally integrated or platform-specific
+ features (e.g. web server filters)
+ </documentation>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="conf:Extensions" minOccurs="0"/>
+ <element ref="conf:ISAPI" minOccurs="0"/>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="logger" type="anyURI"/>
+ <attribute name="unsetHeaderValue" type="conf:string"/>
+ <attribute name="checkSpoofing" type="boolean"/>
+ <attribute name="spoofKey" type="conf:string"/>
+ <attribute name="catchAll" type="boolean"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ </element>
+
+ <element name="ISAPI">
+ <complexType>
+ <sequence>
+ <element name="Site" maxOccurs="unbounded">
+ <complexType>
+ <sequence>
+ <element name="Alias" type="string" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="id" type="unsignedInt" use="required"/>
+ <attribute name="name" type="conf:string" use="required"/>
+ <attribute name="port" type="unsignedInt"/>
+ <attribute name="sslport" type="unsignedInt"/>
+ <attribute name="scheme" type="conf:string"/>
+ </complexType>
+ </element>
+ <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="normalizeRequest" type="boolean"/>
+ <attribute name="safeHeaderNames" type="boolean"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ </element>
+
+ <element name="AccessControl" type="conf:UniOperatorType">
+ <annotation>
+ <documentation>
+ A simple example access policy language extension that supersedes Apache .htaccess
+ </documentation>
+ </annotation>
+ </element>
+ <element name="OR" type="conf:MultiOperatorType"/>
+ <element name="AND" type="conf:MultiOperatorType"/>
+ <element name="NOT" type="conf:UniOperatorType"/>
+ <complexType name="UniOperatorType">
+ <choice>
+ <element ref="conf:AND"/>
+ <element ref="conf:OR"/>
+ <element ref="conf:NOT"/>
+ <element ref="conf:Rule"/>
+ <element ref="conf:RuleRegex"/>
+ </choice>
+ </complexType>
+ <complexType name="MultiOperatorType">
+ <choice minOccurs="2" maxOccurs="unbounded">
+ <element ref="conf:AND"/>
+ <element ref="conf:OR"/>
+ <element ref="conf:NOT"/>
+ <element ref="conf:Rule"/>
+ <element ref="conf:RuleRegex"/>
+ </choice>
+ </complexType>
+ <element name="Rule">
+ <complexType>
+ <simpleContent>
+ <extension base="conf:listOfStrings">
+ <attribute name="require" type="conf:string" use="required"/>
+ <attribute name="list" type="boolean"/>
+ </extension>
+ </simpleContent>
+ </complexType>
+ </element>
+ <element name="RuleRegex">
+ <complexType>
+ <simpleContent>
+ <extension base="conf:string">
+ <attribute name="require" type="conf:string" use="required"/>
+ <attribute name="ignoreCase" type="boolean"/>
+ </extension>
+ </simpleContent>
+ </complexType>
+ </element>
+
+ <attributeGroup name="ContentSettings">
+ <attribute name="authType" type="conf:string"/>
+ <attribute name="requireSession" type="boolean"/>
+ <attribute name="requireSessionWith" type="conf:string"/>
+ <attribute name="exportAssertion" type="boolean"/>
+ <attribute name="redirectToSSL" type="unsignedInt"/>
+ <attribute name="entityID" type="anyURI"/>
+ <attribute name="discoveryURL" type="anyURI"/>
+ <attribute name="isPassive" type="boolean"/>
+ <attribute name="forceAuthn" type="boolean"/>
+ <attribute name="authnContextClassRef" type="anyURI"/>
+ <attribute name="authnContextComparison" type="samlp:AuthnContextComparisonType"/>
+ <attribute name="NameIDFormat" type="anyURI"/>
+ <attribute name="SPNameQualifier" type="conf:string"/>
+ <attribute name="redirectErrors" type="anyURI"/>
+ <attribute name="sessionError" type="anyURI"/>
+ <attribute name="metadataError" type="anyURI"/>
+ <attribute name="accessError" type="anyURI"/>
+ <attribute name="sslError" type="anyURI"/>
+ <attribute name="REMOTE_ADDR" type="conf:string"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </attributeGroup>
+ <element name="AccessControlProvider" type="conf:PluggableType"/>
+ <element name="htaccess" type="conf:PluggableType"/>
+
+ <element name="RequestMap">
+ <annotation>
+ <documentation>
+ Built-in request mapping syntax, decomposes URLs into Host/Path/Path/...
+ </documentation>
+ </annotation>
+ <complexType>
+ <sequence>
+ <choice minOccurs="0">
+ <element ref="conf:htaccess"/>
+ <element ref="conf:AccessControl"/>
+ <element ref="conf:AccessControlProvider"/>
+ </choice>
+ <choice minOccurs="0" maxOccurs="unbounded">
+ <element ref="conf:Host"/>
+ <element ref="conf:HostRegex"/>
+ </choice>
+ </sequence>
+ <attribute name="applicationId" type="conf:string" fixed="default"/>
+ <attributeGroup ref="conf:ContentSettings"/>
+ </complexType>
+ </element>
+
+ <element name="Host">
+ <complexType>
+ <sequence>
+ <choice minOccurs="0">
+ <element ref="conf:htaccess"/>
+ <element ref="conf:AccessControl"/>
+ <element ref="conf:AccessControlProvider"/>
+ </choice>
+ <choice minOccurs="0" maxOccurs="unbounded">
+ <element ref="conf:Path"/>
+ <element ref="conf:PathRegex"/>
+ <element ref="conf:Query"/>
+ </choice>
+ </sequence>
+ <attribute name="scheme">
+ <simpleType>
+ <restriction base="conf:string">
+ <enumeration value="http"/>
+ <enumeration value="https"/>
+ <enumeration value="ftp"/>
+ <enumeration value="ldap"/>
+ <enumeration value="ldaps"/>
+ </restriction>
+ </simpleType>
+ </attribute>
+ <attribute name="name" type="conf:string" use="required"/>
+ <attribute name="port" type="unsignedInt"/>
+ <attribute name="applicationId" type="conf:string"/>
+ <attributeGroup ref="conf:ContentSettings"/>
+ </complexType>
+ </element>
+
+ <element name="HostRegex">
+ <complexType>
+ <sequence>
+ <choice minOccurs="0">
+ <element ref="conf:htaccess"/>
+ <element ref="conf:AccessControl"/>
+ <element ref="conf:AccessControlProvider"/>
+ </choice>
+ <choice minOccurs="0" maxOccurs="unbounded">
+ <element ref="conf:Path"/>
+ <element ref="conf:PathRegex"/>
+ <element ref="conf:Query"/>
+ </choice>
+ </sequence>
+ <attribute name="regex" type="conf:string" use="required"/>
+ <attribute name="ignoreCase" type="boolean"/>
+ <attribute name="applicationId" type="conf:string"/>
+ <attributeGroup ref="conf:ContentSettings"/>
+ </complexType>
+ </element>
+
+ <element name="Path">
+ <complexType>
+ <sequence>
+ <choice minOccurs="0">
+ <element ref="conf:htaccess"/>
+ <element ref="conf:AccessControl"/>
+ <element ref="conf:AccessControlProvider"/>
+ </choice>
+ <choice minOccurs="0" maxOccurs="unbounded">
+ <element ref="conf:Path"/>
+ <element ref="conf:PathRegex"/>
+ <element ref="conf:Query"/>
+ </choice>
+ </sequence>
+ <attribute name="name" type="conf:string" use="required"/>
+ <attribute name="applicationId" type="conf:string"/>
+ <attributeGroup ref="conf:ContentSettings"/>
+ </complexType>
+ </element>
+
+ <element name="PathRegex">
+ <complexType>
+ <sequence>
+ <choice minOccurs="0">
+ <element ref="conf:htaccess"/>
+ <element ref="conf:AccessControl"/>
+ <element ref="conf:AccessControlProvider"/>
+ </choice>
+ <element ref="conf:Query" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="regex" type="conf:string" use="required"/>
+ <attribute name="ignoreCase" type="boolean"/>
+ <attribute name="applicationId" type="conf:string"/>
+ <attributeGroup ref="conf:ContentSettings"/>
+ </complexType>
+ </element>
+
+ <element name="Query">
+ <complexType>
+ <sequence>
+ <choice minOccurs="0">
+ <element ref="conf:htaccess"/>
+ <element ref="conf:AccessControl"/>
+ <element ref="conf:AccessControlProvider"/>
+ </choice>
+ <element ref="conf:Query" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="name" type="conf:string" use="required"/>
+ <attribute name="regex" type="conf:string"/>
+ <attributeGroup ref="conf:ContentSettings"/>
+ </complexType>
+ </element>
+
+ <element name="ApplicationDefaults">
+ <annotation>
+ <documentation>Container for default settings and application-specific overrides</documentation>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="conf:Sessions"/>
+ <element ref="conf:Errors" minOccurs="0"/>
+ <element ref="conf:RelyingParty" minOccurs="0" maxOccurs="unbounded"/>
+ <element ref="conf:Notify" minOccurs="0" maxOccurs="unbounded"/>
+ <element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>
+ <element name="MetadataProvider" type="conf:PluggableType"/>
+ <element name="TrustEngine" type="conf:PluggableType"/>
+ <element name="AttributeExtractor" type="conf:PluggableType" minOccurs="0"/>
+ <element name="AttributeResolver" type="conf:PluggableType" minOccurs="0"/>
+ <element name="AttributeFilter" type="conf:PluggableType" minOccurs="0"/>
+ <element name="CredentialResolver" type="conf:PluggableType" minOccurs="0"/>
+ <element ref="conf:ApplicationOverride" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="id" type="conf:string" fixed="default"/>
+ <attribute name="entityID" type="anyURI" use="required"/>
+ <attribute name="policyId" type="conf:string" use="required"/>
+ <attributeGroup ref="conf:ApplicationGroup"/>
+ <attributeGroup ref="conf:RelyingPartyGroup"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ </element>
+
+ <element name="ApplicationOverride">
+ <annotation>
+ <documentation>Container for application-specific overrides</documentation>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="conf:Sessions" minOccurs="0"/>
+ <element ref="conf:Errors" minOccurs="0"/>
+ <element ref="conf:RelyingParty" minOccurs="0" maxOccurs="unbounded"/>
+ <element ref="conf:Notify" minOccurs="0" maxOccurs="unbounded"/>
+ <element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>
+ <element name="MetadataProvider" type="conf:PluggableType" minOccurs="0"/>
+ <element name="TrustEngine" type="conf:PluggableType" minOccurs="0"/>
+ <element name="AttributeExtractor" type="conf:PluggableType" minOccurs="0"/>
+ <element name="AttributeResolver" type="conf:PluggableType" minOccurs="0"/>
+ <element name="AttributeFilter" type="conf:PluggableType" minOccurs="0"/>
+ <element name="CredentialResolver" type="conf:PluggableType" minOccurs="0"/>
+ </sequence>
+ <attribute name="id" type="conf:string" use="required"/>
+ <attribute name="entityID" type="anyURI"/>
+ <attribute name="policyId" type="conf:string"/>
+ <attributeGroup ref="conf:ApplicationGroup"/>
+ <attributeGroup ref="conf:RelyingPartyGroup"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ </element>
+
+ <attributeGroup name="ApplicationGroup">
+ <attribute name="homeURL" type="anyURI"/>
+ <attribute name="REMOTE_USER" type="conf:listOfStrings"/>
+ <attribute name="unsetHeaders" type="conf:listOfStrings"/>
+ <attribute name="metadataAttributePrefix" type="conf:string"/>
+ <attribute name="attributePrefix" type="conf:string"/>
+ </attributeGroup>
+
+ <attributeGroup name="RelyingPartyGroup">
+ <attribute name="authType" type="conf:string"/>
+ <attribute name="authUsername" type="conf:string"/>
+ <attribute name="authPassword" type="conf:string"/>
+ <attribute name="signing" type="conf:bindingBoolean"/>
+ <attribute name="signingAlg" type="anyURI"/>
+ <attribute name="digestAlg" type="anyURI"/>
+ <attribute name="encryption" type="conf:bindingBoolean"/>
+ <attribute name="encryptionAlg" type="anyURI"/>
+ <attribute name="keyName" type="conf:string"/>
+ <attribute name="artifactEndpointIndex" type="unsignedShort"/>
+ <attribute name="chunkedEncoding" type="boolean"/>
+ <attribute name="connectTimeout" type="unsignedShort"/>
+ <attribute name="timeout" type="unsignedShort"/>
+ <attribute name="requireConfidentiality" type="boolean"/>
+ <attribute name="requireTransportAuth" type="boolean"/>
+ <attribute name="requireSignedAssertions" type="boolean"/>
+ </attributeGroup>
+
+ <element name="Sessions">
+ <annotation>
+ <documentation>Container for specifying protocol handlers and session policy</documentation>
+ </annotation>
+ <complexType>
+ <choice minOccurs="0" maxOccurs="unbounded">
+ <element ref="conf:SessionInitiator"/>
+ <element ref="conf:LogoutInitiator"/>
+ <element ref="md:AssertionConsumerService"/>
+ <element ref="md:ArtifactResolutionService"/>
+ <element ref="md:SingleLogoutService"/>
+ <element ref="md:ManageNameIDService"/>
+ <element name="Handler">
+ <complexType>
+ <complexContent>
+ <restriction base="conf:PluggableType">
+ <sequence>
+ <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Location" type="anyURI" use="required"/>
+ <attribute name="acl" type="conf:listOfStrings"/>
+ <anyAttribute namespace="##any" processContents="skip"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ </element>
+ </choice>
+ <attribute name="handlerURL" type="anyURI" use="required"/>
+ <attribute name="handlerSSL" type="boolean"/>
+ <attribute name="exportLocation" type="conf:string"/>
+ <attribute name="exportACL" type="conf:listOfStrings"/>
+ <attribute name="cookieName" type="conf:string"/>
+ <attribute name="cookieProps" type="conf:string"/>
+ <attribute name="cookieLifetime" type="unsignedInt"/>
+ <attribute name="idpHistory" type="boolean"/>
+ <attribute name="idpHistoryDays" type="unsignedInt"/>
+ <attribute name="lifetime" type="unsignedInt"/>
+ <attribute name="timeout" type="unsignedInt"/>
+ <attribute name="maxTimeSinceAuthn" type="unsignedInt"/>
+ <attribute name="checkAddress" type="boolean"/>
+ <attribute name="consistentAddress" type="boolean"/>
+ <attribute name="postData" type="conf:string"/>
+ <attribute name="postLimit" type="positiveInteger"/>
+ <attribute name="postTemplate" type="conf:string"/>
+ <attribute name="postExpire" type="boolean"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ </element>
+
+ <attribute name="policyId" type="conf:string">
+ <annotation>
+ <documentation>Used to reference Policy elements from profile endpoints.</documentation>
+ </annotation>
+ </attribute>
+
+ <element name="SessionInitiator">
+ <annotation>
+ <documentation>Used to specify handlers that can issue AuthnRequests or perform discovery</documentation>
+ </annotation>
+ <complexType>
+ <complexContent>
+ <restriction base="conf:PluggableType">
+ <sequence>
+ <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Location" type="anyURI"/>
+ <attribute name="id" type="conf:string"/>
+ <attribute name="isDefault" type="boolean"/>
+ <attribute name="relayState" type="conf:string"/>
+ <attribute name="entityIDParam" type="conf:string"/>
+ <attribute name="entityID" type="anyURI"/>
+ <attribute name="URL" type="anyURI"/>
+ <attribute name="outgoingBindings" type="conf:listOfURIs"/>
+ <attribute name="template" type="anyURI"/>
+ <attribute name="postArtifact" type="boolean"/>
+ <attribute name="acsByIndex" type="boolean"/>
+ <attribute name="acsIndex" type="unsignedShort"/>
+ <attribute name="defaultACSIndex" type="unsignedShort"/> <!-- deprecated -->
+ <attribute name="isPassive" type="boolean"/>
+ <attribute name="forceAuthn" type="boolean"/>
+ <attribute name="authnContextClassRef" type="anyURI"/>
+ <attribute name="authnContextComparison" type="samlp:AuthnContextComparisonType"/>
+ <attribute name="NameIDFormat" type="anyURI"/>
+ <attribute name="SPNameQualifier" type="conf:string"/>
+ <attribute name="requestDelegation" type="boolean"/>
+ <anyAttribute namespace="##any" processContents="skip"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ </element>
+
+ <element name="LogoutInitiator">
+ <annotation>
+ <documentation>Used to specify handlers that can issue LogoutRequests</documentation>
+ </annotation>
+ <complexType>
+ <complexContent>
+ <restriction base="conf:PluggableType">
+ <sequence>
+ <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="Location" type="anyURI"/>
+ <attribute name="relayState" type="conf:string"/>
+ <attribute name="outgoingBindings" type="conf:listOfURIs"/>
+ <attribute name="template" type="anyURI"/>
+ <attribute name="postArtifact" type="boolean"/>
+ <anyAttribute namespace="##any" processContents="skip"/>
+ </restriction>
+ </complexContent>
+ </complexType>
+ </element>
+
+ <element name="Errors">
+ <annotation>
+ <documentation>Container for error templates and associated details</documentation>
+ </annotation>
+ <complexType>
+ <sequence>
+ <any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="redirectErrors" type="anyURI"/>
+ <attribute name="session" type="anyURI"/>
+ <attribute name="metadata" type="anyURI"/>
+ <attribute name="access" type="anyURI"/>
+ <attribute name="ssl" type="anyURI"/>
+ <attribute name="localLogout" type="anyURI"/>
+ <attribute name="globalLogout" type="anyURI"/>
+ <attribute name="partialLogout" type="anyURI"/>
+ <attribute name="supportContact" type="conf:string"/>
+ <attribute name="logoLocation" type="anyURI"/>
+ <attribute name="styleSheet" type="anyURI"/>
+ <anyAttribute namespace="##any" processContents="skip"/>
+ </complexType>
+ </element>
+
+ <element name="RelyingParty">
+ <annotation>
+ <documentation>Container for specifying settings to use with particular peers</documentation>
+ </annotation>
+ <complexType>
+ <sequence/>
+ <attribute name="Name" type="conf:string" use="required"/>
+ <attributeGroup ref="conf:RelyingPartyGroup"/>
+ <attribute name="entityID" type="anyURI"/>
+ <anyAttribute namespace="##other" processContents="lax"/>
+ </complexType>
+ </element>
+
+ <element name="Notify">
+ <annotation>
+ <documentation>Used to specify locations to receive application notifications</documentation>
+ </annotation>
+ <complexType>
+ <sequence/>
+ <attribute name="Channel" use="required">
+ <simpleType>
+ <restriction base="string">
+ <enumeration value="front"/>
+ <enumeration value="back"/>
+ </restriction>
+ </simpleType>
+ </attribute>
+ <attribute name="Location" type="anyURI" use="required"/>
+ <anyAttribute namespace="##any" processContents="skip"/>
+ </complexType>
+ </element>
+
+ <element name="SecurityPolicies">
+ <annotation>
+ <documentation>Container for specifying sets of policy rules to apply to incoming messages</documentation>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element name="Policy" minOccurs="1" maxOccurs="unbounded">
+ <annotation>
+ <documentation>Specifies a set of SecurityPolicyRule plugins</documentation>
+ </annotation>
+ <complexType>
+ <choice>
+ <element name="Rule" type="conf:PluggableType" minOccurs="1" maxOccurs="unbounded"/>
+ <element name="PolicyRule" type="conf:PluggableType" minOccurs="1" maxOccurs="unbounded"/>
+ </choice>
+ <attribute name="id" type="conf:string" use="required"/>
+ <attribute name="validate" type="boolean"/>
+ <anyAttribute namespace="##any" processContents="skip"/>
+ </complexType>
+ </element>
+ </sequence>
+ </complexType>
+ </element>
+
+ <element name="TransportOption">
+ <annotation>
+ <documentation>Implementation-specific option to pass to SOAPTransport provider.</documentation>
+ </annotation>
+ <complexType>
+ <simpleContent>
+ <extension base="anySimpleType">
+ <attribute name="provider" type="conf:string" use="required"/>
+ <attribute name="option" type="conf:string" use="required"/>
+ </extension>
+ </simpleContent>
+ </complexType>
+ </element>
+
+</schema>
Name: shibboleth
-Version: 2.3
-Release: 2
+Version: 2.3.1
+Release: 1
Summary: Open source system for attribute-based Web SSO
Group: System Environment/Libraries
Vendor: Internet2
%doc %{pkgdocdir}/api
%changelog
+* Mon Nov 23 2009 Scott Cantor <cantor.2@osu.edu> - 2.3.1-1
+- Reset revision for 2.3.1 release
+
* Wed Aug 19 2009 Scott Cantor <cantor.2@osu.edu> - 2.2.1-2
- SuSE init script changes
- Restart Apache on removal, not just upgrade
Name: @PACKAGE@
Version: @PACKAGE_VERSION@
-Release: 2
+Release: 1
Summary: Open source system for attribute-based Web SSO
Group: System Environment/Libraries
Vendor: Internet2
%doc %{pkgdocdir}/api
%changelog
+* Mon Nov 23 2009 Scott Cantor <cantor.2@osu.edu> - 2.3.1-1
+- Reset revision for 2.3.1 release
+
* Wed Aug 19 2009 Scott Cantor <cantor.2@osu.edu> - 2.2.1-2
- SuSE init script changes
- Restart Apache on removal, not just upgrade
//\r
\r
VS_VERSION_INFO VERSIONINFO\r
- FILEVERSION 2,3,0,0\r
- PRODUCTVERSION 2,3,0,0\r
+ FILEVERSION 2,3,1,0\r
+ PRODUCTVERSION 2,3,1,0\r
FILEFLAGSMASK 0x3fL\r
#ifdef _DEBUG\r
FILEFLAGS 0x1L\r
VALUE "Comments", "\0"\r
VALUE "CompanyName", "Internet2\0"\r
VALUE "FileDescription", "Shibboleth Daemon Service\0"\r
- VALUE "FileVersion", "2, 3, 0, 0\0"\r
+ VALUE "FileVersion", "2, 3, 1, 0\0"\r
VALUE "InternalName", "shibd\0"\r
VALUE "LegalCopyright", "Copyright © 2009 Internet2\0"\r
VALUE "LegalTrademarks", "\0"\r
VALUE "OriginalFilename", "shibd.exe\0"\r
VALUE "PrivateBuild", "\0"\r
- VALUE "ProductName", "Shibboleth 2.3\0"\r
- VALUE "ProductVersion", "2, 3, 0, 0\0"\r
+ VALUE "ProductName", "Shibboleth 2.3.1\0"\r
+ VALUE "ProductVersion", "2, 3, 1, 0\0"\r
VALUE "SpecialBuild", "\0"\r
END\r
END\r
return;
}
- string dispName = string("Shibboleth ") + PACKAGE_VERSION + " Daemon (" + name + ")";
+ string dispName = string("Shibboleth 2 Daemon (") + name + ")";
string realName = string("shibd_") + name;
string cmd(szPath);
if (shar_prefix)
# this is different from the project version
# http://sources.redhat.com/autobook/autobook/autobook_91.html
-libshibsp_la_LDFLAGS = $(XMLSEC_LIBS) -version-info 4:0:0
-libshibsp_lite_la_LDFLAGS = $(LITE_LIBS) -version-info 4:0:0
+libshibsp_la_LDFLAGS = $(XMLSEC_LIBS) -version-info 4:1:0
+libshibsp_lite_la_LDFLAGS = $(LITE_LIBS) -version-info 4:1:0
libshibsp_lite_la_CPPFLAGS = -DSHIBSP_LITE
pkgsysconfdir = $(sysconfdir)/@PACKAGE@
# this is different from the project version
# http://sources.redhat.com/autobook/autobook/autobook_91.html
-libshibsp_la_LDFLAGS = $(XMLSEC_LIBS) -version-info 4:0:0
-libshibsp_lite_la_LDFLAGS = $(LITE_LIBS) -version-info 4:0:0
+libshibsp_la_LDFLAGS = $(XMLSEC_LIBS) -version-info 4:1:0
+libshibsp_lite_la_LDFLAGS = $(LITE_LIBS) -version-info 4:1:0
libshibsp_lite_la_CPPFLAGS = -DSHIBSP_LITE
pkgsysconfdir = $(sysconfdir)/@PACKAGE@
pkgxmldir = $(datadir)/xml/@PACKAGE@
Category& m_log;
string m_policyId;
+ bool m_subjectMatch;
vector<AttributeDesignator*> m_SAML1Designators;
vector<saml2::Attribute*> m_SAML2Designators;
};
return new QueryResolver(e);
}
- static const XMLCh _policyId[] = UNICODE_LITERAL_8(p,o,l,i,c,y,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);
};
-QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.Query"))
+QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.Query")), m_subjectMatch(false)
{
#ifdef _DEBUG
xmltooling::NDC ndc("QueryResolver");
#endif
- const XMLCh* pid = e ? e->getAttributeNS(NULL, _policyId) : NULL;
+ const XMLCh* pid = e ? e->getAttributeNS(NULL, policyId) : NULL;
if (pid && *pid) {
auto_ptr_char temp(pid);
m_policyId = temp.get();
}
+ pid = e ? e->getAttributeNS(NULL, subjectMatch) : NULL;
+ if (pid && (*pid == chLatin_t || *pid == chDigit_1))
+ m_subjectMatch = true;
DOMElement* child = XMLHelper::getFirstChildElement(e);
while (child) {
}
newtoken->detach();
- wrapper.release();
+ wrapper.release(); // detach blows away the Response
ctx.getResolvedAssertions().push_back(newtoken);
// Finally, extract and filter the result.
AttributeExtractor* extractor = application.getAttributeExtractor();
if (extractor) {
Locker extlocker(extractor);
- extractor->extractAttributes(application, AA, *newtoken, ctx.getResolvedAttributes());
+ const vector<saml1::AttributeStatement*>& statements = const_cast<const saml1::Assertion*>(newtoken)->getAttributeStatements();
+ for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {
+ if (m_subjectMatch) {
+ // Check for subject match.
+ const NameIdentifier* respName = (*s)->getSubject() ? (*s)->getSubject()->getNameIdentifier() : NULL;
+ if (!respName || !XMLString::equals(respName->getName(), ctx.getNameID()->getName()) ||
+ !XMLString::equals(respName->getFormat(), ctx.getNameID()->getFormat()) ||
+ !XMLString::equals(respName->getNameQualifier(), ctx.getNameID()->getNameQualifier())) {
+ if (respName)
+ m_log.warnStream() << "ignoring AttributeStatement without strongly matching NameIdentifier in Subject: " <<
+ *respName << logging::eol;
+ else
+ m_log.warn("ignoring AttributeStatement without NameIdentifier in Subject");
+ continue;
+ }
+ }
+ extractor->extractAttributes(application, AA, *(*s), ctx.getResolvedAttributes());
+ }
}
AttributeFilter* filter = application.getAttributeFilter();
// Encrypt the NameID?
if (encryption.first && (!strcmp(encryption.second, "true") || !strcmp(encryption.second, "back"))) {
auto_ptr<EncryptedID> encrypted(EncryptedIDBuilder::buildEncryptedID());
- MetadataCredentialCriteria mcc(*AA);
encrypted->encrypt(
*ctx.getNameID(),
*(application.getMetadataProvider()),
m_log.error("unable to obtain a SAML response from attribute authority");
return false;
}
+
+ auto_ptr<saml2p::StatusResponseType> wrapper(srt);
+
saml2p::Response* response = dynamic_cast<saml2p::Response*>(srt);
if (!response) {
- delete srt;
m_log.error("message was not a samlp:Response");
return true;
}
else if (!response->getStatus() || !response->getStatus()->getStatusCode() ||
!XMLString::equals(response->getStatus()->getStatusCode()->getValue(), saml2p::StatusCode::SUCCESS)) {
- delete srt;
m_log.error("attribute authority returned a SAML error");
return true;
}
+ saml2::Assertion* newtoken = NULL;
const vector<saml2::Assertion*>& assertions = const_cast<const saml2p::Response*>(response)->getAssertions();
if (assertions.empty()) {
- delete srt;
- m_log.warn("response from attribute authority was empty");
- return true;
- }
- else if (assertions.size()>1)
- m_log.warn("simple resolver only supports one assertion in the query response");
+ // Check for encryption.
+ 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;
+ }
+ else if (encassertions.size() > 1) {
+ m_log.warn("simple resolver only supports one assertion in the query response");
+ }
- auto_ptr<saml2p::StatusResponseType> wrapper(srt);
- saml2::Assertion* newtoken = assertions.front();
+ CredentialResolver* cr=application.getCredentialResolver();
+ if (!cr) {
+ m_log.warn("found encrypted assertion, but no CredentialResolver was available");
+ return true;
+ }
+
+ // Attempt to decrypt it.
+ try {
+ Locker credlocker(cr);
+ auto_ptr<XMLObject> tokenwrapper(encassertions.front()->decrypt(*cr, relyingParty->getXMLString("entityID").second, &mcc));
+ newtoken = dynamic_cast<saml2::Assertion*>(tokenwrapper.get());
+ if (newtoken) {
+ tokenwrapper.release();
+ if (m_log.isDebugEnabled())
+ m_log.debugStream() << "decrypted Assertion: " << *newtoken << logging::eol;
+ }
+ }
+ 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;
+ }
+ }
+ else {
+ if (assertions.size() > 1)
+ m_log.warn("simple resolver only supports one assertion in the query response");
+ newtoken = assertions.front();
+ }
if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {
m_log.error("assertion unsigned, rejecting it based on signedAssertions policy");
+ if (!wrapper.get())
+ delete newtoken;
return true;
}
// Now we can check the security status of the policy.
if (!policy.isAuthenticated())
throw SecurityPolicyException("Security of SAML 2.0 query result not established.");
+
+ if (m_subjectMatch) {
+ // Check for subject match.
+ bool ownedName = false;
+ NameID* respName = newtoken->getSubject() ? newtoken->getSubject()->getNameID() : NULL;
+ if (!respName) {
+ // Check for encryption.
+ EncryptedID* encname = newtoken->getSubject() ? newtoken->getSubject()->getEncryptedID() : NULL;
+ if (encname) {
+ CredentialResolver* cr=application.getCredentialResolver();
+ if (!cr)
+ m_log.warn("found EncryptedID, but no CredentialResolver was available");
+ else {
+ Locker credlocker(cr);
+ auto_ptr<XMLObject> decryptedID(encname->decrypt(*cr, relyingParty->getXMLString("entityID").second, &mcc));
+ respName = dynamic_cast<NameID*>(decryptedID.get());
+ if (respName) {
+ ownedName = true;
+ decryptedID.release();
+ if (m_log.isDebugEnabled())
+ m_log.debugStream() << "decrypted NameID: " << *respName << logging::eol;
+ }
+ }
+ }
+ }
+
+ auto_ptr<NameID> nameIDwrapper(ownedName ? respName : NULL);
+
+ if (!respName || !XMLString::equals(respName->getName(), ctx.getNameID()->getName()) ||
+ !XMLString::equals(respName->getFormat(), ctx.getNameID()->getFormat()) ||
+ !XMLString::equals(respName->getNameQualifier(), ctx.getNameID()->getNameQualifier()) ||
+ !XMLString::equals(respName->getSPNameQualifier(), ctx.getNameID()->getSPNameQualifier())) {
+ if (respName)
+ m_log.warnStream() << "ignoring Assertion without strongly matching NameID in Subject: " <<
+ *respName << logging::eol;
+ else
+ m_log.warn("ignoring Assertion without NameID in Subject");
+ if (!wrapper.get())
+ delete newtoken;
+ return true;
+ }
+ }
}
catch (exception& ex) {
m_log.error("assertion failed policy validation: %s", ex.what());
+ if (!wrapper.get())
+ delete newtoken;
return true;
}
- newtoken->detach();
- wrapper.release();
+ if (wrapper.get()) {
+ newtoken->detach();
+ wrapper.release(); // detach blows away the Response
+ }
ctx.getResolvedAssertions().push_back(newtoken);
// Finally, extract and filter the result.
m_log.debug("attempting SAML 1.x attribute query");
SAML1Query(qctx);
}
- else
- m_log.warn("SSO protocol does not allow for attribute query");
+ else {
+ m_log.info("SSO protocol does not allow for attribute query");
+ }
}
- else
+ else {
m_log.warn("can't attempt attribute query, either no NameID or no metadata to use");
+ }
}
Category& m_log;
string m_policyId;
+ bool m_subjectMatch;
vector<string> m_attributeIds;
xstring m_format;
MetadataProvider* m_metadata;
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);
+ static const XMLCh subjectMatch[] = UNICODE_LITERAL_12(s,u,b,j,e,c,t,M,a,t,c,h);
static const XMLCh _TrustEngine[] = UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e);
static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
};
SimpleAggregationResolver::SimpleAggregationResolver(const DOMElement* e)
- : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.SimpleAggregation")), m_metadata(NULL), m_trust(NULL)
+ : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.SimpleAggregation")), m_subjectMatch(false), m_metadata(NULL), m_trust(NULL)
{
#ifdef _DEBUG
xmltooling::NDC ndc("SimpleAggregationResolver");
m_policyId = temp.get();
}
+ pid = e ? e->getAttributeNS(NULL, subjectMatch) : NULL;
+ if (pid && (*pid == chLatin_t || *pid == chDigit_1))
+ m_subjectMatch = true;
+
pid = e ? e->getAttributeNS(NULL, attributeId) : NULL;
if (pid && *pid) {
char* dup = XMLString::transcode(pid);
pid = e->getAttributeNS(NULL, format);
if (pid && *pid)
m_format = pid;
-
}
DOMElement* child = XMLHelper::getFirstChildElement(e, _MetadataProvider);
}
child = XMLHelper::getNextSiblingElement(child);
}
-
}
bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const char* entityID, const NameID* name) const
// Encrypt the NameID?
if (encryption.first && (!strcmp(encryption.second, "true") || !strcmp(encryption.second, "back"))) {
auto_ptr<EncryptedID> encrypted(EncryptedIDBuilder::buildEncryptedID());
- MetadataCredentialCriteria mcc(*AA);
encrypted->encrypt(
*name,
*(policy.getMetadataProvider()),
m_log.error("unable to obtain a SAML response from attribute authority (%s)", entityID);
return false;
}
+
+ auto_ptr<saml2p::StatusResponseType> wrapper(srt);
+
saml2p::Response* response = dynamic_cast<saml2p::Response*>(srt);
if (!response) {
- delete srt;
m_log.error("message was not a samlp:Response");
return true;
}
else if (!response->getStatus() || !response->getStatus()->getStatusCode() ||
!XMLString::equals(response->getStatus()->getStatusCode()->getValue(), saml2p::StatusCode::SUCCESS)) {
- delete srt;
m_log.error("attribute authority (%s) returned a SAML error", entityID);
return true;
}
+ saml2::Assertion* newtoken = NULL;
const vector<saml2::Assertion*>& assertions = const_cast<const saml2p::Response*>(response)->getAssertions();
if (assertions.empty()) {
- delete srt;
- m_log.warn("response from attribute authority (%s) was empty", entityID);
- return true;
- }
- else if (assertions.size()>1)
- m_log.warn("resolver only supports one assertion in the query response");
+ // Check for encryption.
+ 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;
+ }
+ else if (encassertions.size() > 1) {
+ m_log.warn("simple resolver only supports one assertion in the query response");
+ }
- auto_ptr<saml2p::StatusResponseType> wrapper(srt);
- saml2::Assertion* newtoken = assertions.front();
+ CredentialResolver* cr=application.getCredentialResolver();
+ if (!cr) {
+ m_log.warn("found encrypted assertion, but no CredentialResolver was available");
+ return true;
+ }
+
+ // Attempt to decrypt it.
+ try {
+ Locker credlocker(cr);
+ auto_ptr<XMLObject> tokenwrapper(encassertions.front()->decrypt(*cr, relyingParty->getXMLString("entityID").second, &mcc));
+ newtoken = dynamic_cast<saml2::Assertion*>(tokenwrapper.get());
+ if (newtoken) {
+ tokenwrapper.release();
+ if (m_log.isDebugEnabled())
+ m_log.debugStream() << "decrypted Assertion: " << *newtoken << logging::eol;
+ }
+ }
+ 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;
+ }
+ }
+ else {
+ if (assertions.size() > 1)
+ m_log.warn("simple resolver only supports one assertion in the query response");
+ newtoken = assertions.front();
+ }
if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) {
m_log.error("assertion unsigned, rejecting it based on signedAssertions policy");
+ if (!wrapper.get())
+ delete newtoken;
return true;
}
// Now we can check the security status of the policy.
if (!policy.isAuthenticated())
throw SecurityPolicyException("Security of SAML 2.0 query result not established.");
+
+ if (m_subjectMatch) {
+ // Check for subject match.
+ bool ownedName = false;
+ NameID* respName = newtoken->getSubject() ? newtoken->getSubject()->getNameID() : NULL;
+ if (!respName) {
+ // Check for encryption.
+ EncryptedID* encname = newtoken->getSubject() ? newtoken->getSubject()->getEncryptedID() : NULL;
+ if (encname) {
+ CredentialResolver* cr=application.getCredentialResolver();
+ if (!cr)
+ m_log.warn("found EncryptedID, but no CredentialResolver was available");
+ else {
+ Locker credlocker(cr);
+ auto_ptr<XMLObject> decryptedID(encname->decrypt(*cr, relyingParty->getXMLString("entityID").second, &mcc));
+ respName = dynamic_cast<NameID*>(decryptedID.get());
+ if (respName) {
+ ownedName = true;
+ decryptedID.release();
+ if (m_log.isDebugEnabled())
+ m_log.debugStream() << "decrypted NameID: " << *respName << logging::eol;
+ }
+ }
+ }
+ }
+
+ auto_ptr<NameID> nameIDwrapper(ownedName ? respName : NULL);
+
+ if (!respName || !XMLString::equals(respName->getName(), name->getName()) ||
+ !XMLString::equals(respName->getFormat(), name->getFormat()) ||
+ !XMLString::equals(respName->getNameQualifier(), name->getNameQualifier()) ||
+ !XMLString::equals(respName->getSPNameQualifier(), name->getSPNameQualifier())) {
+ if (respName)
+ m_log.warnStream() << "ignoring Assertion without strongly matching NameID in Subject: " <<
+ *respName << logging::eol;
+ else
+ m_log.warn("ignoring Assertion without NameID in Subject");
+ if (!wrapper.get())
+ delete newtoken;
+ return true;
+ }
+ }
}
catch (exception& ex) {
m_log.error("assertion failed policy validation: %s", ex.what());
+ if (!wrapper.get())
+ delete newtoken;
return true;
}
- newtoken->detach();
- wrapper.release();
+ if (wrapper.get()) {
+ newtoken->detach();
+ wrapper.release(); // detach blows away the Response
+ }
ctx.getResolvedAssertions().push_back(newtoken);
// Finally, extract and filter the result.
) const;
void extractAttributes(
const Application& application,
+ const char* assertingParty,
+ const char* relyingParty,
+ const saml1::AttributeStatement& statement,
+ vector<Attribute*>& attributes
+ ) const;
+ void extractAttributes(
+ const Application& application,
+ const char* assertingParty,
+ const char* relyingParty,
+ const saml2::AttributeStatement& statement,
+ vector<Attribute*>& attributes
+ ) const;
+ void extractAttributes(
+ const Application& application,
const ObservableMetadataProvider* observable,
const XMLCh* entityID,
const char* relyingParty,
void XMLExtractorImpl::extractAttributes(
const Application& application,
+ const char* assertingParty,
+ const char* relyingParty,
+ const saml1::AttributeStatement& statement,
+ vector<Attribute*>& attributes
+ ) const
+{
+ const vector<saml1::Attribute*>& attrs = statement.getAttributes();
+ for (vector<saml1::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a)
+ extractAttributes(application, assertingParty, relyingParty, *(*a), attributes);
+}
+
+void XMLExtractorImpl::extractAttributes(
+ const Application& application,
+ const char* assertingParty,
+ const char* relyingParty,
+ const saml2::AttributeStatement& statement,
+ vector<Attribute*>& attributes
+ ) const
+{
+ const vector<saml2::Attribute*>& attrs = statement.getAttributes();
+ for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a)
+ extractAttributes(application, assertingParty, relyingParty, *(*a), attributes);
+}
+
+void XMLExtractorImpl::extractAttributes(
+ const Application& application,
const ObservableMetadataProvider* observable,
const XMLCh* entityID,
const char* relyingParty,
const EntityDescriptor* entity = issuer ? dynamic_cast<const EntityDescriptor*>(issuer->getParent()) : NULL;
const char* relyingParty = application.getRelyingParty(entity)->getString("entityID").second;
+ // Check for statements.
+ if (XMLString::equals(xmlObject.getElementQName().getLocalPart(), saml1::AttributeStatement::LOCAL_NAME)) {
+ const saml2::AttributeStatement* statement2 = dynamic_cast<const saml2::AttributeStatement*>(&xmlObject);
+ if (statement2) {
+ auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL);
+ m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *statement2, attributes);
+ // Handle EncryptedAttributes inline so we have access to the role descriptor.
+ const vector<saml2::EncryptedAttribute*>& encattrs = statement2->getEncryptedAttributes();
+ for (vector<saml2::EncryptedAttribute*>::const_iterator ea = encattrs.begin(); ea!=encattrs.end(); ++ea)
+ extractAttributes(application, issuer, *(*ea), attributes);
+ return;
+ }
+
+ const saml1::AttributeStatement* statement1 = dynamic_cast<const saml1::AttributeStatement*>(&xmlObject);
+ if (statement1) {
+ auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL);
+ m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *statement1, attributes);
+ return;
+ }
+
+ throw AttributeExtractionException("Unable to extract attributes, unknown object type.");
+ }
+
// Check for assertions.
if (XMLString::equals(xmlObject.getElementQName().getLocalPart(), saml1::Assertion::LOCAL_NAME)) {
const saml2::Assertion* token2 = dynamic_cast<const saml2::Assertion*>(&xmlObject);
auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL);
const vector<saml2::AttributeStatement*>& statements = token2->getAttributeStatements();
for (vector<saml2::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {
- const vector<saml2::Attribute*>& attrs = const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();
- for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a)
- m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *(*a), attributes);
-
+ m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *(*s), attributes);
+ // Handle EncryptedAttributes inline so we have access to the role descriptor.
const vector<saml2::EncryptedAttribute*>& encattrs = const_cast<const saml2::AttributeStatement*>(*s)->getEncryptedAttributes();
for (vector<saml2::EncryptedAttribute*>::const_iterator ea = encattrs.begin(); ea!=encattrs.end(); ++ea)
extractAttributes(application, issuer, *(*ea), attributes);
if (token1) {
auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL);
const vector<saml1::AttributeStatement*>& statements = token1->getAttributeStatements();
- for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {
- const vector<saml1::Attribute*>& attrs = const_cast<const saml1::AttributeStatement*>(*s)->getAttributes();
- for (vector<saml1::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a)
- m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *(*a), attributes);
- }
+ for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s)
+ m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *(*s), attributes);
return;
}
-/*\r
- * Copyright 2001-2009 Internet2\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/**\r
- * AbstractHandler.cpp\r
- *\r
- * Base class for handlers based on a DOMPropertySet.\r
- */\r
-\r
-#include "internal.h"\r
-#include "exceptions.h"\r
-#include "Application.h"\r
-#include "ServiceProvider.h"\r
-#include "SPRequest.h"\r
-#include "handler/AbstractHandler.h"\r
-#include "handler/LogoutHandler.h"\r
-#include "remoting/ListenerService.h"\r
-#include "util/CGIParser.h"\r
-#include "util/SPConstants.h"\r
-#include "util/TemplateParameters.h"\r
-\r
-#include <vector>\r
-#include <fstream>\r
-#include <xmltooling/XMLToolingConfig.h>\r
-#include <xmltooling/util/PathResolver.h>\r
-#include <xmltooling/util/URLEncoder.h>\r
-\r
-\r
-#ifndef SHIBSP_LITE\r
-# include <saml/exceptions.h>\r
-# include <saml/SAMLConfig.h>\r
-# include <saml/binding/SAMLArtifact.h>\r
-# include <saml/saml1/core/Protocols.h>\r
-# include <saml/saml2/core/Protocols.h>\r
-# include <saml/saml2/metadata/Metadata.h>\r
-# include <saml/saml2/metadata/MetadataCredentialCriteria.h>\r
-# include <saml/util/SAMLConstants.h>\r
-# include <xmltooling/security/Credential.h>\r
-# include <xmltooling/security/CredentialResolver.h>\r
-# include <xmltooling/util/StorageService.h>\r
-using namespace opensaml::saml2md;\r
-using namespace opensaml;\r
-#else\r
-# include "lite/SAMLConstants.h"\r
-#endif\r
-\r
-#include <xmltooling/XMLToolingConfig.h>\r
-#include <xmltooling/util/URLEncoder.h>\r
-\r
-using namespace shibsp;\r
-using namespace samlconstants;\r
-using namespace xmltooling;\r
-using namespace xercesc;\r
-using namespace std;\r
-\r
-namespace shibsp {\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML1ConsumerFactory;\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2ConsumerFactory;\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2ArtifactResolutionFactory;\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory ChainingLogoutInitiatorFactory;\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory LocalLogoutInitiatorFactory;\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2LogoutInitiatorFactory;\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2LogoutFactory;\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2NameIDMgmtFactory;\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory AssertionLookupFactory;\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory MetadataGeneratorFactory;\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory StatusHandlerFactory;\r
- SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SessionHandlerFactory;\r
-\r
- void SHIBSP_DLLLOCAL generateRandomHex(std::string& buf, unsigned int len) {\r
- static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};\r
- int r;\r
- unsigned char b1,b2;\r
- buf.erase();\r
- for (unsigned int i=0; i<len; i+=4) {\r
- r = rand();\r
- b1 = (0x00FF & r);\r
- b2 = (0xFF00 & r) >> 8;\r
- buf += (DIGITS[(0xF0 & b1) >> 4 ]);\r
- buf += (DIGITS[0x0F & b1]);\r
- buf += (DIGITS[(0xF0 & b2) >> 4 ]);\r
- buf += (DIGITS[0x0F & b2]);\r
- }\r
- }\r
-};\r
-\r
-void SHIBSP_API shibsp::registerHandlers()\r
-{\r
- SPConfig& conf=SPConfig::getConfig();\r
-\r
- conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_ARTIFACT, SAML1ConsumerFactory);\r
- conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_POST, SAML1ConsumerFactory);\r
- conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST, SAML2ConsumerFactory);\r
- conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2ConsumerFactory);\r
- conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_ARTIFACT, SAML2ConsumerFactory);\r
- conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_PAOS, SAML2ConsumerFactory);\r
-\r
- conf.ArtifactResolutionServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2ArtifactResolutionFactory);\r
-\r
- conf.HandlerManager.registerFactory(SAML20_BINDING_URI, AssertionLookupFactory);\r
- conf.HandlerManager.registerFactory(METADATA_GENERATOR_HANDLER, MetadataGeneratorFactory);\r
- conf.HandlerManager.registerFactory(STATUS_HANDLER, StatusHandlerFactory);\r
- conf.HandlerManager.registerFactory(SESSION_HANDLER, SessionHandlerFactory);\r
-\r
- conf.LogoutInitiatorManager.registerFactory(CHAINING_LOGOUT_INITIATOR, ChainingLogoutInitiatorFactory);\r
- conf.LogoutInitiatorManager.registerFactory(LOCAL_LOGOUT_INITIATOR, LocalLogoutInitiatorFactory);\r
- conf.LogoutInitiatorManager.registerFactory(SAML2_LOGOUT_INITIATOR, SAML2LogoutInitiatorFactory);\r
- conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2LogoutFactory);\r
- conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_REDIRECT, SAML2LogoutFactory);\r
- conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_POST, SAML2LogoutFactory);\r
- conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2LogoutFactory);\r
- conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_ARTIFACT, SAML2LogoutFactory);\r
-\r
- conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2NameIDMgmtFactory);\r
- conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_HTTP_REDIRECT, SAML2NameIDMgmtFactory);\r
- conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_HTTP_POST, SAML2NameIDMgmtFactory);\r
- conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2NameIDMgmtFactory);\r
- conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_HTTP_ARTIFACT, SAML2NameIDMgmtFactory);\r
-}\r
-\r
-Handler::Handler()\r
-{\r
-}\r
-\r
-Handler::~Handler()\r
-{\r
-}\r
-\r
-AbstractHandler::AbstractHandler(\r
- const DOMElement* e, Category& log, DOMNodeFilter* filter, const map<string,string>* remapper\r
- ) : m_log(log), m_configNS(shibspconstants::SHIB2SPCONFIG_NS) {\r
- load(e,NULL,filter,remapper);\r
-}\r
-\r
-AbstractHandler::~AbstractHandler()\r
-{\r
-}\r
-\r
-#ifndef SHIBSP_LITE\r
-\r
-const char* Handler::getType() const\r
-{\r
- return getString("type").second;\r
-}\r
-\r
-void AbstractHandler::checkError(const XMLObject* response, const saml2md::RoleDescriptor* role) const\r
-{\r
- const saml2p::StatusResponseType* r2 = dynamic_cast<const saml2p::StatusResponseType*>(response);\r
- if (r2) {\r
- const saml2p::Status* status = r2->getStatus();\r
- if (status) {\r
- const saml2p::StatusCode* sc = status->getStatusCode();\r
- const XMLCh* code = sc ? sc->getValue() : NULL;\r
- if (code && !XMLString::equals(code,saml2p::StatusCode::SUCCESS)) {\r
- FatalProfileException ex("SAML response contained an error.");\r
- annotateException(&ex, role, status); // throws it\r
- }\r
- }\r
- }\r
-\r
- const saml1p::Response* r1 = dynamic_cast<const saml1p::Response*>(response);\r
- if (r1) {\r
- const saml1p::Status* status = r1->getStatus();\r
- if (status) {\r
- const saml1p::StatusCode* sc = status->getStatusCode();\r
- const xmltooling::QName* code = sc ? sc->getValue() : NULL;\r
- if (code && *code != saml1p::StatusCode::SUCCESS) {\r
- FatalProfileException ex("SAML response contained an error.");\r
- ex.addProperty("statusCode", code->toString().c_str());\r
- if (sc->getStatusCode()) {\r
- code = sc->getStatusCode()->getValue();\r
- if (code)\r
- ex.addProperty("statusCode2", code->toString().c_str());\r
- }\r
- if (status->getStatusMessage()) {\r
- auto_ptr_char msg(status->getStatusMessage()->getMessage());\r
- if (msg.get() && *msg.get())\r
- ex.addProperty("statusMessage", msg.get());\r
- }\r
- ex.raise();\r
- }\r
- }\r
- }\r
-}\r
-\r
-void AbstractHandler::fillStatus(saml2p::StatusResponseType& response, const XMLCh* code, const XMLCh* subcode, const char* msg) const\r
-{\r
- saml2p::Status* status = saml2p::StatusBuilder::buildStatus();\r
- saml2p::StatusCode* scode = saml2p::StatusCodeBuilder::buildStatusCode();\r
- status->setStatusCode(scode);\r
- scode->setValue(code);\r
- if (subcode) {\r
- saml2p::StatusCode* ssubcode = saml2p::StatusCodeBuilder::buildStatusCode();\r
- scode->setStatusCode(ssubcode);\r
- ssubcode->setValue(subcode);\r
- }\r
- if (msg) {\r
- pair<bool,bool> flag = getBool("detailedErrors", m_configNS.get());\r
- auto_ptr_XMLCh widemsg((flag.first && flag.second) ? msg : "Error processing request.");\r
- saml2p::StatusMessage* sm = saml2p::StatusMessageBuilder::buildStatusMessage();\r
- status->setStatusMessage(sm);\r
- sm->setMessage(widemsg.get());\r
- }\r
- response.setStatus(status);\r
-}\r
-\r
-long AbstractHandler::sendMessage(\r
- const MessageEncoder& encoder,\r
- XMLObject* msg,\r
- const char* relayState,\r
- const char* destination,\r
- const saml2md::RoleDescriptor* role,\r
- const Application& application,\r
- HTTPResponse& httpResponse,\r
- bool signIfPossible\r
- ) const\r
-{\r
- const EntityDescriptor* entity = role ? dynamic_cast<const EntityDescriptor*>(role->getParent()) : NULL;\r
- const PropertySet* relyingParty = application.getRelyingParty(entity);\r
- pair<bool,const char*> flag = signIfPossible ? make_pair(true,(const char*)"true") : relyingParty->getString("signing");\r
- if (role && flag.first &&\r
- (!strcmp(flag.second, "true") ||\r
- (encoder.isUserAgentPresent() && !strcmp(flag.second, "front")) ||\r
- (!encoder.isUserAgentPresent() && !strcmp(flag.second, "back")))) {\r
- CredentialResolver* credResolver=application.getCredentialResolver();\r
- if (credResolver) {\r
- Locker credLocker(credResolver);\r
- const Credential* cred = NULL;\r
- pair<bool,const char*> keyName = relyingParty->getString("keyName");\r
- pair<bool,const XMLCh*> sigalg = relyingParty->getXMLString("signingAlg");\r
- if (role) {\r
- MetadataCredentialCriteria mcc(*role);\r
- mcc.setUsage(Credential::SIGNING_CREDENTIAL);\r
- if (keyName.first)\r
- mcc.getKeyNames().insert(keyName.second);\r
- if (sigalg.first)\r
- mcc.setXMLAlgorithm(sigalg.second);\r
- cred = credResolver->resolve(&mcc);\r
- }\r
- else {\r
- CredentialCriteria cc;\r
- cc.setUsage(Credential::SIGNING_CREDENTIAL);\r
- if (keyName.first)\r
- cc.getKeyNames().insert(keyName.second);\r
- if (sigalg.first)\r
- cc.setXMLAlgorithm(sigalg.second);\r
- cred = credResolver->resolve(&cc);\r
- }\r
- if (cred) {\r
- // Signed request.\r
- return encoder.encode(\r
- httpResponse,\r
- msg,\r
- destination,\r
- entity,\r
- relayState,\r
- &application,\r
- cred,\r
- sigalg.second,\r
- relyingParty->getXMLString("digestAlg").second\r
- );\r
- }\r
- else {\r
- m_log.warn("no signing credential resolved, leaving message unsigned");\r
- }\r
- }\r
- else {\r
- m_log.warn("no credential resolver installed, leaving message unsigned");\r
- }\r
- }\r
-\r
- // Unsigned request.\r
- return encoder.encode(httpResponse, msg, destination, entity, relayState, &application);\r
-}\r
-\r
-#endif\r
-\r
-void AbstractHandler::preserveRelayState(const Application& application, HTTPResponse& response, string& relayState) const\r
-{\r
- if (relayState.empty())\r
- return;\r
-\r
- // No setting means just pass it by value.\r
- pair<bool,const char*> mech=getString("relayState");\r
- if (!mech.first || !mech.second || !*mech.second)\r
- return;\r
-\r
- if (!strcmp(mech.second, "cookie")) {\r
- // Here we store the state in a cookie and send a fixed\r
- // value so we can recognize it on the way back.\r
- if (relayState.find("cookie:") != 0) {\r
- const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder();\r
- pair<string,const char*> shib_cookie=application.getCookieNameProps("_shibstate_");\r
- string stateval = urlenc->encode(relayState.c_str()) + shib_cookie.second;\r
- // Generate a random key for the cookie name instead of the fixed name.\r
- string rsKey;\r
- generateRandomHex(rsKey,5);\r
- shib_cookie.first = "_shibstate_" + rsKey;\r
- response.setCookie(shib_cookie.first.c_str(),stateval.c_str());\r
- relayState = "cookie:" + rsKey;\r
- }\r
- }\r
- else if (strstr(mech.second,"ss:")==mech.second) {\r
- if (relayState.find("ss:") != 0) {\r
- mech.second+=3;\r
- if (*mech.second) {\r
- if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {\r
-#ifndef SHIBSP_LITE\r
- StorageService* storage = application.getServiceProvider().getStorageService(mech.second);\r
- if (storage) {\r
- string rsKey;\r
- generateRandomHex(rsKey,5);\r
- if (!storage->createString("RelayState", rsKey.c_str(), relayState.c_str(), time(NULL) + 600))\r
- throw IOException("Attempted to insert duplicate storage key.");\r
- relayState = string(mech.second-3) + ':' + rsKey;\r
- }\r
- else {\r
- m_log.error("Storage-backed RelayState with invalid StorageService ID (%s)", mech.second);\r
- relayState.erase();\r
- }\r
-#endif\r
- }\r
- else if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {\r
- DDF out,in = DDF("set::RelayState").structure();\r
- in.addmember("id").string(mech.second);\r
- in.addmember("value").unsafe_string(relayState.c_str());\r
- DDFJanitor jin(in),jout(out);\r
- out = application.getServiceProvider().getListenerService()->send(in);\r
- if (!out.isstring())\r
- throw IOException("StorageService-backed RelayState mechanism did not return a state key.");\r
- relayState = string(mech.second-3) + ':' + out.string();\r
- }\r
- }\r
- }\r
- }\r
- else\r
- throw ConfigurationException("Unsupported relayState mechanism ($1).", params(1,mech.second));\r
-}\r
-\r
-void AbstractHandler::recoverRelayState(\r
- const Application& application, const HTTPRequest& request, HTTPResponse& response, string& relayState, bool clear\r
- ) const\r
-{\r
- SPConfig& conf = SPConfig::getConfig();\r
-\r
- // Look for StorageService-backed state of the form "ss:SSID:key".\r
- const char* state = relayState.c_str();\r
- if (strstr(state,"ss:")==state) {\r
- state += 3;\r
- const char* key = strchr(state,':');\r
- if (key) {\r
- string ssid = relayState.substr(3, key - state);\r
- key++;\r
- if (!ssid.empty() && *key) {\r
- if (conf.isEnabled(SPConfig::OutOfProcess)) {\r
-#ifndef SHIBSP_LITE\r
- StorageService* storage = conf.getServiceProvider()->getStorageService(ssid.c_str());\r
- if (storage) {\r
- ssid = key;\r
- if (storage->readString("RelayState",ssid.c_str(),&relayState)>0) {\r
- if (clear)\r
- storage->deleteString("RelayState",ssid.c_str());\r
- return;\r
- }\r
- else\r
- relayState.erase();\r
- }\r
- else {\r
- m_log.error("Storage-backed RelayState with invalid StorageService ID (%s)", ssid.c_str());\r
- relayState.erase();\r
- }\r
-#endif\r
- }\r
- else if (conf.isEnabled(SPConfig::InProcess)) {\r
- DDF out,in = DDF("get::RelayState").structure();\r
- in.addmember("id").string(ssid.c_str());\r
- in.addmember("key").string(key);\r
- in.addmember("clear").integer(clear ? 1 : 0);\r
- DDFJanitor jin(in),jout(out);\r
- out = application.getServiceProvider().getListenerService()->send(in);\r
- if (!out.isstring()) {\r
- m_log.error("StorageService-backed RelayState mechanism did not return a state value.");\r
- relayState.erase();\r
- }\r
- else {\r
- relayState = out.string();\r
- return;\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- // Look for cookie-backed state of the form "cookie:key".\r
- if (strstr(state,"cookie:")==state) {\r
- state += 7;\r
- if (*state) {\r
- // Pull the value from the "relay state" cookie.\r
- pair<string,const char*> relay_cookie = application.getCookieNameProps("_shibstate_");\r
- relay_cookie.first = string("_shibstate_") + state;\r
- state = request.getCookie(relay_cookie.first.c_str());\r
- if (state && *state) {\r
- // URL-decode the value.\r
- char* rscopy=strdup(state);\r
- XMLToolingConfig::getConfig().getURLEncoder()->decode(rscopy);\r
- relayState = rscopy;\r
- free(rscopy);\r
-\r
- if (clear) {\r
- string exp(relay_cookie.second);\r
- exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT";\r
- response.setCookie(relay_cookie.first.c_str(), exp.c_str());\r
- }\r
- return;\r
- }\r
- }\r
-\r
- relayState.erase();\r
- }\r
-\r
- // Check for "default" value (or the old "cookie" value that might come from stale bookmarks).\r
- if (relayState.empty() || relayState == "default" || relayState == "cookie") {\r
- pair<bool,const char*> homeURL=application.getString("homeURL");\r
- if (homeURL.first)\r
- relayState=homeURL.second;\r
- else {\r
- // Compute a URL to the root of the site.\r
- int port = request.getPort();\r
- const char* scheme = request.getScheme();\r
- relayState = string(scheme) + "://" + request.getHostname();\r
- if ((!strcmp(scheme,"http") && port!=80) || (!strcmp(scheme,"https") && port!=443)) {\r
- ostringstream portstr;\r
- portstr << port;\r
- relayState += ":" + portstr.str();\r
- }\r
- relayState += '/';\r
- }\r
- }\r
-}\r
-\r
-void AbstractHandler::preservePostData(\r
- const Application& application, const HTTPRequest& request, HTTPResponse& response, const char* relayState\r
- ) const\r
-{\r
-#ifdef HAVE_STRCASECMP\r
- if (strcasecmp(request.getMethod(), "POST")) return;\r
-#else\r
- if (stricmp(request.getMethod(), "POST")) return;\r
-#endif\r
-\r
- // No specs mean no save.\r
- const PropertySet* props=application.getPropertySet("Sessions");\r
- pair<bool,const char*> mech = props->getString("postData");\r
- if (!mech.first) {\r
- m_log.info("postData property not supplied, form data will not be preserved across SSO");\r
- return;\r
- }\r
-\r
- DDF postData = getPostData(application, request);\r
- if (postData.isnull())\r
- return;\r
-\r
- if (strstr(mech.second,"ss:") == mech.second) {\r
- mech.second+=3;\r
- if (!*mech.second) {\r
- postData.destroy();\r
- throw ConfigurationException("Unsupported postData mechanism ($1).", params(1, mech.second - 3));\r
- }\r
-\r
- string postkey;\r
- if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {\r
- DDFJanitor postjan(postData);\r
-#ifndef SHIBSP_LITE\r
- StorageService* storage = application.getServiceProvider().getStorageService(mech.second);\r
- if (storage) {\r
- // Use a random key\r
- string rsKey;\r
- SAMLConfig::getConfig().generateRandomBytes(rsKey,20);\r
- rsKey = SAMLArtifact::toHex(rsKey);\r
- ostringstream out;\r
- out << postData;\r
- if (!storage->createString("PostData", rsKey.c_str(), out.str().c_str(), time(NULL) + 600))\r
- throw IOException("Attempted to insert duplicate storage key.");\r
- postkey = string(mech.second-3) + ':' + rsKey;\r
- }\r
- else {\r
- m_log.error("storage-backed PostData mechanism with invalid StorageService ID (%s)", mech.second);\r
- }\r
-#endif\r
- }\r
- else if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {\r
- DDF out,in = DDF("set::PostData").structure();\r
- DDFJanitor jin(in),jout(out);\r
- in.addmember("id").string(mech.second);\r
- in.add(postData);\r
- out = application.getServiceProvider().getListenerService()->send(in);\r
- if (!out.isstring())\r
- throw IOException("StorageService-backed PostData mechanism did not return a state key.");\r
- postkey = string(mech.second-3) + ':' + out.string();\r
- }\r
-\r
- // Set a cookie with key info.\r
- pair<string,const char*> shib_cookie = getPostCookieNameProps(application, relayState);\r
- postkey += shib_cookie.second;\r
- response.setCookie(shib_cookie.first.c_str(), postkey.c_str());\r
- }\r
- else {\r
- postData.destroy();\r
- throw ConfigurationException("Unsupported postData mechanism ($1).", params(1,mech.second));\r
- }\r
-}\r
-\r
-DDF AbstractHandler::recoverPostData(\r
- const Application& application, const HTTPRequest& request, HTTPResponse& response, const char* relayState\r
- ) const\r
-{\r
- // First we need the post recovery cookie.\r
- pair<string,const char*> shib_cookie = getPostCookieNameProps(application, relayState);\r
- const char* cookie = request.getCookie(shib_cookie.first.c_str());\r
- if (!cookie || !*cookie)\r
- return DDF();\r
-\r
- // Clear the cookie.\r
- string exp(shib_cookie.second);\r
- exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT";\r
- response.setCookie(shib_cookie.first.c_str(), exp.c_str());\r
-\r
- // Look for StorageService-backed state of the form "ss:SSID:key".\r
- const char* state = cookie;\r
- if (strstr(state, "ss:") == state) {\r
- state += 3;\r
- const char* key = strchr(state, ':');\r
- if (key) {\r
- string ssid = string(cookie).substr(3, key - state);\r
- key++;\r
- if (!ssid.empty() && *key) {\r
- SPConfig& conf = SPConfig::getConfig();\r
- if (conf.isEnabled(SPConfig::OutOfProcess)) {\r
-#ifndef SHIBSP_LITE\r
- StorageService* storage = conf.getServiceProvider()->getStorageService(ssid.c_str());\r
- if (storage) {\r
- if (storage->readString("PostData", key, &ssid) > 0) {\r
- storage->deleteString("PostData", key);\r
- istringstream inret(ssid);\r
- DDF ret;\r
- inret >> ret;\r
- return ret;\r
- }\r
- else {\r
- m_log.error("failed to recover form post data using key (%s)", key);\r
- }\r
- }\r
- else {\r
- m_log.error("storage-backed PostData with invalid StorageService ID (%s)", ssid.c_str());\r
- }\r
-#endif\r
- }\r
- else if (conf.isEnabled(SPConfig::InProcess)) {\r
- DDF in = DDF("get::PostData").structure();\r
- DDFJanitor jin(in);\r
- in.addmember("id").string(ssid.c_str());\r
- in.addmember("key").string(key);\r
- DDF out = application.getServiceProvider().getListenerService()->send(in);\r
- if (out.islist())\r
- return out;\r
- out.destroy();\r
- m_log.error("storageService-backed PostData mechanism did not return preserved data.");\r
- }\r
- }\r
- }\r
- }\r
- return DDF();\r
-}\r
-\r
-long AbstractHandler::sendPostResponse(\r
- const Application& application, HTTPResponse& httpResponse, const char* url, DDF& postData\r
- ) const\r
-{\r
- HTTPResponse::sanitizeURL(url);\r
-\r
- const PropertySet* props=application.getPropertySet("Sessions");\r
- pair<bool,const char*> postTemplate = props->getString("postTemplate");\r
- if (!postTemplate.first)\r
- throw ConfigurationException("Missing postTemplate property, unable to recreate form post.");\r
-\r
- string fname(postTemplate.second);\r
- ifstream infile(XMLToolingConfig::getConfig().getPathResolver()->resolve(fname, PathResolver::XMLTOOLING_CFG_FILE).c_str());\r
- if (!infile)\r
- throw ConfigurationException("Unable to access HTML template ($1).", params(1, fname.c_str()));\r
- TemplateParameters respParam;\r
- respParam.m_map["action"] = url;\r
-\r
- // Load the parameters into objects for the template.\r
- multimap<string,string>& collection = respParam.m_collectionMap["PostedData"];\r
- DDF param = postData.first();\r
- while (!param.isnull()) {\r
- collection.insert(pair<const string,string>(param.name(), (param.string() ? param.string() : "")));\r
- param = postData.next();\r
- }\r
-\r
- stringstream str;\r
- XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, respParam);\r
-\r
- pair<bool,bool> postExpire = props->getBool("postExpire");\r
-\r
- httpResponse.setContentType("text/html");\r
- if (!postExpire.first || postExpire.second) {\r
- httpResponse.setResponseHeader("Expires", "01-Jan-1997 12:00:00 GMT");\r
- httpResponse.setResponseHeader("Cache-Control", "no-cache, no-store, must-revalidate, private");\r
- httpResponse.setResponseHeader("Pragma", "no-cache");\r
- }\r
- return httpResponse.sendResponse(str);\r
-}\r
-\r
-pair<string,const char*> AbstractHandler::getPostCookieNameProps(const Application& app, const char* relayState) const\r
-{\r
- // Decorates the name of the cookie with the relay state key, if any.\r
- // Doing so gives a better assurance that the recovered data really\r
- // belongs to the relayed request.\r
- pair<string,const char*> shib_cookie=app.getCookieNameProps("_shibpost_");\r
- if (strstr(relayState, "cookie:") == relayState) {\r
- shib_cookie.first = string("_shibpost_") + (relayState + 7);\r
- }\r
- else if (strstr(relayState, "ss:") == relayState) {\r
- const char* pch = strchr(relayState + 3, ':');\r
- if (pch)\r
- shib_cookie.first = string("_shibpost_") + (pch + 1);\r
- }\r
- return shib_cookie;\r
-}\r
-\r
-DDF AbstractHandler::getPostData(const Application& application, const HTTPRequest& request) const\r
-{\r
- string contentType = request.getContentType();\r
- if (contentType.compare("application/x-www-form-urlencoded") == 0) {\r
- const PropertySet* props=application.getPropertySet("Sessions");\r
- pair<bool,unsigned int> plimit = props->getUnsignedInt("postLimit");\r
- if (!plimit.first)\r
- plimit.second = 1024 * 1024;\r
- if (plimit.second == 0 || request.getContentLength() <= plimit.second) {\r
- CGIParser cgi(request);\r
- pair<CGIParser::walker,CGIParser::walker> params = cgi.getParameters(NULL);\r
- if (params.first == params.second)\r
- return DDF("parameters").list();\r
- DDF child;\r
- DDF ret = DDF("parameters").list();\r
- for (; params.first != params.second; ++params.first) {\r
- if (!params.first->first.empty()) {\r
- child = DDF(params.first->first.c_str()).unsafe_string(params.first->second);\r
- ret.add(child);\r
- }\r
- }\r
- return ret;\r
- }\r
- else {\r
- m_log.warn("POST limit exceeded, ignoring %d bytes of posted data", request.getContentLength());\r
- }\r
- }\r
- else {\r
- m_log.info("ignoring POST data with non-standard encoding (%s)", contentType.c_str());\r
- }\r
- return DDF();\r
-}\r
+/*
+ * Copyright 2001-2009 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.
+ */
+
+/**
+ * AbstractHandler.cpp
+ *
+ * Base class for handlers based on a DOMPropertySet.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "Application.h"
+#include "ServiceProvider.h"
+#include "SPRequest.h"
+#include "handler/AbstractHandler.h"
+#include "handler/LogoutHandler.h"
+#include "remoting/ListenerService.h"
+#include "util/CGIParser.h"
+#include "util/SPConstants.h"
+#include "util/TemplateParameters.h"
+
+#include <vector>
+#include <fstream>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/PathResolver.h>
+#include <xmltooling/util/URLEncoder.h>
+
+
+#ifndef SHIBSP_LITE
+# include <saml/exceptions.h>
+# include <saml/SAMLConfig.h>
+# include <saml/binding/SAMLArtifact.h>
+# include <saml/saml1/core/Protocols.h>
+# include <saml/saml2/core/Protocols.h>
+# include <saml/saml2/metadata/Metadata.h>
+# include <saml/saml2/metadata/MetadataCredentialCriteria.h>
+# include <saml/util/SAMLConstants.h>
+# include <xmltooling/security/Credential.h>
+# include <xmltooling/security/CredentialResolver.h>
+# include <xmltooling/util/StorageService.h>
+using namespace opensaml::saml2md;
+using namespace opensaml;
+#else
+# include "lite/SAMLConstants.h"
+#endif
+
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/URLEncoder.h>
+
+using namespace shibsp;
+using namespace samlconstants;
+using namespace xmltooling;
+using namespace xercesc;
+using namespace std;
+
+namespace shibsp {
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML1ConsumerFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2ConsumerFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2ArtifactResolutionFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory ChainingLogoutInitiatorFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory LocalLogoutInitiatorFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2LogoutInitiatorFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2LogoutFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SAML2NameIDMgmtFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory AssertionLookupFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory MetadataGeneratorFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory StatusHandlerFactory;
+ SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<const DOMElement*,const char*> >::Factory SessionHandlerFactory;
+
+ void SHIBSP_DLLLOCAL generateRandomHex(std::string& buf, unsigned int len) {
+ static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ int r;
+ unsigned char b1,b2;
+ buf.erase();
+ for (unsigned int i=0; i<len; i+=4) {
+ r = rand();
+ b1 = (0x00FF & r);
+ b2 = (0xFF00 & r) >> 8;
+ buf += (DIGITS[(0xF0 & b1) >> 4 ]);
+ buf += (DIGITS[0x0F & b1]);
+ buf += (DIGITS[(0xF0 & b2) >> 4 ]);
+ buf += (DIGITS[0x0F & b2]);
+ }
+ }
+};
+
+void SHIBSP_API shibsp::registerHandlers()
+{
+ SPConfig& conf=SPConfig::getConfig();
+
+ conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_ARTIFACT, SAML1ConsumerFactory);
+ conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_POST, SAML1ConsumerFactory);
+ conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST, SAML2ConsumerFactory);
+ conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2ConsumerFactory);
+ conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_ARTIFACT, SAML2ConsumerFactory);
+ conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_PAOS, SAML2ConsumerFactory);
+
+ conf.ArtifactResolutionServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2ArtifactResolutionFactory);
+
+ conf.HandlerManager.registerFactory(SAML20_BINDING_URI, AssertionLookupFactory);
+ conf.HandlerManager.registerFactory(METADATA_GENERATOR_HANDLER, MetadataGeneratorFactory);
+ conf.HandlerManager.registerFactory(STATUS_HANDLER, StatusHandlerFactory);
+ conf.HandlerManager.registerFactory(SESSION_HANDLER, SessionHandlerFactory);
+
+ conf.LogoutInitiatorManager.registerFactory(CHAINING_LOGOUT_INITIATOR, ChainingLogoutInitiatorFactory);
+ conf.LogoutInitiatorManager.registerFactory(LOCAL_LOGOUT_INITIATOR, LocalLogoutInitiatorFactory);
+ conf.LogoutInitiatorManager.registerFactory(SAML2_LOGOUT_INITIATOR, SAML2LogoutInitiatorFactory);
+ conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2LogoutFactory);
+ conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_REDIRECT, SAML2LogoutFactory);
+ conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_POST, SAML2LogoutFactory);
+ conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2LogoutFactory);
+ conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_ARTIFACT, SAML2LogoutFactory);
+
+ conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2NameIDMgmtFactory);
+ conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_HTTP_REDIRECT, SAML2NameIDMgmtFactory);
+ conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_HTTP_POST, SAML2NameIDMgmtFactory);
+ conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2NameIDMgmtFactory);
+ conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_HTTP_ARTIFACT, SAML2NameIDMgmtFactory);
+}
+
+Handler::Handler()
+{
+}
+
+Handler::~Handler()
+{
+}
+
+AbstractHandler::AbstractHandler(
+ const DOMElement* e, Category& log, DOMNodeFilter* filter, const map<string,string>* remapper
+ ) : m_log(log), m_configNS(shibspconstants::SHIB2SPCONFIG_NS) {
+ load(e,NULL,filter,remapper);
+}
+
+AbstractHandler::~AbstractHandler()
+{
+}
+
+#ifndef SHIBSP_LITE
+
+const char* Handler::getType() const
+{
+ return getString("type").second;
+}
+
+void AbstractHandler::checkError(const XMLObject* response, const saml2md::RoleDescriptor* role) const
+{
+ const saml2p::StatusResponseType* r2 = dynamic_cast<const saml2p::StatusResponseType*>(response);
+ if (r2) {
+ const saml2p::Status* status = r2->getStatus();
+ if (status) {
+ const saml2p::StatusCode* sc = status->getStatusCode();
+ const XMLCh* code = sc ? sc->getValue() : NULL;
+ if (code && !XMLString::equals(code,saml2p::StatusCode::SUCCESS)) {
+ FatalProfileException ex("SAML response contained an error.");
+ annotateException(&ex, role, status); // throws it
+ }
+ }
+ }
+
+ const saml1p::Response* r1 = dynamic_cast<const saml1p::Response*>(response);
+ if (r1) {
+ const saml1p::Status* status = r1->getStatus();
+ if (status) {
+ const saml1p::StatusCode* sc = status->getStatusCode();
+ const xmltooling::QName* code = sc ? sc->getValue() : NULL;
+ if (code && *code != saml1p::StatusCode::SUCCESS) {
+ FatalProfileException ex("SAML response contained an error.");
+ ex.addProperty("statusCode", code->toString().c_str());
+ if (sc->getStatusCode()) {
+ code = sc->getStatusCode()->getValue();
+ if (code)
+ ex.addProperty("statusCode2", code->toString().c_str());
+ }
+ if (status->getStatusMessage()) {
+ auto_ptr_char msg(status->getStatusMessage()->getMessage());
+ if (msg.get() && *msg.get())
+ ex.addProperty("statusMessage", msg.get());
+ }
+ ex.raise();
+ }
+ }
+ }
+}
+
+void AbstractHandler::fillStatus(saml2p::StatusResponseType& response, const XMLCh* code, const XMLCh* subcode, const char* msg) const
+{
+ saml2p::Status* status = saml2p::StatusBuilder::buildStatus();
+ saml2p::StatusCode* scode = saml2p::StatusCodeBuilder::buildStatusCode();
+ status->setStatusCode(scode);
+ scode->setValue(code);
+ if (subcode) {
+ saml2p::StatusCode* ssubcode = saml2p::StatusCodeBuilder::buildStatusCode();
+ scode->setStatusCode(ssubcode);
+ ssubcode->setValue(subcode);
+ }
+ if (msg) {
+ pair<bool,bool> flag = getBool("detailedErrors", m_configNS.get());
+ auto_ptr_XMLCh widemsg((flag.first && flag.second) ? msg : "Error processing request.");
+ saml2p::StatusMessage* sm = saml2p::StatusMessageBuilder::buildStatusMessage();
+ status->setStatusMessage(sm);
+ sm->setMessage(widemsg.get());
+ }
+ response.setStatus(status);
+}
+
+long AbstractHandler::sendMessage(
+ const MessageEncoder& encoder,
+ XMLObject* msg,
+ const char* relayState,
+ const char* destination,
+ const saml2md::RoleDescriptor* role,
+ const Application& application,
+ HTTPResponse& httpResponse,
+ bool signIfPossible
+ ) const
+{
+ const EntityDescriptor* entity = role ? dynamic_cast<const EntityDescriptor*>(role->getParent()) : NULL;
+ const PropertySet* relyingParty = application.getRelyingParty(entity);
+ pair<bool,const char*> flag = signIfPossible ? make_pair(true,(const char*)"true") : relyingParty->getString("signing");
+ if (role && flag.first &&
+ (!strcmp(flag.second, "true") ||
+ (encoder.isUserAgentPresent() && !strcmp(flag.second, "front")) ||
+ (!encoder.isUserAgentPresent() && !strcmp(flag.second, "back")))) {
+ CredentialResolver* credResolver=application.getCredentialResolver();
+ if (credResolver) {
+ Locker credLocker(credResolver);
+ const Credential* cred = NULL;
+ pair<bool,const char*> keyName = relyingParty->getString("keyName");
+ pair<bool,const XMLCh*> sigalg = relyingParty->getXMLString("signingAlg");
+ if (role) {
+ MetadataCredentialCriteria mcc(*role);
+ mcc.setUsage(Credential::SIGNING_CREDENTIAL);
+ if (keyName.first)
+ mcc.getKeyNames().insert(keyName.second);
+ if (sigalg.first)
+ mcc.setXMLAlgorithm(sigalg.second);
+ cred = credResolver->resolve(&mcc);
+ }
+ else {
+ CredentialCriteria cc;
+ cc.setUsage(Credential::SIGNING_CREDENTIAL);
+ if (keyName.first)
+ cc.getKeyNames().insert(keyName.second);
+ if (sigalg.first)
+ cc.setXMLAlgorithm(sigalg.second);
+ cred = credResolver->resolve(&cc);
+ }
+ if (cred) {
+ // Signed request.
+ return encoder.encode(
+ httpResponse,
+ msg,
+ destination,
+ entity,
+ relayState,
+ &application,
+ cred,
+ sigalg.second,
+ relyingParty->getXMLString("digestAlg").second
+ );
+ }
+ else {
+ m_log.warn("no signing credential resolved, leaving message unsigned");
+ }
+ }
+ else {
+ m_log.warn("no credential resolver installed, leaving message unsigned");
+ }
+ }
+
+ // Unsigned request.
+ return encoder.encode(httpResponse, msg, destination, entity, relayState, &application);
+}
+
+#endif
+
+void AbstractHandler::preserveRelayState(const Application& application, HTTPResponse& response, string& relayState) const
+{
+ if (relayState.empty())
+ return;
+
+ // No setting means just pass it by value.
+ pair<bool,const char*> mech=getString("relayState");
+ if (!mech.first || !mech.second || !*mech.second)
+ return;
+
+ if (!strcmp(mech.second, "cookie")) {
+ // Here we store the state in a cookie and send a fixed
+ // value so we can recognize it on the way back.
+ if (relayState.find("cookie:") != 0) {
+ const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder();
+ pair<string,const char*> shib_cookie=application.getCookieNameProps("_shibstate_");
+ string stateval = urlenc->encode(relayState.c_str()) + shib_cookie.second;
+ // Generate a random key for the cookie name instead of the fixed name.
+ string rsKey;
+ generateRandomHex(rsKey,5);
+ shib_cookie.first = "_shibstate_" + rsKey;
+ response.setCookie(shib_cookie.first.c_str(),stateval.c_str());
+ relayState = "cookie:" + rsKey;
+ }
+ }
+ else if (strstr(mech.second,"ss:")==mech.second) {
+ if (relayState.find("ss:") != 0) {
+ mech.second+=3;
+ if (*mech.second) {
+ if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+#ifndef SHIBSP_LITE
+ StorageService* storage = application.getServiceProvider().getStorageService(mech.second);
+ if (storage) {
+ string rsKey;
+ generateRandomHex(rsKey,5);
+ if (!storage->createString("RelayState", rsKey.c_str(), relayState.c_str(), time(NULL) + 600))
+ throw IOException("Attempted to insert duplicate storage key.");
+ relayState = string(mech.second-3) + ':' + rsKey;
+ }
+ else {
+ m_log.error("Storage-backed RelayState with invalid StorageService ID (%s)", mech.second);
+ relayState.erase();
+ }
+#endif
+ }
+ else if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
+ DDF out,in = DDF("set::RelayState").structure();
+ in.addmember("id").string(mech.second);
+ in.addmember("value").unsafe_string(relayState.c_str());
+ DDFJanitor jin(in),jout(out);
+ out = application.getServiceProvider().getListenerService()->send(in);
+ if (!out.isstring())
+ throw IOException("StorageService-backed RelayState mechanism did not return a state key.");
+ relayState = string(mech.second-3) + ':' + out.string();
+ }
+ }
+ }
+ }
+ else
+ throw ConfigurationException("Unsupported relayState mechanism ($1).", params(1,mech.second));
+}
+
+void AbstractHandler::recoverRelayState(
+ const Application& application, const HTTPRequest& request, HTTPResponse& response, string& relayState, bool clear
+ ) const
+{
+ SPConfig& conf = SPConfig::getConfig();
+
+ // Look for StorageService-backed state of the form "ss:SSID:key".
+ const char* state = relayState.c_str();
+ if (strstr(state,"ss:")==state) {
+ state += 3;
+ const char* key = strchr(state,':');
+ if (key) {
+ string ssid = relayState.substr(3, key - state);
+ key++;
+ if (!ssid.empty() && *key) {
+ if (conf.isEnabled(SPConfig::OutOfProcess)) {
+#ifndef SHIBSP_LITE
+ StorageService* storage = conf.getServiceProvider()->getStorageService(ssid.c_str());
+ if (storage) {
+ ssid = key;
+ if (storage->readString("RelayState",ssid.c_str(),&relayState)>0) {
+ if (clear)
+ storage->deleteString("RelayState",ssid.c_str());
+ return;
+ }
+ else
+ relayState.erase();
+ }
+ else {
+ m_log.error("Storage-backed RelayState with invalid StorageService ID (%s)", ssid.c_str());
+ relayState.erase();
+ }
+#endif
+ }
+ else if (conf.isEnabled(SPConfig::InProcess)) {
+ DDF out,in = DDF("get::RelayState").structure();
+ in.addmember("id").string(ssid.c_str());
+ in.addmember("key").string(key);
+ in.addmember("clear").integer(clear ? 1 : 0);
+ DDFJanitor jin(in),jout(out);
+ out = application.getServiceProvider().getListenerService()->send(in);
+ if (!out.isstring()) {
+ m_log.error("StorageService-backed RelayState mechanism did not return a state value.");
+ relayState.erase();
+ }
+ else {
+ relayState = out.string();
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // Look for cookie-backed state of the form "cookie:key".
+ if (strstr(state,"cookie:")==state) {
+ state += 7;
+ if (*state) {
+ // Pull the value from the "relay state" cookie.
+ pair<string,const char*> relay_cookie = application.getCookieNameProps("_shibstate_");
+ relay_cookie.first = string("_shibstate_") + state;
+ state = request.getCookie(relay_cookie.first.c_str());
+ if (state && *state) {
+ // URL-decode the value.
+ char* rscopy=strdup(state);
+ XMLToolingConfig::getConfig().getURLEncoder()->decode(rscopy);
+ relayState = rscopy;
+ free(rscopy);
+
+ if (clear) {
+ string exp(relay_cookie.second);
+ exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT";
+ response.setCookie(relay_cookie.first.c_str(), exp.c_str());
+ }
+ return;
+ }
+ }
+
+ relayState.erase();
+ }
+
+ // Check for "default" value (or the old "cookie" value that might come from stale bookmarks).
+ if (relayState.empty() || relayState == "default" || relayState == "cookie") {
+ pair<bool,const char*> homeURL=application.getString("homeURL");
+ if (homeURL.first)
+ relayState=homeURL.second;
+ else {
+ // Compute a URL to the root of the site.
+ int port = request.getPort();
+ const char* scheme = request.getScheme();
+ relayState = string(scheme) + "://" + request.getHostname();
+ if ((!strcmp(scheme,"http") && port!=80) || (!strcmp(scheme,"https") && port!=443)) {
+ ostringstream portstr;
+ portstr << port;
+ relayState += ":" + portstr.str();
+ }
+ relayState += '/';
+ }
+ }
+}
+
+void AbstractHandler::preservePostData(
+ const Application& application, const HTTPRequest& request, HTTPResponse& response, const char* relayState
+ ) const
+{
+#ifdef HAVE_STRCASECMP
+ if (strcasecmp(request.getMethod(), "POST")) return;
+#else
+ if (stricmp(request.getMethod(), "POST")) return;
+#endif
+
+ // No specs mean no save.
+ const PropertySet* props=application.getPropertySet("Sessions");
+ pair<bool,const char*> mech = props->getString("postData");
+ if (!mech.first) {
+ m_log.info("postData property not supplied, form data will not be preserved across SSO");
+ return;
+ }
+
+ DDF postData = getPostData(application, request);
+ if (postData.isnull())
+ return;
+
+ if (strstr(mech.second,"ss:") == mech.second) {
+ mech.second+=3;
+ if (!*mech.second) {
+ postData.destroy();
+ throw ConfigurationException("Unsupported postData mechanism ($1).", params(1, mech.second - 3));
+ }
+
+ string postkey;
+ if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+ DDFJanitor postjan(postData);
+#ifndef SHIBSP_LITE
+ StorageService* storage = application.getServiceProvider().getStorageService(mech.second);
+ if (storage) {
+ // Use a random key
+ string rsKey;
+ SAMLConfig::getConfig().generateRandomBytes(rsKey,20);
+ rsKey = SAMLArtifact::toHex(rsKey);
+ ostringstream out;
+ out << postData;
+ if (!storage->createString("PostData", rsKey.c_str(), out.str().c_str(), time(NULL) + 600))
+ throw IOException("Attempted to insert duplicate storage key.");
+ postkey = string(mech.second-3) + ':' + rsKey;
+ }
+ else {
+ m_log.error("storage-backed PostData mechanism with invalid StorageService ID (%s)", mech.second);
+ }
+#endif
+ }
+ else if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
+ DDF out,in = DDF("set::PostData").structure();
+ DDFJanitor jin(in),jout(out);
+ in.addmember("id").string(mech.second);
+ in.add(postData);
+ out = application.getServiceProvider().getListenerService()->send(in);
+ if (!out.isstring())
+ throw IOException("StorageService-backed PostData mechanism did not return a state key.");
+ postkey = string(mech.second-3) + ':' + out.string();
+ }
+
+ // Set a cookie with key info.
+ pair<string,const char*> shib_cookie = getPostCookieNameProps(application, relayState);
+ postkey += shib_cookie.second;
+ response.setCookie(shib_cookie.first.c_str(), postkey.c_str());
+ }
+ else {
+ postData.destroy();
+ throw ConfigurationException("Unsupported postData mechanism ($1).", params(1,mech.second));
+ }
+}
+
+DDF AbstractHandler::recoverPostData(
+ const Application& application, const HTTPRequest& request, HTTPResponse& response, const char* relayState
+ ) const
+{
+ // First we need the post recovery cookie.
+ pair<string,const char*> shib_cookie = getPostCookieNameProps(application, relayState);
+ const char* cookie = request.getCookie(shib_cookie.first.c_str());
+ if (!cookie || !*cookie)
+ return DDF();
+
+ // Clear the cookie.
+ 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());
+
+ // Look for StorageService-backed state of the form "ss:SSID:key".
+ const char* state = cookie;
+ if (strstr(state, "ss:") == state) {
+ state += 3;
+ const char* key = strchr(state, ':');
+ if (key) {
+ string ssid = string(cookie).substr(3, key - state);
+ key++;
+ if (!ssid.empty() && *key) {
+ SPConfig& conf = SPConfig::getConfig();
+ if (conf.isEnabled(SPConfig::OutOfProcess)) {
+#ifndef SHIBSP_LITE
+ StorageService* storage = conf.getServiceProvider()->getStorageService(ssid.c_str());
+ if (storage) {
+ if (storage->readString("PostData", key, &ssid) > 0) {
+ storage->deleteString("PostData", key);
+ istringstream inret(ssid);
+ DDF ret;
+ inret >> ret;
+ return ret;
+ }
+ else {
+ m_log.error("failed to recover form post data using key (%s)", key);
+ }
+ }
+ else {
+ m_log.error("storage-backed PostData with invalid StorageService ID (%s)", ssid.c_str());
+ }
+#endif
+ }
+ else if (conf.isEnabled(SPConfig::InProcess)) {
+ DDF in = DDF("get::PostData").structure();
+ DDFJanitor jin(in);
+ in.addmember("id").string(ssid.c_str());
+ in.addmember("key").string(key);
+ DDF out = application.getServiceProvider().getListenerService()->send(in);
+ if (out.islist())
+ return out;
+ out.destroy();
+ m_log.error("storageService-backed PostData mechanism did not return preserved data.");
+ }
+ }
+ }
+ }
+ return DDF();
+}
+
+long AbstractHandler::sendPostResponse(
+ const Application& application, HTTPResponse& httpResponse, const char* url, DDF& postData
+ ) const
+{
+ HTTPResponse::sanitizeURL(url);
+
+ const PropertySet* props=application.getPropertySet("Sessions");
+ pair<bool,const char*> postTemplate = props->getString("postTemplate");
+ if (!postTemplate.first)
+ throw ConfigurationException("Missing postTemplate property, unable to recreate form post.");
+
+ string fname(postTemplate.second);
+ ifstream infile(XMLToolingConfig::getConfig().getPathResolver()->resolve(fname, PathResolver::XMLTOOLING_CFG_FILE).c_str());
+ if (!infile)
+ throw ConfigurationException("Unable to access HTML template ($1).", params(1, fname.c_str()));
+ TemplateParameters respParam;
+ respParam.m_map["action"] = url;
+
+ // Load the parameters into objects for the template.
+ multimap<string,string>& collection = respParam.m_collectionMap["PostedData"];
+ DDF param = postData.first();
+ while (!param.isnull()) {
+ collection.insert(pair<const string,string>(param.name(), (param.string() ? param.string() : "")));
+ param = postData.next();
+ }
+
+ stringstream str;
+ XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, respParam);
+
+ pair<bool,bool> postExpire = props->getBool("postExpire");
+
+ httpResponse.setContentType("text/html");
+ if (!postExpire.first || postExpire.second) {
+ httpResponse.setResponseHeader("Expires", "01-Jan-1997 12:00:00 GMT");
+ httpResponse.setResponseHeader("Cache-Control", "no-cache, no-store, must-revalidate, private");
+ httpResponse.setResponseHeader("Pragma", "no-cache");
+ }
+ return httpResponse.sendResponse(str);
+}
+
+pair<string,const char*> AbstractHandler::getPostCookieNameProps(const Application& app, const char* relayState) const
+{
+ // Decorates the name of the cookie with the relay state key, if any.
+ // Doing so gives a better assurance that the recovered data really
+ // belongs to the relayed request.
+ pair<string,const char*> shib_cookie=app.getCookieNameProps("_shibpost_");
+ if (strstr(relayState, "cookie:") == relayState) {
+ shib_cookie.first = string("_shibpost_") + (relayState + 7);
+ }
+ else if (strstr(relayState, "ss:") == relayState) {
+ const char* pch = strchr(relayState + 3, ':');
+ if (pch)
+ shib_cookie.first = string("_shibpost_") + (pch + 1);
+ }
+ return shib_cookie;
+}
+
+DDF AbstractHandler::getPostData(const Application& application, const HTTPRequest& request) const
+{
+ string contentType = request.getContentType();
+ if (contentType.compare("application/x-www-form-urlencoded") == 0) {
+ const PropertySet* props=application.getPropertySet("Sessions");
+ pair<bool,unsigned int> plimit = props->getUnsignedInt("postLimit");
+ if (!plimit.first)
+ plimit.second = 1024 * 1024;
+ if (plimit.second == 0 || request.getContentLength() <= plimit.second) {
+ CGIParser cgi(request);
+ pair<CGIParser::walker,CGIParser::walker> params = cgi.getParameters(NULL);
+ if (params.first == params.second)
+ return DDF("parameters").list();
+ DDF child;
+ DDF ret = DDF("parameters").list();
+ for (; params.first != params.second; ++params.first) {
+ if (!params.first->first.empty()) {
+ child = DDF(params.first->first.c_str()).unsafe_string(params.first->second);
+ ret.add(child);
+ }
+ }
+ return ret;
+ }
+ else {
+ m_log.warn("POST limit exceeded, ignoring %d bytes of posted data", request.getContentLength());
+ }
+ }
+ else {
+ m_log.info("ignoring POST data with non-standard encoding (%s)", contentType.c_str());
+ }
+ return DDF();
+}
void AssertionConsumerService::generateMetadata(SPSSODescriptor& role, const char* handlerURL) const
{
+ // Initial guess at index to use.
+ pair<bool,unsigned int> ix = pair<bool,unsigned int>(false,0);
+ if (!strncmp(handlerURL, "https", 5))
+ ix = getUnsignedInt("sslIndex", shibspconstants::ASCII_SHIB2SPCONFIG_NS);
+ if (!ix.first)
+ ix = getUnsignedInt("index");
+ if (!ix.first)
+ ix.second = 1;
+
+ // Find maximum index in use and go one higher.
+ const vector<saml2md::AssertionConsumerService*>& services = const_cast<const SPSSODescriptor&>(role).getAssertionConsumerServices();
+ if (!services.empty() && ix.second <= services.back()->getIndex().second)
+ ix.second = services.back()->getIndex().second + 1;
+
const char* loc = getString("Location").second;
string hurl(handlerURL);
if (*loc != '/')
hurl += '/';
hurl += loc;
auto_ptr_XMLCh widen(hurl.c_str());
+
saml2md::AssertionConsumerService* ep = saml2md::AssertionConsumerServiceBuilder::buildAssertionConsumerService();
ep->setLocation(widen.get());
ep->setBinding(getXMLString("Binding").second);
- if (!strncmp(handlerURL, "https", 5)) {
- pair<bool,const XMLCh*> index = getXMLString("sslIndex", shibspconstants::ASCII_SHIB2SPCONFIG_NS);
- if (index.first)
- ep->setIndex(index.second);
- else
- ep->setIndex(getXMLString("index").second);
- }
- else {
- ep->setIndex(getXMLString("index").second);
- }
+ ep->setIndex(ix.second);
role.getAssertionConsumerServices().push_back(ep);
}
}
}
}
+ else {
+ m_log.warn("no AttributeExtractor plugin installed, check log during startup");
+ }
try {
AttributeResolver* resolver = application.getAttributeResolver();
const char* getType() const {
return "ArtifactResolutionService";
}
+
+ void generateMetadata(SPSSODescriptor& role, const char* handlerURL) const {
+ // Initial guess at index to use.
+ pair<bool,unsigned int> ix = pair<bool,unsigned int>(false,0);
+ if (!strncmp(handlerURL, "https", 5))
+ ix = getUnsignedInt("sslIndex", shibspconstants::ASCII_SHIB2SPCONFIG_NS);
+ if (!ix.first)
+ ix = getUnsignedInt("index");
+ if (!ix.first)
+ ix.second = 1;
+
+ // Find maximum index in use and go one higher.
+ const vector<ArtifactResolutionService*>& services = const_cast<const SPSSODescriptor&>(role).getArtifactResolutionServices();
+ if (!services.empty() && ix.second <= services.back()->getIndex().second)
+ ix.second = services.back()->getIndex().second + 1;
+
+ const char* loc = getString("Location").second;
+ string hurl(handlerURL);
+ if (*loc != '/')
+ hurl += '/';
+ hurl += loc;
+ auto_ptr_XMLCh widen(hurl.c_str());
+
+ ArtifactResolutionService* ep = ArtifactResolutionServiceBuilder::buildArtifactResolutionService();
+ ep->setLocation(widen.get());
+ ep->setBinding(getXMLString("Binding").second);
+ ep->setIndex(ix.second);
+ role.getArtifactResolutionServices().push_back(ep);
+ }
#endif
private:
pair<bool,long> processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
#ifndef SHIBSP_LITE
- pair<bool,long> samlError(
- const Application& app,
- const ArtifactResolve& request,
- HTTPResponse& httpResponse,
- const EntityDescriptor* recipient,
- const XMLCh* code,
- const XMLCh* subcode=NULL,
- const char* msg=NULL
+ pair<bool,long> emptyResponse(
+ const Application& app, const ArtifactResolve& request, HTTPResponse& httpResponse, const EntityDescriptor* recipient
) const;
MessageEncoder* m_encoder;
try {
auto_ptr_char artifact(req->getArtifact() ? req->getArtifact()->getArtifact() : NULL);
if (!artifact.get() || !*artifact.get())
- return samlError(application, *req, httpResponse, entity, StatusCode::REQUESTER, NULL, "Request did not contain an artifact to resolve.");
+ return emptyResponse(application, *req, httpResponse, entity);
auto_ptr_char issuer(policy.getIssuer() ? policy.getIssuer()->getName() : NULL);
m_log.info("resolving artifact (%s) for (%s)", artifact.get(), issuer.get() ? issuer.get() : "unknown");
if (!policy.isAuthenticated()) {
m_log.error("request for artifact was unauthenticated, purging the artifact mapping");
- return samlError(application, *req, httpResponse, entity, StatusCode::REQUESTER, StatusCode::AUTHN_FAILED, "Unable to authenticate request.");
+ return emptyResponse(application, *req, httpResponse, entity);
}
m_log.debug("artifact resolved, preparing response");
return make_pair(true,ret);
}
catch (exception& ex) {
- // Trap localized errors in a SAML Response.
- m_log.error("error processing artifact request, returning SAML error: %s", ex.what());
- return samlError(application, *req, httpResponse, entity, StatusCode::RESPONDER, NULL, ex.what());
+ // Trap localized errors.
+ m_log.error("error processing artifact request: %s", ex.what());
+ return emptyResponse(application, *req, httpResponse, entity);
}
#else
return make_pair(false,0L);
}
#ifndef SHIBSP_LITE
-pair<bool,long> SAML2ArtifactResolution::samlError(
- const Application& app,
- const ArtifactResolve& request,
- HTTPResponse& httpResponse,
- const EntityDescriptor* recipient,
- const XMLCh* code,
- const XMLCh* subcode,
- const char* msg
+pair<bool,long> SAML2ArtifactResolution::emptyResponse(
+ const Application& app, const ArtifactResolve& request, HTTPResponse& httpResponse, const EntityDescriptor* recipient
) const
{
auto_ptr<ArtifactResponse> resp(ArtifactResponseBuilder::buildArtifactResponse());
resp->setInResponseTo(request.getID());
Issuer* me = IssuerBuilder::buildIssuer();
me->setName(app.getRelyingParty(recipient)->getXMLString("entityID").second);
- fillStatus(*resp.get(), code, subcode, msg);
+ fillStatus(*resp.get(), StatusCode::SUCCESS);
long ret = m_encoder->encode(httpResponse, resp.get(), NULL);
resp.release(); // freed by encoder
return make_pair(true,ret);
worked2 = true;
}
catch (exception& ex) {
- m_log.error("error removing session (%s): %s", session_id, ex.what());
+ m_log.error("error removing session (%s): %s", session_id.c_str(), ex.what());
}
}
else {
# include <saml/saml2/metadata/EndpointManager.h>
# include <saml/saml2/metadata/Metadata.h>
# include <saml/saml2/metadata/MetadataCredentialCriteria.h>
-# include <saml/signature/ContentReference.h>
-# include <xmltooling/security/Credential.h>
-# include <xmltooling/signature/Signature.h>
using namespace opensaml::saml2;
using namespace opensaml::saml2p;
using namespace opensaml::saml2md;
m_log.warn("IdP didn't respond to logout request");
ret = sendLogoutPage(application, httpRequest, httpResponse, "partial");
}
-
- // Check the status, looking for non-success or a partial logout code.
- const StatusCode* sc = logoutResponse->getStatus() ? logoutResponse->getStatus()->getStatusCode() : NULL;
- bool partial = (!sc || !XMLString::equals(sc->getValue(), StatusCode::SUCCESS));
- if (!partial) {
- // Success, but still need to check for partial.
- partial = XMLString::equals(sc->getStatusCode()->getValue(), StatusCode::PARTIAL_LOGOUT);
- }
- delete logoutResponse;
- if (partial)
- ret = sendLogoutPage(application, httpRequest, httpResponse, "partial");
else {
- const char* returnloc = httpRequest.getParameter("return");
- if (returnloc) {
- ret.second = httpResponse.sendRedirect(returnloc);
- ret.first = true;
+ // Check the status, looking for non-success or a partial logout code.
+ const StatusCode* sc = logoutResponse->getStatus() ? logoutResponse->getStatus()->getStatusCode() : NULL;
+ bool partial = (!sc || !XMLString::equals(sc->getValue(), StatusCode::SUCCESS));
+ if (!partial && sc->getStatusCode()) {
+ // Success, but still need to check for partial.
+ partial = XMLString::equals(sc->getStatusCode()->getValue(), StatusCode::PARTIAL_LOGOUT);
+ }
+ delete logoutResponse;
+ if (partial)
+ ret = sendLogoutPage(application, httpRequest, httpResponse, "partial");
+ else {
+ const char* returnloc = httpRequest.getParameter("return");
+ if (returnloc) {
+ ret.second = httpResponse.sendRedirect(returnloc);
+ ret.first = true;
+ }
+ ret = sendLogoutPage(application, httpRequest, httpResponse, "global");
}
- ret = sendLogoutPage(application, httpRequest, httpResponse, "global");
}
if (session) {
session = NULL;
application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse);
}
+
return ret;
}
msg->setNameID(nameid->cloneNameID());
}
- if (!encoder) {
- // No encoder being used, so sign for SOAP client manually.
- flag = relyingParty->getString("signing");
- if (flag.first && (!strcmp(flag.second, "true") || !strcmp(flag.second, "back"))) {
- CredentialResolver* credResolver=application.getCredentialResolver();
- if (credResolver) {
- Locker credLocker(credResolver);
- // Fill in criteria to use.
- MetadataCredentialCriteria mcc(role);
- mcc.setUsage(Credential::SIGNING_CREDENTIAL);
- pair<bool,const char*> keyName = relyingParty->getString("keyName");
- if (keyName.first)
- mcc.getKeyNames().insert(keyName.second);
- pair<bool,const XMLCh*> sigalg = relyingParty->getXMLString("signingAlg");
- if (sigalg.first)
- mcc.setXMLAlgorithm(sigalg.second);
- const Credential* cred = credResolver->resolve(&mcc);
- if (cred) {
- xmlsignature::Signature* sig = xmlsignature::SignatureBuilder::buildSignature();
- msg->setSignature(sig);
- if (sigalg.first)
- sig->setSignatureAlgorithm(sigalg.second);
- sigalg = relyingParty->getXMLString("digestAlg");
- if (sigalg.first) {
- ContentReference* cr = dynamic_cast<ContentReference*>(sig->getContentReference());
- if (cr)
- cr->setDigestAlgorithm(sigalg.second);
- }
-
- // Sign response while marshalling.
- vector<xmlsignature::Signature*> sigs(1,sig);
- msg->marshall((DOMDocument*)NULL,&sigs,cred);
- }
- else {
- m_log.warn("no signing credential resolved, leaving message unsigned");
- }
- }
- else {
- m_log.warn("no credential resolver installed, leaving message unsigned");
- }
- }
- }
-
return msg.release();
}
#ifndef SHIBSP_LITE
void generateMetadata(SPSSODescriptor& role, const char* handlerURL) const {
static const XMLCh LOCAL_NAME[] = UNICODE_LITERAL_17(D,i,s,c,o,v,e,r,y,R,e,s,p,o,n,s,e);
+
+ // Initial guess at index to use.
+ pair<bool,unsigned int> ix = getUnsignedInt("index");
+ if (!ix.first)
+ ix.second = 1;
+
+ // Find maximum index in use and go one higher.
+ if (role.getExtensions()) {
+ const vector<XMLObject*>& exts = const_cast<const Extensions*>(role.getExtensions())->getUnknownXMLObjects();
+ for (vector<XMLObject*>::const_reverse_iterator i = exts.rbegin(); i != exts.rend(); ++i) {
+ if (XMLString::equals((*i)->getElementQName().getLocalPart(), LOCAL_NAME) &&
+ XMLString::equals((*i)->getElementQName().getNamespaceURI(), m_discoNS.get())) {
+ const AttributeExtensibleXMLObject* sub = dynamic_cast<const AttributeExtensibleXMLObject*>(*i);
+ if (sub) {
+ const XMLCh* val = sub->getAttribute(xmltooling::QName(NULL,IndexedEndpointType::INDEX_ATTRIB_NAME));
+ if (val) {
+ int maxindex = XMLString::parseInt(val);
+ if (ix.second <= maxindex)
+ ix.second = maxindex + 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+
const char* loc = getString("Location").second;
string hurl(handlerURL);
if (*loc != '/')
hurl += '/';
hurl += loc;
auto_ptr_XMLCh widen(hurl.c_str());
+
+ ostringstream os;
+ os << ix.second;
+ auto_ptr_XMLCh widen2(os.str().c_str());
+
ElementProxy* ep = new AnyElementImpl(m_discoNS.get(), LOCAL_NAME);
ep->setAttribute(xmltooling::QName(NULL,EndpointType::LOCATION_ATTRIB_NAME), widen.get());
ep->setAttribute(xmltooling::QName(NULL,EndpointType::BINDING_ATTRIB_NAME), m_discoNS.get());
- pair<bool,const XMLCh*> ix = getXMLString("index");
- ep->setAttribute(xmltooling::QName(NULL,IndexedEndpointType::INDEX_ATTRIB_NAME), ix.first ? ix.second : xmlconstants::XML_ONE);
-
+ ep->setAttribute(xmltooling::QName(NULL,IndexedEndpointType::INDEX_ATTRIB_NAME), widen2.get());
Extensions* ext = role.getExtensions();
if (!ext) {
ext = ExtensionsBuilder::buildExtensions();
-/*\r
- * Copyright 2001-2009 Internet2\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/**\r
- * XMLServiceProvider.cpp\r
- *\r
- * XML-based SP configuration and mgmt.\r
- */\r
-\r
-#include "internal.h"\r
-#include "exceptions.h"\r
-#include "version.h"\r
-#include "AccessControl.h"\r
-#include "Application.h"\r
-#include "RequestMapper.h"\r
-#include "ServiceProvider.h"\r
-#include "SessionCache.h"\r
-#include "SPConfig.h"\r
-#include "SPRequest.h"\r
-#include "handler/SessionInitiator.h"\r
-#include "remoting/ListenerService.h"\r
-#include "util/DOMPropertySet.h"\r
-#include "util/SPConstants.h"\r
-\r
-#if defined(XMLTOOLING_LOG4SHIB)\r
-# include <log4shib/PropertyConfigurator.hh>\r
-#elif defined(XMLTOOLING_LOG4CPP)\r
-# include <log4cpp/PropertyConfigurator.hh>\r
-#else\r
-# error "Supported logging library not available."\r
-#endif\r
-#include <algorithm>\r
-#include <xercesc/util/XMLUniDefs.hpp>\r
-#include <xmltooling/XMLToolingConfig.h>\r
-#include <xmltooling/version.h>\r
-#include <xmltooling/util/NDC.h>\r
-#include <xmltooling/util/ReloadableXMLFile.h>\r
-#include <xmltooling/util/TemplateEngine.h>\r
-#include <xmltooling/util/XMLHelper.h>\r
-\r
-#ifndef SHIBSP_LITE\r
-# include "TransactionLog.h"\r
-# include "attribute/filtering/AttributeFilter.h"\r
-# include "attribute/resolver/AttributeExtractor.h"\r
-# include "attribute/resolver/AttributeResolver.h"\r
-# include "security/PKIXTrustEngine.h"\r
-# include <saml/SAMLConfig.h>\r
-# include <saml/version.h>\r
-# include <saml/binding/ArtifactMap.h>\r
-# include <saml/binding/SAMLArtifact.h>\r
-# include <saml/binding/SecurityPolicyRule.h>\r
-# include <saml/saml1/core/Assertions.h>\r
-# include <saml/saml2/core/Assertions.h>\r
-# include <saml/saml2/binding/SAML2ArtifactType0004.h>\r
-# include <saml/saml2/metadata/Metadata.h>\r
-# include <saml/saml2/metadata/MetadataProvider.h>\r
-# include <saml/util/SAMLConstants.h>\r
-# include <xmltooling/security/CredentialResolver.h>\r
-# include <xmltooling/security/SecurityHelper.h>\r
-# include <xmltooling/security/TrustEngine.h>\r
-# include <xmltooling/util/ReplayCache.h>\r
-# include <xmltooling/util/StorageService.h>\r
-using namespace opensaml::saml2;\r
-using namespace opensaml::saml2p;\r
-using namespace opensaml::saml2md;\r
-using namespace opensaml;\r
-#else\r
-# include "lite/SAMLConstants.h"\r
-#endif\r
-\r
-using namespace shibsp;\r
-using namespace xmltooling;\r
-using namespace std;\r
-\r
-#ifndef min\r
-# define min(a,b) (((a) < (b)) ? (a) : (b))\r
-#endif\r
-\r
-namespace {\r
-\r
-#if defined (_MSC_VER)\r
- #pragma warning( push )\r
- #pragma warning( disable : 4250 )\r
-#endif\r
-\r
- static vector<const Handler*> g_noHandlers;\r
-\r
- // Application configuration wrapper\r
- class SHIBSP_DLLLOCAL XMLApplication : public Application, public Remoted, public DOMPropertySet, public DOMNodeFilter\r
- {\r
- public:\r
- XMLApplication(const ServiceProvider*, const DOMElement* e, const XMLApplication* base=NULL);\r
- ~XMLApplication() { cleanup(); }\r
-\r
- const char* getHash() const {return m_hash.c_str();}\r
-\r
-#ifndef SHIBSP_LITE\r
- SAMLArtifact* generateSAML1Artifact(const EntityDescriptor* relyingParty) const {\r
- throw ConfigurationException("No support for SAML 1.x artifact generation.");\r
- }\r
- SAML2Artifact* generateSAML2Artifact(const EntityDescriptor* relyingParty) const {\r
- pair<bool,int> index = make_pair(false,0);\r
- const PropertySet* props = getRelyingParty(relyingParty);\r
- index = props->getInt("artifactEndpointIndex");\r
- if (!index.first)\r
- index = getArtifactEndpointIndex();\r
- pair<bool,const char*> entityID = props->getString("entityID");\r
- return new SAML2ArtifactType0004(\r
- SecurityHelper::doHash("SHA1", entityID.second, strlen(entityID.second), false),\r
- index.first ? index.second : 1\r
- );\r
- }\r
-\r
- MetadataProvider* getMetadataProvider(bool required=true) const {\r
- if (required && !m_base && !m_metadata)\r
- throw ConfigurationException("No MetadataProvider available.");\r
- return (!m_metadata && m_base) ? m_base->getMetadataProvider() : m_metadata;\r
- }\r
- TrustEngine* getTrustEngine(bool required=true) const {\r
- if (required && !m_base && !m_trust)\r
- throw ConfigurationException("No TrustEngine available.");\r
- return (!m_trust && m_base) ? m_base->getTrustEngine() : m_trust;\r
- }\r
- AttributeExtractor* getAttributeExtractor() const {\r
- return (!m_attrExtractor && m_base) ? m_base->getAttributeExtractor() : m_attrExtractor;\r
- }\r
- AttributeFilter* getAttributeFilter() const {\r
- return (!m_attrFilter && m_base) ? m_base->getAttributeFilter() : m_attrFilter;\r
- }\r
- AttributeResolver* getAttributeResolver() const {\r
- return (!m_attrResolver && m_base) ? m_base->getAttributeResolver() : m_attrResolver;\r
- }\r
- CredentialResolver* getCredentialResolver() const {\r
- return (!m_credResolver && m_base) ? m_base->getCredentialResolver() : m_credResolver;\r
- }\r
- const PropertySet* getRelyingParty(const EntityDescriptor* provider) const;\r
- const PropertySet* getRelyingParty(const XMLCh* entityID) const;\r
- const vector<const XMLCh*>* getAudiences() const {\r
- return (m_audiences.empty() && m_base) ? m_base->getAudiences() : &m_audiences;\r
- }\r
-#endif\r
- string getNotificationURL(const char* resource, bool front, unsigned int index) const;\r
-\r
- const vector<string>& getRemoteUserAttributeIds() const {\r
- return (m_remoteUsers.empty() && m_base) ? m_base->getRemoteUserAttributeIds() : m_remoteUsers;\r
- }\r
-\r
- void clearHeader(SPRequest& request, const char* rawname, const char* cginame) const;\r
- void setHeader(SPRequest& request, const char* name, const char* value) const;\r
- string getSecureHeader(const SPRequest& request, const char* name) const;\r
-\r
- const SessionInitiator* getDefaultSessionInitiator() const;\r
- const SessionInitiator* getSessionInitiatorById(const char* id) const;\r
- const Handler* getDefaultAssertionConsumerService() const;\r
- const Handler* getAssertionConsumerServiceByIndex(unsigned short index) const;\r
- const vector<const Handler*>& getAssertionConsumerServicesByBinding(const XMLCh* binding) const;\r
- const Handler* getHandler(const char* path) const;\r
- void getHandlers(vector<const Handler*>& handlers) const;\r
-\r
- void receive(DDF& in, ostream& out) {\r
- // Only current function is to return the headers to clear.\r
- DDF header;\r
- DDF ret=DDF(NULL).list();\r
- DDFJanitor jret(ret);\r
- for (vector< pair<string,string> >::const_iterator i = m_unsetHeaders.begin(); i!=m_unsetHeaders.end(); ++i) {\r
- header = DDF(i->first.c_str()).string(i->second.c_str());\r
- ret.add(header);\r
- }\r
- out << ret;\r
- }\r
-\r
- // Provides filter to exclude special config elements.\r
-#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE\r
- short\r
-#else\r
- FilterAction\r
-#endif\r
- acceptNode(const DOMNode* node) const;\r
-\r
- private:\r
- void cleanup();\r
- const XMLApplication* m_base;\r
- string m_hash;\r
- std::pair<std::string,std::string> m_attributePrefix;\r
-#ifndef SHIBSP_LITE\r
- MetadataProvider* m_metadata;\r
- TrustEngine* m_trust;\r
- AttributeExtractor* m_attrExtractor;\r
- AttributeFilter* m_attrFilter;\r
- AttributeResolver* m_attrResolver;\r
- CredentialResolver* m_credResolver;\r
- vector<const XMLCh*> m_audiences;\r
-\r
- // RelyingParty properties\r
- map<xstring,PropertySet*> m_partyMap;\r
-#endif\r
- vector<string> m_remoteUsers,m_frontLogout,m_backLogout;\r
-\r
- // manage handler objects\r
- vector<Handler*> m_handlers;\r
-\r
- // maps location (path info) to applicable handlers\r
- map<string,const Handler*> m_handlerMap;\r
-\r
- // maps unique indexes to consumer services\r
- map<unsigned int,const Handler*> m_acsIndexMap;\r
-\r
- // pointer to default consumer service\r
- const Handler* m_acsDefault;\r
-\r
- // maps binding strings to supporting consumer service(s)\r
- typedef map<xstring,vector<const Handler*> > ACSBindingMap;\r
- ACSBindingMap m_acsBindingMap;\r
-\r
- // pointer to default session initiator\r
- const SessionInitiator* m_sessionInitDefault;\r
-\r
- // maps unique ID strings to session initiators\r
- map<string,const SessionInitiator*> m_sessionInitMap;\r
-\r
- // pointer to default artifact resolution service\r
- const Handler* m_artifactResolutionDefault;\r
-\r
- pair<bool,int> getArtifactEndpointIndex() const {\r
- if (m_artifactResolutionDefault) return m_artifactResolutionDefault->getInt("index");\r
- return m_base ? m_base->getArtifactEndpointIndex() : make_pair(false,0);\r
- }\r
- };\r
-\r
- // Top-level configuration implementation\r
- class SHIBSP_DLLLOCAL XMLConfig;\r
- class SHIBSP_DLLLOCAL XMLConfigImpl : public DOMPropertySet, public DOMNodeFilter\r
- {\r
- public:\r
- XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer, Category& log);\r
- ~XMLConfigImpl();\r
-\r
- RequestMapper* m_requestMapper;\r
- map<string,Application*> m_appmap;\r
-#ifndef SHIBSP_LITE\r
- map< string,pair< PropertySet*,vector<const SecurityPolicyRule*> > > m_policyMap;\r
- vector< pair< string, pair<string,string> > > m_transportOptions;\r
-#endif\r
-\r
- // Provides filter to exclude special config elements.\r
-#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE\r
- short\r
-#else\r
- FilterAction\r
-#endif\r
- acceptNode(const DOMNode* node) const;\r
-\r
- void setDocument(DOMDocument* doc) {\r
- m_document = doc;\r
- }\r
-\r
- private:\r
- void doExtensions(const DOMElement* e, const char* label, Category& log);\r
- void cleanup();\r
-\r
- const XMLConfig* m_outer;\r
- DOMDocument* m_document;\r
- };\r
-\r
- class SHIBSP_DLLLOCAL XMLConfig : public ServiceProvider, public ReloadableXMLFile\r
-#ifndef SHIBSP_LITE\r
- ,public Remoted\r
-#endif\r
- {\r
- public:\r
- XMLConfig(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".Config")),\r
- m_impl(NULL), m_listener(NULL), m_sessionCache(NULL)\r
-#ifndef SHIBSP_LITE\r
- , m_tranLog(NULL)\r
-#endif\r
- {\r
- }\r
-\r
- void init() {\r
- load();\r
- }\r
-\r
- ~XMLConfig() {\r
- delete m_impl;\r
- delete m_sessionCache;\r
- delete m_listener;\r
-#ifndef SHIBSP_LITE\r
- delete m_tranLog;\r
- SAMLConfig::getConfig().setArtifactMap(NULL);\r
- XMLToolingConfig::getConfig().setReplayCache(NULL);\r
- for_each(m_storage.begin(), m_storage.end(), cleanup_pair<string,StorageService>());\r
-#endif\r
- }\r
-\r
- // PropertySet\r
- const PropertySet* getParent() const { return m_impl->getParent(); }\r
- void setParent(const PropertySet* parent) {return m_impl->setParent(parent);}\r
- pair<bool,bool> getBool(const char* name, const char* ns=NULL) const {return m_impl->getBool(name,ns);}\r
- pair<bool,const char*> getString(const char* name, const char* ns=NULL) const {return m_impl->getString(name,ns);}\r
- pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const {return m_impl->getXMLString(name,ns);}\r
- pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const {return m_impl->getUnsignedInt(name,ns);}\r
- pair<bool,int> getInt(const char* name, const char* ns=NULL) const {return m_impl->getInt(name,ns);}\r
- void getAll(map<string,const char*>& properties) const {return m_impl->getAll(properties);}\r
- const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:2.0:native:sp:config") const {return m_impl->getPropertySet(name,ns);}\r
- const DOMElement* getElement() const {return m_impl->getElement();}\r
-\r
- // ServiceProvider\r
-#ifndef SHIBSP_LITE\r
- // Remoted\r
- void receive(DDF& in, ostream& out);\r
-\r
- TransactionLog* getTransactionLog() const {\r
- if (m_tranLog)\r
- return m_tranLog;\r
- throw ConfigurationException("No TransactionLog available.");\r
- }\r
-\r
- StorageService* getStorageService(const char* id) const {\r
- if (id) {\r
- map<string,StorageService*>::const_iterator i=m_storage.find(id);\r
- if (i!=m_storage.end())\r
- return i->second;\r
- }\r
- return NULL;\r
- }\r
-#endif\r
-\r
- ListenerService* getListenerService(bool required=true) const {\r
- if (required && !m_listener)\r
- throw ConfigurationException("No ListenerService available.");\r
- return m_listener;\r
- }\r
-\r
- SessionCache* getSessionCache(bool required=true) const {\r
- if (required && !m_sessionCache)\r
- throw ConfigurationException("No SessionCache available.");\r
- return m_sessionCache;\r
- }\r
-\r
- RequestMapper* getRequestMapper(bool required=true) const {\r
- if (required && !m_impl->m_requestMapper)\r
- throw ConfigurationException("No RequestMapper available.");\r
- return m_impl->m_requestMapper;\r
- }\r
-\r
- const Application* getApplication(const char* applicationId) const {\r
- map<string,Application*>::const_iterator i=m_impl->m_appmap.find(applicationId);\r
- return (i!=m_impl->m_appmap.end()) ? i->second : NULL;\r
- }\r
-\r
-#ifndef SHIBSP_LITE\r
- const PropertySet* getPolicySettings(const char* id) const {\r
- map<string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::const_iterator i = m_impl->m_policyMap.find(id);\r
- if (i!=m_impl->m_policyMap.end())\r
- return i->second.first;\r
- throw ConfigurationException("Security Policy ($1) not found, check <SecurityPolicies> element.", params(1,id));\r
- }\r
-\r
- const vector<const SecurityPolicyRule*>& getPolicyRules(const char* id) const {\r
- map<string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::const_iterator i = m_impl->m_policyMap.find(id);\r
- if (i!=m_impl->m_policyMap.end())\r
- return i->second.second;\r
- throw ConfigurationException("Security Policy ($1) not found, check <SecurityPolicies> element.", params(1,id));\r
- }\r
-\r
- bool setTransportOptions(SOAPTransport& transport) const {\r
- bool ret = true;\r
- vector< pair< string, pair<string,string> > >::const_iterator opt;\r
- for (opt = m_impl->m_transportOptions.begin(); opt != m_impl->m_transportOptions.end(); ++opt) {\r
- if (!transport.setProviderOption(opt->first.c_str(), opt->second.first.c_str(), opt->second.second.c_str())) {\r
- m_log.error("failed to set SOAPTransport option (%s)", opt->second.first.c_str());\r
- ret = false;\r
- }\r
- }\r
- return ret;\r
- }\r
-#endif\r
-\r
- protected:\r
- pair<bool,DOMElement*> load();\r
-\r
- private:\r
- friend class XMLConfigImpl;\r
- XMLConfigImpl* m_impl;\r
- mutable ListenerService* m_listener;\r
- mutable SessionCache* m_sessionCache;\r
-#ifndef SHIBSP_LITE\r
- mutable TransactionLog* m_tranLog;\r
- mutable map<string,StorageService*> m_storage;\r
-#endif\r
- };\r
-\r
-#if defined (_MSC_VER)\r
- #pragma warning( pop )\r
-#endif\r
-\r
- static const XMLCh ApplicationOverride[] = UNICODE_LITERAL_19(A,p,p,l,i,c,a,t,i,o,n,O,v,e,r,r,i,d,e);\r
- static const XMLCh ApplicationDefaults[] = UNICODE_LITERAL_19(A,p,p,l,i,c,a,t,i,o,n,D,e,f,a,u,l,t,s);\r
- static const XMLCh _ArtifactMap[] = UNICODE_LITERAL_11(A,r,t,i,f,a,c,t,M,a,p);\r
- static const XMLCh _AttributeExtractor[] = UNICODE_LITERAL_18(A,t,t,r,i,b,u,t,e,E,x,t,r,a,c,t,o,r);\r
- static const XMLCh _AttributeFilter[] = UNICODE_LITERAL_15(A,t,t,r,i,b,u,t,e,F,i,l,t,e,r);\r
- static const XMLCh _AttributeResolver[] = UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);\r
- static const XMLCh _AssertionConsumerService[] = UNICODE_LITERAL_24(A,s,s,e,r,t,i,o,n,C,o,n,s,u,m,e,r,S,e,r,v,i,c,e);\r
- static const XMLCh _ArtifactResolutionService[] =UNICODE_LITERAL_25(A,r,t,i,f,a,c,t,R,e,s,o,l,u,t,i,o,n,S,e,r,v,i,c,e);\r
- static const XMLCh _Audience[] = UNICODE_LITERAL_8(A,u,d,i,e,n,c,e);\r
- static const XMLCh Binding[] = UNICODE_LITERAL_7(B,i,n,d,i,n,g);\r
- static const XMLCh Channel[]= UNICODE_LITERAL_7(C,h,a,n,n,e,l);\r
- static const XMLCh _CredentialResolver[] = UNICODE_LITERAL_18(C,r,e,d,e,n,t,i,a,l,R,e,s,o,l,v,e,r);\r
- static const XMLCh _Extensions[] = UNICODE_LITERAL_10(E,x,t,e,n,s,i,o,n,s);\r
- static const XMLCh _fatal[] = UNICODE_LITERAL_5(f,a,t,a,l);\r
- static const XMLCh _Handler[] = UNICODE_LITERAL_7(H,a,n,d,l,e,r);\r
- static const XMLCh _id[] = UNICODE_LITERAL_2(i,d);\r
- static const XMLCh InProcess[] = UNICODE_LITERAL_9(I,n,P,r,o,c,e,s,s);\r
- static const XMLCh Library[] = UNICODE_LITERAL_7(L,i,b,r,a,r,y);\r
- static const XMLCh Listener[] = UNICODE_LITERAL_8(L,i,s,t,e,n,e,r);\r
- static const XMLCh Location[] = UNICODE_LITERAL_8(L,o,c,a,t,i,o,n);\r
- static const XMLCh logger[] = UNICODE_LITERAL_6(l,o,g,g,e,r);\r
- static const XMLCh _LogoutInitiator[] = UNICODE_LITERAL_15(L,o,g,o,u,t,I,n,i,t,i,a,t,o,r);\r
- static const XMLCh _ManageNameIDService[] = UNICODE_LITERAL_19(M,a,n,a,g,e,N,a,m,e,I,D,S,e,r,v,i,c,e);\r
- static const XMLCh _MetadataProvider[] = UNICODE_LITERAL_16(M,e,t,a,d,a,t,a,P,r,o,v,i,d,e,r);\r
- static const XMLCh Notify[] = UNICODE_LITERAL_6(N,o,t,i,f,y);\r
- static const XMLCh _option[] = UNICODE_LITERAL_6(o,p,t,i,o,n);\r
- static const XMLCh OutOfProcess[] = UNICODE_LITERAL_12(O,u,t,O,f,P,r,o,c,e,s,s);\r
- static const XMLCh _path[] = UNICODE_LITERAL_4(p,a,t,h);\r
- static const XMLCh Policy[] = UNICODE_LITERAL_6(P,o,l,i,c,y);\r
- static const XMLCh PolicyRule[] = UNICODE_LITERAL_10(P,o,l,i,c,y,R,u,l,e);\r
- static const XMLCh _provider[] = UNICODE_LITERAL_8(p,r,o,v,i,d,e,r);\r
- static const XMLCh RelyingParty[] = UNICODE_LITERAL_12(R,e,l,y,i,n,g,P,a,r,t,y);\r
- static const XMLCh _ReplayCache[] = UNICODE_LITERAL_11(R,e,p,l,a,y,C,a,c,h,e);\r
- static const XMLCh _RequestMapper[] = UNICODE_LITERAL_13(R,e,q,u,e,s,t,M,a,p,p,e,r);\r
- static const XMLCh Rule[] = UNICODE_LITERAL_4(R,u,l,e);\r
- static const XMLCh SecurityPolicies[] = UNICODE_LITERAL_16(S,e,c,u,r,i,t,y,P,o,l,i,c,i,e,s);\r
- static const XMLCh _SessionCache[] = UNICODE_LITERAL_12(S,e,s,s,i,o,n,C,a,c,h,e);\r
- static const XMLCh _SessionInitiator[] = UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r);\r
- static const XMLCh _SingleLogoutService[] = UNICODE_LITERAL_19(S,i,n,g,l,e,L,o,g,o,u,t,S,e,r,v,i,c,e);\r
- static const XMLCh Site[] = UNICODE_LITERAL_4(S,i,t,e);\r
- static const XMLCh _StorageService[] = UNICODE_LITERAL_14(S,t,o,r,a,g,e,S,e,r,v,i,c,e);\r
- static const XMLCh TCPListener[] = UNICODE_LITERAL_11(T,C,P,L,i,s,t,e,n,e,r);\r
- static const XMLCh TransportOption[] = UNICODE_LITERAL_15(T,r,a,n,s,p,o,r,t,O,p,t,i,o,n);\r
- static const XMLCh _TrustEngine[] = UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e);\r
- static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);\r
- static const XMLCh UnixListener[] = UNICODE_LITERAL_12(U,n,i,x,L,i,s,t,e,n,e,r);\r
-\r
-#ifndef SHIBSP_LITE\r
- class SHIBSP_DLLLOCAL PolicyNodeFilter : public DOMNodeFilter\r
- {\r
- public:\r
-#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE\r
- short\r
-#else\r
- FilterAction\r
-#endif\r
- acceptNode(const DOMNode* node) const {\r
- return FILTER_REJECT;\r
- }\r
- };\r
-#endif\r
-};\r
-\r
-namespace shibsp {\r
- ServiceProvider* XMLServiceProviderFactory(const DOMElement* const & e)\r
- {\r
- return new XMLConfig(e);\r
- }\r
-};\r
-\r
-XMLApplication::XMLApplication(\r
- const ServiceProvider* sp,\r
- const DOMElement* e,\r
- const XMLApplication* base\r
- ) : Application(sp), m_base(base),\r
-#ifndef SHIBSP_LITE\r
- m_metadata(NULL), m_trust(NULL),\r
- m_attrExtractor(NULL), m_attrFilter(NULL), m_attrResolver(NULL),\r
- m_credResolver(NULL),\r
-#endif\r
- m_acsDefault(NULL), m_sessionInitDefault(NULL), m_artifactResolutionDefault(NULL)\r
-{\r
-#ifdef _DEBUG\r
- xmltooling::NDC ndc("XMLApplication");\r
-#endif\r
- Category& log=Category::getInstance(SHIBSP_LOGCAT".Application");\r
-\r
- try {\r
- // First load any property sets.\r
- load(e,NULL,this);\r
- if (base)\r
- setParent(base);\r
-\r
- SPConfig& conf=SPConfig::getConfig();\r
-#ifndef SHIBSP_LITE\r
- SAMLConfig& samlConf=SAMLConfig::getConfig();\r
- XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();\r
-#endif\r
-\r
- // This used to be an actual hash, but now it's just a hex-encode to avoid xmlsec.\r
- static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};\r
- string tohash=getId();\r
- tohash+=getString("entityID").second;\r
- for (const char* ch = tohash.c_str(); *ch; ++ch) {\r
- m_hash += (DIGITS[((unsigned char)(0xF0 & *ch)) >> 4 ]);\r
- m_hash += (DIGITS[0x0F & *ch]);\r
- }\r
-\r
- // Populate prefix pair.\r
- m_attributePrefix.second = "HTTP_";\r
- pair<bool,const char*> prefix = getString("attributePrefix");\r
- if (prefix.first) {\r
- m_attributePrefix.first = prefix.second;\r
- const char* pch = prefix.second;\r
- while (*pch) {\r
- m_attributePrefix.second += (isalnum(*pch) ? toupper(*pch) : '_');\r
- pch++;\r
- }\r
- }\r
-\r
- // Load attribute ID lists for REMOTE_USER and header clearing.\r
- if (conf.isEnabled(SPConfig::InProcess)) {\r
- pair<bool,const char*> attributes = getString("REMOTE_USER");\r
- if (attributes.first) {\r
- char* dup = strdup(attributes.second);\r
- char* pos;\r
- char* start = dup;\r
- while (start && *start) {\r
- while (*start && isspace(*start))\r
- start++;\r
- if (!*start)\r
- break;\r
- pos = strchr(start,' ');\r
- if (pos)\r
- *pos=0;\r
- m_remoteUsers.push_back(start);\r
- start = pos ? pos+1 : NULL;\r
- }\r
- free(dup);\r
- }\r
-\r
- attributes = getString("unsetHeaders");\r
- if (attributes.first) {\r
- string transformedprefix(m_attributePrefix.second);\r
- const char* pch;\r
- prefix = getString("metadataAttributePrefix");\r
- if (prefix.first) {\r
- pch = prefix.second;\r
- while (*pch) {\r
- transformedprefix += (isalnum(*pch) ? toupper(*pch) : '_');\r
- pch++;\r
- }\r
- }\r
- char* dup = strdup(attributes.second);\r
- char* pos;\r
- char* start = dup;\r
- while (start && *start) {\r
- while (*start && isspace(*start))\r
- start++;\r
- if (!*start)\r
- break;\r
- pos = strchr(start,' ');\r
- if (pos)\r
- *pos=0;\r
-\r
- string transformed;\r
- pch = start;\r
- while (*pch) {\r
- transformed += (isalnum(*pch) ? toupper(*pch) : '_');\r
- pch++;\r
- }\r
-\r
- m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + start, m_attributePrefix.second + transformed));\r
- if (prefix.first)\r
- m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + prefix.second + start, transformedprefix + transformed));\r
- start = pos ? pos+1 : NULL;\r
- }\r
- free(dup);\r
- m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + "Shib-Application-ID", m_attributePrefix.second + "SHIB_APPLICATION_ID"));\r
- }\r
- }\r
-\r
- Handler* handler=NULL;\r
- const PropertySet* sessions = getPropertySet("Sessions");\r
-\r
- // Process assertion export handler.\r
- pair<bool,const char*> location = sessions ? sessions->getString("exportLocation") : pair<bool,const char*>(false,NULL);\r
- if (location.first) {\r
- try {\r
- DOMElement* exportElement = e->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS,_Handler);\r
- exportElement->setAttributeNS(NULL,Location,sessions->getXMLString("exportLocation").second);\r
- pair<bool,const XMLCh*> exportACL = sessions->getXMLString("exportACL");\r
- if (exportACL.first) {\r
- static const XMLCh _acl[] = UNICODE_LITERAL_9(e,x,p,o,r,t,A,C,L);\r
- exportElement->setAttributeNS(NULL,_acl,exportACL.second);\r
- }\r
- handler = conf.HandlerManager.newPlugin(\r
- samlconstants::SAML20_BINDING_URI, pair<const DOMElement*,const char*>(exportElement, getId())\r
- );\r
- m_handlers.push_back(handler);\r
-\r
- // Insert into location map. If it contains the handlerURL, we skip past that part.\r
- const char* pch = strstr(location.second, sessions->getString("handlerURL").second);\r
- if (pch)\r
- location.second = pch + strlen(sessions->getString("handlerURL").second);\r
- if (*location.second == '/')\r
- m_handlerMap[location.second]=handler;\r
- else\r
- m_handlerMap[string("/") + location.second]=handler;\r
- }\r
- catch (exception& ex) {\r
- log.error("caught exception installing assertion lookup handler: %s", ex.what());\r
- }\r
- }\r
-\r
- // Process other handlers.\r
- bool hardACS=false, hardSessionInit=false, hardArt=false;\r
- const DOMElement* child = sessions ? XMLHelper::getFirstChildElement(sessions->getElement()) : NULL;\r
- while (child) {\r
- try {\r
- // A handler is based on the Binding property in conjunction with the element name.\r
- // If it's an ACS or SI, also handle index/id mappings and defaulting.\r
- if (XMLString::equals(child->getLocalName(),_AssertionConsumerService)) {\r
- auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));\r
- if (!bindprop.get() || !*(bindprop.get())) {\r
- log.warn("md:AssertionConsumerService element has no Binding attribute, skipping it...");\r
- child = XMLHelper::getNextSiblingElement(child);\r
- continue;\r
- }\r
- handler=conf.AssertionConsumerServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));\r
- // Map by binding (may be > 1 per binding, e.g. SAML 1.0 vs 1.1)\r
- m_acsBindingMap[handler->getXMLString("Binding").second].push_back(handler);\r
- m_acsIndexMap[handler->getUnsignedInt("index").second]=handler;\r
-\r
- if (!hardACS) {\r
- pair<bool,bool> defprop=handler->getBool("isDefault");\r
- if (defprop.first) {\r
- if (defprop.second) {\r
- hardACS=true;\r
- m_acsDefault=handler;\r
- }\r
- }\r
- else if (!m_acsDefault)\r
- m_acsDefault=handler;\r
- }\r
- }\r
- else if (XMLString::equals(child->getLocalName(),_SessionInitiator)) {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- if (!type.get() || !*(type.get())) {\r
- log.warn("SessionInitiator element has no type attribute, skipping it...");\r
- child = XMLHelper::getNextSiblingElement(child);\r
- continue;\r
- }\r
- SessionInitiator* sihandler=conf.SessionInitiatorManager.newPlugin(type.get(),make_pair(child, getId()));\r
- handler=sihandler;\r
- pair<bool,const char*> si_id=handler->getString("id");\r
- if (si_id.first && si_id.second)\r
- m_sessionInitMap[si_id.second]=sihandler;\r
- if (!hardSessionInit) {\r
- pair<bool,bool> defprop=handler->getBool("isDefault");\r
- if (defprop.first) {\r
- if (defprop.second) {\r
- hardSessionInit=true;\r
- m_sessionInitDefault=sihandler;\r
- }\r
- }\r
- else if (!m_sessionInitDefault)\r
- m_sessionInitDefault=sihandler;\r
- }\r
- }\r
- else if (XMLString::equals(child->getLocalName(),_LogoutInitiator)) {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- if (!type.get() || !*(type.get())) {\r
- log.warn("LogoutInitiator element has no type attribute, skipping it...");\r
- child = XMLHelper::getNextSiblingElement(child);\r
- continue;\r
- }\r
- handler=conf.LogoutInitiatorManager.newPlugin(type.get(),make_pair(child, getId()));\r
- }\r
- else if (XMLString::equals(child->getLocalName(),_ArtifactResolutionService)) {\r
- auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));\r
- if (!bindprop.get() || !*(bindprop.get())) {\r
- log.warn("md:ArtifactResolutionService element has no Binding attribute, skipping it...");\r
- child = XMLHelper::getNextSiblingElement(child);\r
- continue;\r
- }\r
- handler=conf.ArtifactResolutionServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));\r
-\r
- if (!hardArt) {\r
- pair<bool,bool> defprop=handler->getBool("isDefault");\r
- if (defprop.first) {\r
- if (defprop.second) {\r
- hardArt=true;\r
- m_artifactResolutionDefault=handler;\r
- }\r
- }\r
- else if (!m_artifactResolutionDefault)\r
- m_artifactResolutionDefault=handler;\r
- }\r
- }\r
- else if (XMLString::equals(child->getLocalName(),_SingleLogoutService)) {\r
- auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));\r
- if (!bindprop.get() || !*(bindprop.get())) {\r
- log.warn("md:SingleLogoutService element has no Binding attribute, skipping it...");\r
- child = XMLHelper::getNextSiblingElement(child);\r
- continue;\r
- }\r
- handler=conf.SingleLogoutServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));\r
- }\r
- else if (XMLString::equals(child->getLocalName(),_ManageNameIDService)) {\r
- auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));\r
- if (!bindprop.get() || !*(bindprop.get())) {\r
- log.warn("md:ManageNameIDService element has no Binding attribute, skipping it...");\r
- child = XMLHelper::getNextSiblingElement(child);\r
- continue;\r
- }\r
- handler=conf.ManageNameIDServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));\r
- }\r
- else {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- if (!type.get() || !*(type.get())) {\r
- log.warn("Handler element has no type attribute, skipping it...");\r
- child = XMLHelper::getNextSiblingElement(child);\r
- continue;\r
- }\r
- handler=conf.HandlerManager.newPlugin(type.get(),make_pair(child, getId()));\r
- }\r
-\r
- m_handlers.push_back(handler);\r
-\r
- // Insert into location map.\r
- location=handler->getString("Location");\r
- if (location.first && *location.second == '/')\r
- m_handlerMap[location.second]=handler;\r
- else if (location.first)\r
- m_handlerMap[string("/") + location.second]=handler;\r
-\r
- }\r
- catch (exception& ex) {\r
- log.error("caught exception processing handler element: %s", ex.what());\r
- }\r
-\r
- child = XMLHelper::getNextSiblingElement(child);\r
- }\r
-\r
- // Notification.\r
- DOMNodeList* nlist=e->getElementsByTagNameNS(shibspconstants::SHIB2SPCONFIG_NS,Notify);\r
- for (XMLSize_t i=0; nlist && i<nlist->getLength(); i++) {\r
- if (nlist->item(i)->getParentNode()->isSameNode(e)) {\r
- const XMLCh* channel = static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,Channel);\r
- auto_ptr_char loc(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,Location));\r
- if (loc.get() && *loc.get()) {\r
- if (channel && *channel == chLatin_f)\r
- m_frontLogout.push_back(loc.get());\r
- else\r
- m_backLogout.push_back(loc.get());\r
- }\r
- }\r
- }\r
-\r
-#ifndef SHIBSP_LITE\r
- nlist=e->getElementsByTagNameNS(samlconstants::SAML20_NS,Audience::LOCAL_NAME);\r
- if (nlist && nlist->getLength()) {\r
- log.warn("use of <saml:Audience> elements outside of a Security Policy Rule is deprecated");\r
- for (XMLSize_t i=0; i<nlist->getLength(); i++)\r
- if (nlist->item(i)->getParentNode()->isSameNode(e) && nlist->item(i)->hasChildNodes())\r
- m_audiences.push_back(nlist->item(i)->getFirstChild()->getNodeValue());\r
- }\r
-\r
- if (conf.isEnabled(SPConfig::Metadata)) {\r
- child = XMLHelper::getFirstChildElement(e,_MetadataProvider);\r
- if (child) {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- log.info("building MetadataProvider of type %s...",type.get());\r
- try {\r
- auto_ptr<MetadataProvider> mp(samlConf.MetadataProviderManager.newPlugin(type.get(),child));\r
- mp->init();\r
- m_metadata = mp.release();\r
- }\r
- catch (exception& ex) {\r
- log.crit("error building/initializing MetadataProvider: %s", ex.what());\r
- }\r
- }\r
- }\r
-\r
- if (conf.isEnabled(SPConfig::Trust)) {\r
- child = XMLHelper::getFirstChildElement(e,_TrustEngine);\r
- if (child) {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- log.info("building TrustEngine of type %s...",type.get());\r
- try {\r
- m_trust = xmlConf.TrustEngineManager.newPlugin(type.get(),child);\r
- }\r
- catch (exception& ex) {\r
- log.crit("error building TrustEngine: %s", ex.what());\r
- }\r
- }\r
- }\r
-\r
- if (conf.isEnabled(SPConfig::AttributeResolution)) {\r
- child = XMLHelper::getFirstChildElement(e,_AttributeExtractor);\r
- if (child) {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- log.info("building AttributeExtractor of type %s...",type.get());\r
- try {\r
- m_attrExtractor = conf.AttributeExtractorManager.newPlugin(type.get(),child);\r
- }\r
- catch (exception& ex) {\r
- log.crit("error building AttributeExtractor: %s", ex.what());\r
- }\r
- }\r
-\r
- child = XMLHelper::getFirstChildElement(e,_AttributeFilter);\r
- if (child) {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- log.info("building AttributeFilter of type %s...",type.get());\r
- try {\r
- m_attrFilter = conf.AttributeFilterManager.newPlugin(type.get(),child);\r
- }\r
- catch (exception& ex) {\r
- log.crit("error building AttributeFilter: %s", ex.what());\r
- }\r
- }\r
-\r
- child = XMLHelper::getFirstChildElement(e,_AttributeResolver);\r
- if (child) {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- log.info("building AttributeResolver of type %s...",type.get());\r
- try {\r
- m_attrResolver = conf.AttributeResolverManager.newPlugin(type.get(),child);\r
- }\r
- catch (exception& ex) {\r
- log.crit("error building AttributeResolver: %s", ex.what());\r
- }\r
- }\r
-\r
- if (m_unsetHeaders.empty()) {\r
- vector<string> unsetHeaders;\r
- if (m_attrExtractor) {\r
- Locker extlock(m_attrExtractor);\r
- m_attrExtractor->getAttributeIds(unsetHeaders);\r
- }\r
- else if (m_base && m_base->m_attrExtractor) {\r
- Locker extlock(m_base->m_attrExtractor);\r
- m_base->m_attrExtractor->getAttributeIds(unsetHeaders);\r
- }\r
- if (m_attrResolver) {\r
- Locker reslock(m_attrResolver);\r
- m_attrResolver->getAttributeIds(unsetHeaders);\r
- }\r
- else if (m_base && m_base->m_attrResolver) {\r
- Locker extlock(m_base->m_attrResolver);\r
- m_base->m_attrResolver->getAttributeIds(unsetHeaders);\r
- }\r
- if (!unsetHeaders.empty()) {\r
- string transformedprefix(m_attributePrefix.second);\r
- const char* pch;\r
- pair<bool,const char*> prefix = getString("metadataAttributePrefix");\r
- if (prefix.first) {\r
- pch = prefix.second;\r
- while (*pch) {\r
- transformedprefix += (isalnum(*pch) ? toupper(*pch) : '_');\r
- pch++;\r
- }\r
- }\r
- for (vector<string>::const_iterator hdr = unsetHeaders.begin(); hdr!=unsetHeaders.end(); ++hdr) {\r
- string transformed;\r
- pch = hdr->c_str();\r
- while (*pch) {\r
- transformed += (isalnum(*pch) ? toupper(*pch) : '_');\r
- pch++;\r
- }\r
- m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + *hdr, m_attributePrefix.second + transformed));\r
- if (prefix.first)\r
- m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + prefix.second + *hdr, transformedprefix + transformed));\r
- }\r
- }\r
- m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + "Shib-Application-ID", m_attributePrefix.second + "SHIB_APPLICATION_ID"));\r
- }\r
- }\r
-\r
- if (conf.isEnabled(SPConfig::Credentials)) {\r
- child = XMLHelper::getFirstChildElement(e,_CredentialResolver);\r
- if (child) {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- log.info("building CredentialResolver of type %s...",type.get());\r
- try {\r
- m_credResolver = xmlConf.CredentialResolverManager.newPlugin(type.get(),child);\r
- }\r
- catch (exception& ex) {\r
- log.crit("error building CredentialResolver: %s", ex.what());\r
- }\r
- }\r
- }\r
-\r
- // Finally, load relying parties.\r
- child = XMLHelper::getFirstChildElement(e,RelyingParty);\r
- while (child) {\r
- auto_ptr<DOMPropertySet> rp(new DOMPropertySet());\r
- rp->load(child,NULL,this);\r
- rp->setParent(this);\r
- m_partyMap[child->getAttributeNS(NULL,saml2::Attribute::NAME_ATTRIB_NAME)]=rp.release();\r
- child = XMLHelper::getNextSiblingElement(child,RelyingParty);\r
- }\r
-#endif\r
-\r
- // Out of process only, we register a listener endpoint.\r
- if (!conf.isEnabled(SPConfig::InProcess)) {\r
- ListenerService* listener = sp->getListenerService(false);\r
- if (listener) {\r
- string addr=string(getId()) + "::getHeaders::Application";\r
- listener->regListener(addr.c_str(),this);\r
- }\r
- else\r
- log.info("no ListenerService available, Application remoting disabled");\r
- }\r
- }\r
- catch (exception&) {\r
- cleanup();\r
- throw;\r
- }\r
-#ifndef _DEBUG\r
- catch (...) {\r
- cleanup();\r
- throw;\r
- }\r
-#endif\r
-}\r
-\r
-void XMLApplication::cleanup()\r
-{\r
- ListenerService* listener=getServiceProvider().getListenerService(false);\r
- if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess) && !SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {\r
- string addr=string(getId()) + "::getHeaders::Application";\r
- listener->unregListener(addr.c_str(),this);\r
- }\r
- for_each(m_handlers.begin(),m_handlers.end(),xmltooling::cleanup<Handler>());\r
- m_handlers.clear();\r
-#ifndef SHIBSP_LITE\r
- for_each(m_partyMap.begin(),m_partyMap.end(),cleanup_pair<xstring,PropertySet>());\r
- m_partyMap.clear();\r
- delete m_credResolver;\r
- m_credResolver = NULL;\r
- delete m_attrResolver;\r
- m_attrResolver = NULL;\r
- delete m_attrFilter;\r
- m_attrFilter = NULL;\r
- delete m_attrExtractor;\r
- m_attrExtractor = NULL;\r
- delete m_trust;\r
- m_trust = NULL;\r
- delete m_metadata;\r
- m_metadata = NULL;\r
-#endif\r
-}\r
-\r
-#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE\r
-short\r
-#else\r
-DOMNodeFilter::FilterAction\r
-#endif\r
-XMLApplication::acceptNode(const DOMNode* node) const\r
-{\r
- const XMLCh* name=node->getLocalName();\r
- if (XMLString::equals(name,ApplicationOverride) ||\r
- XMLString::equals(name,_Audience) ||\r
- XMLString::equals(name,Notify) ||\r
- XMLString::equals(name,_Handler) ||\r
- XMLString::equals(name,_AssertionConsumerService) ||\r
- XMLString::equals(name,_ArtifactResolutionService) ||\r
- XMLString::equals(name,_LogoutInitiator) ||\r
- XMLString::equals(name,_ManageNameIDService) ||\r
- XMLString::equals(name,_SessionInitiator) ||\r
- XMLString::equals(name,_SingleLogoutService) ||\r
- XMLString::equals(name,RelyingParty) ||\r
- XMLString::equals(name,_MetadataProvider) ||\r
- XMLString::equals(name,_TrustEngine) ||\r
- XMLString::equals(name,_CredentialResolver) ||\r
- XMLString::equals(name,_AttributeFilter) ||\r
- XMLString::equals(name,_AttributeExtractor) ||\r
- XMLString::equals(name,_AttributeResolver))\r
- return FILTER_REJECT;\r
-\r
- return FILTER_ACCEPT;\r
-}\r
-\r
-#ifndef SHIBSP_LITE\r
-\r
-const PropertySet* XMLApplication::getRelyingParty(const EntityDescriptor* provider) const\r
-{\r
- if (!provider)\r
- return this;\r
-\r
- map<xstring,PropertySet*>::const_iterator i=m_partyMap.find(provider->getEntityID());\r
- if (i!=m_partyMap.end())\r
- return i->second;\r
- const EntitiesDescriptor* group=dynamic_cast<const EntitiesDescriptor*>(provider->getParent());\r
- while (group) {\r
- if (group->getName()) {\r
- i=m_partyMap.find(group->getName());\r
- if (i!=m_partyMap.end())\r
- return i->second;\r
- }\r
- group=dynamic_cast<const EntitiesDescriptor*>(group->getParent());\r
- }\r
- return this;\r
-}\r
-\r
-const PropertySet* XMLApplication::getRelyingParty(const XMLCh* entityID) const\r
-{\r
- if (!entityID)\r
- return this;\r
-\r
- map<xstring,PropertySet*>::const_iterator i=m_partyMap.find(entityID);\r
- if (i!=m_partyMap.end())\r
- return i->second;\r
- return this;\r
-}\r
-\r
-#endif\r
-\r
-string XMLApplication::getNotificationURL(const char* resource, bool front, unsigned int index) const\r
-{\r
- const vector<string>& locs = front ? m_frontLogout : m_backLogout;\r
- if (locs.empty())\r
- return m_base ? m_base->getNotificationURL(resource, front, index) : string();\r
- else if (index >= locs.size())\r
- return string();\r
-\r
-#ifdef HAVE_STRCASECMP\r
- if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))\r
-#else\r
- if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))\r
-#endif\r
- throw ConfigurationException("Request URL was not absolute.");\r
-\r
- const char* handler=locs[index].c_str();\r
-\r
- // Should never happen...\r
- if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)))\r
- throw ConfigurationException(\r
- "Invalid Location property ($1) in Notify element for Application ($2)",\r
- params(2, handler ? handler : "null", getId())\r
- );\r
-\r
- // The "Location" property can be in one of three formats:\r
- //\r
- // 1) a full URI: http://host/foo/bar\r
- // 2) a hostless URI: http:///foo/bar\r
- // 3) a relative path: /foo/bar\r
- //\r
- // # Protocol Host Path\r
- // 1 handler handler handler\r
- // 2 handler resource handler\r
- // 3 resource resource handler\r
-\r
- const char* path = NULL;\r
-\r
- // Decide whether to use the handler or the resource for the "protocol"\r
- const char* prot;\r
- if (*handler != '/') {\r
- prot = handler;\r
- }\r
- else {\r
- prot = resource;\r
- path = handler;\r
- }\r
-\r
- // break apart the "protocol" string into protocol, host, and "the rest"\r
- const char* colon=strchr(prot,':');\r
- colon += 3;\r
- const char* slash=strchr(colon,'/');\r
- if (!path)\r
- path = slash;\r
-\r
- // Compute the actual protocol and store.\r
- string notifyURL(prot, colon-prot);\r
-\r
- // create the "host" from either the colon/slash or from the target string\r
- // If prot == handler then we're in either #1 or #2, else #3.\r
- // If slash == colon then we're in #2.\r
- if (prot != handler || slash == colon) {\r
- colon = strchr(resource, ':');\r
- colon += 3; // Get past the ://\r
- slash = strchr(colon, '/');\r
- }\r
- string host(colon, (slash ? slash-colon : strlen(colon)));\r
-\r
- // Build the URL\r
- notifyURL += host + path;\r
- return notifyURL;\r
-}\r
-\r
-void XMLApplication::clearHeader(SPRequest& request, const char* rawname, const char* cginame) const\r
-{\r
- if (!m_attributePrefix.first.empty()) {\r
- string temp = m_attributePrefix.first + rawname;\r
- string temp2 = m_attributePrefix.second + (cginame + 5);\r
- request.clearHeader(temp.c_str(), temp2.c_str());\r
- }\r
- else if (m_base) {\r
- m_base->clearHeader(request, rawname, cginame);\r
- }\r
- else {\r
- request.clearHeader(rawname, cginame);\r
- }\r
-}\r
-\r
-void XMLApplication::setHeader(SPRequest& request, const char* name, const char* value) const\r
-{\r
- if (!m_attributePrefix.first.empty()) {\r
- string temp = m_attributePrefix.first + name;\r
- request.setHeader(temp.c_str(), value);\r
- }\r
- else if (m_base) {\r
- m_base->setHeader(request, name, value);\r
- }\r
- else {\r
- request.setHeader(name, value);\r
- }\r
-}\r
-\r
-string XMLApplication::getSecureHeader(const SPRequest& request, const char* name) const\r
-{\r
- if (!m_attributePrefix.first.empty()) {\r
- string temp = m_attributePrefix.first + name;\r
- return request.getSecureHeader(temp.c_str());\r
- }\r
- else if (m_base) {\r
- return m_base->getSecureHeader(request,name);\r
- }\r
- else {\r
- return request.getSecureHeader(name);\r
- }\r
-}\r
-\r
-const SessionInitiator* XMLApplication::getDefaultSessionInitiator() const\r
-{\r
- if (m_sessionInitDefault) return m_sessionInitDefault;\r
- return m_base ? m_base->getDefaultSessionInitiator() : NULL;\r
-}\r
-\r
-const SessionInitiator* XMLApplication::getSessionInitiatorById(const char* id) const\r
-{\r
- map<string,const SessionInitiator*>::const_iterator i=m_sessionInitMap.find(id);\r
- if (i!=m_sessionInitMap.end()) return i->second;\r
- return m_base ? m_base->getSessionInitiatorById(id) : NULL;\r
-}\r
-\r
-const Handler* XMLApplication::getDefaultAssertionConsumerService() const\r
-{\r
- if (m_acsDefault) return m_acsDefault;\r
- return m_base ? m_base->getDefaultAssertionConsumerService() : NULL;\r
-}\r
-\r
-const Handler* XMLApplication::getAssertionConsumerServiceByIndex(unsigned short index) const\r
-{\r
- map<unsigned int,const Handler*>::const_iterator i=m_acsIndexMap.find(index);\r
- if (i!=m_acsIndexMap.end()) return i->second;\r
- return m_base ? m_base->getAssertionConsumerServiceByIndex(index) : NULL;\r
-}\r
-\r
-const vector<const Handler*>& XMLApplication::getAssertionConsumerServicesByBinding(const XMLCh* binding) const\r
-{\r
- ACSBindingMap::const_iterator i=m_acsBindingMap.find(binding);\r
- if (i!=m_acsBindingMap.end())\r
- return i->second;\r
- return m_base ? m_base->getAssertionConsumerServicesByBinding(binding) : g_noHandlers;\r
-}\r
-\r
-const Handler* XMLApplication::getHandler(const char* path) const\r
-{\r
- string wrap(path);\r
- wrap = wrap.substr(0,wrap.find(';'));\r
- map<string,const Handler*>::const_iterator i=m_handlerMap.find(wrap.substr(0,wrap.find('?')));\r
- if (i!=m_handlerMap.end())\r
- return i->second;\r
- return m_base ? m_base->getHandler(path) : NULL;\r
-}\r
-\r
-void XMLApplication::getHandlers(vector<const Handler*>& handlers) const\r
-{\r
- handlers.insert(handlers.end(), m_handlers.begin(), m_handlers.end());\r
- if (m_base) {\r
- for (map<string,const Handler*>::const_iterator h = m_base->m_handlerMap.begin(); h != m_base->m_handlerMap.end(); ++h) {\r
- if (m_handlerMap.count(h->first) == 0)\r
- handlers.push_back(h->second);\r
- }\r
- }\r
-}\r
-\r
-#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE\r
-short\r
-#else\r
-DOMNodeFilter::FilterAction\r
-#endif\r
-XMLConfigImpl::acceptNode(const DOMNode* node) const\r
-{\r
- if (!XMLString::equals(node->getNamespaceURI(),shibspconstants::SHIB2SPCONFIG_NS))\r
- return FILTER_ACCEPT;\r
- const XMLCh* name=node->getLocalName();\r
- if (XMLString::equals(name,ApplicationDefaults) ||\r
- XMLString::equals(name,_ArtifactMap) ||\r
- XMLString::equals(name,_Extensions) ||\r
- XMLString::equals(name,Listener) ||\r
- XMLString::equals(name,_RequestMapper) ||\r
- XMLString::equals(name,_ReplayCache) ||\r
- XMLString::equals(name,SecurityPolicies) ||\r
- XMLString::equals(name,_SessionCache) ||\r
- XMLString::equals(name,Site) ||\r
- XMLString::equals(name,_StorageService) ||\r
- XMLString::equals(name,TCPListener) ||\r
- XMLString::equals(name,TransportOption) ||\r
- XMLString::equals(name,UnixListener))\r
- return FILTER_REJECT;\r
-\r
- return FILTER_ACCEPT;\r
-}\r
-\r
-void XMLConfigImpl::doExtensions(const DOMElement* e, const char* label, Category& log)\r
-{\r
- const DOMElement* exts=XMLHelper::getFirstChildElement(e,_Extensions);\r
- if (exts) {\r
- exts=XMLHelper::getFirstChildElement(exts,Library);\r
- while (exts) {\r
- auto_ptr_char path(exts->getAttributeNS(NULL,_path));\r
- try {\r
- if (path.get()) {\r
- if (!XMLToolingConfig::getConfig().load_library(path.get(),(void*)exts))\r
- throw ConfigurationException("XMLToolingConfig::load_library failed.");\r
- log.debug("loaded %s extension library (%s)", label, path.get());\r
- }\r
- }\r
- catch (exception& e) {\r
- const XMLCh* fatal=exts->getAttributeNS(NULL,_fatal);\r
- if (fatal && (*fatal==chLatin_t || *fatal==chDigit_1)) {\r
- log.fatal("unable to load mandatory %s extension library %s: %s", label, path.get(), e.what());\r
- throw;\r
- }\r
- else {\r
- log.crit("unable to load optional %s extension library %s: %s", label, path.get(), e.what());\r
- }\r
- }\r
- exts=XMLHelper::getNextSiblingElement(exts,Library);\r
- }\r
- }\r
-}\r
-\r
-XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer, Category& log)\r
- : m_requestMapper(NULL), m_outer(outer), m_document(NULL)\r
-{\r
-#ifdef _DEBUG\r
- xmltooling::NDC ndc("XMLConfigImpl");\r
-#endif\r
-\r
- try {\r
- SPConfig& conf=SPConfig::getConfig();\r
-#ifndef SHIBSP_LITE\r
- SAMLConfig& samlConf=SAMLConfig::getConfig();\r
-#endif\r
- XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();\r
- const DOMElement* SHAR=XMLHelper::getFirstChildElement(e,OutOfProcess);\r
- const DOMElement* SHIRE=XMLHelper::getFirstChildElement(e,InProcess);\r
-\r
- // Initialize log4cpp manually in order to redirect log messages as soon as possible.\r
- if (conf.isEnabled(SPConfig::Logging)) {\r
- const XMLCh* logconf=NULL;\r
- if (conf.isEnabled(SPConfig::OutOfProcess))\r
- logconf=SHAR->getAttributeNS(NULL,logger);\r
- else if (conf.isEnabled(SPConfig::InProcess))\r
- logconf=SHIRE->getAttributeNS(NULL,logger);\r
- if (!logconf || !*logconf)\r
- logconf=e->getAttributeNS(NULL,logger);\r
- if (logconf && *logconf) {\r
- auto_ptr_char logpath(logconf);\r
- log.debug("loading new logging configuration from (%s), check log destination for status of configuration",logpath.get());\r
- if (!XMLToolingConfig::getConfig().log_config(logpath.get()))\r
- log.crit("failed to load new logging configuration from (%s)", logpath.get());\r
- }\r
-\r
-#ifndef SHIBSP_LITE\r
- if (first)\r
- m_outer->m_tranLog = new TransactionLog();\r
-#endif\r
- }\r
-\r
- // Re-log library versions now that logging is set up.\r
-#ifndef SHIBSP_LITE\r
- log.info(\r
- "Library versions: Xerces-C %s, XML-Security-C %s, XMLTooling-C %s, OpenSAML-C %s, Shibboleth %s",\r
- XERCES_FULLVERSIONDOT, XSEC_FULLVERSIONDOT, XMLTOOLING_FULLVERSIONDOT, OPENSAML_FULLVERSIONDOT, SHIBSP_FULLVERSIONDOT\r
- );\r
-#else\r
- log.info(\r
- "Library versions: Xerces-C %s, XMLTooling-C %s, Shibboleth %s",\r
- XERCES_FULLVERSIONDOT, XMLTOOLING_FULLVERSIONDOT, SHIBSP_FULLVERSIONDOT\r
- );\r
-#endif\r
-\r
- // First load any property sets.\r
- load(e,NULL,this);\r
-\r
- const DOMElement* child;\r
- string plugtype;\r
-\r
- // Much of the processing can only occur on the first instantiation.\r
- if (first) {\r
- // Set clock skew.\r
- pair<bool,unsigned int> skew=getUnsignedInt("clockSkew");\r
- if (skew.first)\r
- xmlConf.clock_skew_secs=min(skew.second,(60*60*24*7*28));\r
-\r
- pair<bool,const char*> unsafe = getString("unsafeChars");\r
- if (unsafe.first)\r
- TemplateEngine::unsafe_chars = unsafe.second;\r
-\r
- unsafe = getString("allowedSchemes");\r
- if (unsafe.first) {\r
- HTTPResponse::getAllowedSchemes().clear();\r
- string schemes=unsafe.second;\r
- unsigned int j_sch=0;\r
- for (unsigned int i_sch=0; i_sch < schemes.length(); i_sch++) {\r
- if (schemes.at(i_sch)==' ') {\r
- HTTPResponse::getAllowedSchemes().push_back(schemes.substr(j_sch, i_sch-j_sch));\r
- j_sch = i_sch + 1;\r
- }\r
- }\r
- HTTPResponse::getAllowedSchemes().push_back(schemes.substr(j_sch, schemes.length()-j_sch));\r
- }\r
-\r
- // Extensions\r
- doExtensions(e, "global", log);\r
- if (conf.isEnabled(SPConfig::OutOfProcess))\r
- doExtensions(SHAR, "out of process", log);\r
-\r
- if (conf.isEnabled(SPConfig::InProcess))\r
- doExtensions(SHIRE, "in process", log);\r
-\r
- // Instantiate the ListenerService and SessionCache objects.\r
- if (conf.isEnabled(SPConfig::Listener)) {\r
- child=XMLHelper::getFirstChildElement(e,UnixListener);\r
- if (child)\r
- plugtype=UNIX_LISTENER_SERVICE;\r
- else {\r
- child=XMLHelper::getFirstChildElement(e,TCPListener);\r
- if (child)\r
- plugtype=TCP_LISTENER_SERVICE;\r
- else {\r
- child=XMLHelper::getFirstChildElement(e,Listener);\r
- if (child) {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- if (type.get())\r
- plugtype=type.get();\r
- }\r
- }\r
- }\r
- if (child) {\r
- log.info("building ListenerService of type %s...", plugtype.c_str());\r
- m_outer->m_listener = conf.ListenerServiceManager.newPlugin(plugtype.c_str(), child);\r
- }\r
- else {\r
- log.fatal("can't build ListenerService, missing conf:Listener element?");\r
- throw ConfigurationException("Can't build ListenerService, missing conf:Listener element?");\r
- }\r
- }\r
-\r
-#ifndef SHIBSP_LITE\r
- if (m_outer->m_listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess)) {\r
- m_outer->m_listener->regListener("set::RelayState", const_cast<XMLConfig*>(m_outer));\r
- m_outer->m_listener->regListener("get::RelayState", const_cast<XMLConfig*>(m_outer));\r
- m_outer->m_listener->regListener("set::PostData", const_cast<XMLConfig*>(m_outer));\r
- m_outer->m_listener->regListener("get::PostData", const_cast<XMLConfig*>(m_outer));\r
- }\r
-#endif\r
-\r
- if (conf.isEnabled(SPConfig::Caching)) {\r
- if (conf.isEnabled(SPConfig::OutOfProcess)) {\r
-#ifndef SHIBSP_LITE\r
- // First build any StorageServices.\r
- child=XMLHelper::getFirstChildElement(e,_StorageService);\r
- while (child) {\r
- auto_ptr_char id(child->getAttributeNS(NULL,_id));\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- try {\r
- log.info("building StorageService (%s) of type %s...", id.get(), type.get());\r
- m_outer->m_storage[id.get()] = xmlConf.StorageServiceManager.newPlugin(type.get(),child);\r
- }\r
- catch (exception& ex) {\r
- log.crit("failed to instantiate StorageService (%s): %s", id.get(), ex.what());\r
- }\r
- child=XMLHelper::getNextSiblingElement(child,_StorageService);\r
- }\r
-\r
- // Replay cache.\r
- StorageService* replaySS=NULL;\r
- child=XMLHelper::getFirstChildElement(e,_ReplayCache);\r
- if (child) {\r
- auto_ptr_char ssid(child->getAttributeNS(NULL,_StorageService));\r
- if (ssid.get() && *ssid.get()) {\r
- if (m_outer->m_storage.count(ssid.get()))\r
- replaySS = m_outer->m_storage[ssid.get()];\r
- if (replaySS)\r
- log.info("building ReplayCache on top of StorageService (%s)...", ssid.get());\r
- else\r
- log.warn("unable to locate StorageService (%s) for ReplayCache, using dedicated in-memory instance", ssid.get());\r
- }\r
- xmlConf.setReplayCache(new ReplayCache(replaySS));\r
- }\r
- else {\r
- log.warn("no ReplayCache built, missing conf:ReplayCache element?");\r
- }\r
-\r
- // ArtifactMap\r
- child=XMLHelper::getFirstChildElement(e,_ArtifactMap);\r
- if (child) {\r
- auto_ptr_char ssid(child->getAttributeNS(NULL,_StorageService));\r
- if (ssid.get() && *ssid.get() && m_outer->m_storage.count(ssid.get())) {\r
- log.info("building ArtifactMap on top of StorageService (%s)...", ssid.get());\r
- samlConf.setArtifactMap(new ArtifactMap(child, m_outer->m_storage[ssid.get()]));\r
- }\r
- }\r
- if (samlConf.getArtifactMap()==NULL) {\r
- log.info("building in-memory ArtifactMap...");\r
- samlConf.setArtifactMap(new ArtifactMap(child));\r
- }\r
-#endif\r
- }\r
- child=XMLHelper::getFirstChildElement(e,_SessionCache);\r
- if (child) {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- log.info("building SessionCache of type %s...",type.get());\r
- m_outer->m_sessionCache=conf.SessionCacheManager.newPlugin(type.get(), child);\r
- }\r
- else {\r
- log.fatal("can't build SessionCache, missing conf:SessionCache element?");\r
- throw ConfigurationException("Can't build SessionCache, missing conf:SessionCache element?");\r
- }\r
- }\r
- } // end of first-time-only stuff\r
-\r
- // Back to the fully dynamic stuff...next up is the RequestMapper.\r
- if (conf.isEnabled(SPConfig::RequestMapping)) {\r
- child=XMLHelper::getFirstChildElement(e,_RequestMapper);\r
- if (child) {\r
- auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
- log.info("building RequestMapper of type %s...",type.get());\r
- m_requestMapper=conf.RequestMapperManager.newPlugin(type.get(),child);\r
- }\r
- else {\r
- log.fatal("can't build RequestMapper, missing conf:RequestMapper element?");\r
- throw ConfigurationException("Can't build RequestMapper, missing conf:RequestMapper element?");\r
- }\r
- }\r
-\r
-#ifndef SHIBSP_LITE\r
- // Load security policies.\r
- child = XMLHelper::getLastChildElement(e,SecurityPolicies);\r
- if (child) {\r
- PolicyNodeFilter filter;\r
- child = XMLHelper::getFirstChildElement(child,Policy);\r
- while (child) {\r
- auto_ptr_char id(child->getAttributeNS(NULL,_id));\r
- pair< PropertySet*,vector<const SecurityPolicyRule*> >& rules = m_policyMap[id.get()];\r
- rules.first = NULL;\r
- auto_ptr<DOMPropertySet> settings(new DOMPropertySet());\r
- settings->load(child, NULL, &filter);\r
- rules.first = settings.release();\r
-\r
- // Process PolicyRule elements.\r
- const DOMElement* rule = XMLHelper::getFirstChildElement(child,PolicyRule);\r
- while (rule) {\r
- auto_ptr_char type(rule->getAttributeNS(NULL,_type));\r
- try {\r
- rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule));\r
- }\r
- catch (exception& ex) {\r
- log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what());\r
- }\r
- rule = XMLHelper::getNextSiblingElement(rule,PolicyRule);\r
- }\r
-\r
- if (rules.second.size() == 0) {\r
- // Process Rule elements.\r
- log.warn("detected legacy Policy configuration, please convert to new PolicyRule syntax");\r
- rule = XMLHelper::getFirstChildElement(child,Rule);\r
- while (rule) {\r
- auto_ptr_char type(rule->getAttributeNS(NULL,_type));\r
- try {\r
- rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule));\r
- }\r
- catch (exception& ex) {\r
- log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what());\r
- }\r
- rule = XMLHelper::getNextSiblingElement(rule,Rule);\r
- }\r
-\r
- // Manually add a basic Conditions rule.\r
- log.info("installing a default Conditions rule in policy (%s) for compatibility with legacy configuration", id.get());\r
- rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(CONDITIONS_POLICY_RULE, NULL));\r
- }\r
-\r
- child = XMLHelper::getNextSiblingElement(child,Policy);\r
- }\r
- }\r
-\r
- // Process TransportOption elements.\r
- child = XMLHelper::getLastChildElement(e,TransportOption);\r
- while (child) {\r
- if (child->hasChildNodes()) {\r
- auto_ptr_char provider(child->getAttributeNS(NULL,_provider));\r
- auto_ptr_char option(child->getAttributeNS(NULL,_option));\r
- auto_ptr_char value(child->getFirstChild()->getNodeValue());\r
- if (provider.get() && *provider.get() && option.get() && *option.get() && value.get() && *value.get()) {\r
- m_transportOptions.push_back(make_pair(string(provider.get()), make_pair(string(option.get()), string(value.get()))));\r
- }\r
- }\r
- child = XMLHelper::getPreviousSiblingElement(child,TransportOption);\r
- }\r
-#endif\r
-\r
- // Load the default application. This actually has a fixed ID of "default". ;-)\r
- child=XMLHelper::getLastChildElement(e,ApplicationDefaults);\r
- if (!child) {\r
- log.fatal("can't build default Application object, missing conf:ApplicationDefaults element?");\r
- throw ConfigurationException("can't build default Application object, missing conf:ApplicationDefaults element?");\r
- }\r
- XMLApplication* defapp=new XMLApplication(m_outer,child);\r
- m_appmap[defapp->getId()]=defapp;\r
-\r
- // Load any overrides.\r
- child = XMLHelper::getFirstChildElement(child,ApplicationOverride);\r
- while (child) {\r
- auto_ptr<XMLApplication> iapp(new XMLApplication(m_outer,child,defapp));\r
- if (m_appmap.count(iapp->getId()))\r
- log.crit("found conf:ApplicationOverride element with duplicate id attribute (%s), skipping it", iapp->getId());\r
- else {\r
- const char* iappid=iapp->getId();\r
- m_appmap[iappid]=iapp.release();\r
- }\r
-\r
- child = XMLHelper::getNextSiblingElement(child,ApplicationOverride);\r
- }\r
- }\r
- catch (exception&) {\r
- cleanup();\r
- throw;\r
- }\r
-}\r
-\r
-XMLConfigImpl::~XMLConfigImpl()\r
-{\r
- cleanup();\r
-}\r
-\r
-void XMLConfigImpl::cleanup()\r
-{\r
- for_each(m_appmap.begin(),m_appmap.end(),cleanup_pair<string,Application>());\r
- m_appmap.clear();\r
-#ifndef SHIBSP_LITE\r
- for (map< string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::iterator i=m_policyMap.begin(); i!=m_policyMap.end(); ++i) {\r
- delete i->second.first;\r
- for_each(i->second.second.begin(), i->second.second.end(), xmltooling::cleanup<SecurityPolicyRule>());\r
- }\r
- m_policyMap.clear();\r
-#endif\r
- delete m_requestMapper;\r
- m_requestMapper = NULL;\r
- if (m_document)\r
- m_document->release();\r
- m_document = NULL;\r
-}\r
-\r
-#ifndef SHIBSP_LITE\r
-void XMLConfig::receive(DDF& in, ostream& out)\r
-{\r
- if (!strcmp(in.name(), "get::RelayState")) {\r
- const char* id = in["id"].string();\r
- const char* key = in["key"].string();\r
- if (!id || !key)\r
- throw ListenerException("Required parameters missing for RelayState recovery.");\r
-\r
- string relayState;\r
- StorageService* storage = getStorageService(id);\r
- if (storage) {\r
- if (storage->readString("RelayState",key,&relayState)>0) {\r
- if (in["clear"].integer())\r
- storage->deleteString("RelayState",key);\r
- }\r
- }\r
- else {\r
- Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(\r
- "Storage-backed RelayState with invalid StorageService ID (%s)", id\r
- );\r
- }\r
-\r
- // Repack for return to caller.\r
- DDF ret=DDF(NULL).unsafe_string(relayState.c_str());\r
- DDFJanitor jret(ret);\r
- out << ret;\r
- }\r
- else if (!strcmp(in.name(), "set::RelayState")) {\r
- const char* id = in["id"].string();\r
- const char* value = in["value"].string();\r
- if (!id || !value)\r
- throw ListenerException("Required parameters missing for RelayState creation.");\r
-\r
- string rsKey;\r
- StorageService* storage = getStorageService(id);\r
- if (storage) {\r
- SAMLConfig::getConfig().generateRandomBytes(rsKey,20);\r
- rsKey = SAMLArtifact::toHex(rsKey);\r
- storage->createString("RelayState", rsKey.c_str(), value, time(NULL) + 600);\r
- }\r
- else {\r
- Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(\r
- "Storage-backed RelayState with invalid StorageService ID (%s)", id\r
- );\r
- }\r
-\r
- // Repack for return to caller.\r
- DDF ret=DDF(NULL).string(rsKey.c_str());\r
- DDFJanitor jret(ret);\r
- out << ret;\r
- }\r
- else if (!strcmp(in.name(), "get::PostData")) {\r
- const char* id = in["id"].string();\r
- const char* key = in["key"].string();\r
- if (!id || !key)\r
- throw ListenerException("Required parameters missing for PostData recovery.");\r
-\r
- string postData;\r
- StorageService* storage = getStorageService(id);\r
- if (storage) {\r
- if (storage->readString("PostData",key,&postData) > 0) {\r
- storage->deleteString("PostData",key);\r
- }\r
- }\r
- else {\r
- Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(\r
- "Storage-backed PostData with invalid StorageService ID (%s)", id\r
- );\r
- }\r
- // If the data's empty, we'll send nothing back.\r
- // If not, we don't need to round trip it, just send back the serialized DDF list.\r
- if (postData.empty()) {\r
- DDF ret(NULL);\r
- DDFJanitor jret(ret);\r
- out << ret;\r
- }\r
- else {\r
- out << postData;\r
- }\r
- }\r
- else if (!strcmp(in.name(), "set::PostData")) {\r
- const char* id = in["id"].string();\r
- if (!id || !in["parameters"].islist())\r
- throw ListenerException("Required parameters missing for PostData creation.");\r
-\r
- string rsKey;\r
- StorageService* storage = getStorageService(id);\r
- if (storage) {\r
- SAMLConfig::getConfig().generateRandomBytes(rsKey,20);\r
- rsKey = SAMLArtifact::toHex(rsKey);\r
- ostringstream params;\r
- params << in["parameters"];\r
- storage->createString("PostData", rsKey.c_str(), params.str().c_str(), time(NULL) + 600);\r
- }\r
- else {\r
- Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(\r
- "Storage-backed PostData with invalid StorageService ID (%s)", id\r
- );\r
- }\r
-\r
- // Repack for return to caller.\r
- DDF ret=DDF(NULL).string(rsKey.c_str());\r
- DDFJanitor jret(ret);\r
- out << ret;\r
- }\r
-}\r
-#endif\r
-\r
-pair<bool,DOMElement*> XMLConfig::load()\r
-{\r
- // Load from source using base class.\r
- pair<bool,DOMElement*> raw = ReloadableXMLFile::load();\r
-\r
- // If we own it, wrap it.\r
- XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);\r
-\r
- XMLConfigImpl* impl = new XMLConfigImpl(raw.second,(m_impl==NULL),this,m_log);\r
-\r
- // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
- impl->setDocument(docjanitor.release());\r
-\r
- delete m_impl;\r
- m_impl = impl;\r
-\r
- return make_pair(false,(DOMElement*)NULL);\r
-}\r
+/*
+ * Copyright 2001-2009 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.
+ */
+
+/**
+ * XMLServiceProvider.cpp
+ *
+ * XML-based SP configuration and mgmt.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "version.h"
+#include "AccessControl.h"
+#include "Application.h"
+#include "RequestMapper.h"
+#include "ServiceProvider.h"
+#include "SessionCache.h"
+#include "SPConfig.h"
+#include "SPRequest.h"
+#include "handler/SessionInitiator.h"
+#include "remoting/ListenerService.h"
+#include "util/DOMPropertySet.h"
+#include "util/SPConstants.h"
+
+#if defined(XMLTOOLING_LOG4SHIB)
+# include <log4shib/PropertyConfigurator.hh>
+#elif defined(XMLTOOLING_LOG4CPP)
+# include <log4cpp/PropertyConfigurator.hh>
+#else
+# error "Supported logging library not available."
+#endif
+#include <algorithm>
+#include <xercesc/util/XMLUniDefs.hpp>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/version.h>
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/ReloadableXMLFile.h>
+#include <xmltooling/util/TemplateEngine.h>
+#include <xmltooling/util/XMLHelper.h>
+
+#ifndef SHIBSP_LITE
+# include "TransactionLog.h"
+# include "attribute/filtering/AttributeFilter.h"
+# include "attribute/resolver/AttributeExtractor.h"
+# include "attribute/resolver/AttributeResolver.h"
+# include "security/PKIXTrustEngine.h"
+# include <saml/SAMLConfig.h>
+# include <saml/version.h>
+# include <saml/binding/ArtifactMap.h>
+# include <saml/binding/SAMLArtifact.h>
+# include <saml/binding/SecurityPolicyRule.h>
+# include <saml/saml1/core/Assertions.h>
+# include <saml/saml2/core/Assertions.h>
+# include <saml/saml2/binding/SAML2ArtifactType0004.h>
+# include <saml/saml2/metadata/Metadata.h>
+# include <saml/saml2/metadata/MetadataProvider.h>
+# include <saml/util/SAMLConstants.h>
+# include <xmltooling/security/CredentialResolver.h>
+# include <xmltooling/security/SecurityHelper.h>
+# include <xmltooling/security/TrustEngine.h>
+# include <xmltooling/util/ReplayCache.h>
+# include <xmltooling/util/StorageService.h>
+using namespace opensaml::saml2;
+using namespace opensaml::saml2p;
+using namespace opensaml::saml2md;
+using namespace opensaml;
+#else
+# include "lite/SAMLConstants.h"
+#endif
+
+using namespace shibsp;
+using namespace xmltooling;
+using namespace std;
+
+#ifndef min
+# define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+namespace {
+
+#if defined (_MSC_VER)
+ #pragma warning( push )
+ #pragma warning( disable : 4250 )
+#endif
+
+ static vector<const Handler*> g_noHandlers;
+
+ // Application configuration wrapper
+ class SHIBSP_DLLLOCAL XMLApplication : public Application, public Remoted, public DOMPropertySet, public DOMNodeFilter
+ {
+ public:
+ XMLApplication(const ServiceProvider*, const DOMElement* e, const XMLApplication* base=NULL);
+ ~XMLApplication() { cleanup(); }
+
+ const char* getHash() const {return m_hash.c_str();}
+
+#ifndef SHIBSP_LITE
+ SAMLArtifact* generateSAML1Artifact(const EntityDescriptor* relyingParty) const {
+ throw ConfigurationException("No support for SAML 1.x artifact generation.");
+ }
+ SAML2Artifact* generateSAML2Artifact(const EntityDescriptor* relyingParty) const {
+ pair<bool,int> index = make_pair(false,0);
+ const PropertySet* props = getRelyingParty(relyingParty);
+ index = props->getInt("artifactEndpointIndex");
+ if (!index.first)
+ index = getArtifactEndpointIndex();
+ pair<bool,const char*> entityID = props->getString("entityID");
+ return new SAML2ArtifactType0004(
+ SecurityHelper::doHash("SHA1", entityID.second, strlen(entityID.second), false),
+ index.first ? index.second : 1
+ );
+ }
+
+ MetadataProvider* getMetadataProvider(bool required=true) const {
+ if (required && !m_base && !m_metadata)
+ throw ConfigurationException("No MetadataProvider available.");
+ return (!m_metadata && m_base) ? m_base->getMetadataProvider() : m_metadata;
+ }
+ TrustEngine* getTrustEngine(bool required=true) const {
+ if (required && !m_base && !m_trust)
+ throw ConfigurationException("No TrustEngine available.");
+ return (!m_trust && m_base) ? m_base->getTrustEngine() : m_trust;
+ }
+ AttributeExtractor* getAttributeExtractor() const {
+ return (!m_attrExtractor && m_base) ? m_base->getAttributeExtractor() : m_attrExtractor;
+ }
+ AttributeFilter* getAttributeFilter() const {
+ return (!m_attrFilter && m_base) ? m_base->getAttributeFilter() : m_attrFilter;
+ }
+ AttributeResolver* getAttributeResolver() const {
+ return (!m_attrResolver && m_base) ? m_base->getAttributeResolver() : m_attrResolver;
+ }
+ CredentialResolver* getCredentialResolver() const {
+ return (!m_credResolver && m_base) ? m_base->getCredentialResolver() : m_credResolver;
+ }
+ const PropertySet* getRelyingParty(const EntityDescriptor* provider) const;
+ const PropertySet* getRelyingParty(const XMLCh* entityID) const;
+ const vector<const XMLCh*>* getAudiences() const {
+ return (m_audiences.empty() && m_base) ? m_base->getAudiences() : &m_audiences;
+ }
+#endif
+ string getNotificationURL(const char* resource, bool front, unsigned int index) const;
+
+ const vector<string>& getRemoteUserAttributeIds() const {
+ return (m_remoteUsers.empty() && m_base) ? m_base->getRemoteUserAttributeIds() : m_remoteUsers;
+ }
+
+ void clearHeader(SPRequest& request, const char* rawname, const char* cginame) const;
+ void setHeader(SPRequest& request, const char* name, const char* value) const;
+ string getSecureHeader(const SPRequest& request, const char* name) const;
+
+ const SessionInitiator* getDefaultSessionInitiator() const;
+ const SessionInitiator* getSessionInitiatorById(const char* id) const;
+ const Handler* getDefaultAssertionConsumerService() const;
+ const Handler* getAssertionConsumerServiceByIndex(unsigned short index) const;
+ const vector<const Handler*>& getAssertionConsumerServicesByBinding(const XMLCh* binding) const;
+ const Handler* getHandler(const char* path) const;
+ void getHandlers(vector<const Handler*>& handlers) const;
+
+ void receive(DDF& in, ostream& out) {
+ // Only current function is to return the headers to clear.
+ DDF header;
+ DDF ret=DDF(NULL).list();
+ DDFJanitor jret(ret);
+ for (vector< pair<string,string> >::const_iterator i = m_unsetHeaders.begin(); i!=m_unsetHeaders.end(); ++i) {
+ header = DDF(i->first.c_str()).string(i->second.c_str());
+ ret.add(header);
+ }
+ out << ret;
+ }
+
+ // Provides filter to exclude special config elements.
+#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
+ short
+#else
+ FilterAction
+#endif
+ acceptNode(const DOMNode* node) const;
+
+ private:
+ void cleanup();
+ const XMLApplication* m_base;
+ string m_hash;
+ std::pair<std::string,std::string> m_attributePrefix;
+#ifndef SHIBSP_LITE
+ MetadataProvider* m_metadata;
+ TrustEngine* m_trust;
+ AttributeExtractor* m_attrExtractor;
+ AttributeFilter* m_attrFilter;
+ AttributeResolver* m_attrResolver;
+ CredentialResolver* m_credResolver;
+ vector<const XMLCh*> m_audiences;
+
+ // RelyingParty properties
+ map<xstring,PropertySet*> m_partyMap;
+#endif
+ vector<string> m_remoteUsers,m_frontLogout,m_backLogout;
+
+ // manage handler objects
+ vector<Handler*> m_handlers;
+
+ // maps location (path info) to applicable handlers
+ map<string,const Handler*> m_handlerMap;
+
+ // maps unique indexes to consumer services
+ map<unsigned int,const Handler*> m_acsIndexMap;
+
+ // pointer to default consumer service
+ const Handler* m_acsDefault;
+
+ // maps binding strings to supporting consumer service(s)
+ typedef map<xstring,vector<const Handler*> > ACSBindingMap;
+ ACSBindingMap m_acsBindingMap;
+
+ // pointer to default session initiator
+ const SessionInitiator* m_sessionInitDefault;
+
+ // maps unique ID strings to session initiators
+ map<string,const SessionInitiator*> m_sessionInitMap;
+
+ // pointer to default artifact resolution service
+ const Handler* m_artifactResolutionDefault;
+
+ pair<bool,int> getArtifactEndpointIndex() const {
+ if (m_artifactResolutionDefault) return m_artifactResolutionDefault->getInt("index");
+ return m_base ? m_base->getArtifactEndpointIndex() : make_pair(false,0);
+ }
+ };
+
+ // Top-level configuration implementation
+ class SHIBSP_DLLLOCAL XMLConfig;
+ class SHIBSP_DLLLOCAL XMLConfigImpl : public DOMPropertySet, public DOMNodeFilter
+ {
+ public:
+ XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer, Category& log);
+ ~XMLConfigImpl();
+
+ RequestMapper* m_requestMapper;
+ map<string,Application*> m_appmap;
+#ifndef SHIBSP_LITE
+ map< string,pair< PropertySet*,vector<const SecurityPolicyRule*> > > m_policyMap;
+ vector< pair< string, pair<string,string> > > m_transportOptions;
+#endif
+
+ // Provides filter to exclude special config elements.
+#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
+ short
+#else
+ FilterAction
+#endif
+ acceptNode(const DOMNode* node) const;
+
+ void setDocument(DOMDocument* doc) {
+ m_document = doc;
+ }
+
+ private:
+ void doExtensions(const DOMElement* e, const char* label, Category& log);
+ void cleanup();
+
+ const XMLConfig* m_outer;
+ DOMDocument* m_document;
+ };
+
+ class SHIBSP_DLLLOCAL XMLConfig : public ServiceProvider, public ReloadableXMLFile
+#ifndef SHIBSP_LITE
+ ,public Remoted
+#endif
+ {
+ public:
+ XMLConfig(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".Config")),
+ m_impl(NULL), m_listener(NULL), m_sessionCache(NULL)
+#ifndef SHIBSP_LITE
+ , m_tranLog(NULL)
+#endif
+ {
+ }
+
+ void init() {
+ load();
+ }
+
+ ~XMLConfig() {
+ delete m_impl;
+ delete m_sessionCache;
+ delete m_listener;
+#ifndef SHIBSP_LITE
+ delete m_tranLog;
+ SAMLConfig::getConfig().setArtifactMap(NULL);
+ XMLToolingConfig::getConfig().setReplayCache(NULL);
+ for_each(m_storage.begin(), m_storage.end(), cleanup_pair<string,StorageService>());
+#endif
+ }
+
+ // PropertySet
+ const PropertySet* getParent() const { return m_impl->getParent(); }
+ void setParent(const PropertySet* parent) {return m_impl->setParent(parent);}
+ pair<bool,bool> getBool(const char* name, const char* ns=NULL) const {return m_impl->getBool(name,ns);}
+ pair<bool,const char*> getString(const char* name, const char* ns=NULL) const {return m_impl->getString(name,ns);}
+ pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const {return m_impl->getXMLString(name,ns);}
+ pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const {return m_impl->getUnsignedInt(name,ns);}
+ pair<bool,int> getInt(const char* name, const char* ns=NULL) const {return m_impl->getInt(name,ns);}
+ void getAll(map<string,const char*>& properties) const {return m_impl->getAll(properties);}
+ const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:2.0:native:sp:config") const {return m_impl->getPropertySet(name,ns);}
+ const DOMElement* getElement() const {return m_impl->getElement();}
+
+ // ServiceProvider
+#ifndef SHIBSP_LITE
+ // Remoted
+ void receive(DDF& in, ostream& out);
+
+ TransactionLog* getTransactionLog() const {
+ if (m_tranLog)
+ return m_tranLog;
+ throw ConfigurationException("No TransactionLog available.");
+ }
+
+ StorageService* getStorageService(const char* id) const {
+ if (id) {
+ map<string,StorageService*>::const_iterator i=m_storage.find(id);
+ if (i!=m_storage.end())
+ return i->second;
+ }
+ return NULL;
+ }
+#endif
+
+ ListenerService* getListenerService(bool required=true) const {
+ if (required && !m_listener)
+ throw ConfigurationException("No ListenerService available.");
+ return m_listener;
+ }
+
+ SessionCache* getSessionCache(bool required=true) const {
+ if (required && !m_sessionCache)
+ throw ConfigurationException("No SessionCache available.");
+ return m_sessionCache;
+ }
+
+ RequestMapper* getRequestMapper(bool required=true) const {
+ if (required && !m_impl->m_requestMapper)
+ throw ConfigurationException("No RequestMapper available.");
+ return m_impl->m_requestMapper;
+ }
+
+ const Application* getApplication(const char* applicationId) const {
+ map<string,Application*>::const_iterator i=m_impl->m_appmap.find(applicationId);
+ return (i!=m_impl->m_appmap.end()) ? i->second : NULL;
+ }
+
+#ifndef SHIBSP_LITE
+ const PropertySet* getPolicySettings(const char* id) const {
+ map<string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::const_iterator i = m_impl->m_policyMap.find(id);
+ if (i!=m_impl->m_policyMap.end())
+ return i->second.first;
+ throw ConfigurationException("Security Policy ($1) not found, check <SecurityPolicies> element.", params(1,id));
+ }
+
+ const vector<const SecurityPolicyRule*>& getPolicyRules(const char* id) const {
+ map<string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::const_iterator i = m_impl->m_policyMap.find(id);
+ if (i!=m_impl->m_policyMap.end())
+ return i->second.second;
+ throw ConfigurationException("Security Policy ($1) not found, check <SecurityPolicies> element.", params(1,id));
+ }
+
+ bool setTransportOptions(SOAPTransport& transport) const {
+ bool ret = true;
+ vector< pair< string, pair<string,string> > >::const_iterator opt;
+ for (opt = m_impl->m_transportOptions.begin(); opt != m_impl->m_transportOptions.end(); ++opt) {
+ if (!transport.setProviderOption(opt->first.c_str(), opt->second.first.c_str(), opt->second.second.c_str())) {
+ m_log.error("failed to set SOAPTransport option (%s)", opt->second.first.c_str());
+ ret = false;
+ }
+ }
+ return ret;
+ }
+#endif
+
+ protected:
+ pair<bool,DOMElement*> load();
+
+ private:
+ friend class XMLConfigImpl;
+ XMLConfigImpl* m_impl;
+ mutable ListenerService* m_listener;
+ mutable SessionCache* m_sessionCache;
+#ifndef SHIBSP_LITE
+ mutable TransactionLog* m_tranLog;
+ mutable map<string,StorageService*> m_storage;
+#endif
+ };
+
+#if defined (_MSC_VER)
+ #pragma warning( pop )
+#endif
+
+ static const XMLCh ApplicationOverride[] = UNICODE_LITERAL_19(A,p,p,l,i,c,a,t,i,o,n,O,v,e,r,r,i,d,e);
+ static const XMLCh ApplicationDefaults[] = UNICODE_LITERAL_19(A,p,p,l,i,c,a,t,i,o,n,D,e,f,a,u,l,t,s);
+ static const XMLCh _ArtifactMap[] = UNICODE_LITERAL_11(A,r,t,i,f,a,c,t,M,a,p);
+ static const XMLCh _AttributeExtractor[] = UNICODE_LITERAL_18(A,t,t,r,i,b,u,t,e,E,x,t,r,a,c,t,o,r);
+ static const XMLCh _AttributeFilter[] = UNICODE_LITERAL_15(A,t,t,r,i,b,u,t,e,F,i,l,t,e,r);
+ static const XMLCh _AttributeResolver[] = UNICODE_LITERAL_17(A,t,t,r,i,b,u,t,e,R,e,s,o,l,v,e,r);
+ static const XMLCh _AssertionConsumerService[] = UNICODE_LITERAL_24(A,s,s,e,r,t,i,o,n,C,o,n,s,u,m,e,r,S,e,r,v,i,c,e);
+ static const XMLCh _ArtifactResolutionService[] =UNICODE_LITERAL_25(A,r,t,i,f,a,c,t,R,e,s,o,l,u,t,i,o,n,S,e,r,v,i,c,e);
+ static const XMLCh _Audience[] = UNICODE_LITERAL_8(A,u,d,i,e,n,c,e);
+ static const XMLCh Binding[] = UNICODE_LITERAL_7(B,i,n,d,i,n,g);
+ static const XMLCh Channel[]= UNICODE_LITERAL_7(C,h,a,n,n,e,l);
+ static const XMLCh _CredentialResolver[] = UNICODE_LITERAL_18(C,r,e,d,e,n,t,i,a,l,R,e,s,o,l,v,e,r);
+ static const XMLCh _Extensions[] = UNICODE_LITERAL_10(E,x,t,e,n,s,i,o,n,s);
+ static const XMLCh _fatal[] = UNICODE_LITERAL_5(f,a,t,a,l);
+ static const XMLCh _Handler[] = UNICODE_LITERAL_7(H,a,n,d,l,e,r);
+ static const XMLCh _id[] = UNICODE_LITERAL_2(i,d);
+ static const XMLCh InProcess[] = UNICODE_LITERAL_9(I,n,P,r,o,c,e,s,s);
+ static const XMLCh Library[] = UNICODE_LITERAL_7(L,i,b,r,a,r,y);
+ static const XMLCh Listener[] = UNICODE_LITERAL_8(L,i,s,t,e,n,e,r);
+ static const XMLCh Location[] = UNICODE_LITERAL_8(L,o,c,a,t,i,o,n);
+ static const XMLCh logger[] = UNICODE_LITERAL_6(l,o,g,g,e,r);
+ static const XMLCh _LogoutInitiator[] = UNICODE_LITERAL_15(L,o,g,o,u,t,I,n,i,t,i,a,t,o,r);
+ static const XMLCh _ManageNameIDService[] = UNICODE_LITERAL_19(M,a,n,a,g,e,N,a,m,e,I,D,S,e,r,v,i,c,e);
+ 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 Notify[] = UNICODE_LITERAL_6(N,o,t,i,f,y);
+ static const XMLCh _option[] = UNICODE_LITERAL_6(o,p,t,i,o,n);
+ static const XMLCh OutOfProcess[] = UNICODE_LITERAL_12(O,u,t,O,f,P,r,o,c,e,s,s);
+ static const XMLCh _path[] = UNICODE_LITERAL_4(p,a,t,h);
+ static const XMLCh Policy[] = UNICODE_LITERAL_6(P,o,l,i,c,y);
+ static const XMLCh PolicyRule[] = UNICODE_LITERAL_10(P,o,l,i,c,y,R,u,l,e);
+ static const XMLCh _provider[] = UNICODE_LITERAL_8(p,r,o,v,i,d,e,r);
+ static const XMLCh RelyingParty[] = UNICODE_LITERAL_12(R,e,l,y,i,n,g,P,a,r,t,y);
+ static const XMLCh _ReplayCache[] = UNICODE_LITERAL_11(R,e,p,l,a,y,C,a,c,h,e);
+ static const XMLCh _RequestMapper[] = UNICODE_LITERAL_13(R,e,q,u,e,s,t,M,a,p,p,e,r);
+ static const XMLCh Rule[] = UNICODE_LITERAL_4(R,u,l,e);
+ static const XMLCh SecurityPolicies[] = UNICODE_LITERAL_16(S,e,c,u,r,i,t,y,P,o,l,i,c,i,e,s);
+ static const XMLCh _SessionCache[] = UNICODE_LITERAL_12(S,e,s,s,i,o,n,C,a,c,h,e);
+ static const XMLCh _SessionInitiator[] = UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r);
+ static const XMLCh _SingleLogoutService[] = UNICODE_LITERAL_19(S,i,n,g,l,e,L,o,g,o,u,t,S,e,r,v,i,c,e);
+ static const XMLCh Site[] = UNICODE_LITERAL_4(S,i,t,e);
+ static const XMLCh _StorageService[] = UNICODE_LITERAL_14(S,t,o,r,a,g,e,S,e,r,v,i,c,e);
+ static const XMLCh TCPListener[] = UNICODE_LITERAL_11(T,C,P,L,i,s,t,e,n,e,r);
+ static const XMLCh TransportOption[] = UNICODE_LITERAL_15(T,r,a,n,s,p,o,r,t,O,p,t,i,o,n);
+ static const XMLCh _TrustEngine[] = UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e);
+ static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e);
+ static const XMLCh UnixListener[] = UNICODE_LITERAL_12(U,n,i,x,L,i,s,t,e,n,e,r);
+
+#ifndef SHIBSP_LITE
+ class SHIBSP_DLLLOCAL PolicyNodeFilter : public DOMNodeFilter
+ {
+ public:
+#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
+ short
+#else
+ FilterAction
+#endif
+ acceptNode(const DOMNode* node) const {
+ return FILTER_REJECT;
+ }
+ };
+#endif
+};
+
+namespace shibsp {
+ ServiceProvider* XMLServiceProviderFactory(const DOMElement* const & e)
+ {
+ return new XMLConfig(e);
+ }
+};
+
+XMLApplication::XMLApplication(
+ const ServiceProvider* sp,
+ const DOMElement* e,
+ const XMLApplication* base
+ ) : Application(sp), m_base(base),
+#ifndef SHIBSP_LITE
+ m_metadata(NULL), m_trust(NULL),
+ m_attrExtractor(NULL), m_attrFilter(NULL), m_attrResolver(NULL),
+ m_credResolver(NULL),
+#endif
+ m_acsDefault(NULL), m_sessionInitDefault(NULL), m_artifactResolutionDefault(NULL)
+{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("XMLApplication");
+#endif
+ Category& log=Category::getInstance(SHIBSP_LOGCAT".Application");
+
+ try {
+ // First load any property sets.
+ load(e,NULL,this);
+ if (base)
+ setParent(base);
+
+ SPConfig& conf=SPConfig::getConfig();
+#ifndef SHIBSP_LITE
+ SAMLConfig& samlConf=SAMLConfig::getConfig();
+ XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();
+#endif
+
+ // This used to be an actual hash, but now it's just a hex-encode to avoid xmlsec.
+ static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ string tohash=getId();
+ tohash+=getString("entityID").second;
+ for (const char* ch = tohash.c_str(); *ch; ++ch) {
+ m_hash += (DIGITS[((unsigned char)(0xF0 & *ch)) >> 4 ]);
+ m_hash += (DIGITS[0x0F & *ch]);
+ }
+
+ // Populate prefix pair.
+ m_attributePrefix.second = "HTTP_";
+ pair<bool,const char*> prefix = getString("attributePrefix");
+ if (prefix.first) {
+ m_attributePrefix.first = prefix.second;
+ const char* pch = prefix.second;
+ while (*pch) {
+ m_attributePrefix.second += (isalnum(*pch) ? toupper(*pch) : '_');
+ pch++;
+ }
+ }
+
+ // Load attribute ID lists for REMOTE_USER and header clearing.
+ if (conf.isEnabled(SPConfig::InProcess)) {
+ pair<bool,const char*> attributes = getString("REMOTE_USER");
+ if (attributes.first) {
+ char* dup = strdup(attributes.second);
+ char* pos;
+ char* start = dup;
+ while (start && *start) {
+ while (*start && isspace(*start))
+ start++;
+ if (!*start)
+ break;
+ pos = strchr(start,' ');
+ if (pos)
+ *pos=0;
+ m_remoteUsers.push_back(start);
+ start = pos ? pos+1 : NULL;
+ }
+ free(dup);
+ }
+
+ attributes = getString("unsetHeaders");
+ if (attributes.first) {
+ string transformedprefix(m_attributePrefix.second);
+ const char* pch;
+ prefix = getString("metadataAttributePrefix");
+ if (prefix.first) {
+ pch = prefix.second;
+ while (*pch) {
+ transformedprefix += (isalnum(*pch) ? toupper(*pch) : '_');
+ pch++;
+ }
+ }
+ char* dup = strdup(attributes.second);
+ char* pos;
+ char* start = dup;
+ while (start && *start) {
+ while (*start && isspace(*start))
+ start++;
+ if (!*start)
+ break;
+ pos = strchr(start,' ');
+ if (pos)
+ *pos=0;
+
+ string transformed;
+ pch = start;
+ while (*pch) {
+ transformed += (isalnum(*pch) ? toupper(*pch) : '_');
+ pch++;
+ }
+
+ m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + start, m_attributePrefix.second + transformed));
+ if (prefix.first)
+ m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + prefix.second + start, transformedprefix + transformed));
+ start = pos ? pos+1 : NULL;
+ }
+ free(dup);
+ m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + "Shib-Application-ID", m_attributePrefix.second + "SHIB_APPLICATION_ID"));
+ }
+ }
+
+ Handler* handler=NULL;
+ const PropertySet* sessions = getPropertySet("Sessions");
+
+ // Process assertion export handler.
+ pair<bool,const char*> location = sessions ? sessions->getString("exportLocation") : pair<bool,const char*>(false,NULL);
+ if (location.first) {
+ try {
+ DOMElement* exportElement = e->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS,_Handler);
+ exportElement->setAttributeNS(NULL,Location,sessions->getXMLString("exportLocation").second);
+ pair<bool,const XMLCh*> exportACL = sessions->getXMLString("exportACL");
+ if (exportACL.first) {
+ static const XMLCh _acl[] = UNICODE_LITERAL_9(e,x,p,o,r,t,A,C,L);
+ exportElement->setAttributeNS(NULL,_acl,exportACL.second);
+ }
+ handler = conf.HandlerManager.newPlugin(
+ samlconstants::SAML20_BINDING_URI, pair<const DOMElement*,const char*>(exportElement, getId())
+ );
+ m_handlers.push_back(handler);
+
+ // Insert into location map. If it contains the handlerURL, we skip past that part.
+ const char* pch = strstr(location.second, sessions->getString("handlerURL").second);
+ if (pch)
+ location.second = pch + strlen(sessions->getString("handlerURL").second);
+ if (*location.second == '/')
+ m_handlerMap[location.second]=handler;
+ else
+ m_handlerMap[string("/") + location.second]=handler;
+ }
+ catch (exception& ex) {
+ log.error("caught exception installing assertion lookup handler: %s", ex.what());
+ }
+ }
+
+ // Process other handlers.
+ bool hardACS=false, hardSessionInit=false, hardArt=false;
+ const DOMElement* child = sessions ? XMLHelper::getFirstChildElement(sessions->getElement()) : NULL;
+ while (child) {
+ try {
+ // A handler is based on the Binding property in conjunction with the element name.
+ // If it's an ACS or SI, also handle index/id mappings and defaulting.
+ if (XMLString::equals(child->getLocalName(),_AssertionConsumerService)) {
+ auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));
+ if (!bindprop.get() || !*(bindprop.get())) {
+ log.warn("md:AssertionConsumerService element has no Binding attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler=conf.AssertionConsumerServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));
+ // Map by binding (may be > 1 per binding, e.g. SAML 1.0 vs 1.1)
+ m_acsBindingMap[handler->getXMLString("Binding").second].push_back(handler);
+ m_acsIndexMap[handler->getUnsignedInt("index").second]=handler;
+
+ if (!hardACS) {
+ pair<bool,bool> defprop=handler->getBool("isDefault");
+ if (defprop.first) {
+ if (defprop.second) {
+ hardACS=true;
+ m_acsDefault=handler;
+ }
+ }
+ else if (!m_acsDefault)
+ m_acsDefault=handler;
+ }
+ }
+ else if (XMLString::equals(child->getLocalName(),_SessionInitiator)) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ if (!type.get() || !*(type.get())) {
+ log.warn("SessionInitiator element has no type attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ SessionInitiator* sihandler=conf.SessionInitiatorManager.newPlugin(type.get(),make_pair(child, getId()));
+ handler=sihandler;
+ pair<bool,const char*> si_id=handler->getString("id");
+ if (si_id.first && si_id.second)
+ m_sessionInitMap[si_id.second]=sihandler;
+ if (!hardSessionInit) {
+ pair<bool,bool> defprop=handler->getBool("isDefault");
+ if (defprop.first) {
+ if (defprop.second) {
+ hardSessionInit=true;
+ m_sessionInitDefault=sihandler;
+ }
+ }
+ else if (!m_sessionInitDefault)
+ m_sessionInitDefault=sihandler;
+ }
+ }
+ else if (XMLString::equals(child->getLocalName(),_LogoutInitiator)) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ if (!type.get() || !*(type.get())) {
+ log.warn("LogoutInitiator element has no type attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler=conf.LogoutInitiatorManager.newPlugin(type.get(),make_pair(child, getId()));
+ }
+ else if (XMLString::equals(child->getLocalName(),_ArtifactResolutionService)) {
+ auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));
+ if (!bindprop.get() || !*(bindprop.get())) {
+ log.warn("md:ArtifactResolutionService element has no Binding attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler=conf.ArtifactResolutionServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));
+
+ if (!hardArt) {
+ pair<bool,bool> defprop=handler->getBool("isDefault");
+ if (defprop.first) {
+ if (defprop.second) {
+ hardArt=true;
+ m_artifactResolutionDefault=handler;
+ }
+ }
+ else if (!m_artifactResolutionDefault)
+ m_artifactResolutionDefault=handler;
+ }
+ }
+ else if (XMLString::equals(child->getLocalName(),_SingleLogoutService)) {
+ auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));
+ if (!bindprop.get() || !*(bindprop.get())) {
+ log.warn("md:SingleLogoutService element has no Binding attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler=conf.SingleLogoutServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));
+ }
+ else if (XMLString::equals(child->getLocalName(),_ManageNameIDService)) {
+ auto_ptr_char bindprop(child->getAttributeNS(NULL,Binding));
+ if (!bindprop.get() || !*(bindprop.get())) {
+ log.warn("md:ManageNameIDService element has no Binding attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler=conf.ManageNameIDServiceManager.newPlugin(bindprop.get(),make_pair(child, getId()));
+ }
+ else {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ if (!type.get() || !*(type.get())) {
+ log.warn("Handler element has no type attribute, skipping it...");
+ child = XMLHelper::getNextSiblingElement(child);
+ continue;
+ }
+ handler=conf.HandlerManager.newPlugin(type.get(),make_pair(child, getId()));
+ }
+
+ m_handlers.push_back(handler);
+
+ // Insert into location map.
+ location=handler->getString("Location");
+ if (location.first && *location.second == '/')
+ m_handlerMap[location.second]=handler;
+ else if (location.first)
+ m_handlerMap[string("/") + location.second]=handler;
+
+ }
+ catch (exception& ex) {
+ log.error("caught exception processing handler element: %s", ex.what());
+ }
+
+ child = XMLHelper::getNextSiblingElement(child);
+ }
+
+ // Notification.
+ DOMNodeList* nlist=e->getElementsByTagNameNS(shibspconstants::SHIB2SPCONFIG_NS,Notify);
+ for (XMLSize_t i=0; nlist && i<nlist->getLength(); i++) {
+ if (nlist->item(i)->getParentNode()->isSameNode(e)) {
+ const XMLCh* channel = static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,Channel);
+ auto_ptr_char loc(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,Location));
+ if (loc.get() && *loc.get()) {
+ if (channel && *channel == chLatin_f)
+ m_frontLogout.push_back(loc.get());
+ else
+ m_backLogout.push_back(loc.get());
+ }
+ }
+ }
+
+#ifndef SHIBSP_LITE
+ nlist=e->getElementsByTagNameNS(samlconstants::SAML20_NS,Audience::LOCAL_NAME);
+ if (nlist && nlist->getLength()) {
+ log.warn("use of <saml:Audience> elements outside of a Security Policy Rule is deprecated");
+ for (XMLSize_t i=0; i<nlist->getLength(); i++)
+ if (nlist->item(i)->getParentNode()->isSameNode(e) && nlist->item(i)->hasChildNodes())
+ m_audiences.push_back(nlist->item(i)->getFirstChild()->getNodeValue());
+ }
+
+ if (conf.isEnabled(SPConfig::Metadata)) {
+ child = XMLHelper::getFirstChildElement(e,_MetadataProvider);
+ if (child) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ log.info("building MetadataProvider of type %s...",type.get());
+ try {
+ auto_ptr<MetadataProvider> mp(samlConf.MetadataProviderManager.newPlugin(type.get(),child));
+ mp->init();
+ m_metadata = mp.release();
+ }
+ catch (exception& ex) {
+ log.crit("error building/initializing MetadataProvider: %s", ex.what());
+ }
+ }
+ }
+
+ if (conf.isEnabled(SPConfig::Trust)) {
+ child = XMLHelper::getFirstChildElement(e,_TrustEngine);
+ if (child) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ log.info("building TrustEngine of type %s...",type.get());
+ try {
+ m_trust = xmlConf.TrustEngineManager.newPlugin(type.get(),child);
+ }
+ catch (exception& ex) {
+ log.crit("error building TrustEngine: %s", ex.what());
+ }
+ }
+ }
+
+ if (conf.isEnabled(SPConfig::AttributeResolution)) {
+ child = XMLHelper::getFirstChildElement(e,_AttributeExtractor);
+ if (child) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ log.info("building AttributeExtractor of type %s...",type.get());
+ try {
+ m_attrExtractor = conf.AttributeExtractorManager.newPlugin(type.get(),child);
+ }
+ catch (exception& ex) {
+ log.crit("error building AttributeExtractor: %s", ex.what());
+ }
+ }
+
+ child = XMLHelper::getFirstChildElement(e,_AttributeFilter);
+ if (child) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ log.info("building AttributeFilter of type %s...",type.get());
+ try {
+ m_attrFilter = conf.AttributeFilterManager.newPlugin(type.get(),child);
+ }
+ catch (exception& ex) {
+ log.crit("error building AttributeFilter: %s", ex.what());
+ }
+ }
+
+ child = XMLHelper::getFirstChildElement(e,_AttributeResolver);
+ if (child) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ log.info("building AttributeResolver of type %s...",type.get());
+ try {
+ m_attrResolver = conf.AttributeResolverManager.newPlugin(type.get(),child);
+ }
+ catch (exception& ex) {
+ log.crit("error building AttributeResolver: %s", ex.what());
+ }
+ }
+
+ if (m_unsetHeaders.empty()) {
+ vector<string> unsetHeaders;
+ if (m_attrExtractor) {
+ Locker extlock(m_attrExtractor);
+ m_attrExtractor->getAttributeIds(unsetHeaders);
+ }
+ else if (m_base && m_base->m_attrExtractor) {
+ Locker extlock(m_base->m_attrExtractor);
+ m_base->m_attrExtractor->getAttributeIds(unsetHeaders);
+ }
+ if (m_attrResolver) {
+ Locker reslock(m_attrResolver);
+ m_attrResolver->getAttributeIds(unsetHeaders);
+ }
+ else if (m_base && m_base->m_attrResolver) {
+ Locker extlock(m_base->m_attrResolver);
+ m_base->m_attrResolver->getAttributeIds(unsetHeaders);
+ }
+ if (!unsetHeaders.empty()) {
+ string transformedprefix(m_attributePrefix.second);
+ const char* pch;
+ pair<bool,const char*> prefix = getString("metadataAttributePrefix");
+ if (prefix.first) {
+ pch = prefix.second;
+ while (*pch) {
+ transformedprefix += (isalnum(*pch) ? toupper(*pch) : '_');
+ pch++;
+ }
+ }
+ for (vector<string>::const_iterator hdr = unsetHeaders.begin(); hdr!=unsetHeaders.end(); ++hdr) {
+ string transformed;
+ pch = hdr->c_str();
+ while (*pch) {
+ transformed += (isalnum(*pch) ? toupper(*pch) : '_');
+ pch++;
+ }
+ m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + *hdr, m_attributePrefix.second + transformed));
+ if (prefix.first)
+ m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + prefix.second + *hdr, transformedprefix + transformed));
+ }
+ }
+ m_unsetHeaders.push_back(pair<string,string>(m_attributePrefix.first + "Shib-Application-ID", m_attributePrefix.second + "SHIB_APPLICATION_ID"));
+ }
+ }
+
+ if (conf.isEnabled(SPConfig::Credentials)) {
+ child = XMLHelper::getFirstChildElement(e,_CredentialResolver);
+ if (child) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ log.info("building CredentialResolver of type %s...",type.get());
+ try {
+ m_credResolver = xmlConf.CredentialResolverManager.newPlugin(type.get(),child);
+ }
+ catch (exception& ex) {
+ log.crit("error building CredentialResolver: %s", ex.what());
+ }
+ }
+ }
+
+ // Finally, load relying parties.
+ child = XMLHelper::getFirstChildElement(e,RelyingParty);
+ while (child) {
+ auto_ptr<DOMPropertySet> rp(new DOMPropertySet());
+ rp->load(child,NULL,this);
+ rp->setParent(this);
+ m_partyMap[child->getAttributeNS(NULL,saml2::Attribute::NAME_ATTRIB_NAME)]=rp.release();
+ child = XMLHelper::getNextSiblingElement(child,RelyingParty);
+ }
+#endif
+
+ // Out of process only, we register a listener endpoint.
+ if (!conf.isEnabled(SPConfig::InProcess)) {
+ ListenerService* listener = sp->getListenerService(false);
+ if (listener) {
+ string addr=string(getId()) + "::getHeaders::Application";
+ listener->regListener(addr.c_str(),this);
+ }
+ else
+ log.info("no ListenerService available, Application remoting disabled");
+ }
+ }
+ catch (exception&) {
+ cleanup();
+ throw;
+ }
+#ifndef _DEBUG
+ catch (...) {
+ cleanup();
+ throw;
+ }
+#endif
+}
+
+void XMLApplication::cleanup()
+{
+ ListenerService* listener=getServiceProvider().getListenerService(false);
+ if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess) && !SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
+ string addr=string(getId()) + "::getHeaders::Application";
+ listener->unregListener(addr.c_str(),this);
+ }
+ for_each(m_handlers.begin(),m_handlers.end(),xmltooling::cleanup<Handler>());
+ m_handlers.clear();
+#ifndef SHIBSP_LITE
+ for_each(m_partyMap.begin(),m_partyMap.end(),cleanup_pair<xstring,PropertySet>());
+ m_partyMap.clear();
+ delete m_credResolver;
+ m_credResolver = NULL;
+ delete m_attrResolver;
+ m_attrResolver = NULL;
+ delete m_attrFilter;
+ m_attrFilter = NULL;
+ delete m_attrExtractor;
+ m_attrExtractor = NULL;
+ delete m_trust;
+ m_trust = NULL;
+ delete m_metadata;
+ m_metadata = NULL;
+#endif
+}
+
+#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
+short
+#else
+DOMNodeFilter::FilterAction
+#endif
+XMLApplication::acceptNode(const DOMNode* node) const
+{
+ const XMLCh* name=node->getLocalName();
+ if (XMLString::equals(name,ApplicationOverride) ||
+ XMLString::equals(name,_Audience) ||
+ XMLString::equals(name,Notify) ||
+ XMLString::equals(name,_Handler) ||
+ XMLString::equals(name,_AssertionConsumerService) ||
+ XMLString::equals(name,_ArtifactResolutionService) ||
+ XMLString::equals(name,_LogoutInitiator) ||
+ XMLString::equals(name,_ManageNameIDService) ||
+ XMLString::equals(name,_SessionInitiator) ||
+ XMLString::equals(name,_SingleLogoutService) ||
+ XMLString::equals(name,RelyingParty) ||
+ XMLString::equals(name,_MetadataProvider) ||
+ XMLString::equals(name,_TrustEngine) ||
+ XMLString::equals(name,_CredentialResolver) ||
+ XMLString::equals(name,_AttributeFilter) ||
+ XMLString::equals(name,_AttributeExtractor) ||
+ XMLString::equals(name,_AttributeResolver))
+ return FILTER_REJECT;
+
+ return FILTER_ACCEPT;
+}
+
+#ifndef SHIBSP_LITE
+
+const PropertySet* XMLApplication::getRelyingParty(const EntityDescriptor* provider) const
+{
+ if (!provider)
+ return this;
+
+ map<xstring,PropertySet*>::const_iterator i=m_partyMap.find(provider->getEntityID());
+ if (i!=m_partyMap.end())
+ return i->second;
+ const EntitiesDescriptor* group=dynamic_cast<const EntitiesDescriptor*>(provider->getParent());
+ while (group) {
+ if (group->getName()) {
+ i=m_partyMap.find(group->getName());
+ if (i!=m_partyMap.end())
+ return i->second;
+ }
+ group=dynamic_cast<const EntitiesDescriptor*>(group->getParent());
+ }
+ return this;
+}
+
+const PropertySet* XMLApplication::getRelyingParty(const XMLCh* entityID) const
+{
+ if (!entityID)
+ return this;
+
+ map<xstring,PropertySet*>::const_iterator i=m_partyMap.find(entityID);
+ if (i!=m_partyMap.end())
+ return i->second;
+ return this;
+}
+
+#endif
+
+string XMLApplication::getNotificationURL(const char* resource, bool front, unsigned int index) const
+{
+ const vector<string>& locs = front ? m_frontLogout : m_backLogout;
+ if (locs.empty())
+ return m_base ? m_base->getNotificationURL(resource, front, index) : string();
+ else if (index >= locs.size())
+ return string();
+
+#ifdef HAVE_STRCASECMP
+ if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
+#else
+ if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))
+#endif
+ throw ConfigurationException("Request URL was not absolute.");
+
+ const char* handler=locs[index].c_str();
+
+ // Should never happen...
+ if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)))
+ throw ConfigurationException(
+ "Invalid Location property ($1) in Notify element for Application ($2)",
+ params(2, handler ? handler : "null", getId())
+ );
+
+ // The "Location" property can be in one of three formats:
+ //
+ // 1) a full URI: http://host/foo/bar
+ // 2) a hostless URI: http:///foo/bar
+ // 3) a relative path: /foo/bar
+ //
+ // # Protocol Host Path
+ // 1 handler handler handler
+ // 2 handler resource handler
+ // 3 resource resource handler
+
+ const char* path = NULL;
+
+ // Decide whether to use the handler or the resource for the "protocol"
+ const char* prot;
+ if (*handler != '/') {
+ prot = handler;
+ }
+ else {
+ prot = resource;
+ path = handler;
+ }
+
+ // break apart the "protocol" string into protocol, host, and "the rest"
+ const char* colon=strchr(prot,':');
+ colon += 3;
+ const char* slash=strchr(colon,'/');
+ if (!path)
+ path = slash;
+
+ // Compute the actual protocol and store.
+ string notifyURL(prot, colon-prot);
+
+ // create the "host" from either the colon/slash or from the target string
+ // If prot == handler then we're in either #1 or #2, else #3.
+ // If slash == colon then we're in #2.
+ if (prot != handler || slash == colon) {
+ colon = strchr(resource, ':');
+ colon += 3; // Get past the ://
+ slash = strchr(colon, '/');
+ }
+ string host(colon, (slash ? slash-colon : strlen(colon)));
+
+ // Build the URL
+ notifyURL += host + path;
+ return notifyURL;
+}
+
+void XMLApplication::clearHeader(SPRequest& request, const char* rawname, const char* cginame) const
+{
+ if (!m_attributePrefix.first.empty()) {
+ string temp = m_attributePrefix.first + rawname;
+ string temp2 = m_attributePrefix.second + (cginame + 5);
+ request.clearHeader(temp.c_str(), temp2.c_str());
+ }
+ else if (m_base) {
+ m_base->clearHeader(request, rawname, cginame);
+ }
+ else {
+ request.clearHeader(rawname, cginame);
+ }
+}
+
+void XMLApplication::setHeader(SPRequest& request, const char* name, const char* value) const
+{
+ if (!m_attributePrefix.first.empty()) {
+ string temp = m_attributePrefix.first + name;
+ request.setHeader(temp.c_str(), value);
+ }
+ else if (m_base) {
+ m_base->setHeader(request, name, value);
+ }
+ else {
+ request.setHeader(name, value);
+ }
+}
+
+string XMLApplication::getSecureHeader(const SPRequest& request, const char* name) const
+{
+ if (!m_attributePrefix.first.empty()) {
+ string temp = m_attributePrefix.first + name;
+ return request.getSecureHeader(temp.c_str());
+ }
+ else if (m_base) {
+ return m_base->getSecureHeader(request,name);
+ }
+ else {
+ return request.getSecureHeader(name);
+ }
+}
+
+const SessionInitiator* XMLApplication::getDefaultSessionInitiator() const
+{
+ if (m_sessionInitDefault) return m_sessionInitDefault;
+ return m_base ? m_base->getDefaultSessionInitiator() : NULL;
+}
+
+const SessionInitiator* XMLApplication::getSessionInitiatorById(const char* id) const
+{
+ map<string,const SessionInitiator*>::const_iterator i=m_sessionInitMap.find(id);
+ if (i!=m_sessionInitMap.end()) return i->second;
+ return m_base ? m_base->getSessionInitiatorById(id) : NULL;
+}
+
+const Handler* XMLApplication::getDefaultAssertionConsumerService() const
+{
+ if (m_acsDefault) return m_acsDefault;
+ return m_base ? m_base->getDefaultAssertionConsumerService() : NULL;
+}
+
+const Handler* XMLApplication::getAssertionConsumerServiceByIndex(unsigned short index) const
+{
+ map<unsigned int,const Handler*>::const_iterator i=m_acsIndexMap.find(index);
+ if (i!=m_acsIndexMap.end()) return i->second;
+ return m_base ? m_base->getAssertionConsumerServiceByIndex(index) : NULL;
+}
+
+const vector<const Handler*>& XMLApplication::getAssertionConsumerServicesByBinding(const XMLCh* binding) const
+{
+ ACSBindingMap::const_iterator i=m_acsBindingMap.find(binding);
+ if (i!=m_acsBindingMap.end())
+ return i->second;
+ return m_base ? m_base->getAssertionConsumerServicesByBinding(binding) : g_noHandlers;
+}
+
+const Handler* XMLApplication::getHandler(const char* path) const
+{
+ string wrap(path);
+ wrap = wrap.substr(0,wrap.find(';'));
+ map<string,const Handler*>::const_iterator i=m_handlerMap.find(wrap.substr(0,wrap.find('?')));
+ if (i!=m_handlerMap.end())
+ return i->second;
+ return m_base ? m_base->getHandler(path) : NULL;
+}
+
+void XMLApplication::getHandlers(vector<const Handler*>& handlers) const
+{
+ handlers.insert(handlers.end(), m_handlers.begin(), m_handlers.end());
+ if (m_base) {
+ for (map<string,const Handler*>::const_iterator h = m_base->m_handlerMap.begin(); h != m_base->m_handlerMap.end(); ++h) {
+ if (m_handlerMap.count(h->first) == 0)
+ handlers.push_back(h->second);
+ }
+ }
+}
+
+#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE
+short
+#else
+DOMNodeFilter::FilterAction
+#endif
+XMLConfigImpl::acceptNode(const DOMNode* node) const
+{
+ if (!XMLString::equals(node->getNamespaceURI(),shibspconstants::SHIB2SPCONFIG_NS))
+ return FILTER_ACCEPT;
+ const XMLCh* name=node->getLocalName();
+ if (XMLString::equals(name,ApplicationDefaults) ||
+ XMLString::equals(name,_ArtifactMap) ||
+ XMLString::equals(name,_Extensions) ||
+ XMLString::equals(name,Listener) ||
+ XMLString::equals(name,_RequestMapper) ||
+ XMLString::equals(name,_ReplayCache) ||
+ XMLString::equals(name,SecurityPolicies) ||
+ XMLString::equals(name,_SessionCache) ||
+ XMLString::equals(name,Site) ||
+ XMLString::equals(name,_StorageService) ||
+ XMLString::equals(name,TCPListener) ||
+ XMLString::equals(name,TransportOption) ||
+ XMLString::equals(name,UnixListener))
+ return FILTER_REJECT;
+
+ return FILTER_ACCEPT;
+}
+
+void XMLConfigImpl::doExtensions(const DOMElement* e, const char* label, Category& log)
+{
+ const DOMElement* exts=XMLHelper::getFirstChildElement(e,_Extensions);
+ if (exts) {
+ exts=XMLHelper::getFirstChildElement(exts,Library);
+ while (exts) {
+ auto_ptr_char path(exts->getAttributeNS(NULL,_path));
+ try {
+ if (path.get()) {
+ if (!XMLToolingConfig::getConfig().load_library(path.get(),(void*)exts))
+ throw ConfigurationException("XMLToolingConfig::load_library failed.");
+ log.debug("loaded %s extension library (%s)", label, path.get());
+ }
+ }
+ catch (exception& e) {
+ const XMLCh* fatal=exts->getAttributeNS(NULL,_fatal);
+ if (fatal && (*fatal==chLatin_t || *fatal==chDigit_1)) {
+ log.fatal("unable to load mandatory %s extension library %s: %s", label, path.get(), e.what());
+ throw;
+ }
+ else {
+ log.crit("unable to load optional %s extension library %s: %s", label, path.get(), e.what());
+ }
+ }
+ exts=XMLHelper::getNextSiblingElement(exts,Library);
+ }
+ }
+}
+
+XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer, Category& log)
+ : m_requestMapper(NULL), m_outer(outer), m_document(NULL)
+{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("XMLConfigImpl");
+#endif
+
+ try {
+ SPConfig& conf=SPConfig::getConfig();
+#ifndef SHIBSP_LITE
+ SAMLConfig& samlConf=SAMLConfig::getConfig();
+#endif
+ XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();
+ const DOMElement* SHAR=XMLHelper::getFirstChildElement(e,OutOfProcess);
+ const DOMElement* SHIRE=XMLHelper::getFirstChildElement(e,InProcess);
+
+ // Initialize log4cpp manually in order to redirect log messages as soon as possible.
+ if (conf.isEnabled(SPConfig::Logging)) {
+ const XMLCh* logconf=NULL;
+ if (conf.isEnabled(SPConfig::OutOfProcess))
+ logconf=SHAR->getAttributeNS(NULL,logger);
+ else if (conf.isEnabled(SPConfig::InProcess))
+ logconf=SHIRE->getAttributeNS(NULL,logger);
+ if (!logconf || !*logconf)
+ logconf=e->getAttributeNS(NULL,logger);
+ if (logconf && *logconf) {
+ auto_ptr_char logpath(logconf);
+ log.debug("loading new logging configuration from (%s), check log destination for status of configuration",logpath.get());
+ if (!XMLToolingConfig::getConfig().log_config(logpath.get()))
+ log.crit("failed to load new logging configuration from (%s)", logpath.get());
+ }
+
+#ifndef SHIBSP_LITE
+ if (first)
+ m_outer->m_tranLog = new TransactionLog();
+#endif
+ }
+
+ // Re-log library versions now that logging is set up.
+ log.info("Shibboleth SP Version %s", PACKAGE_VERSION);
+#ifndef SHIBSP_LITE
+ log.info(
+ "Library versions: Xerces-C %s, XML-Security-C %s, XMLTooling-C %s, OpenSAML-C %s, Shibboleth %s",
+ XERCES_FULLVERSIONDOT, XSEC_FULLVERSIONDOT, XMLTOOLING_FULLVERSIONDOT, OPENSAML_FULLVERSIONDOT, SHIBSP_FULLVERSIONDOT
+ );
+#else
+ log.info(
+ "Library versions: Xerces-C %s, XMLTooling-C %s, Shibboleth %s",
+ XERCES_FULLVERSIONDOT, XMLTOOLING_FULLVERSIONDOT, SHIBSP_FULLVERSIONDOT
+ );
+#endif
+
+ // First load any property sets.
+ load(e,NULL,this);
+
+ const DOMElement* child;
+ string plugtype;
+
+ // Much of the processing can only occur on the first instantiation.
+ if (first) {
+ // Set clock skew.
+ pair<bool,unsigned int> skew=getUnsignedInt("clockSkew");
+ if (skew.first)
+ xmlConf.clock_skew_secs=min(skew.second,(60*60*24*7*28));
+
+ pair<bool,const char*> unsafe = getString("unsafeChars");
+ if (unsafe.first)
+ TemplateEngine::unsafe_chars = unsafe.second;
+
+ unsafe = getString("allowedSchemes");
+ if (unsafe.first) {
+ HTTPResponse::getAllowedSchemes().clear();
+ string schemes=unsafe.second;
+ unsigned int j_sch=0;
+ for (unsigned int i_sch=0; i_sch < schemes.length(); i_sch++) {
+ if (schemes.at(i_sch)==' ') {
+ HTTPResponse::getAllowedSchemes().push_back(schemes.substr(j_sch, i_sch-j_sch));
+ j_sch = i_sch + 1;
+ }
+ }
+ HTTPResponse::getAllowedSchemes().push_back(schemes.substr(j_sch, schemes.length()-j_sch));
+ }
+
+ // Extensions
+ doExtensions(e, "global", log);
+ if (conf.isEnabled(SPConfig::OutOfProcess))
+ doExtensions(SHAR, "out of process", log);
+
+ if (conf.isEnabled(SPConfig::InProcess))
+ doExtensions(SHIRE, "in process", log);
+
+ // Instantiate the ListenerService and SessionCache objects.
+ if (conf.isEnabled(SPConfig::Listener)) {
+ child=XMLHelper::getFirstChildElement(e,UnixListener);
+ if (child)
+ plugtype=UNIX_LISTENER_SERVICE;
+ else {
+ child=XMLHelper::getFirstChildElement(e,TCPListener);
+ if (child)
+ plugtype=TCP_LISTENER_SERVICE;
+ else {
+ child=XMLHelper::getFirstChildElement(e,Listener);
+ if (child) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ if (type.get())
+ plugtype=type.get();
+ }
+ }
+ }
+ if (child) {
+ log.info("building ListenerService of type %s...", plugtype.c_str());
+ m_outer->m_listener = conf.ListenerServiceManager.newPlugin(plugtype.c_str(), child);
+ }
+ else {
+ log.fatal("can't build ListenerService, missing conf:Listener element?");
+ throw ConfigurationException("Can't build ListenerService, missing conf:Listener element?");
+ }
+ }
+
+#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));
+ }
+#endif
+
+ if (conf.isEnabled(SPConfig::Caching)) {
+ if (conf.isEnabled(SPConfig::OutOfProcess)) {
+#ifndef SHIBSP_LITE
+ // First build any StorageServices.
+ child=XMLHelper::getFirstChildElement(e,_StorageService);
+ while (child) {
+ auto_ptr_char id(child->getAttributeNS(NULL,_id));
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ try {
+ log.info("building StorageService (%s) of type %s...", id.get(), type.get());
+ m_outer->m_storage[id.get()] = xmlConf.StorageServiceManager.newPlugin(type.get(),child);
+ }
+ catch (exception& ex) {
+ log.crit("failed to instantiate StorageService (%s): %s", id.get(), ex.what());
+ }
+ child=XMLHelper::getNextSiblingElement(child,_StorageService);
+ }
+
+ // Replay cache.
+ StorageService* replaySS=NULL;
+ child=XMLHelper::getFirstChildElement(e,_ReplayCache);
+ if (child) {
+ auto_ptr_char ssid(child->getAttributeNS(NULL,_StorageService));
+ if (ssid.get() && *ssid.get()) {
+ if (m_outer->m_storage.count(ssid.get()))
+ replaySS = m_outer->m_storage[ssid.get()];
+ if (replaySS)
+ log.info("building ReplayCache on top of StorageService (%s)...", ssid.get());
+ else
+ log.warn("unable to locate StorageService (%s) for ReplayCache, using dedicated in-memory instance", ssid.get());
+ }
+ xmlConf.setReplayCache(new ReplayCache(replaySS));
+ }
+ else {
+ log.warn("no ReplayCache built, missing conf:ReplayCache element?");
+ }
+
+ // ArtifactMap
+ child=XMLHelper::getFirstChildElement(e,_ArtifactMap);
+ if (child) {
+ auto_ptr_char ssid(child->getAttributeNS(NULL,_StorageService));
+ if (ssid.get() && *ssid.get() && m_outer->m_storage.count(ssid.get())) {
+ log.info("building ArtifactMap on top of StorageService (%s)...", ssid.get());
+ samlConf.setArtifactMap(new ArtifactMap(child, m_outer->m_storage[ssid.get()]));
+ }
+ }
+ if (samlConf.getArtifactMap()==NULL) {
+ log.info("building in-memory ArtifactMap...");
+ samlConf.setArtifactMap(new ArtifactMap(child));
+ }
+#endif
+ }
+ child=XMLHelper::getFirstChildElement(e,_SessionCache);
+ if (child) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ log.info("building SessionCache of type %s...",type.get());
+ m_outer->m_sessionCache=conf.SessionCacheManager.newPlugin(type.get(), child);
+ }
+ else {
+ log.fatal("can't build SessionCache, missing conf:SessionCache element?");
+ throw ConfigurationException("Can't build SessionCache, missing conf:SessionCache element?");
+ }
+ }
+ } // end of first-time-only stuff
+
+ // Back to the fully dynamic stuff...next up is the RequestMapper.
+ if (conf.isEnabled(SPConfig::RequestMapping)) {
+ child=XMLHelper::getFirstChildElement(e,_RequestMapper);
+ if (child) {
+ auto_ptr_char type(child->getAttributeNS(NULL,_type));
+ log.info("building RequestMapper of type %s...",type.get());
+ m_requestMapper=conf.RequestMapperManager.newPlugin(type.get(),child);
+ }
+ else {
+ log.fatal("can't build RequestMapper, missing conf:RequestMapper element?");
+ throw ConfigurationException("Can't build RequestMapper, missing conf:RequestMapper element?");
+ }
+ }
+
+#ifndef SHIBSP_LITE
+ // Load security policies.
+ child = XMLHelper::getLastChildElement(e,SecurityPolicies);
+ if (child) {
+ PolicyNodeFilter filter;
+ child = XMLHelper::getFirstChildElement(child,Policy);
+ while (child) {
+ auto_ptr_char id(child->getAttributeNS(NULL,_id));
+ pair< PropertySet*,vector<const SecurityPolicyRule*> >& rules = m_policyMap[id.get()];
+ rules.first = NULL;
+ auto_ptr<DOMPropertySet> settings(new DOMPropertySet());
+ settings->load(child, NULL, &filter);
+ rules.first = settings.release();
+
+ // Process PolicyRule elements.
+ const DOMElement* rule = XMLHelper::getFirstChildElement(child,PolicyRule);
+ while (rule) {
+ auto_ptr_char type(rule->getAttributeNS(NULL,_type));
+ try {
+ rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule));
+ }
+ catch (exception& ex) {
+ log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what());
+ }
+ rule = XMLHelper::getNextSiblingElement(rule,PolicyRule);
+ }
+
+ if (rules.second.size() == 0) {
+ // Process Rule elements.
+ log.warn("detected legacy Policy configuration, please convert to new PolicyRule syntax");
+ rule = XMLHelper::getFirstChildElement(child,Rule);
+ while (rule) {
+ auto_ptr_char type(rule->getAttributeNS(NULL,_type));
+ try {
+ rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(type.get(),rule));
+ }
+ catch (exception& ex) {
+ log.crit("error instantiating policy rule (%s) in policy (%s): %s", type.get(), id.get(), ex.what());
+ }
+ rule = XMLHelper::getNextSiblingElement(rule,Rule);
+ }
+
+ // Manually add a basic Conditions rule.
+ log.info("installing a default Conditions rule in policy (%s) for compatibility with legacy configuration", id.get());
+ rules.second.push_back(samlConf.SecurityPolicyRuleManager.newPlugin(CONDITIONS_POLICY_RULE, NULL));
+ }
+
+ child = XMLHelper::getNextSiblingElement(child,Policy);
+ }
+ }
+
+ // Process TransportOption elements.
+ child = XMLHelper::getLastChildElement(e,TransportOption);
+ while (child) {
+ if (child->hasChildNodes()) {
+ auto_ptr_char provider(child->getAttributeNS(NULL,_provider));
+ auto_ptr_char option(child->getAttributeNS(NULL,_option));
+ auto_ptr_char value(child->getFirstChild()->getNodeValue());
+ if (provider.get() && *provider.get() && option.get() && *option.get() && value.get() && *value.get()) {
+ m_transportOptions.push_back(make_pair(string(provider.get()), make_pair(string(option.get()), string(value.get()))));
+ }
+ }
+ child = XMLHelper::getPreviousSiblingElement(child,TransportOption);
+ }
+#endif
+
+ // Load the default application. This actually has a fixed ID of "default". ;-)
+ child=XMLHelper::getLastChildElement(e,ApplicationDefaults);
+ if (!child) {
+ 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,child);
+ m_appmap[defapp->getId()]=defapp;
+
+ // Load any overrides.
+ child = XMLHelper::getFirstChildElement(child,ApplicationOverride);
+ while (child) {
+ auto_ptr<XMLApplication> iapp(new XMLApplication(m_outer,child,defapp));
+ if (m_appmap.count(iapp->getId()))
+ log.crit("found conf:ApplicationOverride element with duplicate id attribute (%s), skipping it", iapp->getId());
+ else {
+ const char* iappid=iapp->getId();
+ m_appmap[iappid]=iapp.release();
+ }
+
+ child = XMLHelper::getNextSiblingElement(child,ApplicationOverride);
+ }
+ }
+ catch (exception&) {
+ cleanup();
+ throw;
+ }
+}
+
+XMLConfigImpl::~XMLConfigImpl()
+{
+ cleanup();
+}
+
+void XMLConfigImpl::cleanup()
+{
+ for_each(m_appmap.begin(),m_appmap.end(),cleanup_pair<string,Application>());
+ m_appmap.clear();
+#ifndef SHIBSP_LITE
+ for (map< string,pair<PropertySet*,vector<const SecurityPolicyRule*> > >::iterator i=m_policyMap.begin(); i!=m_policyMap.end(); ++i) {
+ delete i->second.first;
+ for_each(i->second.second.begin(), i->second.second.end(), xmltooling::cleanup<SecurityPolicyRule>());
+ }
+ m_policyMap.clear();
+#endif
+ delete m_requestMapper;
+ m_requestMapper = NULL;
+ if (m_document)
+ m_document->release();
+ m_document = NULL;
+}
+
+#ifndef SHIBSP_LITE
+void XMLConfig::receive(DDF& in, ostream& out)
+{
+ if (!strcmp(in.name(), "get::RelayState")) {
+ const char* id = in["id"].string();
+ const char* key = in["key"].string();
+ if (!id || !key)
+ throw ListenerException("Required parameters missing for RelayState recovery.");
+
+ string relayState;
+ StorageService* storage = getStorageService(id);
+ if (storage) {
+ if (storage->readString("RelayState",key,&relayState)>0) {
+ if (in["clear"].integer())
+ storage->deleteString("RelayState",key);
+ }
+ }
+ else {
+ Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(
+ "Storage-backed RelayState with invalid StorageService ID (%s)", id
+ );
+ }
+
+ // Repack for return to caller.
+ DDF ret=DDF(NULL).unsafe_string(relayState.c_str());
+ DDFJanitor jret(ret);
+ out << ret;
+ }
+ else if (!strcmp(in.name(), "set::RelayState")) {
+ const char* id = in["id"].string();
+ const char* value = in["value"].string();
+ if (!id || !value)
+ throw ListenerException("Required parameters missing for RelayState creation.");
+
+ string rsKey;
+ StorageService* storage = getStorageService(id);
+ if (storage) {
+ SAMLConfig::getConfig().generateRandomBytes(rsKey,20);
+ rsKey = SAMLArtifact::toHex(rsKey);
+ storage->createString("RelayState", rsKey.c_str(), value, time(NULL) + 600);
+ }
+ else {
+ Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(
+ "Storage-backed RelayState with invalid StorageService ID (%s)", id
+ );
+ }
+
+ // Repack for return to caller.
+ DDF ret=DDF(NULL).string(rsKey.c_str());
+ DDFJanitor jret(ret);
+ out << ret;
+ }
+ else if (!strcmp(in.name(), "get::PostData")) {
+ const char* id = in["id"].string();
+ const char* key = in["key"].string();
+ if (!id || !key)
+ throw ListenerException("Required parameters missing for PostData recovery.");
+
+ string postData;
+ StorageService* storage = getStorageService(id);
+ if (storage) {
+ if (storage->readString("PostData",key,&postData) > 0) {
+ storage->deleteString("PostData",key);
+ }
+ }
+ else {
+ Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(
+ "Storage-backed PostData with invalid StorageService ID (%s)", id
+ );
+ }
+ // If the data's empty, we'll send nothing back.
+ // If not, we don't need to round trip it, just send back the serialized DDF list.
+ if (postData.empty()) {
+ DDF ret(NULL);
+ DDFJanitor jret(ret);
+ out << ret;
+ }
+ else {
+ out << postData;
+ }
+ }
+ else if (!strcmp(in.name(), "set::PostData")) {
+ const char* id = in["id"].string();
+ if (!id || !in["parameters"].islist())
+ throw ListenerException("Required parameters missing for PostData creation.");
+
+ string rsKey;
+ StorageService* storage = getStorageService(id);
+ if (storage) {
+ SAMLConfig::getConfig().generateRandomBytes(rsKey,20);
+ rsKey = SAMLArtifact::toHex(rsKey);
+ ostringstream params;
+ params << in["parameters"];
+ storage->createString("PostData", rsKey.c_str(), params.str().c_str(), time(NULL) + 600);
+ }
+ else {
+ Category::getInstance(SHIBSP_LOGCAT".ServiceProvider").error(
+ "Storage-backed PostData with invalid StorageService ID (%s)", id
+ );
+ }
+
+ // Repack for return to caller.
+ DDF ret=DDF(NULL).string(rsKey.c_str());
+ DDFJanitor jret(ret);
+ out << ret;
+ }
+}
+#endif
+
+pair<bool,DOMElement*> XMLConfig::load()
+{
+ // Load from source using base class.
+ pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
+
+ // If we own it, wrap it.
+ XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL);
+
+ XMLConfigImpl* impl = new XMLConfigImpl(raw.second,(m_impl==NULL),this,m_log);
+
+ // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
+ impl->setDocument(docjanitor.release());
+
+ delete m_impl;
+ m_impl = impl;
+
+ return make_pair(false,(DOMElement*)NULL);
+}
#define __shibsp_paths_h__
/** Default schema catalogs. */
-#define SHIBSP_SCHEMAS "/opt/shibboleth-sp/share/xml/xmltooling/catalog.xml:/opt/shibboleth-sp/share/xml/opensaml/saml20-catalog.xml:/opt/shibboleth-sp/share/xml/opensaml/saml11-catalog.xml:/opt/shibboleth-sp/share/xml/shibboleth/catalog.xml"
+#define SHIBSP_SCHEMAS "/opt/shibboleth-sp/share/xml/xmltooling/catalog.xml:/usr/share/xml/opensaml/saml20-catalog.xml:/usr/share/xml/opensaml/saml11-catalog.xml:/opt/shibboleth-sp/share/xml/shibboleth/catalog.xml"
/** Default name of SP configuration file. */
#define SHIBSP_CONFIG "shibboleth2.xml"
//\r
\r
VS_VERSION_INFO VERSIONINFO\r
- FILEVERSION 1,3,0,0\r
- PRODUCTVERSION 2,3,0,0\r
+ FILEVERSION 1,3,1,0\r
+ PRODUCTVERSION 2,3,1,0\r
FILEFLAGSMASK 0x3fL\r
#ifdef _DEBUG\r
FILEFLAGS 0x1L\r
#endif\r
#endif\r
VALUE "PrivateBuild", "\0"\r
- VALUE "ProductName", "Shibboleth 2.3\0"\r
- VALUE "ProductVersion", "2, 3, 0, 0\0"\r
+ VALUE "ProductName", "Shibboleth 2.3.1\0"\r
+ VALUE "ProductVersion", "2, 3, 1, 0\0"\r
VALUE "SpecialBuild", "\0"\r
END\r
END\r
#define SHIBSP_VERSION_MAJOR 1
#define SHIBSP_VERSION_MINOR 3
-#define SHIBSP_VERSION_REVISION 0
+#define SHIBSP_VERSION_REVISION 1
/** DO NOT MODIFY BELOW THIS LINE */