[cpp-sp] 03/13: Imported Upstream version 2.3.1+dfsg
Scott Cantor
cantor.2 at osu.edu
Thu Dec 20 19:43:44 EST 2018
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to annotated tag debian/2.3.1+dfsg-1
in repository cpp-sp.
View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=fb303a3a93c0edc6dffa170e5d7440289532140e
commit fb303a3a93c0edc6dffa170e5d7440289532140e
Author: Russ Allbery <rra at debian.org>
AuthorDate: Thu Dec 17 16:52:52 2009 -0800
Imported Upstream version 2.3.1+dfsg
---
Portfile | 14 +-
Portfile.in | 12 +
adfs/adfs.rc | 10 +-
apache/mod_shib_13.rc | 10 +-
apache/mod_shib_20.rc | 10 +-
apache/mod_shib_22.rc | 10 +-
config_win32.h | 6 +-
configs/shibboleth2.xml | 2 +-
configs/shibd-redhat.in | 14 +-
configs/shibd-suse.in | 7 +-
configure | 20 +-
configure.ac | 4 +-
doc/README.txt | 2 +-
doc/RELEASE.txt | 6 +-
fastcgi/shibauthorizer.rc | 10 +-
fastcgi/shibresponder.rc | 10 +-
isapi_shib/isapi_shib.rc | 10 +-
memcache-store/memcache-store.rc | 10 +-
nsapi_shib/nsapi_shib.rc | 10 +-
odbc-store/odbc-store.rc | 10 +-
pkginfo | 2 +-
schemas/shibboleth-2.0-native-sp-config.xsd | 1450 ++++-----
shibboleth.spec | 7 +-
shibboleth.spec.in | 5 +-
shibd/shibd.rc | 10 +-
shibd/shibd_win32.cpp | 2 +-
shibsp/Makefile.am | 4 +-
shibsp/Makefile.in | 4 +-
.../resolver/impl/QueryAttributeResolver.cpp | 150 +-
.../impl/SimpleAggregationAttributeResolver.cpp | 121 +-
.../resolver/impl/XMLAttributeExtractor.cpp | 76 +-
shibsp/handler/impl/AbstractHandler.cpp | 1352 ++++----
shibsp/handler/impl/AssertionConsumerService.cpp | 29 +-
shibsp/handler/impl/SAML2ArtifactResolution.cpp | 61 +-
shibsp/handler/impl/SAML2Logout.cpp | 2 +-
shibsp/handler/impl/SAML2LogoutInitiator.cpp | 80 +-
shibsp/handler/impl/SAMLDSSessionInitiator.cpp | 35 +-
shibsp/impl/XMLServiceProvider.cpp | 3417 ++++++++++----------
shibsp/paths.h | 2 +-
shibsp/shibsp.rc | 8 +-
shibsp/version.h | 2 +-
41 files changed, 3649 insertions(+), 3357 deletions(-)
diff --git a/Portfile b/Portfile
index 6a37645..7e6f657 100644
--- a/Portfile
+++ b/Portfile
@@ -1,7 +1,7 @@
PortSystem 1.0
name shibboleth
-version 2.3
+version 2.3.1
categories security www shibboleth
maintainers scantor snc
description Shibboleth Native Service Provider
@@ -60,6 +60,18 @@ post-destroot {
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
diff --git a/Portfile.in b/Portfile.in
index 2408f92..3adab00 100644
--- a/Portfile.in
+++ b/Portfile.in
@@ -60,6 +60,18 @@ post-destroot {
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
diff --git a/adfs/adfs.rc b/adfs/adfs.rc
index 9e46d04..c3a45fe 100644
--- a/adfs/adfs.rc
+++ b/adfs/adfs.rc
@@ -54,8 +54,8 @@ 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
@@ -73,7 +73,7 @@ BEGIN
VALUE "Comments", "\0"
VALUE "CompanyName", "Internet2\0"
VALUE "FileDescription", "Shibboleth ADFSv1 Plugin\0"
- VALUE "FileVersion", "2, 3, 0, 0\0"
+ VALUE "FileVersion", "2, 3, 1, 0\0"
#ifdef SHIBSP_LITE
VALUE "InternalName", "adfs-lite\0"
#else
@@ -87,8 +87,8 @@ BEGIN
VALUE "OriginalFilename", "adfs.so\0"
#endif
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
diff --git a/apache/mod_shib_13.rc b/apache/mod_shib_13.rc
index 1ef85b6..2bff418 100644
--- a/apache/mod_shib_13.rc
+++ b/apache/mod_shib_13.rc
@@ -28,8 +28,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
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
@@ -47,14 +47,14 @@ BEGIN
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
diff --git a/apache/mod_shib_20.rc b/apache/mod_shib_20.rc
index 79dd05e..bffa8f8 100644
--- a/apache/mod_shib_20.rc
+++ b/apache/mod_shib_20.rc
@@ -28,8 +28,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
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
@@ -47,14 +47,14 @@ BEGIN
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
diff --git a/apache/mod_shib_22.rc b/apache/mod_shib_22.rc
index 24182bd..2864f5c 100644
--- a/apache/mod_shib_22.rc
+++ b/apache/mod_shib_22.rc
@@ -28,8 +28,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
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
@@ -47,14 +47,14 @@ BEGIN
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
diff --git a/config_win32.h b/config_win32.h
index 9778d65..ec6787b 100644
--- a/config_win32.h
+++ b/config_win32.h
@@ -117,13 +117,13 @@
#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. */
@@ -136,7 +136,7 @@
/* #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 */
diff --git a/configs/shibboleth2.xml b/configs/shibboleth2.xml
index d935c7f..8ed5938 100644
--- a/configs/shibboleth2.xml
+++ b/configs/shibboleth2.xml
@@ -227,7 +227,7 @@
<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"/>
diff --git a/configs/shibd-redhat.in b/configs/shibd-redhat.in
index fd80ee5..a526cbd 100644
--- a/configs/shibd-redhat.in
+++ b/configs/shibd-redhat.in
@@ -14,7 +14,7 @@ shibd="@-PREFIX-@/sbin/shibd"
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() {
@@ -37,11 +37,7 @@ 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
@@ -51,11 +47,7 @@ start() {
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
diff --git a/configs/shibd-suse.in b/configs/shibd-suse.in
index 62a1619..0be3acf 100644
--- a/configs/shibd-suse.in
+++ b/configs/shibd-suse.in
@@ -7,9 +7,12 @@
#
### 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.
diff --git a/configure b/configure
index 1471e1e..2f64d93 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /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 at internet2.edu>.
#
@@ -423,8 +423,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# 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 at internet2.edu'
# Factoring default headers for most tests.
@@ -957,7 +957,7 @@ if test "$ac_init_help" = "long"; then
# 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]...
@@ -1023,7 +1023,7 @@ fi
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
@@ -1199,7 +1199,7 @@ fi
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.
@@ -1213,7 +1213,7 @@ cat >&5 <<_ACEOF
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 $@
@@ -1858,7 +1858,7 @@ fi
# Define the identity of the package.
PACKAGE=shibboleth
- VERSION=2.3
+ VERSION=2.3.1
cat >>confdefs.h <<_ACEOF
@@ -27092,7 +27092,7 @@ _ASBOX
} >&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
@@ -27155,7 +27155,7 @@ _ACEOF
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'`\\"
diff --git a/configure.ac b/configure.ac
index 7265638..0326aba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,7 @@
AC_PREREQ([2.50])
-AC_INIT([shibboleth], [2.3], [shibboleth-users at internet2.edu], [shibboleth])
+AC_INIT([shibboleth], [2.3.1], [shibboleth-users at 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)
diff --git a/doc/README.txt b/doc/README.txt
index e21e018..3d8f7a0 100644
--- a/doc/README.txt
+++ b/doc/README.txt
@@ -1,4 +1,4 @@
-Version 2.3
+Version 2.3.1
Welcome to Internet2's Shibboleth
diff --git a/doc/RELEASE.txt b/doc/RELEASE.txt
index 51a97cc..19132fc 100644
--- a/doc/RELEASE.txt
+++ b/doc/RELEASE.txt
@@ -1,14 +1,14 @@
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
diff --git a/fastcgi/shibauthorizer.rc b/fastcgi/shibauthorizer.rc
index 37258c9..e1f7b95 100644
--- a/fastcgi/shibauthorizer.rc
+++ b/fastcgi/shibauthorizer.rc
@@ -28,8 +28,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
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
@@ -47,14 +47,14 @@ BEGIN
VALUE "Comments", "\0"
VALUE "CompanyName", "Internet2\0"
VALUE "FileDescription", "Shibboleth FastCGI Authorizer\0"
- VALUE "FileVersion", "2, 3, 0, 0\0"
+ VALUE "FileVersion", "2, 3, 1, 0\0"
VALUE "InternalName", "shibauthorizer\0"
VALUE "LegalCopyright", "Copyright � 2009 Internet2\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "shibauthorizer.exe\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
diff --git a/fastcgi/shibresponder.rc b/fastcgi/shibresponder.rc
index f95c347..c41b96c 100644
--- a/fastcgi/shibresponder.rc
+++ b/fastcgi/shibresponder.rc
@@ -28,8 +28,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
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
@@ -47,14 +47,14 @@ BEGIN
VALUE "Comments", "\0"
VALUE "CompanyName", "Internet2\0"
VALUE "FileDescription", "Shibboleth FastCGI Responder\0"
- VALUE "FileVersion", "2, 3, 0, 0\0"
+ VALUE "FileVersion", "2, 3, 1, 0\0"
VALUE "InternalName", "shibresponder\0"
VALUE "LegalCopyright", "Copyright � 2009 Internet2\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "shibresponder.exe\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
diff --git a/isapi_shib/isapi_shib.rc b/isapi_shib/isapi_shib.rc
index 116438f..63c0d2f 100644
--- a/isapi_shib/isapi_shib.rc
+++ b/isapi_shib/isapi_shib.rc
@@ -28,8 +28,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
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
@@ -47,14 +47,14 @@ BEGIN
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
diff --git a/memcache-store/memcache-store.rc b/memcache-store/memcache-store.rc
index 5edb5b1..fb78af6 100644
--- a/memcache-store/memcache-store.rc
+++ b/memcache-store/memcache-store.rc
@@ -53,8 +53,8 @@ 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
@@ -71,12 +71,12 @@ BEGIN
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"
diff --git a/nsapi_shib/nsapi_shib.rc b/nsapi_shib/nsapi_shib.rc
index a551403..c4004f5 100644
--- a/nsapi_shib/nsapi_shib.rc
+++ b/nsapi_shib/nsapi_shib.rc
@@ -28,8 +28,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
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
@@ -47,14 +47,14 @@ BEGIN
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
diff --git a/odbc-store/odbc-store.rc b/odbc-store/odbc-store.rc
index 0190222..bfcbae5 100644
--- a/odbc-store/odbc-store.rc
+++ b/odbc-store/odbc-store.rc
@@ -53,8 +53,8 @@ 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
@@ -71,12 +71,12 @@ BEGIN
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"
diff --git a/pkginfo b/pkginfo
index 6184208..8511d13 100644
--- a/pkginfo
+++ b/pkginfo
@@ -1,5 +1,5 @@
PKG=SHIBsp
-VERSION=2.3
+VERSION=2.3.1
BASEDIR=/
NAME=shibboleth-sp
CATEGORY=application,security
diff --git a/schemas/shibboleth-2.0-native-sp-config.xsd b/schemas/shibboleth-2.0-native-sp-config.xsd
index 411b037..7b54585 100644
--- a/schemas/shibboleth-2.0-native-sp-config.xsd
+++ b/schemas/shibboleth-2.0-native-sp-config.xsd
@@ -1,725 +1,725 @@
-<?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>
+<?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>
diff --git a/shibboleth.spec b/shibboleth.spec
index b5bb7c8..81c64fa 100644
--- a/shibboleth.spec
+++ b/shibboleth.spec
@@ -1,6 +1,6 @@
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
@@ -244,6 +244,9 @@ cd /
%doc %{pkgdocdir}/api
%changelog
+* Mon Nov 23 2009 Scott Cantor <cantor.2 at osu.edu> - 2.3.1-1
+- Reset revision for 2.3.1 release
+
* Wed Aug 19 2009 Scott Cantor <cantor.2 at osu.edu> - 2.2.1-2
- SuSE init script changes
- Restart Apache on removal, not just upgrade
diff --git a/shibboleth.spec.in b/shibboleth.spec.in
index a6c612f..21cadbc 100644
--- a/shibboleth.spec.in
+++ b/shibboleth.spec.in
@@ -1,6 +1,6 @@
Name: @PACKAGE@
Version: @PACKAGE_VERSION@
-Release: 2
+Release: 1
Summary: Open source system for attribute-based Web SSO
Group: System Environment/Libraries
Vendor: Internet2
@@ -244,6 +244,9 @@ cd /
%doc %{pkgdocdir}/api
%changelog
+* Mon Nov 23 2009 Scott Cantor <cantor.2 at osu.edu> - 2.3.1-1
+- Reset revision for 2.3.1 release
+
* Wed Aug 19 2009 Scott Cantor <cantor.2 at osu.edu> - 2.2.1-2
- SuSE init script changes
- Restart Apache on removal, not just upgrade
diff --git a/shibd/shibd.rc b/shibd/shibd.rc
index 264fc0c..a2ce8b1 100644
--- a/shibd/shibd.rc
+++ b/shibd/shibd.rc
@@ -28,8 +28,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
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
@@ -47,14 +47,14 @@ BEGIN
VALUE "Comments", "\0"
VALUE "CompanyName", "Internet2\0"
VALUE "FileDescription", "Shibboleth Daemon Service\0"
- VALUE "FileVersion", "2, 3, 0, 0\0"
+ VALUE "FileVersion", "2, 3, 1, 0\0"
VALUE "InternalName", "shibd\0"
VALUE "LegalCopyright", "Copyright � 2009 Internet2\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "shibd.exe\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
diff --git a/shibd/shibd_win32.cpp b/shibd/shibd_win32.cpp
index 7a9a602..7069a91 100644
--- a/shibd/shibd_win32.cpp
+++ b/shibd/shibd_win32.cpp
@@ -360,7 +360,7 @@ void CmdInstallService(LPCSTR name)
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)
diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am
index dea7654..213f834 100644
--- a/shibsp/Makefile.am
+++ b/shibsp/Makefile.am
@@ -206,8 +206,8 @@ libshibsp_la_SOURCES = \
# 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@
diff --git a/shibsp/Makefile.in b/shibsp/Makefile.in
index 6e3bd9c..8040d75 100644
--- a/shibsp/Makefile.in
+++ b/shibsp/Makefile.in
@@ -598,8 +598,8 @@ libshibsp_la_SOURCES = \
# 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@
diff --git a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp
index 2c9c8fc..f080088 100644
--- a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp
+++ b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp
@@ -205,6 +205,7 @@ namespace shibsp {
Category& m_log;
string m_policyId;
+ bool m_subjectMatch;
vector<AttributeDesignator*> m_SAML1Designators;
vector<saml2::Attribute*> m_SAML2Designators;
};
@@ -214,20 +215,24 @@ namespace shibsp {
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) {
@@ -366,7 +371,7 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const
}
newtoken->detach();
- wrapper.release();
+ wrapper.release(); // detach blows away the Response
ctx.getResolvedAssertions().push_back(newtoken);
// Finally, extract and filter the result.
@@ -374,7 +379,24 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const
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();
@@ -437,7 +459,6 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) 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(
*ctx.getNameID(),
*(application.getMetadataProvider()),
@@ -473,33 +494,72 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const
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;
}
@@ -515,14 +575,60 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const
// 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.
@@ -571,9 +677,11 @@ void QueryResolver::resolveAttributes(ResolutionContext& ctx) const
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");
+ }
}
diff --git a/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp b/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp
index c4d3336..f3d9f79 100644
--- a/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp
+++ b/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp
@@ -178,6 +178,7 @@ namespace shibsp {
Category& m_log;
string m_policyId;
+ bool m_subjectMatch;
vector<string> m_attributeIds;
xstring m_format;
MetadataProvider* m_metadata;
@@ -197,12 +198,13 @@ namespace shibsp {
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");
@@ -214,6 +216,10 @@ SimpleAggregationResolver::SimpleAggregationResolver(const DOMElement* e)
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);
@@ -235,7 +241,6 @@ SimpleAggregationResolver::SimpleAggregationResolver(const DOMElement* e)
pid = e->getAttributeNS(NULL, format);
if (pid && *pid)
m_format = pid;
-
}
DOMElement* child = XMLHelper::getFirstChildElement(e, _MetadataProvider);
@@ -295,7 +300,6 @@ SimpleAggregationResolver::SimpleAggregationResolver(const DOMElement* e)
}
child = XMLHelper::getNextSiblingElement(child);
}
-
}
bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const char* entityID, const NameID* name) const
@@ -353,7 +357,6 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha
// 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()),
@@ -389,33 +392,73 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha
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;
}
@@ -431,14 +474,60 @@ bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const cha
// 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.
diff --git a/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp b/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp
index e77133a..9b9d98d 100644
--- a/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp
+++ b/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp
@@ -127,6 +127,20 @@ namespace shibsp {
) 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,
@@ -540,6 +554,32 @@ void XMLExtractorImpl::extractAttributes(
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,
@@ -792,6 +832,29 @@ void XMLExtractor::extractAttributes(
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);
@@ -799,10 +862,8 @@ void XMLExtractor::extractAttributes(
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);
@@ -814,11 +875,8 @@ void XMLExtractor::extractAttributes(
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;
}
diff --git a/shibsp/handler/impl/AbstractHandler.cpp b/shibsp/handler/impl/AbstractHandler.cpp
index ca7e3c2..d1c8d58 100644
--- a/shibsp/handler/impl/AbstractHandler.cpp
+++ b/shibsp/handler/impl/AbstractHandler.cpp
@@ -1,676 +1,676 @@
-/*
- * 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();
-}
+/*
+ * 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();
+}
diff --git a/shibsp/handler/impl/AssertionConsumerService.cpp b/shibsp/handler/impl/AssertionConsumerService.cpp
index f88ba99..f5d4e5e 100644
--- a/shibsp/handler/impl/AssertionConsumerService.cpp
+++ b/shibsp/handler/impl/AssertionConsumerService.cpp
@@ -234,25 +234,31 @@ const char* AssertionConsumerService::getType() const
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);
}
@@ -364,6 +370,9 @@ ResolutionContext* AssertionConsumerService::resolveAttributes(
}
}
}
+ else {
+ m_log.warn("no AttributeExtractor plugin installed, check log during startup");
+ }
try {
AttributeResolver* resolver = application.getAttributeResolver();
diff --git a/shibsp/handler/impl/SAML2ArtifactResolution.cpp b/shibsp/handler/impl/SAML2ArtifactResolution.cpp
index 6e609a3..bec9c01 100644
--- a/shibsp/handler/impl/SAML2ArtifactResolution.cpp
+++ b/shibsp/handler/impl/SAML2ArtifactResolution.cpp
@@ -75,19 +75,42 @@ namespace shibsp {
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;
@@ -285,7 +308,7 @@ pair<bool,long> SAML2ArtifactResolution::processMessage(const Application& appli
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");
@@ -296,7 +319,7 @@ pair<bool,long> SAML2ArtifactResolution::processMessage(const Application& appli
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");
@@ -315,9 +338,9 @@ pair<bool,long> SAML2ArtifactResolution::processMessage(const Application& appli
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);
@@ -325,21 +348,15 @@ pair<bool,long> SAML2ArtifactResolution::processMessage(const Application& appli
}
#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);
diff --git a/shibsp/handler/impl/SAML2Logout.cpp b/shibsp/handler/impl/SAML2Logout.cpp
index d6931ce..5f5379d 100644
--- a/shibsp/handler/impl/SAML2Logout.cpp
+++ b/shibsp/handler/impl/SAML2Logout.cpp
@@ -284,7 +284,7 @@ pair<bool,long> SAML2Logout::doRequest(const Application& application, const HTT
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 {
diff --git a/shibsp/handler/impl/SAML2LogoutInitiator.cpp b/shibsp/handler/impl/SAML2LogoutInitiator.cpp
index 24b8bfa..15d38c8 100644
--- a/shibsp/handler/impl/SAML2LogoutInitiator.cpp
+++ b/shibsp/handler/impl/SAML2LogoutInitiator.cpp
@@ -40,9 +40,6 @@
# 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;
@@ -361,24 +358,25 @@ pair<bool,long> SAML2LogoutInitiator::doRequest(
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) {
@@ -386,6 +384,7 @@ pair<bool,long> SAML2LogoutInitiator::doRequest(
session = NULL;
application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse);
}
+
return ret;
}
@@ -461,49 +460,6 @@ LogoutRequest* SAML2LogoutInitiator::buildRequest(
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();
}
diff --git a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp
index 1c0a7e3..85b65bb 100644
--- a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp
+++ b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp
@@ -73,18 +73,47 @@ namespace shibsp {
#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();
diff --git a/shibsp/impl/XMLServiceProvider.cpp b/shibsp/impl/XMLServiceProvider.cpp
index 05f3ef4..ab4dce6 100644
--- a/shibsp/impl/XMLServiceProvider.cpp
+++ b/shibsp/impl/XMLServiceProvider.cpp
@@ -1,1708 +1,1709 @@
-/*
- * 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.
-#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);
-}
+/*
+ * 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);
+}
diff --git a/shibsp/paths.h b/shibsp/paths.h
index 2f9ac99..473e06a 100644
--- a/shibsp/paths.h
+++ b/shibsp/paths.h
@@ -24,7 +24,7 @@
#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"
diff --git a/shibsp/shibsp.rc b/shibsp/shibsp.rc
index d69080c..c503f8b 100644
--- a/shibsp/shibsp.rc
+++ b/shibsp/shibsp.rc
@@ -28,8 +28,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,3,0,0
- PRODUCTVERSION 2,3,0,0
+ FILEVERSION 1,3,1,0
+ PRODUCTVERSION 2,3,1,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -81,8 +81,8 @@ BEGIN
#endif
#endif
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
diff --git a/shibsp/version.h b/shibsp/version.h
index e8707e4..80230ba 100644
--- a/shibsp/version.h
+++ b/shibsp/version.h
@@ -39,7 +39,7 @@
#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 */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list