From: Russ Allbery Date: Fri, 21 Aug 2009 17:32:58 +0000 (-0700) Subject: Imported Upstream version 2.2.1+dfsg X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fsp.git;a=commitdiff_plain;h=a5b1914f888d2ac8992cc4985d65e9d727aa8df4 Imported Upstream version 2.2.1+dfsg --- diff --git a/Makefile.am b/Makefile.am index 28448cf..96b8823 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,34 +13,18 @@ SUBDIRS = $(WANT_SUBDIRS) DIST_SUBDIRS = doc schemas configs shibsp shibd adfs util apache nsapi_shib fastcgi odbc-store memcache-store selinux if DX_COND_doc -all-local: shibboleth.spec pkginfo doxygen-doc -APIDOCS = doc/api +all-local: doxygen-doc else -all-local: shibboleth.spec pkginfo +all-local: endif -install-data-hook: - rm -rf $(DESTDIR)$(datadir)/doc/@PACKAGE@/api - cp -r doc/api $(DESTDIR)$(datadir)/doc/@PACKAGE@ - dist-hook: rm -rf `find $(distdir)/isapi_shib -name .svn` rm -rf `find $(distdir)/doc/api -name .svn` -shibboleth.spec: shibboleth.spec.in Makefile - rm -f $@.tmp - sed < $@.in > $@.tmp \ - -e 's:@-VERSION-@:${VERSION}:' - mv $@.tmp $@ - -pkginfo: pkginfo.in Makefile - rm -f $@.tmp - sed < $@.in > $@.tmp \ - -e 's:@-VERSION-@:${VERSION}:' - mv $@.tmp $@ +GENFILES = shibboleth.spec pkginfo Portfile EXTRA_DIST = $(DX_CONFIG) \ - $(APIDOCS) \ isapi_shib \ Shibboleth.sln \ libtool.m4 \ @@ -48,11 +32,7 @@ EXTRA_DIST = $(DX_CONFIG) \ acx_pthread.m4 \ depcomp \ config_win32.h \ - shibboleth.spec.in \ - shibboleth.spec \ - pkginfo.in \ - pkginfo \ + $(GENFILES) \ + $(GENFILES:%=%.in) \ depend \ postinstall - -DISTCLEANFILES = shibboleth.spec pkginfo diff --git a/Makefile.in b/Makefile.in index 7f86166..b296c95 100644 --- a/Makefile.in +++ b/Makefile.in @@ -66,9 +66,11 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ DIST_COMMON = $(am__configure_deps) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in $(srcdir)/config.h.in \ - $(srcdir)/doxygen.am $(top_srcdir)/configure config.guess \ - config.sub depcomp install-sh ltmain.sh missing + $(srcdir)/Makefile.in $(srcdir)/Portfile.in \ + $(srcdir)/config.h.in $(srcdir)/doxygen.am \ + $(srcdir)/pkginfo.in $(srcdir)/shibboleth.spec.in \ + $(top_srcdir)/configure config.guess config.sub depcomp \ + install-sh ltmain.sh missing subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/libtool.m4 \ @@ -80,7 +82,7 @@ am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno configure.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h -CONFIG_CLEAN_FILES = +CONFIG_CLEAN_FILES = shibboleth.spec pkginfo Portfile SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ @@ -328,9 +330,8 @@ ACLOCAL_AMFLAGS = -I . MOSTLYCLEANFILES = $(DX_CLEANFILES) SUBDIRS = $(WANT_SUBDIRS) DIST_SUBDIRS = doc schemas configs shibsp shibd adfs util apache nsapi_shib fastcgi odbc-store memcache-store selinux -@DX_COND_doc_TRUE@APIDOCS = doc/api +GENFILES = shibboleth.spec pkginfo Portfile EXTRA_DIST = $(DX_CONFIG) \ - $(APIDOCS) \ isapi_shib \ Shibboleth.sln \ libtool.m4 \ @@ -338,14 +339,11 @@ EXTRA_DIST = $(DX_CONFIG) \ acx_pthread.m4 \ depcomp \ config_win32.h \ - shibboleth.spec.in \ - shibboleth.spec \ - pkginfo.in \ - pkginfo \ + $(GENFILES) \ + $(GENFILES:%=%.in) \ depend \ postinstall -DISTCLEANFILES = shibboleth.spec pkginfo all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive @@ -400,6 +398,12 @@ $(srcdir)/config.h.in: $(am__configure_deps) distclean-hdr: -rm -f config.h stamp-h1 +shibboleth.spec: $(top_builddir)/config.status $(srcdir)/shibboleth.spec.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +pkginfo: $(top_builddir)/config.status $(srcdir)/pkginfo.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +Portfile: $(top_builddir)/config.status $(srcdir)/Portfile.in + cd $(top_builddir) && $(SHELL) ./config.status $@ mostlyclean-libtool: -rm -f *.lo @@ -546,7 +550,7 @@ distclean-tags: distdir: $(DISTFILES) $(am__remove_distdir) mkdir $(distdir) - $(mkdir_p) $(distdir)/doc + $(mkdir_p) $(distdir)/. @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ list='$(DISTFILES)'; for file in $$list; do \ @@ -714,7 +718,6 @@ clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) - -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @@ -740,8 +743,6 @@ info: info-recursive info-am: install-data-am: - @$(NORMAL_INSTALL) - $(MAKE) $(AM_MAKEFLAGS) install-data-hook install-exec-am: @@ -780,14 +781,13 @@ uninstall-info: uninstall-info-recursive distclean distclean-generic distclean-hdr distclean-libtool \ distclean-recursive distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am \ - install-data-hook install-exec install-exec-am install-info \ - install-info-am install-man install-strip installcheck \ - installcheck-am installdirs installdirs-am maintainer-clean \ - maintainer-clean-generic maintainer-clean-recursive \ - mostlyclean mostlyclean-generic mostlyclean-libtool \ - mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \ - uninstall uninstall-am uninstall-info-am + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-libtool mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am @DX_COND_doc_TRUE@@DX_COND_ps_TRUE@doxygen-ps: @DX_DOCDIR@/@PACKAGE@.ps @@ -836,28 +836,12 @@ uninstall-info: uninstall-info-recursive @DX_COND_doc_TRUE@ rm -rf @DX_DOCDIR@ @DX_COND_doc_TRUE@ $(DX_ENV) DX_INCLUDE=$(DX_INCLUDE) $(DX_DOXYGEN) $(srcdir)/$(DX_CONFIG) -@DX_COND_doc_TRUE@all-local: shibboleth.spec pkginfo doxygen-doc -@DX_COND_doc_FALSE@all-local: shibboleth.spec pkginfo - -install-data-hook: - rm -rf $(DESTDIR)$(datadir)/doc/@PACKAGE@/api - cp -r doc/api $(DESTDIR)$(datadir)/doc/@PACKAGE@ +@DX_COND_doc_TRUE@all-local: doxygen-doc +@DX_COND_doc_FALSE@all-local: dist-hook: rm -rf `find $(distdir)/isapi_shib -name .svn` rm -rf `find $(distdir)/doc/api -name .svn` - -shibboleth.spec: shibboleth.spec.in Makefile - rm -f $@.tmp - sed < $@.in > $@.tmp \ - -e 's:@-VERSION-@:${VERSION}:' - mv $@.tmp $@ - -pkginfo: pkginfo.in Makefile - rm -f $@.tmp - sed < $@.in > $@.tmp \ - -e 's:@-VERSION-@:${VERSION}:' - mv $@.tmp $@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/Portfile b/Portfile new file mode 100644 index 0000000..50c87a1 --- /dev/null +++ b/Portfile @@ -0,0 +1,69 @@ +PortSystem 1.0 + +name shibboleth +version 2.2.1 +categories security www shibboleth +maintainers scantor snc +description Shibboleth Native Service Provider +long_description Standards-based attribute-based Web SSO system +homepage http://shibboleth.internet2.edu/ +master_sites http://shibboleth.internet2.edu/downloads/${name}/cppsp/${version}/ +distname ${name}-sp-${version} +worksrcdir ${name}-${version} +checksums sha1 @CHECKSUM@ + +depends_lib port:opensaml \ + port:xmltooling \ + port:xercesc \ + port:xml-security-c \ + port:log4shib + +pre-fetch { + set status 0 + if {[catch {exec ${prefix}/bin/curl --version | grep SSL} results]} { + if {[lindex $::errorCode 0] eq "CHILDSTATUS"} { + set status [lindex $::errorCode 2] + } else { + set status [lindex $::errorCode 3] + } + } + if {${status} != 0} { + return -code error "\n + ${name} requires curl to be installed with SSL support. + Please deactivate your current curl installation and + install the proper version of curl: + sudo port deactivate curl + sudo port install curl +ssl\n" + } +} + +configure.args --with-xmltooling=${prefix} \ + --with-opensaml=${prefix} \ + --with-xerces=${prefix} \ + --with-xmlsec=${prefix} \ + --with-log4shib=${prefix} + +variant odbc { + depends_lib-append port:unixODBC + configure.args-append --enable-odbc +} + +destroot.keepdirs ${destroot}${prefix}/var/log/${name} ${destroot}${prefix}/var/log/httpd ${destroot}${prefix}/var/run/${name} + +destroot.args NOKEYGEN=1 + +post-destroot { + eval file delete [glob ${destroot}${prefix}/etc/${name}/*.logger] + eval file delete [glob ${destroot}${prefix}/etc/${name}/*.html] + eval file delete [glob ${destroot}${prefix}/etc/${name}/*.xml] + eval file delete [glob ${destroot}${prefix}/etc/${name}/shibd-*] + eval file delete [glob ${destroot}${prefix}/lib/${name}/*.la] +} + +startupitem.create yes +startupitem.name shibd +startupitem.executable ${prefix}/sbin/shibd -F -f -p ${prefix}/var/run/${name}/shibd.pid + +livecheck.check regex +livecheck.url http://shibboleth.internet2.edu/downloads/shibboleth/cppsp/latest/mac/ports/shibboleth/shibboleth/Portfile +livecheck.regex "version *(\\d+\\.\\d+(\\.\\d+)?)" diff --git a/Portfile.in b/Portfile.in new file mode 100644 index 0000000..2408f92 --- /dev/null +++ b/Portfile.in @@ -0,0 +1,69 @@ +PortSystem 1.0 + +name @PACKAGE@ +version @PACKAGE_VERSION@ +categories security www shibboleth +maintainers scantor snc +description Shibboleth Native Service Provider +long_description Standards-based attribute-based Web SSO system +homepage http://shibboleth.internet2.edu/ +master_sites http://shibboleth.internet2.edu/downloads/${name}/cppsp/${version}/ +distname ${name}-sp-${version} +worksrcdir ${name}-${version} +checksums sha1 @CHECKSUM@ + +depends_lib port:opensaml \ + port:xmltooling \ + port:xercesc \ + port:xml-security-c \ + port:log4shib + +pre-fetch { + set status 0 + if {[catch {exec ${prefix}/bin/curl --version | grep SSL} results]} { + if {[lindex $::errorCode 0] eq "CHILDSTATUS"} { + set status [lindex $::errorCode 2] + } else { + set status [lindex $::errorCode 3] + } + } + if {${status} != 0} { + return -code error "\n + ${name} requires curl to be installed with SSL support. + Please deactivate your current curl installation and + install the proper version of curl: + sudo port deactivate curl + sudo port install curl +ssl\n" + } +} + +configure.args --with-xmltooling=${prefix} \ + --with-opensaml=${prefix} \ + --with-xerces=${prefix} \ + --with-xmlsec=${prefix} \ + --with-log4shib=${prefix} + +variant odbc { + depends_lib-append port:unixODBC + configure.args-append --enable-odbc +} + +destroot.keepdirs ${destroot}${prefix}/var/log/${name} ${destroot}${prefix}/var/log/httpd ${destroot}${prefix}/var/run/${name} + +destroot.args NOKEYGEN=1 + +post-destroot { + eval file delete [glob ${destroot}${prefix}/etc/${name}/*.logger] + eval file delete [glob ${destroot}${prefix}/etc/${name}/*.html] + eval file delete [glob ${destroot}${prefix}/etc/${name}/*.xml] + eval file delete [glob ${destroot}${prefix}/etc/${name}/shibd-*] + eval file delete [glob ${destroot}${prefix}/lib/${name}/*.la] +} + +startupitem.create yes +startupitem.name shibd +startupitem.executable ${prefix}/sbin/shibd -F -f -p ${prefix}/var/run/${name}/shibd.pid + +livecheck.check regex +livecheck.url http://shibboleth.internet2.edu/downloads/shibboleth/cppsp/latest/mac/ports/shibboleth/shibboleth/Portfile +livecheck.regex "version *(\\d+\\.\\d+(\\.\\d+)?)" diff --git a/Shibboleth.sln b/Shibboleth.sln index 8988b06..1fd1708 100644 --- a/Shibboleth.sln +++ b/Shibboleth.sln @@ -1,160 +1,80 @@ -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server Modules", "Server Modules", "{26BA8F84-6E42-41FA-9B13-5D3F4B5B2050}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{96AE4FC9-45EF-4C18-9F3B-EDA439E26E4C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{FED80230-119E-4B2F-9F53-D2660A5F022B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fastcgi", "fastcgi", "{8E1AF2CF-24E1-4983-8681-394D89DF9AD2}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "isapi_shib", "isapi_shib\isapi_shib.vcproj", "{87C25D4E-8D19-4513-B0BA-BC668BC2DEE3}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_shib13", "apache\mod_shib13.vcproj", "{D243B43E-728E-4F32-BDFF-B3A897037C6D}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_shib20", "apache\mod_shib20.vcproj", "{68E9568B-476C-4289-B93C-893432378ADC}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsapi_shib", "nsapi_shib\nsapi_shib.vcproj", "{1396D80A-8672-4224-9B02-95F3F4207CDB}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mod_shib22", "apache\mod_shib22.vcproj", "{B44C0852-83B8-4FB2-A86E-097C9C8256D0}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server Modules", "Server Modules", "{26BA8F84-6E42-41FA-9B13-5D3F4B5B2050}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{96AE4FC9-45EF-4C18-9F3B-EDA439E26E4C}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{FED80230-119E-4B2F-9F53-D2660A5F022B}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "odbc-store", "odbc-store\odbc-store.vcproj", "{666A63A7-983F-4C19-8411-207F24305197}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shibsp", "shibsp\shibsp.vcproj", "{81F0F7A6-DC36-46EF-957F-F9E81D4403F6}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shibd", "shibd\shibd.vcproj", "{F13141B5-6C87-40BB-8D4E-5CC56EBB4C59}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F6} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F6} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shibsp-lite", "shibsp\shibsp-lite.vcproj", "{81F0F7A6-DC36-46EF-957F-F9E81D4403F7}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "adfs", "adfs\adfs.vcproj", "{26D4FABF-ACDE-4947-9C4A-7AE1B50CD83A}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F6} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F6} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "adfs-lite", "adfs\adfs-lite.vcproj", "{26D4FABF-ACDE-4947-9C4A-7AE1B50CD83B}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mdquery", "util\mdquery.vcproj", "{F13141B6-6C87-40BB-8D4E-5CC56EBB4C5A}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F6} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F6} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "resolvertest", "util\resolvertest.vcproj", "{F13141B6-6C87-40BB-8D4E-5CC56EBB4C59}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F6} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F6} EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fastcgi", "fastcgi", "{8E1AF2CF-24E1-4983-8681-394D89DF9AD2}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shibauthorizer", "fastcgi\shibauthorizer.vcproj", "{8CF7DDFA-EAA0-416E-853E-3DCB210C4AE0}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shibresponder", "fastcgi\shibresponder.vcproj", "{B2423DCE-048D-4BAA-9AB9-F5D1FCDD3D25}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "memcache-store", "memcache-store\memcache-store.vcproj", "{666A63A7-983F-4C19-8411-207F24305198}" - ProjectSection(WebsiteProperties) = preProject - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.Debug = "False" - EndProjectSection ProjectSection(ProjectDependencies) = postProject {81F0F7A6-DC36-46EF-957F-F9E81D4403F6} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F6} EndProjectSection @@ -278,20 +198,22 @@ Global {B2423DCE-048D-4BAA-9AB9-F5D1FCDD3D25}.Release|Win32.Build.0 = Release|Win32 {B2423DCE-048D-4BAA-9AB9-F5D1FCDD3D25}.Release|x64.ActiveCfg = Release|x64 {666A63A7-983F-4C19-8411-207F24305198}.Debug|Win32.ActiveCfg = Debug|Win32 + {666A63A7-983F-4C19-8411-207F24305198}.Debug|Win32.Build.0 = Debug|Win32 {666A63A7-983F-4C19-8411-207F24305198}.Debug|x64.ActiveCfg = Debug|x64 {666A63A7-983F-4C19-8411-207F24305198}.Release|Win32.ActiveCfg = Release|Win32 + {666A63A7-983F-4C19-8411-207F24305198}.Release|Win32.Build.0 = Release|Win32 {666A63A7-983F-4C19-8411-207F24305198}.Release|x64.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {8E1AF2CF-24E1-4983-8681-394D89DF9AD2} = {26BA8F84-6E42-41FA-9B13-5D3F4B5B2050} + {87C25D4E-8D19-4513-B0BA-BC668BC2DEE3} = {26BA8F84-6E42-41FA-9B13-5D3F4B5B2050} {D243B43E-728E-4F32-BDFF-B3A897037C6D} = {26BA8F84-6E42-41FA-9B13-5D3F4B5B2050} {68E9568B-476C-4289-B93C-893432378ADC} = {26BA8F84-6E42-41FA-9B13-5D3F4B5B2050} {1396D80A-8672-4224-9B02-95F3F4207CDB} = {26BA8F84-6E42-41FA-9B13-5D3F4B5B2050} {B44C0852-83B8-4FB2-A86E-097C9C8256D0} = {26BA8F84-6E42-41FA-9B13-5D3F4B5B2050} - {87C25D4E-8D19-4513-B0BA-BC668BC2DEE3} = {26BA8F84-6E42-41FA-9B13-5D3F4B5B2050} - {8E1AF2CF-24E1-4983-8681-394D89DF9AD2} = {26BA8F84-6E42-41FA-9B13-5D3F4B5B2050} {666A63A7-983F-4C19-8411-207F24305197} = {96AE4FC9-45EF-4C18-9F3B-EDA439E26E4C} {26D4FABF-ACDE-4947-9C4A-7AE1B50CD83A} = {96AE4FC9-45EF-4C18-9F3B-EDA439E26E4C} {26D4FABF-ACDE-4947-9C4A-7AE1B50CD83B} = {96AE4FC9-45EF-4C18-9F3B-EDA439E26E4C} diff --git a/adfs/adfs-lite.vcproj b/adfs/adfs-lite.vcproj index cbc2a53..5017792 100644 --- a/adfs/adfs-lite.vcproj +++ b/adfs/adfs-lite.vcproj @@ -1,11 +1,12 @@ - - - @@ -306,7 +304,7 @@ /> - diff --git a/adfs/adfs.cpp b/adfs/adfs.cpp index 0dd273b..bc84c2f 100644 --- a/adfs/adfs.cpp +++ b/adfs/adfs.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2005 Internet2 - * + * 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 @@ -87,7 +87,7 @@ namespace { public: ADFSDecoder() : m_ns(WSTRUST_NS) {} virtual ~ADFSDecoder() {} - + XMLObject* decode(string& relayState, const GenericRequest& genericRequest, SecurityPolicy& policy) const; protected: @@ -121,7 +121,7 @@ namespace { } } virtual ~ADFSSessionInitiator() {} - + void setParent(const PropertySet* parent) { DOMPropertySet::setParent(parent); pair loc = getString("Location"); @@ -135,11 +135,13 @@ namespace { } void receive(DDF& in, ostream& out); + pair unwrap(SPRequest& request, DDF& out) const; pair run(SPRequest& request, string& entityID, bool isHandler=true) const; private: pair doRequest( const Application& application, + const HTTPRequest* httpRequest, HTTPResponse& httpResponse, const char* entityID, const char* acsLocation, @@ -194,7 +196,7 @@ namespace { } } virtual ~ADFSLogoutInitiator() {} - + void setParent(const PropertySet* parent) { DOMPropertySet::setParent(parent); pair loc = getString("Location"); @@ -228,8 +230,8 @@ namespace { public: ADFSLogout(const DOMElement* e, const char* appId) : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".Logout.ADFS")), m_login(e, appId) { -#ifndef SHIBSP_LITE m_initiator = false; +#ifndef SHIBSP_LITE m_preserve.push_back("wreply"); string address = string(appId) + getString("Location").second + "::run::ADFSLO"; setAddress(address.c_str()); @@ -295,8 +297,8 @@ extern "C" int ADFS_EXPORTS xmltooling_extension_init(void*) conf.AssertionConsumerServiceManager.registerFactory(WSFED_NS, ADFSLogoutFactory); #ifndef SHIBSP_LITE SAMLConfig::getConfig().MessageDecoderManager.registerFactory(WSFED_NS, ADFSDecoderFactory); - XMLObjectBuilder::registerBuilder(QName(WSTRUST_NS,"RequestedSecurityToken"), new AnyElementBuilder()); - XMLObjectBuilder::registerBuilder(QName(WSTRUST_NS,"RequestSecurityTokenResponse"), new AnyElementBuilder()); + XMLObjectBuilder::registerBuilder(xmltooling::QName(WSTRUST_NS,"RequestedSecurityToken"), new AnyElementBuilder()); + XMLObjectBuilder::registerBuilder(xmltooling::QName(WSTRUST_NS,"RequestSecurityTokenResponse"), new AnyElementBuilder()); #endif return 0; } @@ -328,6 +330,13 @@ pair ADFSSessionInitiator::run(SPRequest& request, string& entityID, const Application& app=request.getApplication(); if (isHandler) { + option=request.getParameter("acsIndex"); + if (option) { + ACS = app.getAssertionConsumerServiceByIndex(atoi(option)); + if (!ACS) + request.log(SPRequest::SPWarn, "invalid acsIndex specified in request, using default ACS location"); + } + option = request.getParameter("target"); if (option) target = option; @@ -353,29 +362,25 @@ pair ADFSSessionInitiator::run(SPRequest& request, string& entityID, } // Since we're not passing by index, we need to fully compute the return URL. - // Get all the ADFS endpoints. - const vector& handlers = app.getAssertionConsumerServicesByBinding(m_binding.get()); - - // Index comes from request, or default set in the handler, or we just pick the first endpoint. - pair index(false,0); - if (isHandler) { - option = request.getParameter("acsIndex"); - if (option) - index = pair(true, atoi(option)); - } - if (!index.first) - index = getUnsignedInt("defaultACSIndex"); - if (index.first) { - for (vector::const_iterator h = handlers.begin(); !ACS && h!=handlers.end(); ++h) { - if (index.second == (*h)->getUnsignedInt("index").second) - ACS = *h; + if (!ACS) { + pair index = getUnsignedInt("defaultACSIndex"); + if (index.first) { + ACS = app.getAssertionConsumerServiceByIndex(index.second); + if (!ACS) + request.log(SPRequest::SPWarn, "invalid defaultACSIndex, using default ACS location"); } + if (!ACS) + ACS = app.getDefaultAssertionConsumerService(); } - else if (!handlers.empty()) { - ACS = handlers.front(); + + // Validate the ACS for use with this protocol. + pair ACSbinding = ACS ? ACS->getXMLString("Binding") : pair(false,NULL); + if (ACSbinding.first) { + if (!XMLString::equals(ACSbinding.second, m_binding.get())) { + m_log.info("configured or requested ACS has non-ADFS binding"); + return make_pair(false,0L); + } } - if (!ACS) - throw ConfigurationException("Unable to locate ADFS response endpoint."); // Compute the ACS URL. We add the ACS location to the base handlerURL. string ACSloc=request.getHandlerURL(target.c_str()); @@ -393,8 +398,12 @@ pair ADFSSessionInitiator::run(SPRequest& request, string& entityID, m_log.debug("attempting to initiate session using ADFS with provider (%s)", entityID.c_str()); - if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) - return doRequest(app, request, entityID.c_str(), ACSloc.c_str(), (acClass.first ? acClass.second : NULL), target); + if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { + // Out of process means the POST data via the request can be exposed directly to the private method. + // The method will handle POST preservation if necessary *before* issuing the response, but only if + // it dispatches to an IdP. + return doRequest(app, &request, request, entityID.c_str(), ACSloc.c_str(), (acClass.first ? acClass.second : NULL), target); + } // Remote the call. DDF out,in = DDF(m_address.c_str()).structure(); @@ -403,7 +412,7 @@ pair ADFSSessionInitiator::run(SPRequest& request, string& entityID, in.addmember("entity_id").string(entityID.c_str()); in.addmember("acsLocation").string(ACSloc.c_str()); if (!target.empty()) - in.addmember("RelayState").string(target.c_str()); + in.addmember("RelayState").unsafe_string(target.c_str()); if (acClass.first) in.addmember("authnContextClassRef").string(acClass.second); @@ -412,6 +421,16 @@ pair ADFSSessionInitiator::run(SPRequest& request, string& entityID, return unwrap(request, out); } +pair ADFSSessionInitiator::unwrap(SPRequest& request, DDF& out) const +{ + // See if there's any response to send back. + if (!out["redirect"].isnull() || !out["response"].isnull()) { + // If so, we're responsible for handling the POST data, probably by dropping a cookie. + preservePostData(request.getApplication(), request, request, out["RelayState"].string()); + } + return RemotedHandler::unwrap(request, out); +} + void ADFSSessionInitiator::receive(DDF& in, ostream& out) { // Find application. @@ -439,12 +458,16 @@ void ADFSSessionInitiator::receive(DDF& in, ostream& out) // Since we're remoted, the result should either be a throw, which we pass on, // a false/0 return, which we just return as an empty structure, or a response/redirect, // which we capture in the facade and send back. - doRequest(*app, *http.get(), entityID, acsLocation, in["authnContextClassRef"].string(), relayState); + doRequest(*app, NULL, *http.get(), entityID, acsLocation, in["authnContextClassRef"].string(), relayState); + if (!ret.isstruct()) + ret.structure(); + ret.addmember("RelayState").unsafe_string(relayState.c_str()); out << ret; } pair ADFSSessionInitiator::doRequest( const Application& app, + const HTTPRequest* httpRequest, HTTPResponse& httpResponse, const char* entityID, const char* acsLocation, @@ -463,7 +486,7 @@ pair ADFSSessionInitiator::doRequest( throw MetadataException("Unable to locate metadata for identity provider ($entityID)", namedparams(1, "entityID", entityID)); } else if (!entity.second) { - m_log.warn("unable to locate ADFS-aware identity provider role for provider (%s)", entityID); + m_log.log(getParent() ? Priority::INFO : Priority::WARN, "unable to locate ADFS-aware identity provider role for provider (%s)", entityID); if (getParent()) return make_pair(false,0L); throw MetadataException("Unable to locate ADFS-aware identity provider role for provider ($entityID)", namedparams(1, "entityID", entityID)); @@ -501,6 +524,11 @@ pair ADFSSessionInitiator::doRequest( if (!relayState.empty()) req += "&wctx=" + urlenc->encode(relayState.c_str()); + if (httpRequest) { + // If the request object is available, we're responsible for the POST data. + preservePostData(app, *httpRequest, httpResponse, relayState.c_str()); + } + return make_pair(true, httpResponse.sendRedirect(req.c_str())); #else return make_pair(false,0L); @@ -538,7 +566,7 @@ XMLObject* ADFSDecoder::decode(string& relayState, const GenericRequest& generic // Parse and bind the document into an XMLObject. istringstream is(param); DOMDocument* doc = (policy.getValidating() ? XMLToolingConfig::getConfig().getValidatingParser() - : XMLToolingConfig::getConfig().getParser()).parse(is); + : XMLToolingConfig::getConfig().getParser()).parse(is); XercesJanitor janitor(doc); auto_ptr xmlObject(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true)); janitor.release(); @@ -548,12 +576,11 @@ XMLObject* ADFSDecoder::decode(string& relayState, const GenericRequest& generic throw BindingException("Decoded message was not of the appropriate type."); } - if (!policy.getValidating()) - SchemaValidators.validate(xmlObject.get()); + SchemaValidators.validate(xmlObject.get()); // Skip policy step here, there's no security in the wrapper. // policy.evaluate(*xmlObject.get(), &genericRequest); - + return xmlObject.release(); } @@ -574,7 +601,7 @@ void ADFSConsumer::implementProtocol( const ElementProxy* response = dynamic_cast(&xmlObject); if (!response || !response->hasChildren()) throw FatalProfileException("Incoming message was not of the proper type or contains no security token."); - + const Assertion* token = NULL; for (vector::const_iterator xo = response->getUnknownXMLObjects().begin(); xo != response->getUnknownXMLObjects().end(); ++xo) { // Look for the RequestedSecurityToken element. @@ -588,20 +615,20 @@ void ADFSConsumer::implementProtocol( break; } } - + // Extract message and issuer details from assertion. extractMessageDetails(*token, m_protocol.get(), policy); // Run the policy over the assertion. Handles replay, freshness, and // signature verification, assuming the relevant rules are configured. - policy.evaluate(*token); - + policy.evaluate(*token, &httpRequest); + // If no security is in place now, we kick it. if (!policy.isAuthenticated()) throw SecurityPolicyException("Unable to establish security of incoming assertion."); time_t now = time(NULL); - + const PropertySet* sessionProps = application.getPropertySet("Sessions"); const EntityDescriptor* entity = policy.getIssuerMetadata() ? dynamic_cast(policy.getIssuerMetadata()->getParent()) : NULL; @@ -610,7 +637,7 @@ void ADFSConsumer::implementProtocol( const XMLCh* authMethod=NULL; const XMLCh* authInstant=NULL; time_t sessionExp = 0; - + const saml1::Assertion* saml1token = dynamic_cast(token); if (saml1token) { // Now do profile and core semantic validation to ensure we can use it for SSO. @@ -620,7 +647,7 @@ void ADFSConsumer::implementProtocol( throw FatalProfileException("Assertion did not contain time conditions."); else if (saml1token->getAuthenticationStatements().empty()) throw FatalProfileException("Assertion did not contain an authentication statement."); - + // authnskew allows rejection of SSO if AuthnInstant is too old. pair authnskew = sessionProps ? sessionProps->getUnsignedInt("maxTimeSinceAuthn") : pair(false,0); @@ -659,7 +686,7 @@ void ADFSConsumer::implementProtocol( throw FatalProfileException("Assertion did not contain time conditions."); else if (saml2token->getAuthnStatements().empty()) throw FatalProfileException("Assertion did not contain an authentication statement."); - + // authnskew allows rejection of SSO if AuthnInstant is too old. pair authnskew = sessionProps ? sessionProps->getUnsignedInt("maxTimeSinceAuthn") : pair(false,0); @@ -691,7 +718,7 @@ void ADFSConsumer::implementProtocol( else sessionExp = min(sessionExp, now + lifetime.second); // Use the lowest. } - + m_log.debug("ADFS profile processing completed successfully"); // We've successfully "accepted" the SSO token. @@ -802,7 +829,7 @@ void ADFSLogoutInitiator::receive(DDF& in, ostream& out) m_log.error("couldn't find application (%s) for logout", aid ? aid : "(missing)"); throw ConfigurationException("Unable to locate application for logout, deleted?"); } - + // Unpack the request. auto_ptr req(getRequest(in)); @@ -810,7 +837,7 @@ void ADFSLogoutInitiator::receive(DDF& in, ostream& out) DDF ret(NULL); DDFJanitor jout(ret); auto_ptr resp(getResponse(ret)); - + Session* session = NULL; try { session = app->getServiceProvider().getSessionCache()->find(*app, *req.get(), NULL, NULL); @@ -870,7 +897,7 @@ pair ADFSLogoutInitiator::doRequest( "Unable to locate ADFS IdP role for identity provider ($entityID).", namedparams(1, "entityID", session->getEntityID()) ); } - + const EndpointType* ep = EndpointManager( dynamic_cast(entity.second)->getSingleLogoutServices() ).getByBinding(m_binding.get()); diff --git a/adfs/adfs.rc b/adfs/adfs.rc index 7cd4e1b..7db3d05 100644 --- a/adfs/adfs.rc +++ b/adfs/adfs.rc @@ -54,8 +54,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,1,0,0 - PRODUCTVERSION 2,1,0,0 + FILEVERSION 2,2,1,0 + PRODUCTVERSION 2,2,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -73,13 +73,13 @@ BEGIN VALUE "Comments", "\0" VALUE "CompanyName", "Internet2\0" VALUE "FileDescription", "Shibboleth ADFSv1 Plugin\0" - VALUE "FileVersion", "2, 1, 0, 0\0" + VALUE "FileVersion", "2, 2, 1, 0\0" #ifdef SHIBSP_LITE VALUE "InternalName", "adfs-lite\0" #else VALUE "InternalName", "adfs\0" #endif - VALUE "LegalCopyright", "Copyright © 2008 Internet2\0" + VALUE "LegalCopyright", "Copyright © 2009 Internet2\0" VALUE "LegalTrademarks", "\0" #ifdef SHIBSP_LITE VALUE "OriginalFilename", "adfs-lite.so\0" @@ -87,8 +87,8 @@ BEGIN VALUE "OriginalFilename", "adfs.so\0" #endif VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Shibboleth 2.1\0" - VALUE "ProductVersion", "2, 1, 0, 0\0" + VALUE "ProductName", "Shibboleth 2.2.1\0" + VALUE "ProductVersion", "2, 2, 1, 0\0" VALUE "SpecialBuild", "\0" END END diff --git a/adfs/adfs.vcproj b/adfs/adfs.vcproj index efb995f..4a4613b 100644 --- a/adfs/adfs.vcproj +++ b/adfs/adfs.vcproj @@ -1,11 +1,12 @@ - - - @@ -304,7 +302,7 @@ /> - diff --git a/apache/mod_apache.cpp b/apache/mod_apache.cpp index 4fd81e8..661b448 100644 --- a/apache/mod_apache.cpp +++ b/apache/mod_apache.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -91,12 +91,10 @@ namespace { char* g_szSchemaDir = NULL; char* g_szPrefix = NULL; SPConfig* g_Config = NULL; - string g_unsetHeaderValue; + string g_unsetHeaderValue,g_spoofKey; bool g_checkSpoofing = true; bool g_catchAll = false; static const char* g_UserDataKey = "_shib_check_user_"; - static const XMLCh path[] = UNICODE_LITERAL_4(p,a,t,h); - static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e); } /* Apache 2.2.x headers must be accumulated and set in the output filter. @@ -302,7 +300,7 @@ class ShibTargetApache : public AbstractSPRequest { bool m_handler; mutable string m_body; - mutable bool m_gotBody; + mutable bool m_gotBody,m_firsttime; mutable vector m_certs; set m_allhttp; @@ -312,13 +310,29 @@ public: shib_server_config* m_sc; shib_request_config* m_rc; - ShibTargetApache(request_rec* req, bool handler) : AbstractSPRequest(SHIBSP_LOGCAT".Apache"), m_handler(handler), m_gotBody(false) { + ShibTargetApache(request_rec* req, bool handler, bool shib_check_user) + : AbstractSPRequest(SHIBSP_LOGCAT".Apache"), m_handler(handler), m_gotBody(false),m_firsttime(true) { m_sc = (shib_server_config*)ap_get_module_config(req->server->module_config, &mod_shib); m_dc = (shib_dir_config*)ap_get_module_config(req->per_dir_config, &mod_shib); m_rc = (shib_request_config*)ap_get_module_config(req->request_config, &mod_shib); m_req = req; setRequestURI(m_req->unparsed_uri); + + if (shib_check_user && m_dc->bUseHeaders == 1) { + // Try and see if this request was already processed, to skip spoof checking. + if (!ap_is_initial_req(m_req)) { + m_firsttime = false; + } + else if (!g_spoofKey.empty()) { + const char* hdr = ap_table_get(m_req->headers_in, "Shib-Spoof-Check"); + if (hdr && g_spoofKey == hdr) + m_firsttime=false; + } + + if (!m_firsttime) + log(SPDebug, "shib_check_user running more than once"); + } } virtual ~ShibTargetApache() {} @@ -342,7 +356,8 @@ public: return m_gotBody ? m_body.length() : m_req->remaining; } string getRemoteAddr() const { - return m_req->connection->remote_ip; + string ret = AbstractSPRequest::getRemoteAddr(); + return ret.empty() ? m_req->connection->remote_ip : ret; } void log(SPLogLevel level, const string& msg) const { AbstractSPRequest::log(level,msg); @@ -421,7 +436,7 @@ public: void clearHeader(const char* rawname, const char* cginame) { if (m_dc->bUseHeaders == 1) { // ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(m_req), "shib_clear_header: hdr\n"); - if (g_checkSpoofing && ap_is_initial_req(m_req)) { + if (g_checkSpoofing && m_firsttime) { if (m_allhttp.empty()) { // First time, so populate set with "CGI" versions of client-supplied headers. #ifdef SHIB_APACHE_13 @@ -483,10 +498,27 @@ public: } void setRemoteUser(const char* user) { SH_AP_USER(m_req) = user ? ap_pstrdup(m_req->pool, user) : NULL; + if (m_dc->bUseHeaders == 1) { + if (user) { + ap_table_set(m_req->headers_in, "REMOTE_USER", user); + } + else { + ap_table_unset(m_req->headers_in, "REMOTE_USER"); + ap_table_set(m_req->headers_in, "REMOTE_USER", g_unsetHeaderValue.c_str()); + } + } } string getRemoteUser() const { return string(SH_AP_USER(m_req) ? SH_AP_USER(m_req) : ""); } + void setAuthType(const char* authtype) { + if (authtype && m_dc->bBasicHijack == 1) + authtype = "Basic"; + SH_AP_AUTH_TYPE(m_req) = authtype ? ap_pstrdup(m_req->pool, authtype) : NULL; + } + string getAuthType() const { + return string(SH_AP_AUTH_TYPE(m_req) ? SH_AP_AUTH_TYPE(m_req) : ""); + } void setContentType(const char* type) { m_req->content_type = ap_psprintf(m_req->pool, type); } @@ -495,8 +527,11 @@ public: if (!m_rc) // this happens on subrequests m_rc = init_request_config(m_req); - if (m_handler) + if (m_handler) { + if (!m_rc->hdr_out) + m_rc->hdr_out = ap_make_table(m_req->pool, 5); ap_table_add(m_rc->hdr_out, name, value); + } else #endif ap_table_add(m_req->err_headers_out, name, value); @@ -554,11 +589,14 @@ extern "C" int shib_check_user(request_rec* r) xmltooling::NDC ndc(threadid.str().c_str()); try { - ShibTargetApache sta(r,false); + ShibTargetApache sta(r,false,true); // Check user authentication and export information, then set the handler bypass pair res = sta.getServiceProvider().doAuthentication(sta,true); apr_pool_userdata_setn((const void*)42,g_UserDataKey,NULL,r->pool); + // If directed, install a spoof key to recognize when we've already cleared headers. + if (!g_spoofKey.empty() && (((shib_dir_config*)ap_get_module_config(r->per_dir_config, &mod_shib))->bUseHeaders==1)) + ap_table_set(r->headers_in, "Shib-Spoof-Check", g_spoofKey.c_str()); if (res.first) return res.second; // user auth was okay -- export the assertions now @@ -605,7 +643,7 @@ extern "C" int shib_handler(request_rec* r) ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_handler(%d): ENTER: %s", (int)getpid(), r->handler); try { - ShibTargetApache sta(r,true); + ShibTargetApache sta(r,true,false); pair res = sta.getServiceProvider().doHandler(sta); if (res.first) return res.second; @@ -642,7 +680,7 @@ extern "C" int shib_auth_checker(request_rec* r) xmltooling::NDC ndc(threadid.str().c_str()); try { - ShibTargetApache sta(r,false); + ShibTargetApache sta(r,false,false); pair res = sta.getServiceProvider().doAuthorization(sta); if (res.first) return res.second; @@ -1114,9 +1152,10 @@ AccessControl::aclresult_t htAccessControl::authorized(const SPRequest& request, auto_ptr temp(new xercesc::RegularExpression(trans.get())); re=temp; } - - for (; !status && attrs.first!=attrs.second; ++attrs.first) { - if (checkAttribute(request, attrs.first->second, w, regexp ? re.get() : NULL)) { + + pair::const_iterator,multimap::const_iterator> attrs2(attrs); + for (; !status && attrs2.first!=attrs2.second; ++attrs2.first) { + if (checkAttribute(request, attrs2.first->second, w, regexp ? re.get() : NULL)) { status = true; } } @@ -1178,12 +1217,7 @@ AccessControl::aclresult_t htAccessControl::authorized(const SPRequest& request, static int shib_post_read(request_rec *r) { shib_request_config* rc = init_request_config(r); - - ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r), "shib_post_read"); - -#ifdef SHIB_DEFERRED_HEADERS - rc->hdr_out = ap_make_table(r->pool, 5); -#endif + //ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r), "shib_post_read"); return DECLINED; } @@ -1285,13 +1319,18 @@ extern "C" void shib_child_init(apr_pool_t* p, server_rec* s) ServiceProvider* sp=g_Config->getServiceProvider(); xmltooling::Locker locker(sp); - const PropertySet* props=sp->getPropertySet("Local"); + const PropertySet* props=sp->getPropertySet("InProcess"); if (props) { pair unsetValue=props->getString("unsetHeaderValue"); if (unsetValue.first) g_unsetHeaderValue = unsetValue.second; pair flag=props->getBool("checkSpoofing"); g_checkSpoofing = !flag.first || flag.second; + if (g_checkSpoofing) { + unsetValue=props->getString("spoofKey"); + if (unsetValue.first) + g_spoofKey = unsetValue.second; + } flag=props->getBool("catchAll"); g_catchAll = flag.first && flag.second; } @@ -1325,11 +1364,11 @@ static apr_status_t do_output_filter(ap_filter_t *f, apr_bucket_brigade *in) request_rec *r = f->r; shib_request_config *rc = (shib_request_config*) ap_get_module_config(r->request_config, &mod_shib); - if (rc) { + if (rc && rc->hdr_out) { ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_out_filter: merging %d headers", apr_table_elts(rc->hdr_out)->nelts); - apr_table_do(_table_add,r->headers_out, rc->hdr_out,NULL); // can't use overlap call because it will collapse Set-Cookie headers //apr_table_overlap(r->headers_out, rc->hdr_out, APR_OVERLAP_TABLES_MERGE); + apr_table_do(_table_add,r->headers_out, rc->hdr_out,NULL); } /* remove ourselves from the filter chain */ @@ -1344,11 +1383,11 @@ static apr_status_t do_error_filter(ap_filter_t *f, apr_bucket_brigade *in) request_rec *r = f->r; shib_request_config *rc = (shib_request_config*) ap_get_module_config(r->request_config, &mod_shib); - if (rc) { + if (rc && rc->hdr_out) { ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,SH_AP_R(r),"shib_err_filter: merging %d headers", apr_table_elts(rc->hdr_out)->nelts); - apr_table_do(_table_add,r->err_headers_out, rc->hdr_out,NULL); // can't use overlap call because it will collapse Set-Cookie headers //apr_table_overlap(r->err_headers_out, rc->hdr_out, APR_OVERLAP_TABLES_MERGE); + apr_table_do(_table_add,r->err_headers_out, rc->hdr_out,NULL); } /* remove ourselves from the filter chain */ @@ -1369,7 +1408,7 @@ static command_rec shire_cmds[] = { {"ShibPrefix", (config_fn_t)ap_set_global_string_slot, &g_szPrefix, RSRC_CONF, TAKE1, "Shibboleth installation directory"}, {"ShibConfig", (config_fn_t)ap_set_global_string_slot, &g_szSHIBConfig, - RSRC_CONF, TAKE1, "Path to shibboleth.xml config file"}, + RSRC_CONF, TAKE1, "Path to shibboleth2.xml config file"}, {"ShibCatalogs", (config_fn_t)ap_set_global_string_slot, &g_szSchemaDir, RSRC_CONF, TAKE1, "Paths of XML schema catalogs"}, @@ -1473,7 +1512,7 @@ static command_rec shib_cmds[] = { AP_INIT_TAKE1("ShibPrefix", (config_fn_t)ap_set_global_string_slot, &g_szPrefix, RSRC_CONF, "Shibboleth installation directory"), AP_INIT_TAKE1("ShibConfig", (config_fn_t)ap_set_global_string_slot, &g_szSHIBConfig, - RSRC_CONF, "Path to shibboleth.xml config file"), + RSRC_CONF, "Path to shibboleth2.xml config file"), AP_INIT_TAKE1("ShibCatalogs", (config_fn_t)ap_set_global_string_slot, &g_szSchemaDir, RSRC_CONF, "Paths of XML schema catalogs"), diff --git a/apache/mod_shib13.vcproj b/apache/mod_shib13.vcproj index 5f207f4..e83f9ad 100644 --- a/apache/mod_shib13.vcproj +++ b/apache/mod_shib13.vcproj @@ -1,9 +1,10 @@ - - - @@ -346,12 +344,14 @@ /> - @@ -395,7 +392,7 @@ /> - - - @@ -350,12 +348,14 @@ /> - @@ -399,7 +396,7 @@ /> - - - @@ -350,12 +348,14 @@ /> - @@ -400,7 +397,7 @@ /> connection->user +#define SH_AP_AUTH_TYPE(r) r->connection->ap_auth_type #ifdef WIN32 # define _USE_32BIT_TIME_T diff --git a/apache/mod_shib_13.rc b/apache/mod_shib_13.rc index e6939bb..503cf30 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,1,0,0 - PRODUCTVERSION 2,1,0,0 + FILEVERSION 2,2,1,0 + PRODUCTVERSION 2,2,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, 1, 0, 0\0" + VALUE "FileVersion", "2, 2, 1, 0\0" VALUE "InternalName", "mod_shib_13\0" - VALUE "LegalCopyright", "Copyright © 2008 Internet2\0" + VALUE "LegalCopyright", "Copyright © 2009 Internet2\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "mod_shib_13.so\0" VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Shibboleth 2.1\0" - VALUE "ProductVersion", "2, 1, 0, 0\0" + VALUE "ProductName", "Shibboleth 2.2.1\0" + VALUE "ProductVersion", "2, 2, 1, 0\0" VALUE "SpecialBuild", "\0" END END diff --git a/apache/mod_shib_20.cpp b/apache/mod_shib_20.cpp index 065b24e..c4260c1 100644 --- a/apache/mod_shib_20.cpp +++ b/apache/mod_shib_20.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -31,6 +31,7 @@ #define SH_AP_R(r) 0,r #define SH_AP_USER(r) r->user +#define SH_AP_AUTH_TYPE(r) r->ap_auth_type #define SERVER_ERROR HTTP_INTERNAL_SERVER_ERROR #define REDIRECT HTTP_MOVED_TEMPORARILY diff --git a/apache/mod_shib_20.rc b/apache/mod_shib_20.rc index 108338c..d3e619e 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,1,0,0 - PRODUCTVERSION 2,1,0,0 + FILEVERSION 2,2,1,0 + PRODUCTVERSION 2,2,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, 1, 0, 0\0" + VALUE "FileVersion", "2, 2, 1, 0\0" VALUE "InternalName", "mod_shib_20\0" - VALUE "LegalCopyright", "Copyright © 2008 Internet2\0" + VALUE "LegalCopyright", "Copyright © 2009 Internet2\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "mod_shib_20.so\0" VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Shibboleth 2.1\0" - VALUE "ProductVersion", "2, 1, 0, 0\0" + VALUE "ProductName", "Shibboleth 2.2.1\0" + VALUE "ProductVersion", "2, 2, 1, 0\0" VALUE "SpecialBuild", "\0" END END diff --git a/apache/mod_shib_22.cpp b/apache/mod_shib_22.cpp index 5cab9e4..fbaf17d 100644 --- a/apache/mod_shib_22.cpp +++ b/apache/mod_shib_22.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -31,6 +31,7 @@ #define SH_AP_R(r) 0,r #define SH_AP_USER(r) r->user +#define SH_AP_AUTH_TYPE(r) r->ap_auth_type #define SERVER_ERROR HTTP_INTERNAL_SERVER_ERROR #define REDIRECT HTTP_MOVED_TEMPORARILY diff --git a/apache/mod_shib_22.rc b/apache/mod_shib_22.rc index ede13f4..5f92326 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,1,0,0 - PRODUCTVERSION 2,1,0,0 + FILEVERSION 2,2,1,0 + PRODUCTVERSION 2,2,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, 1, 0, 0\0" + VALUE "FileVersion", "2, 2, 1, 0\0" VALUE "InternalName", "mod_shib_22\0" - VALUE "LegalCopyright", "Copyright © 2008 Internet2\0" + VALUE "LegalCopyright", "Copyright © 2009 Internet2\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "mod_shib_22.so\0" VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Shibboleth 2.1\0" - VALUE "ProductVersion", "2, 1, 0, 0\0" + VALUE "ProductName", "Shibboleth 2.2.1\0" + VALUE "ProductVersion", "2, 2, 1, 0\0" VALUE "SpecialBuild", "\0" END END diff --git a/config.h.in b/config.h.in index 0d29263..e74571c 100644 --- a/config.h.in +++ b/config.h.in @@ -107,6 +107,12 @@ /* Define if log4shib library is used. */ #undef SHIBSP_LOG4SHIB +/* Define to 1 if Xerces XMLString includes XMLByte release. */ +#undef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE + +/* Define to 1 if Xerces DOMNodeFilter API returns a short. */ +#undef SHIBSP_XERCESC_SHORT_ACCEPTNODE + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS diff --git a/config_win32.h b/config_win32.h index 57a9ec7..04b4dd2 100644 --- a/config_win32.h +++ b/config_win32.h @@ -40,6 +40,13 @@ /* Define if Xerces-C library was found */ #define HAVE_LIBXERCESC 1 +#include + +#if (XERCES_VERSION_MAJOR < 3) +# define SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE 1 +# define SHIBSP_XERCESC_SHORT_ACCEPTNODE 1 +#endif + /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 @@ -110,13 +117,13 @@ #define PACKAGE_NAME "shibboleth" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "shibboleth 2.0" +#define PACKAGE_STRING "shibboleth 2.2.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.0" +#define PACKAGE_VERSION "2.2.1" /* Define to the necessary symbol if this constant uses a non-standard name on your system. */ @@ -129,7 +136,7 @@ /* #undef TM_IN_SYS_TIME */ /* Version number of package */ -#define VERSION "2.0" +#define VERSION "2.2.1" /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ diff --git a/configs/Makefile.am b/configs/Makefile.am index d745b5d..cd01d03 100644 --- a/configs/Makefile.am +++ b/configs/Makefile.am @@ -1,22 +1,24 @@ -## $Id: Makefile.am 2779 2008-03-15 03:54:53Z cantor $ +## $Id: Makefile.am 3088 2009-08-10 16:43:09Z cantor $ AUTOMAKE_OPTIONS = foreign pkglibdir = ${libdir}/@PACKAGE@ pkglogdir = ${localstatedir}/log/@PACKAGE@ -pkgdocdir = ${datadir}/doc/@PACKAGE@ +pkgdocdir = $(datadir)/doc/@PACKAGE@-@PACKAGE_VERSION@ shirelogdir = ${localstatedir}/log/httpd pkgxmldir = $(datadir)/xml/@PACKAGE@ pkgrundir = $(localstatedir)/run/@PACKAGE@ pkgsysconfdir = $(sysconfdir)/@PACKAGE@ pkgsysconf_DATA = \ shibd-redhat \ + shibd-suse \ shibd-debian \ shibd-osx.plist \ apache.config \ apache2.config \ apache22.config \ keygen.sh \ + metagen.sh \ upgrade.xsl # The config files are installed "special". Unlike the entries in @@ -41,6 +43,7 @@ CONFIGFILES = \ metadataError.html \ bindingTemplate.html \ discoveryTemplate.html \ + postTemplate.html \ localLogout.html \ globalLogout.html \ sslError.html @@ -74,6 +77,9 @@ apache22.config: ${srcdir}/apache22.config.in Makefile ${top_builddir}/config.st shibd-redhat: ${srcdir}/shibd-redhat.in Makefile ${top_builddir}/config.status $(MAKE) do-build-file FILE=$@ +shibd-suse: ${srcdir}/shibd-suse.in Makefile ${top_builddir}/config.status + $(MAKE) do-build-file FILE=$@ + shibd-debian: ${srcdir}/shibd-debian.in Makefile ${top_builddir}/config.status $(MAKE) do-build-file FILE=$@ @@ -109,6 +115,8 @@ install-data-local: all-data-local done install-data-hook: + chmod +x $(DESTDIR)$(pkgsysconfdir)/keygen.sh + chmod +x $(DESTDIR)$(pkgsysconfdir)/metagen.sh if test -z "$(NOKEYGEN)"; then \ cd $(DESTDIR)$(pkgsysconfdir); \ sh ./keygen.sh -b ; \ @@ -119,6 +127,7 @@ CLEANFILES = \ apache2.config \ apache22.config \ shibd-redhat \ + shibd-suse \ shibd-debian \ shibd-osx.plist \ shibd.logger \ @@ -131,10 +140,12 @@ EXTRA_DIST = \ apache2.config.in \ apache22.config.in \ shibd-redhat.in \ + shibd-suse.in \ shibd-debian.in \ shibd-osx.plist.in \ keygen.bat \ keygen.sh \ + metagen.sh \ upgrade.xsl \ xsltproc.js \ $(CONFIGFILES) diff --git a/configs/Makefile.in b/configs/Makefile.in index 6a9a049..40939a8 100644 --- a/configs/Makefile.in +++ b/configs/Makefile.in @@ -258,19 +258,21 @@ target_alias = @target_alias@ xs = @xs@ AUTOMAKE_OPTIONS = foreign pkglogdir = ${localstatedir}/log/@PACKAGE@ -pkgdocdir = ${datadir}/doc/@PACKAGE@ +pkgdocdir = $(datadir)/doc/@PACKAGE@-@PACKAGE_VERSION@ shirelogdir = ${localstatedir}/log/httpd pkgxmldir = $(datadir)/xml/@PACKAGE@ pkgrundir = $(localstatedir)/run/@PACKAGE@ pkgsysconfdir = $(sysconfdir)/@PACKAGE@ pkgsysconf_DATA = \ shibd-redhat \ + shibd-suse \ shibd-debian \ shibd-osx.plist \ apache.config \ apache2.config \ apache22.config \ keygen.sh \ + metagen.sh \ upgrade.xsl @@ -297,6 +299,7 @@ CONFIGFILES = \ metadataError.html \ bindingTemplate.html \ discoveryTemplate.html \ + postTemplate.html \ localLogout.html \ globalLogout.html \ sslError.html @@ -306,6 +309,7 @@ CLEANFILES = \ apache2.config \ apache22.config \ shibd-redhat \ + shibd-suse \ shibd-debian \ shibd-osx.plist \ shibd.logger \ @@ -318,10 +322,12 @@ EXTRA_DIST = \ apache2.config.in \ apache22.config.in \ shibd-redhat.in \ + shibd-suse.in \ shibd-debian.in \ shibd-osx.plist.in \ keygen.bat \ keygen.sh \ + metagen.sh \ upgrade.xsl \ xsltproc.js \ $(CONFIGFILES) @@ -541,6 +547,9 @@ apache22.config: ${srcdir}/apache22.config.in Makefile ${top_builddir}/config.st shibd-redhat: ${srcdir}/shibd-redhat.in Makefile ${top_builddir}/config.status $(MAKE) do-build-file FILE=$@ +shibd-suse: ${srcdir}/shibd-suse.in Makefile ${top_builddir}/config.status + $(MAKE) do-build-file FILE=$@ + shibd-debian: ${srcdir}/shibd-debian.in Makefile ${top_builddir}/config.status $(MAKE) do-build-file FILE=$@ @@ -576,6 +585,8 @@ install-data-local: all-data-local done install-data-hook: + chmod +x $(DESTDIR)$(pkgsysconfdir)/keygen.sh + chmod +x $(DESTDIR)$(pkgsysconfdir)/metagen.sh if test -z "$(NOKEYGEN)"; then \ cd $(DESTDIR)$(pkgsysconfdir); \ sh ./keygen.sh -b ; \ diff --git a/configs/attribute-map.xml b/configs/attribute-map.xml index 4f7477c..431db53 100644 --- a/configs/attribute-map.xml +++ b/configs/attribute-map.xml @@ -25,30 +25,32 @@ + + - + - + - + diff --git a/configs/keygen.bat b/configs/keygen.bat index 1af0518..401269c 100644 --- a/configs/keygen.bat +++ b/configs/keygen.bat @@ -74,7 +74,7 @@ shift goto opt_start :opt_years -set DAYS=%2 +set YEARS=%2 shift shift goto opt_start diff --git a/configs/metagen.sh b/configs/metagen.sh new file mode 100755 index 0000000..faf89af --- /dev/null +++ b/configs/metagen.sh @@ -0,0 +1,211 @@ +#! /bin/sh + +while getopts a:c:e:h:n:o:s:t: c + do + case $c in + c) CERTS[${#CERTS[*]}]=$OPTARG;; + e) ENTITYID=$OPTARG;; + h) HOSTS[${#HOSTS[*]}]=$OPTARG;; + n) NAKEDHOSTS[${#NAKEDHOSTS[*]}]=$OPTARG;; + o) ORGNAME=$OPTARG;; + a) ADMIN[${#ADMIN[*]}]=$OPTARG;; + s) SUP[${#SUP[*]}]=$OPTARG;; + t) TECH[${#TECH[*]}]=$OPTARG;; + \?) echo metagen -c cert1 [-c cert2 ...] -h host1 [-h host2 ...] [-e entityID] + exit 1;; + esac + done + +if [ ${#HOSTS[*]} -eq 0 -a ${#NAKEDHOSTS[*]} -eq 0 ] ; then + echo metagen -c cert1 [-c cert2 ...] -h host1 [-h host2 ...] [-e entityID] + exit 1 +fi + +if [ ${#CERTS[*]} -eq 0 ] ; then + CERTS[${#CERTS[*]}]=sp-cert.pem +fi + +for c in ${CERTS[@]} +do + if [ ! -s $c ] ; then + echo Certificate file $c does not exist! + exit 2 + fi +done + +if [ -z $ENTITYID ] ; then + ENTITYID=https://${HOSTS[0]}/shibboleth +fi + +cat < + + +EOF + +count=1 +for h in ${HOSTS[@]} +do + cat << EOF + +EOF + let "count++" +done + +for h in ${NAKEDHOSTS[@]} +do + cat << EOF + +EOF + let "count++" +done + +cat << EOF + +EOF + +for c in ${CERTS[@]} +do +cat << EOF + + + + +EOF +grep -v ^- $c +cat << EOF + + + + +EOF +done + +cat << EOF + +EOF + +count=0 +for h in ${HOSTS[@]} +do + cat < + + + + + +EOF + let "count+=6" +done + +for h in ${NAKEDHOSTS[@]} +do + cat < + + + + + +EOF + let "count+=6" +done + +cat < +EOF + +if [ -n "$ORGNAME" ] ; then + cat < + $ORGNAME + $ORGNAME + $ENTITYID + +EOF +fi + +for c in ${ADMIN[@]} +do + c=(${c//\// }) + cat < + ${c[0]} + ${c[1]} + ${c[2]} + +EOF +done + +for c in ${SUP[@]} +do + c=(${c//\// }) + cat < + ${c[0]} + ${c[1]} + ${c[2]} + +EOF +done + +for c in ${TECH[@]} +do + c=(${c//\// }) + cat < + ${c[0]} + ${c[1]} + ${c[2]} + +EOF +done + +cat < +EOF diff --git a/configs/postTemplate.html b/configs/postTemplate.html new file mode 100644 index 0000000..4883feb --- /dev/null +++ b/configs/postTemplate.html @@ -0,0 +1,41 @@ + + + Login Completed + + + +

Login Completed

+ + + +
+ + + + +
+ + diff --git a/configs/shibboleth2.xml b/configs/shibboleth2.xml index afe286d..d02da4c 100644 --- a/configs/shibboleth2.xml +++ b/configs/shibboleth2.xml @@ -16,19 +16,25 @@ - + + + - + @@ -76,10 +82,8 @@ --> + signing="false" encryption="false"> - + - + - + - + @@ -245,14 +250,21 @@ - + - - - - + + + + + + + + + - - + \ No newline at end of file diff --git a/configs/shibd-debian.in b/configs/shibd-debian.in index 99ff4d1..13e8240 100644 --- a/configs/shibd-debian.in +++ b/configs/shibd-debian.in @@ -4,7 +4,7 @@ # Required-Start: $local_fs $remote_fs $network # Required-Stop: $local_fs $remote_fs $network # Default-Start: 2 3 4 5 -# Default-Stop: S 0 1 6 +# Default-Stop: 0 1 6 # Short-Description: Shibboleth 2 Service Provider Daemon # Description: Starts the separate daemon used by the Shibboleth # Apache module to manage sessions and to retrieve @@ -56,7 +56,7 @@ start) exit 0 fi echo -n "Starting $DESC: " - start-stop-daemon --background --start --quiet \ + start-stop-daemon --start --quiet \ --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; @@ -71,7 +71,7 @@ restart|force-reload) start-stop-daemon --stop --quiet --pidfile $PIDFILE \ --exec $DAEMON sleep 1 - start-stop-daemon --background --start --quiet \ + start-stop-daemon --start --quiet \ --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_OPTS echo "$NAME." ;; diff --git a/configs/shibd-osx.plist.in b/configs/shibd-osx.plist.in index 26c90b1..a05cc9d 100644 --- a/configs/shibd-osx.plist.in +++ b/configs/shibd-osx.plist.in @@ -8,6 +8,7 @@ ProgramArguments @-PREFIX-@/sbin/shibd + -F -f -p @-PKGRUNDIR-@/shibd.pid diff --git a/configs/shibd-redhat.in b/configs/shibd-redhat.in index 496f631..02e3426 100644 --- a/configs/shibd-redhat.in +++ b/configs/shibd-redhat.in @@ -36,8 +36,7 @@ start() { if [ -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled; then /sbin/restorecon $pidfile fi - # daemon function just hangs, so I'm using su directly - su - $SHIBD_USER -c "$shibd -p $pidfile -f &" + daemon --user $SHIBD_USER --pidfile $pidfile $shibd -p $pidfile -f -w 3 RETVAL=$? echo @@ -48,10 +47,9 @@ start() { stop() { echo -n $"Stopping $prog: " if [ -f $pidfile ]; then - read kpid < $pidfile - kill $kpid + killproc -p $pidfile shibd else - killproc shibd + killproc shibd fi RETVAL=$? @@ -77,7 +75,7 @@ case "$1" in start ;; *) - echo $"Usage: $prog {start|stop|restart}" + echo $"Usage: $prog {start|stop|status|restart}" exit 1 esac diff --git a/configs/shibd-suse.in b/configs/shibd-suse.in new file mode 100644 index 0000000..1dceeda --- /dev/null +++ b/configs/shibd-suse.in @@ -0,0 +1,112 @@ +#! /bin/sh +# Author: Peter Schober and many others +# based on shibd-debian (from Shibboleth's 1.3.1 SP source distribution) +# and SUSE's /etc/init.d/cyrus +# +# /etc/init.d/shibd +# +### BEGIN INIT INFO +# Provides: shibd +# Required-Start: network +# Required-Stop: $null +# Default-Start: 3 5 +# 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. +### END INIT INFO +# + +DESC="Shibboleth 2 daemon" +NAME=shibd +SHIB_CONFIG=@-PKGSYSCONFDIR-@/shibboleth2.xml +DAEMON=@-PREFIX-@/sbin/$NAME +SCRIPTNAME=/etc/init.d/$NAME +PID_FILE=@-PKGRUNDIR-@/shibd.pid +DAEMON_OPTS="" + +# Force removal of socket +DAEMON_OPTS="$DAEMON_OPTS -f" + +# Use defined configuration file +DAEMON_OPTS="$DAEMON_OPTS -c $SHIB_CONFIG" + +# Specify pid file to use +DAEMON_OPTS="$DAEMON_OPTS -p $PID_FILE" + +# Exit if the package is not installed. +test -x "$DAEMON" || exit 5 + +. /etc/rc.status + +# First reset status of this service +rc_reset + +case "$1" in + start) + echo -n "Starting $DESC ($NAME)" + ## Start daemon with startproc(8). If this fails + ## the echo return value is set appropriate. + + # NOTE: startproc return 0, even if service is + # already running to match LSB spec. + /sbin/startproc -p $PID_FILE $DAEMON $DAEMON_OPTS > /dev/null 2>&1 + + # Remember status and be verbose + rc_status -v + ;; + stop) + echo -n "Shutting down $DESC ($NAME)" + ## Stop daemon with killproc(8) and if this fails + ## set echo the echo return value. + + /sbin/killproc -p $PID_FILE -TERM $DAEMON > /dev/null 2>&1 + + # Remember status and be verbose + rc_status -v + ;; + try-restart) + ## Stop the service and if this succeeds (i.e. the + ## service was running before), start it again. + ## Note: try-restart is not (yet) part of LSB (as of 0.7.5) + $0 status >/dev/null && $0 restart + + # Remember status and be quiet + rc_status + ;; + restart) + ## Stop the service and regardless of whether it was + ## running or not, start it again. + $0 stop + $0 start + + # Remember status and be quiet + rc_status + ;; + configtest) + ## Check config files + + echo -n "Checking config for $DESC ($NAME): " + $DAEMON $DAEMON_OPTS -t + rc_status -v + ;; + status) + echo -n "Checking for service $DESC ($NAME)" + ## Check status with checkproc(8), if process is running + ## checkproc will return with exit status 0. + + # Status has a slightly different for the status command: + # 0 - service running + # 1 - service dead, but /var/run/ pid file exists + # 2 - service dead, but /var/lock/ lock file exists + # 3 - service not running + + # NOTE: checkproc returns LSB compliant status values. + /sbin/checkproc -p $PID_FILE $DAEMON + rc_status -v + ;; + *) + echo "Usage: $0 {start|stop|status|configtest|try-restart|restart}" + exit 1 + ;; +esac +rc_exit diff --git a/configure b/configure index e3cf3b9..cd4b0c2 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.1. +# Generated by GNU Autoconf 2.59 for shibboleth 2.2.1. # # Report bugs to . # @@ -423,8 +423,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='shibboleth' PACKAGE_TARNAME='shibboleth' -PACKAGE_VERSION='2.1' -PACKAGE_STRING='shibboleth 2.1' +PACKAGE_VERSION='2.2.1' +PACKAGE_STRING='shibboleth 2.2.1' PACKAGE_BUGREPORT='shibboleth-users@internet2.edu' # Factoring default headers for most tests. @@ -464,7 +464,7 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar DX_PROJECT DX_CONFIG DX_DOCDIR DX_ENV DX_FLAG_doc DX_DOXYGEN ac_pt_DX_DOXYGEN DX_FLAG_[]DX_CURRENT_FEATURE DX_PERL ac_pt_DX_PERL DX_FLAG_[]DX_CURRENT_FEATURE DX_COND_doc_TRUE DX_COND_doc_FALSE DX_FLAG_dot DX_FLAG_[]DX_CURRENT_FEATURE DX_DOT ac_pt_DX_DOT DX_FLAG_[]DX_CURRENT_FEATURE DX_COND_dot_TRUE DX_COND_dot_FALSE DX_FLAG_man DX_FLAG_[]DX_CURRENT_FEATURE DX_COND_man_TRUE DX_COND_man_FALSE DX_FLAG_rtf DX_FLAG_[]DX_CURRENT_FEATURE DX_COND_rtf_TRUE DX_COND_rtf_FALSE DX_FLAG_xml DX_FLAG_[]DX_CURRENT_FEATURE DX_COND_xml_TRUE DX_COND_xml_FALSE DX_FLAG_chm DX_FLAG_[]DX_CURRENT_FEATURE DX_HHC ac_pt_DX_HHC DX_FLAG_[]DX_CURRENT_FEATURE DX_COND_chm_TRUE DX_COND_chm_FALSE DX_FLAG_chi DX_FLAG_[]DX_CURRENT_FEATURE DX_COND_chi_TRUE DX_COND_chi_FALSE DX_FLAG_html DX_FLAG_[]DX_CURRENT_FEATURE DX_FLAG_[]DX_CURRENT_FEATURE DX_COND_html_TRUE DX_COND_html_FALSE DX_FLAG_ps DX_FLAG_[]DX_CURRENT_FEATURE DX_LATEX ac_pt_DX_LATEX DX_FLAG_[]DX_CURRENT_FEATURE DX_MAKEINDEX ac_pt_DX_MAKEINDEX DX_FLAG_[]DX_CURRENT_FEATURE DX_DVIPS ac_pt_DX_DVIPS DX_FLAG_[]DX_CURRENT_FEATURE DX_EGREP ac_pt_DX_EGREP DX_FLAG_[]DX_CURRENT_FEATURE DX_COND_ps_TRUE DX_COND_ps_FALSE DX_FLAG_pdf DX_FLAG_[]DX_CURRENT_FEATURE DX_PDFLATEX ac_pt_DX_PDFLATEX DX_FLAG_[]DX_CURRENT_FEATURE DX_FLAG_[]DX_CURRENT_FEATURE DX_FLAG_[]DX_CURRENT_FEATURE DX_COND_pdf_TRUE DX_COND_pdf_FALSE DX_COND_latex_TRUE DX_COND_latex_FALSE DOXYGEN_PAPER_SIZE CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE build build_cpu build_vendor build_os host host_cpu host_vendor host_os EGREP LN_S ECHO AR ac_ct_AR RANLIB ac_ct_RANLIB CPP CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS PKG_CONFIG LOG4SHIB_CONFIG LOG4CPP_CONFIG XMLTOOLINGXMLDIR OPENSAMLXMLDIR LITE_LIBS XMLSEC_LIBS NSAPI_INCLUDE BUILD_NSAPI_TRUE BUILD_NSAPI_FALSE FASTCGI_INCLUDE FASTCGI_LDFLAGS FASTCGI_LIBS BUILD_FASTCGI_TRUE BUILD_FASTCGI_FALSE MEMCACHED_INCLUDE MEMCACHED_LDFLAGS MEMCACHED_LIBS BUILD_MEMCACHED_TRUE BUILD_MEMCACHED_FALSE xs APXS APXS_CFLAGS APXS_INCLUDE APXS2 APR_CONFIG APXS2_CFLAGS APXS2_INCLUDE APXS22 APR1_CONFIG APXS22_CFLAGS APXS22_INCLUDE BUILD_AP13_TRUE BUILD_AP13_FALSE BUILD_AP20_TRUE BUILD_AP20_FALSE BUILD_AP22_TRUE BUILD_AP22_FALSE ODBC_CONFIG ODBC_CFLAGS ODBC_LIBS WANT_SUBDIRS LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar DX_PROJECT DX_CONFIG DX_DOCDIR DX_ENV DX_FLAG_doc DX_DOXYGEN ac_pt_DX_DOXYGEN DX_PERL ac_pt_DX_PERL DX_COND_doc_TRUE DX_COND_doc_FALSE DX_FLAG_dot DX_DOT ac_pt_DX_DOT DX_COND_dot_TRUE DX_COND_dot_FALSE DX_FLAG_man DX_COND_man_TRUE DX_COND_man_FALSE DX_FLAG_rtf DX_COND_rtf_TRUE DX_COND_rtf_FALSE DX_FLAG_xml DX_COND_xml_TRUE DX_COND_xml_FALSE DX_FLAG_chm DX_HHC ac_pt_DX_HHC DX_COND_chm_TRUE DX_COND_chm_FALSE DX_FLAG_chi DX_COND_chi_TRUE DX_COND_chi_FALSE DX_FLAG_html DX_COND_html_TRUE DX_COND_html_FALSE DX_FLAG_ps DX_LATEX ac_pt_DX_LATEX DX_MAKEINDEX ac_pt_DX_MAKEINDEX DX_DVIPS ac_pt_DX_DVIPS DX_EGREP ac_pt_DX_EGREP DX_COND_ps_TRUE DX_COND_ps_FALSE DX_FLAG_pdf DX_PDFLATEX ac_pt_DX_PDFLATEX DX_COND_pdf_TRUE DX_COND_pdf_FALSE DX_COND_latex_TRUE DX_COND_latex_FALSE DOXYGEN_PAPER_SIZE CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE build build_cpu build_vendor build_os host host_cpu host_vendor host_os EGREP LN_S ECHO AR ac_ct_AR RANLIB ac_ct_RANLIB CPP CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS PKG_CONFIG LOG4SHIB_CONFIG LOG4CPP_CONFIG XMLTOOLINGXMLDIR OPENSAMLXMLDIR LITE_LIBS XMLSEC_LIBS NSAPI_INCLUDE BUILD_NSAPI_TRUE BUILD_NSAPI_FALSE FASTCGI_INCLUDE FASTCGI_LDFLAGS FASTCGI_LIBS BUILD_FASTCGI_TRUE BUILD_FASTCGI_FALSE MEMCACHED_INCLUDE MEMCACHED_LDFLAGS MEMCACHED_LIBS BUILD_MEMCACHED_TRUE BUILD_MEMCACHED_FALSE xs APXS APXS_CFLAGS APXS_INCLUDE APXS2 APR_CONFIG APXS2_CFLAGS APXS2_INCLUDE APXS22 APR1_CONFIG APXS22_CFLAGS APXS22_INCLUDE BUILD_AP13_TRUE BUILD_AP13_FALSE BUILD_AP20_TRUE BUILD_AP20_FALSE BUILD_AP22_TRUE BUILD_AP22_FALSE ODBC_CONFIG ODBC_CFLAGS ODBC_LIBS WANT_SUBDIRS LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -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.1 to adapt to many kinds of systems. +\`configure' configures shibboleth 2.2.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.1:";; + short | recursive ) echo "Configuration of shibboleth 2.2.1:";; esac cat <<\_ACEOF @@ -1197,7 +1197,7 @@ fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF -shibboleth configure 2.1 +shibboleth configure 2.2.1 generated by GNU Autoconf 2.59 Copyright (C) 2003 Free Software Foundation, Inc. @@ -1211,7 +1211,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.1, which was +It was created by shibboleth $as_me 2.2.1, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ @@ -1856,7 +1856,7 @@ fi # Define the identity of the package. PACKAGE=shibboleth - VERSION=2.1 + VERSION=2.2.1 cat >>confdefs.h <<_ACEOF @@ -2040,6 +2040,9 @@ DX_ENV="" +# Compatibility with older autoconf versions. + + ## --------------- ## ## Private macros. ## ## --------------- ## @@ -6097,7 +6100,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 6100 "configure"' > conftest.$ac_ext + echo '#line 6103 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -7205,7 +7208,7 @@ fi # Provide some information about the compiler. -echo "$as_me:7208:" \ +echo "$as_me:7211:" \ "checking for Fortran 77 compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 @@ -8243,11 +8246,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:8246: $lt_compile\"" >&5) + (eval echo "\"\$as_me:8249: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:8250: \$? = $ac_status" >&5 + echo "$as_me:8253: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -8476,11 +8479,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:8479: $lt_compile\"" >&5) + (eval echo "\"\$as_me:8482: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:8483: \$? = $ac_status" >&5 + echo "$as_me:8486: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -8536,11 +8539,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:8539: $lt_compile\"" >&5) + (eval echo "\"\$as_me:8542: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:8543: \$? = $ac_status" >&5 + echo "$as_me:8546: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -9870,7 +9873,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 9873 "configure"' > conftest.$ac_ext + echo '#line 9876 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -10741,7 +10744,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5) + (eval echo "\"\$as_me:13028: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:13029: \$? = $ac_status" >&5 + echo "$as_me:13032: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -13082,11 +13085,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:13085: $lt_compile\"" >&5) + (eval echo "\"\$as_me:13088: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:13089: \$? = $ac_status" >&5 + echo "$as_me:13092: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -13593,7 +13596,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 13596 "configure"' > conftest.$ac_ext + echo '#line 13599 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -14464,7 +14467,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5) + (eval echo "\"\$as_me:15395: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:15396: \$? = $ac_status" >&5 + echo "$as_me:15399: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -15449,11 +15452,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:15452: $lt_compile\"" >&5) + (eval echo "\"\$as_me:15455: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:15456: \$? = $ac_status" >&5 + echo "$as_me:15459: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -16763,7 +16766,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 16766 "configure"' > conftest.$ac_ext + echo '#line 16769 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -17508,11 +17511,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:17511: $lt_compile\"" >&5) + (eval echo "\"\$as_me:17514: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:17515: \$? = $ac_status" >&5 + echo "$as_me:17518: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -17741,11 +17744,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:17744: $lt_compile\"" >&5) + (eval echo "\"\$as_me:17747: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:17748: \$? = $ac_status" >&5 + echo "$as_me:17751: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings @@ -17801,11 +17804,11 @@ else -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:17804: $lt_compile\"" >&5) + (eval echo "\"\$as_me:17807: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:17808: \$? = $ac_status" >&5 + echo "$as_me:17811: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -19135,7 +19138,7 @@ linux*) libsuff= case "$host_cpu" in x86_64*|s390x*|powerpc64*) - echo '#line 19138 "configure"' > conftest.$ac_ext + echo '#line 19141 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -20006,7 +20009,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5 -echo $ECHO_N "checking for ctime_r... $ECHO_C" >&6 - if test -z "$ac_cv_ctime_args"; then - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -int -main () -{ - - time_t clock; - char buf[26]; - ctime_r(&clock, buf); - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_ctime_args=2 -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext - - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -int -main () -{ - - time_t clock; - char buf[26]; - ctime_r(&clock, buf, 26); - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" - || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest.$ac_objext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_ctime_args=3 -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext conftest.$ac_ext - fi - if test -z "$ac_cv_ctime_args"; then - echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6 - else - if test "$ac_cv_ctime_args" = 2; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_CTIME_R_2 1 -_ACEOF - - elif test "$ac_cv_ctime_args" = 3; then - -cat >>confdefs.h <<\_ACEOF -#define HAVE_CTIME_R_3 1 -_ACEOF - - fi - echo "$as_me:$LINENO: result: yes, and it takes $ac_cv_ctime_args arguments" >&5 -echo "${ECHO_T}yes, and it takes $ac_cv_ctime_args arguments" >&6 - fi - # OpenSSL settings # Check whether --with-openssl or --without-openssl was given. @@ -22970,6 +22850,129 @@ _ACEOF fi +# Thank you Solaris, really. +echo "$as_me:$LINENO: checking for ctime_r" >&5 +echo $ECHO_N "checking for ctime_r... $ECHO_C" >&6 + if test -z "$ac_cv_ctime_args"; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ + + time_t clock; + char buf[26]; + ctime_r(&clock, buf); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_ctime_args=2 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ + + time_t clock; + char buf[26]; + ctime_r(&clock, buf, 26); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_ctime_args=3 +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test -z "$ac_cv_ctime_args"; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + else + if test "$ac_cv_ctime_args" = 2; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CTIME_R_2 1 +_ACEOF + + elif test "$ac_cv_ctime_args" = 3; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CTIME_R_3 1 +_ACEOF + + fi + echo "$as_me:$LINENO: result: yes, and it takes $ac_cv_ctime_args arguments" >&5 +echo "${ECHO_T}yes, and it takes $ac_cv_ctime_args arguments" >&6 + fi + # log4shib settings (favor this version over the log4cpp code) # Extract the first word of "log4shib-config", so it can be a program name with args. set dummy log4shib-config; ac_word=$2 @@ -23754,6 +23757,126 @@ fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext +echo "$as_me:$LINENO: checking whether Xerces XMLString::release(XMLByte**) exists" >&5 +echo $ECHO_N "checking whether Xerces XMLString::release(XMLByte**) exists... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +using namespace XERCES_CPP_NAMESPACE; + XMLByte* buf=NULL; + XMLString::release(&buf); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE 1 +_ACEOF + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +echo "$as_me:$LINENO: checking whether Xerces DOMNodeFilter API returns a short" >&5 +echo $ECHO_N "checking whether Xerces DOMNodeFilter API returns a short... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +using namespace XERCES_CPP_NAMESPACE; + class Blocker : public DOMNodeFilter { + public: + short acceptNode(const DOMNode* node) const { + return FILTER_REJECT; + } + }; + static Blocker g_Blocker; + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define SHIBSP_XERCESC_SHORT_ACCEPTNODE 1 +_ACEOF + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext #XML-Tooling settings @@ -24377,10 +24500,10 @@ cat >>conftest.$ac_ext <<_ACEOF int main () { -#if _OPENSAML_VERSION >= 20000 +#if _OPENSAML_VERSION >= 20200 opensaml::SAMLConfig::getConfig(); #else -#error Need OpenSAML version 2.0 or higher +#error Need OpenSAML version 2.2 or higher #endif ; return 0; @@ -24452,6 +24575,9 @@ OPENSAMLXMLDIR="$OPENSAMLXMLDIR/share/xml/opensaml" + ac_config_files="$ac_config_files shibboleth.spec pkginfo Portfile" + + # output the underlying makefiles WANT_SUBDIRS="doc schemas configs shibsp shibd util" ac_config_files="$ac_config_files Makefile doc/Makefile schemas/Makefile configs/Makefile shibsp/Makefile shibd/Makefile util/Makefile selinux/Makefile" @@ -26765,7 +26891,7 @@ _ASBOX } >&5 cat >&5 <<_CSEOF -This file was extended by shibboleth $as_me 2.1, which was +This file was extended by shibboleth $as_me 2.2.1, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -26828,7 +26954,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -shibboleth config.status 2.1 +shibboleth config.status 2.2.1 configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" @@ -26938,6 +27064,9 @@ for ac_config_target in $ac_config_targets do case "$ac_config_target" in # Handling of arguments. + "shibboleth.spec" ) CONFIG_FILES="$CONFIG_FILES shibboleth.spec" ;; + "pkginfo" ) CONFIG_FILES="$CONFIG_FILES pkginfo" ;; + "Portfile" ) CONFIG_FILES="$CONFIG_FILES Portfile" ;; "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; "doc/Makefile" ) CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "schemas/Makefile" ) CONFIG_FILES="$CONFIG_FILES schemas/Makefile" ;; @@ -27068,70 +27197,49 @@ s,@DX_ENV@,$DX_ENV,;t t s,@DX_FLAG_doc@,$DX_FLAG_doc,;t t s,@DX_DOXYGEN@,$DX_DOXYGEN,;t t s,@ac_pt_DX_DOXYGEN@,$ac_pt_DX_DOXYGEN,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_PERL@,$DX_PERL,;t t s,@ac_pt_DX_PERL@,$ac_pt_DX_PERL,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_COND_doc_TRUE@,$DX_COND_doc_TRUE,;t t s,@DX_COND_doc_FALSE@,$DX_COND_doc_FALSE,;t t s,@DX_FLAG_dot@,$DX_FLAG_dot,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_DOT@,$DX_DOT,;t t s,@ac_pt_DX_DOT@,$ac_pt_DX_DOT,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_COND_dot_TRUE@,$DX_COND_dot_TRUE,;t t s,@DX_COND_dot_FALSE@,$DX_COND_dot_FALSE,;t t s,@DX_FLAG_man@,$DX_FLAG_man,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_COND_man_TRUE@,$DX_COND_man_TRUE,;t t s,@DX_COND_man_FALSE@,$DX_COND_man_FALSE,;t t s,@DX_FLAG_rtf@,$DX_FLAG_rtf,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_COND_rtf_TRUE@,$DX_COND_rtf_TRUE,;t t s,@DX_COND_rtf_FALSE@,$DX_COND_rtf_FALSE,;t t s,@DX_FLAG_xml@,$DX_FLAG_xml,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_COND_xml_TRUE@,$DX_COND_xml_TRUE,;t t s,@DX_COND_xml_FALSE@,$DX_COND_xml_FALSE,;t t s,@DX_FLAG_chm@,$DX_FLAG_chm,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_HHC@,$DX_HHC,;t t s,@ac_pt_DX_HHC@,$ac_pt_DX_HHC,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_COND_chm_TRUE@,$DX_COND_chm_TRUE,;t t s,@DX_COND_chm_FALSE@,$DX_COND_chm_FALSE,;t t s,@DX_FLAG_chi@,$DX_FLAG_chi,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_COND_chi_TRUE@,$DX_COND_chi_TRUE,;t t s,@DX_COND_chi_FALSE@,$DX_COND_chi_FALSE,;t t s,@DX_FLAG_html@,$DX_FLAG_html,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_COND_html_TRUE@,$DX_COND_html_TRUE,;t t s,@DX_COND_html_FALSE@,$DX_COND_html_FALSE,;t t s,@DX_FLAG_ps@,$DX_FLAG_ps,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_LATEX@,$DX_LATEX,;t t s,@ac_pt_DX_LATEX@,$ac_pt_DX_LATEX,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_MAKEINDEX@,$DX_MAKEINDEX,;t t s,@ac_pt_DX_MAKEINDEX@,$ac_pt_DX_MAKEINDEX,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_DVIPS@,$DX_DVIPS,;t t s,@ac_pt_DX_DVIPS@,$ac_pt_DX_DVIPS,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_EGREP@,$DX_EGREP,;t t s,@ac_pt_DX_EGREP@,$ac_pt_DX_EGREP,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_COND_ps_TRUE@,$DX_COND_ps_TRUE,;t t s,@DX_COND_ps_FALSE@,$DX_COND_ps_FALSE,;t t s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_PDFLATEX@,$DX_PDFLATEX,;t t s,@ac_pt_DX_PDFLATEX@,$ac_pt_DX_PDFLATEX,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t -s,@DX_FLAG_pdf@,$DX_FLAG_pdf,;t t s,@DX_COND_pdf_TRUE@,$DX_COND_pdf_TRUE,;t t s,@DX_COND_pdf_FALSE@,$DX_COND_pdf_FALSE,;t t s,@DX_COND_latex_TRUE@,$DX_COND_latex_TRUE,;t t @@ -27943,4 +28051,3 @@ if test "$no_create" != yes; then $ac_cs_success || { (exit 1); exit 1; } fi - diff --git a/configure.ac b/configure.ac index effa6af..fd07125 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.50]) -AC_INIT([shibboleth], [2.1], [shibboleth-users@internet2.edu], [shibboleth]) +AC_INIT([shibboleth], [2.2.1], [shibboleth-users@internet2.edu], [shibboleth]) AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE([shibboleth],[2.1]) +AM_INIT_AUTOMAKE([shibboleth],[2.2.1]) sinclude(doxygen.m4) sinclude(acx_pthread.m4) @@ -85,36 +85,6 @@ else CXXFLAGS="$PTHREAD_CFLAGS $CXXFLAGS" fi -# Thank you Solaris, really. -AC_MSG_CHECKING(for ctime_r) - if test -z "$ac_cv_ctime_args"; then - AC_TRY_COMPILE( - [#include ], - [ - time_t clock; - char buf[26]; - ctime_r(&clock, buf); - ], ac_cv_ctime_args=2) - - AC_TRY_COMPILE( - [#include ], - [ - time_t clock; - char buf[26]; - ctime_r(&clock, buf, 26); - ], ac_cv_ctime_args=3) - fi - if test -z "$ac_cv_ctime_args"; then - AC_MSG_RESULT(no) - else - if test "$ac_cv_ctime_args" = 2; then - AC_DEFINE(HAVE_CTIME_R_2,1,[Define if ctime_r is present with 2 parameters.]) - elif test "$ac_cv_ctime_args" = 3; then - AC_DEFINE(HAVE_CTIME_R_3,1,[Define if ctime_r is present with 3 parameters.]) - fi - AC_MSG_RESULT([yes, and it takes $ac_cv_ctime_args arguments]) - fi - # OpenSSL settings AC_ARG_WITH(openssl, AC_HELP_STRING([--with-openssl=PATH], [where openssl is installed]), @@ -146,6 +116,36 @@ AC_LANG(C++) AC_CXX_REQUIRE_STL AC_CXX_NAMESPACES +# Thank you Solaris, really. +AC_MSG_CHECKING(for ctime_r) + if test -z "$ac_cv_ctime_args"; then + AC_TRY_COMPILE( + [#include ], + [ + time_t clock; + char buf[26]; + ctime_r(&clock, buf); + ], ac_cv_ctime_args=2) + + AC_TRY_COMPILE( + [#include ], + [ + time_t clock; + char buf[26]; + ctime_r(&clock, buf, 26); + ], ac_cv_ctime_args=3) + fi + if test -z "$ac_cv_ctime_args"; then + AC_MSG_RESULT(no) + else + if test "$ac_cv_ctime_args" = 2; then + AC_DEFINE(HAVE_CTIME_R_2,1,[Define if ctime_r is present with 2 parameters.]) + elif test "$ac_cv_ctime_args" = 3; then + AC_DEFINE(HAVE_CTIME_R_3,1,[Define if ctime_r is present with 3 parameters.]) + fi + AC_MSG_RESULT([yes, and it takes $ac_cv_ctime_args arguments]) + fi + # log4shib settings (favor this version over the log4cpp code) AC_PATH_PROG(LOG4SHIB_CONFIG,log4shib-config) AC_ARG_WITH(log4shib, @@ -222,6 +222,30 @@ AC_TRY_LINK( [AC_DEFINE(HAVE_LIBXERCESC,1,[Define if Xerces-C library was found])], [AC_MSG_ERROR([unable to link with Xerces])]) +AC_MSG_CHECKING([whether Xerces XMLString::release(XMLByte**) exists]) +AC_TRY_COMPILE([#include ], + [using namespace XERCES_CPP_NAMESPACE; + XMLByte* buf=NULL; + XMLString::release(&buf); + ], + [AC_MSG_RESULT([yes])] + [AC_DEFINE([SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE], [1], [Define to 1 if Xerces XMLString includes XMLByte release.])], + [AC_MSG_RESULT([no])]) + +AC_MSG_CHECKING([whether Xerces DOMNodeFilter API returns a short]) +AC_TRY_COMPILE([#include ], + [using namespace XERCES_CPP_NAMESPACE; + class Blocker : public DOMNodeFilter { + public: + short acceptNode(const DOMNode* node) const { + return FILTER_REJECT; + } + }; + static Blocker g_Blocker; + ], + [AC_MSG_RESULT([yes])] + [AC_DEFINE([SHIBSP_XERCESC_SHORT_ACCEPTNODE], [1], [Define to 1 if Xerces DOMNodeFilter API returns a short.])], + [AC_MSG_RESULT([no])]) #XML-Tooling settings AC_ARG_WITH(xmltooling, @@ -305,10 +329,10 @@ AC_CHECK_HEADER([saml/saml2/metadata/Metadata.h],, AC_TRY_LINK( [#include #include ], - [#if _OPENSAML_VERSION >= 20000 + [#if _OPENSAML_VERSION >= 20200 opensaml::SAMLConfig::getConfig(); #else -#error Need OpenSAML version 2.0 or higher +#error Need OpenSAML version 2.2 or higher #endif], [AC_DEFINE(HAVE_SAML,1,[Define if saml library was found])], [AC_MSG_ERROR([unable to link with OpenSAML, or version was too old])]) @@ -339,6 +363,8 @@ AC_SUBST(OPENSAMLXMLDIR) AC_SUBST(LITE_LIBS) AC_SUBST(XMLSEC_LIBS) +AC_CONFIG_FILES([shibboleth.spec pkginfo Portfile]) + # output the underlying makefiles WANT_SUBDIRS="doc schemas configs shibsp shibd util" AC_CONFIG_FILES([Makefile doc/Makefile schemas/Makefile \ @@ -921,4 +947,3 @@ fi LIBTOOL="$LIBTOOL --silent" AC_OUTPUT - diff --git a/depend b/depend index 5cd3481..e14a6ee 100644 --- a/depend +++ b/depend @@ -5,8 +5,8 @@ P SHIBxerces xerces-c P SHIBxmlsec xml-security-c 1.4.0 P SHIBxmltool xmltooling-c - 1.1 + 1.2 P SHIBosaml opensaml-c - 2.1 + 2.2 P SHIBlog4shib log4shib 1.0 diff --git a/doc/FASTCGI.LICENSE b/doc/FASTCGI.LICENSE index 55dde7c..7e6bdfd 100644 --- a/doc/FASTCGI.LICENSE +++ b/doc/FASTCGI.LICENSE @@ -1,28 +1,28 @@ -This FastCGI application library source and object code (the -"Software") and its documentation (the "Documentation") are -copyrighted by Open Market, Inc ("Open Market"). The following terms -apply to all files associated with the Software and Documentation -unless explicitly disclaimed in individual files. - -Open Market permits you to use, copy, modify, distribute, and license -this Software and the Documentation for any purpose, provided that -existing copyright notices are retained in all copies and that this -notice is included verbatim in any distributions. No written -agreement, license, or royalty fee is required for any of the -authorized uses. Modifications to this Software and Documentation may -be copyrighted by their authors and need not follow the licensing -terms described here. If modifications to this Software and -Documentation have new licensing terms, the new terms must be clearly -indicated on the first page of each file where they apply. - -OPEN MARKET MAKES NO EXPRESS OR IMPLIED WARRANTY WITH RESPECT TO THE -SOFTWARE OR THE DOCUMENTATION, INCLUDING WITHOUT LIMITATION ANY -WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN -NO EVENT SHALL OPEN MARKET BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY -DAMAGES ARISING FROM OR RELATING TO THIS SOFTWARE OR THE -DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY INDIRECT, SPECIAL OR -CONSEQUENTIAL DAMAGES OR SIMILAR DAMAGES, INCLUDING LOST PROFITS OR -LOST DATA, EVEN IF OPEN MARKET HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS". -OPEN MARKET HAS NO LIABILITY IN CONTRACT, TORT, NEGLIGENCE OR -OTHERWISE ARISING OUT OF THIS SOFTWARE OR THE DOCUMENTATION. +This FastCGI application library source and object code (the +"Software") and its documentation (the "Documentation") are +copyrighted by Open Market, Inc ("Open Market"). The following terms +apply to all files associated with the Software and Documentation +unless explicitly disclaimed in individual files. + +Open Market permits you to use, copy, modify, distribute, and license +this Software and the Documentation for any purpose, provided that +existing copyright notices are retained in all copies and that this +notice is included verbatim in any distributions. No written +agreement, license, or royalty fee is required for any of the +authorized uses. Modifications to this Software and Documentation may +be copyrighted by their authors and need not follow the licensing +terms described here. If modifications to this Software and +Documentation have new licensing terms, the new terms must be clearly +indicated on the first page of each file where they apply. + +OPEN MARKET MAKES NO EXPRESS OR IMPLIED WARRANTY WITH RESPECT TO THE +SOFTWARE OR THE DOCUMENTATION, INCLUDING WITHOUT LIMITATION ANY +WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN +NO EVENT SHALL OPEN MARKET BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY +DAMAGES ARISING FROM OR RELATING TO THIS SOFTWARE OR THE +DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES OR SIMILAR DAMAGES, INCLUDING LOST PROFITS OR +LOST DATA, EVEN IF OPEN MARKET HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS". +OPEN MARKET HAS NO LIABILITY IN CONTRACT, TORT, NEGLIGENCE OR +OTHERWISE ARISING OUT OF THIS SOFTWARE OR THE DOCUMENTATION. diff --git a/doc/LOG4CPP.LICENSE b/doc/LOG4CPP.LICENSE index cbee875..b1e3f5a 100644 --- a/doc/LOG4CPP.LICENSE +++ b/doc/LOG4CPP.LICENSE @@ -1,504 +1,504 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/doc/Makefile.am b/doc/Makefile.am index 3b312ee..03f93ee 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,6 +1,10 @@ AUTOMAKE_OPTIONS = foreign -pkgdocdir = $(datadir)/doc/@PACKAGE@ +pkgdocdir = $(datadir)/doc/@PACKAGE@-@PACKAGE_VERSION@ + +install-data-hook: + cp -r api $(DESTDIR)$(pkgdocdir) + rm -rf `find $(DESTDIR)$(pkgdocdir)/api -name .svn` docfiles = \ CREDITS.txt \ diff --git a/doc/Makefile.in b/doc/Makefile.in index 7e4107e..ab75e34 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -257,7 +257,7 @@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ xs = @xs@ AUTOMAKE_OPTIONS = foreign -pkgdocdir = $(datadir)/doc/@PACKAGE@ +pkgdocdir = $(datadir)/doc/@PACKAGE@-@PACKAGE_VERSION@ docfiles = \ CREDITS.txt \ LICENSE.txt \ @@ -415,6 +415,8 @@ info: info-am info-am: install-data-am: install-pkgdocDATA + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook install-exec-am: @@ -445,13 +447,18 @@ uninstall-am: uninstall-info-am uninstall-pkgdocDATA .PHONY: all all-am check check-am clean clean-generic clean-libtool \ distclean distclean-generic distclean-libtool distdir dvi \ dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-exec install-exec-am \ - install-info install-info-am install-man install-pkgdocDATA \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - uninstall uninstall-am uninstall-info-am uninstall-pkgdocDATA - + install-data install-data-am install-data-hook install-exec \ + install-exec-am install-info install-info-am install-man \ + install-pkgdocDATA install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am uninstall uninstall-am uninstall-info-am \ + uninstall-pkgdocDATA + + +install-data-hook: + cp -r api $(DESTDIR)$(pkgdocdir) + rm -rf `find $(DESTDIR)$(pkgdocdir)/api -name .svn` # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/doc/OPENSSL.LICENSE b/doc/OPENSSL.LICENSE index cdc9ade..4027788 100644 --- a/doc/OPENSSL.LICENSE +++ b/doc/OPENSSL.LICENSE @@ -1,127 +1,127 @@ - - LICENSE ISSUES - ============== - - The OpenSSL toolkit stays under a dual license, i.e. both the conditions of - the OpenSSL License and the original SSLeay license apply to the toolkit. - See below for the actual license texts. Actually both licenses are BSD-style - Open Source licenses. In case of any license issues related to OpenSSL - please contact openssl-core@openssl.org. - - OpenSSL License - --------------- - -/* ==================================================================== - * Copyright (c) 1998-2004 The OpenSSL Project. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" - * - * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * openssl-core@openssl.org. - * - * 5. Products derived from this software may not be called "OpenSSL" - * nor may "OpenSSL" appear in their names without prior written - * permission of the OpenSSL Project. - * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the OpenSSL Project - * for use in the OpenSSL Toolkit (http://www.openssl.org/)" - * - * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * ==================================================================== - * - * This product includes cryptographic software written by Eric Young - * (eay@cryptsoft.com). This product includes software written by Tim - * Hudson (tjh@cryptsoft.com). - * - */ - - Original SSLeay License - ----------------------- - -/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young (eay@cryptsoft.com). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@cryptsoft.com). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@cryptsoft.com)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] - */ - + + LICENSE ISSUES + ============== + + The OpenSSL toolkit stays under a dual license, i.e. both the conditions of + the OpenSSL License and the original SSLeay license apply to the toolkit. + See below for the actual license texts. Actually both licenses are BSD-style + Open Source licenses. In case of any license issues related to OpenSSL + please contact openssl-core@openssl.org. + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2004 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + diff --git a/doc/README.txt b/doc/README.txt index 7b15351..6443108 100644 --- a/doc/README.txt +++ b/doc/README.txt @@ -1,5 +1,4 @@ -August 8, 2008 -Version 2.1 +Version 2.2.1 Welcome to Internet2's Shibboleth diff --git a/doc/RELEASE.txt b/doc/RELEASE.txt index 3efe64b..66ee2a7 100644 --- a/doc/RELEASE.txt +++ b/doc/RELEASE.txt @@ -1,14 +1,14 @@ Release Notes Shibboleth Native SP -2.1 -8/8/2008 +2.2.1 NOTE: The shibboleth2.xml configuration format in this release -is fully compatible with the 2.0 release. +is fully compatible with the 2.1 release, 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/secure/IssueNavigator.jspa?reset=true&&pid=10011&fixfor=10129&status=5&status=6&sorter/field=issuekey&sorter/order=ASC +https://bugs.internet2.edu/jira/secure/IssueNavigator.jspa?reset=true&&pid=10011&fixfor=10232&status=5&status=6&sorter/field=issuekey&sorter/order=ASC Fully Supported @@ -38,7 +38,8 @@ Fully Supported - Metadata Providers - Bulk resolution via local file, or URL with local file backup - Dynamic resolution and caching based on entityID - - Filtering based on whitelist, blacklist, or signature verification + - Filtering based on whitelist, blacklist, or signature verification + - Support for enhanced PKI processing in transport and signature verification - Metadata Generation Handler - Generates and optionally signs SAML metadata based on SP configuration @@ -58,6 +59,7 @@ Fully Supported - XML signing - Simple "blob" signing - TLS X.509 certificate authentication + - SAML condition handling - Client transport authentication to SOAP endpoints via libcurl - TLS X.509 client certificates @@ -74,6 +76,10 @@ Fully Supported - Strings - Value/scope pairs (legacy and value@scope syntaxes supported) - NameIDs + - XML to base64-encoded XML + - DOM to internal data structure + - KeyInfo-based data, including metadata-derived KeyDescriptors + - Metadata EntityAttributes extension "tags" - Attribute Filtering - Policy language compatible with IdP filtering, except that references @@ -89,7 +95,7 @@ Fully Supported - Enhanced Spoofing Detection - Detects and blocks client headers that would match known attribute headers - - Does not support Apache mod_rewrite, but can be disabled when necessary + - Key-based mechanism to handle internal server redirection while maintaining protection - ODBC Clustering Support - Tested against a few different servers with various drivers @@ -102,6 +108,9 @@ Fully Supported - Reporting of SAML status errors - Optional redirection to custom error handler +- Form POST data preservation + - Support on Apache for preserving URL-encoded form data across SSO + - Apache module enhancements - "OR" coexistence with other authorization modules - htaccess-based override of any valid RequestMap property diff --git a/doc/main.css b/doc/main.css index a3eb681..a11cebe 100644 --- a/doc/main.css +++ b/doc/main.css @@ -1,39 +1,39 @@ -body { - background-color: #FFFFFF; - font-family : Geneva, Arial, Helvetica, sans-serif; - text-align: center; - letter-spacing: 0px; - color: black; - text-align: left; -} - -p { - font-size: 10pt; - margin-top: 20px; - margin-bottom: 20px; -} - - -li { - - font-size: 10pt; -} - -h1 { - font-size: 14pt; - font-weight: bold; -} - -h2 { - font-size: 12pt; - font-weight: bold; -} - -.error { - font-size: 10pt; - font-weight: bold; -} - -img { - margin-bottom: 15px; +body { + background-color: #FFFFFF; + font-family : Geneva, Arial, Helvetica, sans-serif; + text-align: center; + letter-spacing: 0px; + color: black; + text-align: left; +} + +p { + font-size: 10pt; + margin-top: 20px; + margin-bottom: 20px; +} + + +li { + + font-size: 10pt; +} + +h1 { + font-size: 14pt; + font-weight: bold; +} + +h2 { + font-size: 12pt; + font-weight: bold; +} + +.error { + font-size: 10pt; + font-weight: bold; +} + +img { + margin-bottom: 15px; } \ No newline at end of file diff --git a/doxygen.m4 b/doxygen.m4 index e4688de..9bda4f6 100644 --- a/doxygen.m4 +++ b/doxygen.m4 @@ -51,6 +51,9 @@ AC_DEFUN([DX_FEATURE_xml], OFF) AC_DEFUN([DX_FEATURE_pdf], ON) AC_DEFUN([DX_FEATURE_ps], ON) +# Compatibility with older autoconf versions. +m4_ifdef([AS_HELP_STRING], , [m4_define([AS_HELP_STRING], m4_defn([AC_HELP_STRING]))]) + ## --------------- ## ## Private macros. ## ## --------------- ## @@ -78,7 +81,7 @@ AC_DEFUN([DX_REQUIRE_PROG], [ AC_PATH_TOOL([$1], [$2]) if test "$DX_FLAG_[]DX_CURRENT_FEATURE$$1" = 1; then AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) - AC_SUBST([DX_FLAG_[]DX_CURRENT_FEATURE], 0) + AC_SUBST([DX_FLAG_]DX_CURRENT_FEATURE, 0) fi ]) @@ -101,7 +104,7 @@ test "$DX_FLAG_$1" = "$2" \ # ---------------------------------------------------------- # Turn off the DX_CURRENT_FEATURE if the required feature is off. AC_DEFUN([DX_CLEAR_DEPEND], [ -test "$DX_FLAG_$1" = "$2" || AC_SUBST([DX_FLAG_[]DX_CURRENT_FEATURE], 0) +test "$DX_FLAG_$1" = "$2" || AC_SUBST([DX_FLAG_]DX_CURRENT_FEATURE, 0) ]) # DX_FEATURE_ARG(FEATURE, DESCRIPTION, diff --git a/fastcgi/shibauthorizer.cpp b/fastcgi/shibauthorizer.cpp index 282dad9..59f4417 100644 --- a/fastcgi/shibauthorizer.cpp +++ b/fastcgi/shibauthorizer.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -115,6 +115,9 @@ public: return s ? atol(s) : 0; } string getRemoteAddr() const { + string ret = AbstractSPRequest::getRemoteAddr(); + if (!ret.empty()) + return ret; const char* s = FCGX_GetParam("REMOTE_ADDR", m_req->envp); return s ? s : ""; } @@ -172,6 +175,23 @@ public: } return ""; } + void setAuthType(const char* authtype) { + if (authtype) + m_request_headers["AUTH_TYPE"] = authtype; + else + m_request_headers.erase("AUTH_TYPE"); + } + string getAuthType() const { + map::const_iterator i = m_request_headers.find("AUTH_TYPE"); + if (i != m_request_headers.end()) + return i->second; + else { + char* auth_type = FCGX_GetParam("AUTH_TYPE", m_req->envp); + if (auth_type) + return auth_type; + } + return ""; + } void setResponseHeader(const char* name, const char* value) { // Set for later. if (value) diff --git a/fastcgi/shibauthorizer.rc b/fastcgi/shibauthorizer.rc index 4a2069a..c9b4c1d 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,1,0,0 - PRODUCTVERSION 2,1,0,0 + FILEVERSION 2,2,1,0 + PRODUCTVERSION 2,2,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, 1, 0, 0\0" + VALUE "FileVersion", "2, 2, 1, 0\0" VALUE "InternalName", "shibauthorizer\0" - VALUE "LegalCopyright", "Copyright © 2008 Internet2\0" + VALUE "LegalCopyright", "Copyright © 2009 Internet2\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "shibauthorizer.exe\0" VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Shibboleth 2.1\0" - VALUE "ProductVersion", "2, 1, 0, 0\0" + VALUE "ProductName", "Shibboleth 2.2.1\0" + VALUE "ProductVersion", "2, 2, 1, 0\0" VALUE "SpecialBuild", "\0" END END diff --git a/fastcgi/shibauthorizer.vcproj b/fastcgi/shibauthorizer.vcproj index e8f395a..56e1896 100644 --- a/fastcgi/shibauthorizer.vcproj +++ b/fastcgi/shibauthorizer.vcproj @@ -1,9 +1,10 @@ - - - @@ -326,13 +324,14 @@ /> - @@ -381,7 +377,7 @@ />
envp); return s ? s : ""; } diff --git a/fastcgi/shibresponder.rc b/fastcgi/shibresponder.rc index c8bded3..4bccb0f 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,1,0,0 - PRODUCTVERSION 2,1,0,0 + FILEVERSION 2,2,1,0 + PRODUCTVERSION 2,2,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, 1, 0, 0\0" + VALUE "FileVersion", "2, 2, 1, 0\0" VALUE "InternalName", "shibresponder\0" - VALUE "LegalCopyright", "Copyright © 2008 Internet2\0" + VALUE "LegalCopyright", "Copyright © 2009 Internet2\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "shibresponder.exe\0" VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Shibboleth 2.1\0" - VALUE "ProductVersion", "2, 1, 0, 0\0" + VALUE "ProductName", "Shibboleth 2.2.1\0" + VALUE "ProductVersion", "2, 2, 1, 0\0" VALUE "SpecialBuild", "\0" END END diff --git a/fastcgi/shibresponder.vcproj b/fastcgi/shibresponder.vcproj index 0d6518f..164fa25 100644 --- a/fastcgi/shibresponder.vcproj +++ b/fastcgi/shibresponder.vcproj @@ -1,9 +1,11 @@ - - - @@ -326,13 +325,14 @@ /> - @@ -381,7 +378,7 @@ /> #include @@ -88,18 +89,14 @@ namespace { set m_aliases; }; - struct context_t { - char* m_user; - bool m_checked; - }; - HINSTANCE g_hinstDLL; SPConfig* g_Config = NULL; map g_Sites; bool g_bNormalizeRequest = true; - string g_unsetHeaderValue; + string g_unsetHeaderValue,g_spoofKey; bool g_checkSpoofing = true; bool g_catchAll = false; + bool g_bSafeHeaderNames = false; vector g_NoCerts; } @@ -117,6 +114,17 @@ BOOL LogEvent( return (DeregisterEventSource(hElog) && res); } +void _my_invalid_parameter_handler( + const wchar_t * expression, + const wchar_t * function, + const wchar_t * file, + unsigned int line, + uintptr_t pReserved + ) +{ + return; +} + extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID) { if (fdwReason==DLL_PROCESS_ATTACH) @@ -189,18 +197,72 @@ extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer) Locker locker(sp); const PropertySet* props=sp->getPropertySet("InProcess"); if (props) { - pair unsetValue=props->getString("unsetHeaderValue"); - if (unsetValue.first) - g_unsetHeaderValue = unsetValue.second; pair flag=props->getBool("checkSpoofing"); g_checkSpoofing = !flag.first || flag.second; flag=props->getBool("catchAll"); g_catchAll = flag.first && flag.second; +<<<<<<< .mine + pair unsetValue=props->getString("unsetHeaderValue"); + if (unsetValue.first) + g_unsetHeaderValue = unsetValue.second; + if (g_checkSpoofing) { + unsetValue = props->getString("spoofKey"); + if (unsetValue.first) + g_spoofKey = unsetValue.second; + else { + unsigned int randkey=0,randkey2=0,randkey3=0,randkey4=0; + if (rand_s(&randkey) == 0 && rand_s(&randkey2) == 0 && rand_s(&randkey3) == 0 && rand_s(&randkey4) == 0) { + ostringstream keystr; + keystr << randkey << randkey2 << randkey3 << randkey4; + g_spoofKey = keystr.str(); + } + else { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Filter failed to generate a random anti-spoofing key (if this is Windows 2000 set one manually)."); + g_Config->term(); + g_Config=NULL; + return FALSE; + } + } + } + +======= + pair unsetValue=props->getString("unsetHeaderValue"); + if (unsetValue.first) + g_unsetHeaderValue = unsetValue.second; + if (g_checkSpoofing) { + unsetValue = props->getString("spoofKey"); + if (unsetValue.first) + g_spoofKey = unsetValue.second; + else { + _invalid_parameter_handler old = _set_invalid_parameter_handler(_my_invalid_parameter_handler); + unsigned int randkey=0,randkey2=0,randkey3=0,randkey4=0; + if (rand_s(&randkey) == 0 && rand_s(&randkey2) == 0 && rand_s(&randkey3) == 0 && rand_s(&randkey4) == 0) { + _set_invalid_parameter_handler(old); + ostringstream keystr; + keystr << randkey << randkey2 << randkey3 << randkey4; + g_spoofKey = keystr.str(); + } + else { + _set_invalid_parameter_handler(old); + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Filter failed to generate a random anti-spoofing key (if this is Windows 2000 set one manually)."); + locker.assign(); // pops lock on SP config + g_Config->term(); + g_Config=NULL; + return FALSE; + } + } + } + +>>>>>>> .r3097 props = props->getPropertySet("ISAPI"); if (props) { flag = props->getBool("normalizeRequest"); g_bNormalizeRequest = !flag.first || flag.second; + flag = props->getBool("safeHeaderNames"); + g_bSafeHeaderNames = flag.first && flag.second; const DOMElement* child = XMLHelper::getFirstChildElement(props->getElement(),Site); while (child) { auto_ptr_char id(child->getAttributeNS(NULL,id)); @@ -348,10 +410,11 @@ class ShibTargetIsapiF : public AbstractSPRequest string m_scheme,m_hostname; mutable string m_remote_addr,m_content_type,m_method; dynabuf m_allhttp; + bool m_firsttime; public: ShibTargetIsapiF(PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, const site_t& site) - : AbstractSPRequest(SHIBSP_LOGCAT".ISAPI"), m_pfc(pfc), m_pn(pn), m_allhttp(4096) { + : AbstractSPRequest(SHIBSP_LOGCAT".ISAPI"), m_pfc(pfc), m_pn(pn), m_allhttp(4096), m_firsttime(true) { // URL path always come from IIS. dynabuf var(256); @@ -382,13 +445,14 @@ public: if (site.m_name!=m_hostname && site.m_aliases.find(m_hostname)==site.m_aliases.end()) m_hostname=site.m_name; - if (!pfc->pFilterContext) { - pfc->pFilterContext = pfc->AllocMem(pfc, sizeof(context_t), NULL); - if (static_cast(pfc->pFilterContext)) { - static_cast(pfc->pFilterContext)->m_user = NULL; - static_cast(pfc->pFilterContext)->m_checked = false; - } + if (!g_spoofKey.empty()) { + GetHeader(pn, pfc, "ShibSpoofCheck:", var, 32, false); + if (!var.empty() && g_spoofKey == (char*)var) + m_firsttime = false; } + + if (!m_firsttime) + log(SPDebug, "ISAPI filter running more than once"); } ~ShibTargetIsapiF() { } @@ -401,10 +465,15 @@ public: int getPort() const { return m_port; } + const char* getQueryString() const { + const char* uri = getRequestURI(); + uri = (uri ? strchr(uri, '?') : NULL); + return uri ? (uri + 1) : NULL; + } const char* getMethod() const { if (m_method.empty()) { dynabuf var(5); - GetServerVariable(m_pfc,"REQUEST_METHOD",var,5,false); + GetServerVariable(m_pfc,"HTTP_METHOD",var,5,false); if (!var.empty()) m_method = var; } @@ -413,16 +482,14 @@ public: string getContentType() const { if (m_content_type.empty()) { dynabuf var(32); - GetServerVariable(m_pfc,"CONTENT_TYPE",var,32,false); + GetServerVariable(m_pfc,"HTTP_CONTENT_TYPE",var,32,false); if (!var.empty()) m_content_type = var; } return m_content_type; } - long getContentLength() const { - return 0; - } string getRemoteAddr() const { + m_remote_addr = AbstractSPRequest::getRemoteAddr(); if (m_remote_addr.empty()) { dynabuf var(16); GetServerVariable(m_pfc,"REMOTE_ADDR",var,16,false); @@ -433,43 +500,64 @@ public: } void log(SPLogLevel level, const string& msg) { AbstractSPRequest::log(level,msg); - if (level >= SPError) + if (level >= SPCrit) LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str()); } + string makeSafeHeader(const char* rawname) const { + string hdr; + for (; *rawname; ++rawname) { + if (isalnum(*rawname)) + hdr += *rawname; + } + return (hdr + ':'); + } void clearHeader(const char* rawname, const char* cginame) { - if (g_checkSpoofing && m_pfc->pFilterContext && !static_cast(m_pfc->pFilterContext)->m_checked) { + if (g_checkSpoofing && m_firsttime) { if (m_allhttp.empty()) - GetServerVariable(m_pfc,"ALL_HTTP",m_allhttp,4096); - if (strstr(m_allhttp, cginame)) - throw opensaml::SecurityPolicyException("Attempt to spoof header ($1) was detected.", params(1, rawname)); + GetServerVariable(m_pfc, "ALL_HTTP", m_allhttp, 4096); + string hdr = g_bSafeHeaderNames ? ("HTTP_" + makeSafeHeader(cginame + 5)) : (string(cginame) + ':'); + if (strstr(m_allhttp, hdr.c_str())) + throw opensaml::SecurityPolicyException("Attempt to spoof header ($1) was detected.", params(1, hdr.c_str())); } - string hdr(!strcmp(rawname,"REMOTE_USER") ? "remote-user" : rawname); - hdr += ':'; - m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(g_unsetHeaderValue.c_str())); + if (g_bSafeHeaderNames) { + string hdr = makeSafeHeader(rawname); + m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(g_unsetHeaderValue.c_str())); + } + else if (!strcmp(rawname,"REMOTE_USER")) { + m_pn->SetHeader(m_pfc, "remote-user:", const_cast(g_unsetHeaderValue.c_str())); + m_pn->SetHeader(m_pfc, "remote_user:", const_cast(g_unsetHeaderValue.c_str())); + } + else { + string hdr = string(rawname) + ':'; + m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(g_unsetHeaderValue.c_str())); + } } void setHeader(const char* name, const char* value) { - string hdr(name); - hdr += ':'; + string hdr = g_bSafeHeaderNames ? makeSafeHeader(name) : (string(name) + ':'); m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(value)); } + string getSecureHeader(const char* name) const { + string hdr = g_bSafeHeaderNames ? makeSafeHeader(name) : (string(name) + ':'); + dynabuf buf(256); + GetHeader(m_pn, m_pfc, const_cast(hdr.c_str()), buf, 256, false); + return string(buf.empty() ? "" : buf); + } string getHeader(const char* name) const { string hdr(name); hdr += ':'; dynabuf buf(256); GetHeader(m_pn, m_pfc, const_cast(hdr.c_str()), buf, 256, false); - return string(buf); + return string(buf.empty() ? "" : buf); } void setRemoteUser(const char* user) { setHeader("remote-user", user); - if (m_pfc->pFilterContext) { - if (!user || !*user) - static_cast(m_pfc->pFilterContext)->m_user = NULL; - else if (static_cast(m_pfc->pFilterContext)->m_user = (char*)m_pfc->AllocMem(m_pfc, sizeof(char) * (strlen(user) + 1), NULL)) - strcpy(static_cast(m_pfc->pFilterContext)->m_user, user); - } + if (!user || !*user) + m_pfc->pFilterContext = NULL; + else if (m_pfc->pFilterContext = m_pfc->AllocMem(m_pfc, sizeof(char) * (strlen(user) + 1), NULL)) + strcpy(reinterpret_cast(m_pfc->pFilterContext), user); } string getRemoteUser() const { - return getHeader("remote-user"); + return getSecureHeader("remote-user"); } void setResponseHeader(const char* name, const char* value) { // Set for later. @@ -527,8 +615,8 @@ public: } // The filter never processes the POST, so stub these methods. - const char* getQueryString() const { throw IOException("getQueryString not implemented"); } - const char* getRequestBody() const { throw IOException("getRequestBody not implemented"); } + long getContentLength() const { throw IOException("The request's Content-Length is not available to an ISAPI filter."); } + const char* getRequestBody() const { throw IOException("The request body is not available to an ISAPI filter."); } }; DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const char* msg) @@ -552,8 +640,8 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat { // Is this a log notification? if (notificationType==SF_NOTIFY_LOG) { - if (pfc->pFilterContext && static_cast(pfc->pFilterContext)->m_user) - ((PHTTP_FILTER_LOG)pvNotification)->pszClientUserName=static_cast(pfc->pFilterContext)->m_user; + if (pfc->pFilterContext) + ((PHTTP_FILTER_LOG)pvNotification)->pszClientUserName=reinterpret_cast(pfc->pFilterContext); return SF_STATUS_REQ_NEXT_NOTIFICATION; } @@ -577,8 +665,8 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat // "false" because we don't override the Shib settings pair res = stf.getServiceProvider().doAuthentication(stf); - if (pfc->pFilterContext) - static_cast(pfc->pFilterContext)->m_checked = true; + if (!g_spoofKey.empty()) + pn->SetHeader(pfc, "ShibSpoofCheck:", const_cast(g_spoofKey.c_str())); if (res.first) return res.second; // "false" because we don't override the Shib settings @@ -754,6 +842,7 @@ public: return m_remote_user; } string getRemoteAddr() const { + m_remote_addr = AbstractSPRequest::getRemoteAddr(); if (m_remote_addr.empty()) { dynabuf var(16); GetServerVariable(m_lpECB, "REMOTE_ADDR", var, 16, false); @@ -764,7 +853,7 @@ public: } void log(SPLogLevel level, const string& msg) const { AbstractSPRequest::log(level,msg); - if (level >= SPError) + if (level >= SPCrit) LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str()); } string getHeader(const char* name) const { @@ -796,13 +885,19 @@ public: throw opensaml::SecurityPolicyException("Size of request body exceeded 1M size limit."); else if (m_lpECB->cbTotalBytes > m_lpECB->cbAvailable) { m_gotBody=true; - char buf[8192]; DWORD datalen=m_lpECB->cbTotalBytes; + if (m_lpECB->cbAvailable > 0) { + m_body.assign(reinterpret_cast(m_lpECB->lpbData),m_lpECB->cbAvailable); + datalen-=m_lpECB->cbAvailable; + } + char buf[8192]; while (datalen) { DWORD buflen=8192; BOOL ret = m_lpECB->ReadClient(m_lpECB->ConnID, buf, &buflen); - if (!ret || !buflen) + if (!ret) throw IOException("Error reading request body from browser."); + else if (!buflen) + throw IOException("Socket closed while reading request body from browser."); m_body.append(buf, buflen); datalen-=buflen; } @@ -872,10 +967,14 @@ public: if (m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_GET_CERT_INFO_EX, (LPVOID)&ccex, (LPDWORD)dwSize, NULL)) { if (ccex.CertContext.cbCertEncoded) { - unsigned int outlen; + xsecsize_t outlen; XMLByte* serialized = Base64::encode(reinterpret_cast(CertificateBuf), ccex.CertContext.cbCertEncoded, &outlen); m_certs.push_back(reinterpret_cast(serialized)); +#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE XMLString::release(&serialized); +#else + XMLString::release((char**)&serialized); +#endif } } } diff --git a/isapi_shib/isapi_shib.cpp.mine b/isapi_shib/isapi_shib.cpp.mine new file mode 100644 index 0000000..9d2278e --- /dev/null +++ b/isapi_shib/isapi_shib.cpp.mine @@ -0,0 +1,993 @@ +/* + * 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. + */ + +/** + * isapi_shib.cpp + * + * Shibboleth ISAPI filter + */ + +#define SHIBSP_LITE +#include "config_win32.h" + +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_RAND_S + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace shibsp; +using namespace xmltooling; +using namespace xercesc; +using namespace std; + +// globals +namespace { + static const XMLCh path[] = UNICODE_LITERAL_4(p,a,t,h); + static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e); + static const XMLCh name[] = UNICODE_LITERAL_4(n,a,m,e); + static const XMLCh port[] = UNICODE_LITERAL_4(p,o,r,t); + static const XMLCh sslport[] = UNICODE_LITERAL_7(s,s,l,p,o,r,t); + static const XMLCh scheme[] = UNICODE_LITERAL_6(s,c,h,e,m,e); + static const XMLCh id[] = UNICODE_LITERAL_2(i,d); + static const XMLCh Alias[] = UNICODE_LITERAL_5(A,l,i,a,s); + static const XMLCh Site[] = UNICODE_LITERAL_4(S,i,t,e); + + struct site_t { + site_t(const DOMElement* e) + { + auto_ptr_char n(e->getAttributeNS(NULL,name)); + auto_ptr_char s(e->getAttributeNS(NULL,scheme)); + auto_ptr_char p(e->getAttributeNS(NULL,port)); + auto_ptr_char p2(e->getAttributeNS(NULL,sslport)); + if (n.get()) m_name=n.get(); + if (s.get()) m_scheme=s.get(); + if (p.get()) m_port=p.get(); + if (p2.get()) m_sslport=p2.get(); + e = XMLHelper::getFirstChildElement(e, Alias); + while (e) { + if (e->hasChildNodes()) { + auto_ptr_char alias(e->getFirstChild()->getNodeValue()); + m_aliases.insert(alias.get()); + } + e = XMLHelper::getNextSiblingElement(e, Alias); + } + } + string m_scheme,m_port,m_sslport,m_name; + set m_aliases; + }; + + HINSTANCE g_hinstDLL; + SPConfig* g_Config = NULL; + map g_Sites; + bool g_bNormalizeRequest = true; + string g_unsetHeaderValue,g_spoofKey; + bool g_checkSpoofing = true; + bool g_catchAll = false; + bool g_bSafeHeaderNames = false; + vector g_NoCerts; +} + +BOOL LogEvent( + LPCSTR lpUNCServerName, + WORD wType, + DWORD dwEventID, + PSID lpUserSid, + LPCSTR message) +{ + LPCSTR messages[] = {message, NULL}; + + HANDLE hElog = RegisterEventSource(lpUNCServerName, "Shibboleth ISAPI Filter"); + BOOL res = ReportEvent(hElog, wType, 0, dwEventID, lpUserSid, 1, 0, messages, NULL); + return (DeregisterEventSource(hElog) && res); +} + +extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID) +{ + if (fdwReason==DLL_PROCESS_ATTACH) + g_hinstDLL=hinstDLL; + return TRUE; +} + +extern "C" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO* pVer) +{ + if (!pVer) + return FALSE; + + if (!g_Config) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Extension mode startup not possible, is the DLL loaded as a filter?"); + return FALSE; + } + + pVer->dwExtensionVersion=HSE_VERSION; + strncpy(pVer->lpszExtensionDesc,"Shibboleth ISAPI Extension",HSE_MAX_EXT_DLL_NAME_LEN-1); + return TRUE; +} + +extern "C" BOOL WINAPI TerminateExtension(DWORD) +{ + return TRUE; // cleanup should happen when filter unloads +} + +extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer) +{ + if (!pVer) + return FALSE; + else if (g_Config) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Reentrant filter initialization, ignoring..."); + return TRUE; + } + + g_Config=&SPConfig::getConfig(); + g_Config->setFeatures( + SPConfig::Listener | + SPConfig::Caching | + SPConfig::RequestMapping | + SPConfig::InProcess | + SPConfig::Logging | + SPConfig::Handlers + ); + if (!g_Config->init()) { + g_Config=NULL; + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Filter startup failed during library initialization, check native log for help."); + return FALSE; + } + + try { + if (!g_Config->instantiate(NULL, true)) + throw runtime_error("unknown error"); + } + catch (exception& ex) { + g_Config->term(); + g_Config=NULL; + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, ex.what()); + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Filter startup failed to load configuration, check native log for details."); + return FALSE; + } + + // Access implementation-specifics and site mappings. + ServiceProvider* sp=g_Config->getServiceProvider(); + Locker locker(sp); + const PropertySet* props=sp->getPropertySet("InProcess"); + if (props) { + pair flag=props->getBool("checkSpoofing"); + g_checkSpoofing = !flag.first || flag.second; + flag=props->getBool("catchAll"); + g_catchAll = flag.first && flag.second; + + pair unsetValue=props->getString("unsetHeaderValue"); + if (unsetValue.first) + g_unsetHeaderValue = unsetValue.second; + if (g_checkSpoofing) { + unsetValue = props->getString("spoofKey"); + if (unsetValue.first) + g_spoofKey = unsetValue.second; + else { + unsigned int randkey=0,randkey2=0,randkey3=0,randkey4=0; + if (rand_s(&randkey) == 0 && rand_s(&randkey2) == 0 && rand_s(&randkey3) == 0 && rand_s(&randkey4) == 0) { + ostringstream keystr; + keystr << randkey << randkey2 << randkey3 << randkey4; + g_spoofKey = keystr.str(); + } + else { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Filter failed to generate a random anti-spoofing key (if this is Windows 2000 set one manually)."); + g_Config->term(); + g_Config=NULL; + return FALSE; + } + } + } + + props = props->getPropertySet("ISAPI"); + if (props) { + flag = props->getBool("normalizeRequest"); + g_bNormalizeRequest = !flag.first || flag.second; + flag = props->getBool("safeHeaderNames"); + g_bSafeHeaderNames = flag.first && flag.second; + const DOMElement* child = XMLHelper::getFirstChildElement(props->getElement(),Site); + while (child) { + auto_ptr_char id(child->getAttributeNS(NULL,id)); + if (id.get()) + g_Sites.insert(pair(id.get(),site_t(child))); + child=XMLHelper::getNextSiblingElement(child,Site); + } + } + } + + pVer->dwFilterVersion=HTTP_FILTER_REVISION; + strncpy(pVer->lpszFilterDesc,"Shibboleth ISAPI Filter",SF_MAX_FILTER_DESC_LEN); + pVer->dwFlags=(SF_NOTIFY_ORDER_HIGH | + SF_NOTIFY_SECURE_PORT | + SF_NOTIFY_NONSECURE_PORT | + SF_NOTIFY_PREPROC_HEADERS | + SF_NOTIFY_LOG); + LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter initialized..."); + return TRUE; +} + +extern "C" BOOL WINAPI TerminateFilter(DWORD) +{ + if (g_Config) + g_Config->term(); + g_Config = NULL; + LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter shut down..."); + return TRUE; +} + +/* Next up, some suck-free versions of various APIs. + + You DON'T require people to guess the buffer size and THEN tell them the right size. + Returning an LPCSTR is apparently way beyond their ken. Not to mention the fact that + constant strings aren't typed as such, making it just that much harder. These versions + are now updated to use a special growable buffer object, modeled after the standard + string class. The standard string won't work because they left out the option to + pre-allocate a non-constant buffer. +*/ + +class dynabuf +{ +public: + dynabuf() { bufptr=NULL; buflen=0; } + dynabuf(size_t s) { bufptr=new char[buflen=s]; *bufptr=0; } + ~dynabuf() { delete[] bufptr; } + size_t length() const { return bufptr ? strlen(bufptr) : 0; } + size_t size() const { return buflen; } + bool empty() const { return length()==0; } + void reserve(size_t s, bool keep=false); + void erase() { if (bufptr) memset(bufptr,0,buflen); } + operator char*() { return bufptr; } + bool operator ==(const char* s) const; + bool operator !=(const char* s) const { return !(*this==s); } +private: + char* bufptr; + size_t buflen; +}; + +void dynabuf::reserve(size_t s, bool keep) +{ + if (s<=buflen) + return; + char* p=new char[s]; + if (keep) + while (buflen--) + p[buflen]=bufptr[buflen]; + buflen=s; + delete[] bufptr; + bufptr=p; +} + +bool dynabuf::operator==(const char* s) const +{ + if (buflen==NULL || s==NULL) + return (buflen==NULL && s==NULL); + else + return strcmp(bufptr,s)==0; +} + +void GetServerVariable(PHTTP_FILTER_CONTEXT pfc, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true) +{ + s.reserve(size); + s.erase(); + size=s.size(); + + while (!pfc->GetServerVariable(pfc,lpszVariable,s,&size)) { + // Grumble. Check the error. + DWORD e=GetLastError(); + if (e==ERROR_INSUFFICIENT_BUFFER) + s.reserve(size); + else + break; + } + if (bRequired && s.empty()) + throw ERROR_NO_DATA; +} + +void GetServerVariable(LPEXTENSION_CONTROL_BLOCK lpECB, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true) +{ + s.reserve(size); + s.erase(); + size=s.size(); + + while (!lpECB->GetServerVariable(lpECB->ConnID,lpszVariable,s,&size)) { + // Grumble. Check the error. + DWORD e=GetLastError(); + if (e==ERROR_INSUFFICIENT_BUFFER) + s.reserve(size); + else + break; + } + if (bRequired && s.empty()) + throw ERROR_NO_DATA; +} + +void GetHeader(PHTTP_FILTER_PREPROC_HEADERS pn, PHTTP_FILTER_CONTEXT pfc, + LPSTR lpszName, dynabuf& s, DWORD size=80, bool bRequired=true) +{ + s.reserve(size); + s.erase(); + size=s.size(); + + while (!pn->GetHeader(pfc,lpszName,s,&size)) { + // Grumble. Check the error. + DWORD e=GetLastError(); + if (e==ERROR_INSUFFICIENT_BUFFER) + s.reserve(size); + else + break; + } + if (bRequired && s.empty()) + throw ERROR_NO_DATA; +} + +/****************************************************************************/ +// ISAPI Filter + +class ShibTargetIsapiF : public AbstractSPRequest +{ + PHTTP_FILTER_CONTEXT m_pfc; + PHTTP_FILTER_PREPROC_HEADERS m_pn; + multimap m_headers; + int m_port; + string m_scheme,m_hostname; + mutable string m_remote_addr,m_content_type,m_method; + dynabuf m_allhttp; + bool m_firsttime; + +public: + ShibTargetIsapiF(PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, const site_t& site) + : AbstractSPRequest(SHIBSP_LOGCAT".ISAPI"), m_pfc(pfc), m_pn(pn), m_allhttp(4096), m_firsttime(true) { + + // URL path always come from IIS. + dynabuf var(256); + GetHeader(pn,pfc,"url",var,256,false); + setRequestURI(var); + + // Port may come from IIS or from site def. + if (!g_bNormalizeRequest || (pfc->fIsSecurePort && site.m_sslport.empty()) || (!pfc->fIsSecurePort && site.m_port.empty())) { + GetServerVariable(pfc,"SERVER_PORT",var,10); + m_port = atoi(var); + } + else if (pfc->fIsSecurePort) { + m_port = atoi(site.m_sslport.c_str()); + } + else { + m_port = atoi(site.m_port.c_str()); + } + + // Scheme may come from site def or be derived from IIS. + m_scheme=site.m_scheme; + if (m_scheme.empty() || !g_bNormalizeRequest) + m_scheme=pfc->fIsSecurePort ? "https" : "http"; + + GetServerVariable(pfc,"SERVER_NAME",var,32); + + // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name. + m_hostname = var; + if (site.m_name!=m_hostname && site.m_aliases.find(m_hostname)==site.m_aliases.end()) + m_hostname=site.m_name; + + if (!g_spoofKey.empty()) { + GetHeader(pn, pfc, "ShibSpoofCheck:", var, 32, false); + if (!var.empty() && g_spoofKey == (char*)var) + m_firsttime = false; + } + + if (!m_firsttime) + log(SPDebug, "ISAPI filter running more than once"); + } + ~ShibTargetIsapiF() { } + + const char* getScheme() const { + return m_scheme.c_str(); + } + const char* getHostname() const { + return m_hostname.c_str(); + } + int getPort() const { + return m_port; + } + const char* getQueryString() const { + const char* uri = getRequestURI(); + uri = (uri ? strchr(uri, '?') : NULL); + return uri ? (uri + 1) : NULL; + } + const char* getMethod() const { + if (m_method.empty()) { + dynabuf var(5); + GetServerVariable(m_pfc,"HTTP_METHOD",var,5,false); + if (!var.empty()) + m_method = var; + } + return m_method.c_str(); + } + string getContentType() const { + if (m_content_type.empty()) { + dynabuf var(32); + GetServerVariable(m_pfc,"HTTP_CONTENT_TYPE",var,32,false); + if (!var.empty()) + m_content_type = var; + } + return m_content_type; + } + string getRemoteAddr() const { + m_remote_addr = AbstractSPRequest::getRemoteAddr(); + if (m_remote_addr.empty()) { + dynabuf var(16); + GetServerVariable(m_pfc,"REMOTE_ADDR",var,16,false); + if (!var.empty()) + m_remote_addr = var; + } + return m_remote_addr; + } + void log(SPLogLevel level, const string& msg) { + AbstractSPRequest::log(level,msg); + if (level >= SPCrit) + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str()); + } + string makeSafeHeader(const char* rawname) const { + string hdr; + for (; *rawname; ++rawname) { + if (isalnum(*rawname)) + hdr += *rawname; + } + return (hdr + ':'); + } + void clearHeader(const char* rawname, const char* cginame) { + if (g_checkSpoofing && m_firsttime) { + if (m_allhttp.empty()) + GetServerVariable(m_pfc, "ALL_HTTP", m_allhttp, 4096); + string hdr = g_bSafeHeaderNames ? ("HTTP_" + makeSafeHeader(cginame + 5)) : (string(cginame) + ':'); + if (strstr(m_allhttp, hdr.c_str())) + throw opensaml::SecurityPolicyException("Attempt to spoof header ($1) was detected.", params(1, hdr.c_str())); + } + if (g_bSafeHeaderNames) { + string hdr = makeSafeHeader(rawname); + m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(g_unsetHeaderValue.c_str())); + } + else if (!strcmp(rawname,"REMOTE_USER")) { + m_pn->SetHeader(m_pfc, "remote-user:", const_cast(g_unsetHeaderValue.c_str())); + m_pn->SetHeader(m_pfc, "remote_user:", const_cast(g_unsetHeaderValue.c_str())); + } + else { + string hdr = string(rawname) + ':'; + m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(g_unsetHeaderValue.c_str())); + } + } + void setHeader(const char* name, const char* value) { + string hdr = g_bSafeHeaderNames ? makeSafeHeader(name) : (string(name) + ':'); + m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(value)); + } + string getSecureHeader(const char* name) const { + string hdr = g_bSafeHeaderNames ? makeSafeHeader(name) : (string(name) + ':'); + dynabuf buf(256); + GetHeader(m_pn, m_pfc, const_cast(hdr.c_str()), buf, 256, false); + return string(buf.empty() ? "" : buf); + } + string getHeader(const char* name) const { + string hdr(name); + hdr += ':'; + dynabuf buf(256); + GetHeader(m_pn, m_pfc, const_cast(hdr.c_str()), buf, 256, false); + return string(buf.empty() ? "" : buf); + } + void setRemoteUser(const char* user) { + setHeader("remote-user", user); + if (!user || !*user) + m_pfc->pFilterContext = NULL; + else if (m_pfc->pFilterContext = m_pfc->AllocMem(m_pfc, sizeof(char) * (strlen(user) + 1), NULL)) + strcpy(reinterpret_cast(m_pfc->pFilterContext), user); + } + string getRemoteUser() const { + return getSecureHeader("remote-user"); + } + void setResponseHeader(const char* name, const char* value) { + // Set for later. + if (value) + m_headers.insert(make_pair(name,value)); + else + m_headers.erase(name); + } + long sendResponse(istream& in, long status) { + string hdr = string("Connection: close\r\n"); + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + const char* codestr="200 OK"; + switch (status) { + case XMLTOOLING_HTTP_STATUS_UNAUTHORIZED: codestr="401 Authorization Required"; break; + case XMLTOOLING_HTTP_STATUS_FORBIDDEN: codestr="403 Forbidden"; break; + case XMLTOOLING_HTTP_STATUS_NOTFOUND: codestr="404 Not Found"; break; + case XMLTOOLING_HTTP_STATUS_ERROR: codestr="500 Server Error"; break; + } + m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, (void*)codestr, (DWORD)hdr.c_str(), 0); + char buf[1024]; + while (in) { + in.read(buf,1024); + DWORD resplen = in.gcount(); + m_pfc->WriteClient(m_pfc, buf, &resplen, 0); + } + return SF_STATUS_REQ_FINISHED; + } + long sendRedirect(const char* url) { + // XXX: Don't support the httpRedirect option, yet. + string hdr=string("Location: ") + url + "\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 40\r\n" + "Expires: 01-Jan-1997 12:00:00 GMT\r\n" + "Cache-Control: private,no-store,no-cache\r\n"; + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, "302 Please Wait", (DWORD)hdr.c_str(), 0); + static const char* redmsg="Redirecting..."; + DWORD resplen=40; + m_pfc->WriteClient(m_pfc, (LPVOID)redmsg, &resplen, 0); + return SF_STATUS_REQ_FINISHED; + } + long returnDecline() { + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + long returnOK() { + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + + const vector& getClientCertificates() const { + return g_NoCerts; + } + + // The filter never processes the POST, so stub these methods. + long getContentLength() const { throw IOException("The request's Content-Length is not available to an ISAPI filter."); } + const char* getRequestBody() const { throw IOException("The request body is not available to an ISAPI filter."); } +}; + +DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const char* msg) +{ + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg); + static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n"; + pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)ctype,0); + static const char* xmsg="Shibboleth Filter Error" + "

Shibboleth Filter Error

"; + DWORD resplen=strlen(xmsg); + pfc->WriteClient(pfc,(LPVOID)xmsg,&resplen,0); + resplen=strlen(msg); + pfc->WriteClient(pfc,(LPVOID)msg,&resplen,0); + static const char* xmsg2=""; + resplen=strlen(xmsg2); + pfc->WriteClient(pfc,(LPVOID)xmsg2,&resplen,0); + return SF_STATUS_REQ_FINISHED; +} + +extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification) +{ + // Is this a log notification? + if (notificationType==SF_NOTIFY_LOG) { + if (pfc->pFilterContext) + ((PHTTP_FILTER_LOG)pvNotification)->pszClientUserName=reinterpret_cast(pfc->pFilterContext); + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + + PHTTP_FILTER_PREPROC_HEADERS pn=(PHTTP_FILTER_PREPROC_HEADERS)pvNotification; + try + { + // Determine web site number. This can't really fail, I don't think. + dynabuf buf(128); + GetServerVariable(pfc,"INSTANCE_ID",buf,10); + + // Match site instance to host name, skip if no match. + map::const_iterator map_i=g_Sites.find(static_cast(buf)); + if (map_i==g_Sites.end()) + return SF_STATUS_REQ_NEXT_NOTIFICATION; + + ostringstream threadid; + threadid << "[" << getpid() << "] isapi_shib" << '\0'; + xmltooling::NDC ndc(threadid.str().c_str()); + + ShibTargetIsapiF stf(pfc, pn, map_i->second); + + // "false" because we don't override the Shib settings + pair res = stf.getServiceProvider().doAuthentication(stf); + if (!g_spoofKey.empty()) + pn->SetHeader(pfc, "ShibSpoofCheck:", const_cast(g_spoofKey.c_str())); + if (res.first) return res.second; + + // "false" because we don't override the Shib settings + res = stf.getServiceProvider().doExport(stf); + if (res.first) return res.second; + + res = stf.getServiceProvider().doAuthorization(stf); + if (res.first) return res.second; + + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + catch(bad_alloc) { + return WriteClientError(pfc,"Out of Memory"); + } + catch(long e) { + if (e==ERROR_NO_DATA) + return WriteClientError(pfc,"A required variable or header was empty."); + else + return WriteClientError(pfc,"Shibboleth Filter detected unexpected IIS error."); + } + catch (exception& e) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, e.what()); + return WriteClientError(pfc,"Shibboleth Filter caught an exception, check Event Log for details."); + } + catch(...) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Shibboleth Filter threw an unknown exception."); + if (g_catchAll) + return WriteClientError(pfc,"Shibboleth Filter threw an unknown exception."); + throw; + } + + return WriteClientError(pfc,"Shibboleth Filter reached unreachable code, save my walrus!"); +} + + +/****************************************************************************/ +// ISAPI Extension + +DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const char* msg) +{ + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg); + static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n"; + lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)ctype); + static const char* xmsg="Shibboleth Error

Shibboleth Error

"; + DWORD resplen=strlen(xmsg); + lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg,&resplen,HSE_IO_SYNC); + resplen=strlen(msg); + lpECB->WriteClient(lpECB->ConnID,(LPVOID)msg,&resplen,HSE_IO_SYNC); + static const char* xmsg2=""; + resplen=strlen(xmsg2); + lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg2,&resplen,HSE_IO_SYNC); + return HSE_STATUS_SUCCESS; +} + + +class ShibTargetIsapiE : public AbstractSPRequest +{ + LPEXTENSION_CONTROL_BLOCK m_lpECB; + multimap m_headers; + mutable vector m_certs; + mutable string m_body; + mutable bool m_gotBody; + int m_port; + string m_scheme,m_hostname,m_uri; + mutable string m_remote_addr,m_remote_user; + +public: + ShibTargetIsapiE(LPEXTENSION_CONTROL_BLOCK lpECB, const site_t& site) + : AbstractSPRequest(SHIBSP_LOGCAT".ISAPI"), m_lpECB(lpECB), m_gotBody(false) { + dynabuf ssl(5); + GetServerVariable(lpECB,"HTTPS",ssl,5); + bool SSL=(ssl=="on" || ssl=="ON"); + + // Scheme may come from site def or be derived from IIS. + m_scheme=site.m_scheme; + if (m_scheme.empty() || !g_bNormalizeRequest) + m_scheme = SSL ? "https" : "http"; + + // URL path always come from IIS. + dynabuf url(256); + GetServerVariable(lpECB,"URL",url,255); + + // Port may come from IIS or from site def. + dynabuf port(11); + if (!g_bNormalizeRequest || (SSL && site.m_sslport.empty()) || (!SSL && site.m_port.empty())) + GetServerVariable(lpECB,"SERVER_PORT",port,10); + else if (SSL) { + strncpy(port,site.m_sslport.c_str(),10); + static_cast(port)[10]=0; + } + else { + strncpy(port,site.m_port.c_str(),10); + static_cast(port)[10]=0; + } + m_port = atoi(port); + + dynabuf var(32); + GetServerVariable(lpECB, "SERVER_NAME", var, 32); + + // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name. + m_hostname=var; + if (site.m_name!=m_hostname && site.m_aliases.find(m_hostname)==site.m_aliases.end()) + m_hostname=site.m_name; + + /* + * IIS screws us over on PATH_INFO (the hits keep on coming). We need to figure out if + * the server is set up for proper PATH_INFO handling, or "IIS sucks rabid weasels mode", + * which is the default. No perfect way to tell, but we can take a good guess by checking + * whether the URL is a substring of the PATH_INFO: + * + * e.g. for /Shibboleth.sso/SAML/POST + * + * Bad mode (default): + * URL: /Shibboleth.sso + * PathInfo: /Shibboleth.sso/SAML/POST + * + * Good mode: + * URL: /Shibboleth.sso + * PathInfo: /SAML/POST + */ + + string uri; + + // Clearly we're only in bad mode if path info exists at all. + if (lpECB->lpszPathInfo && *(lpECB->lpszPathInfo)) { + if (strstr(lpECB->lpszPathInfo,url)) + // Pretty good chance we're in bad mode, unless the PathInfo repeats the path itself. + uri = lpECB->lpszPathInfo; + else { + uri = url; + uri += lpECB->lpszPathInfo; + } + } + else { + uri = url; + } + + // For consistency with Apache, let's add the query string. + if (lpECB->lpszQueryString && *(lpECB->lpszQueryString)) { + uri += '?'; + uri += lpECB->lpszQueryString; + } + + setRequestURI(uri.c_str()); + } + ~ShibTargetIsapiE() { } + + const char* getScheme() const { + return m_scheme.c_str(); + } + const char* getHostname() const { + return m_hostname.c_str(); + } + int getPort() const { + return m_port; + } + const char* getMethod() const { + return m_lpECB->lpszMethod; + } + string getContentType() const { + return m_lpECB->lpszContentType ? m_lpECB->lpszContentType : ""; + } + long getContentLength() const { + return m_lpECB->cbTotalBytes; + } + string getRemoteUser() const { + if (m_remote_user.empty()) { + dynabuf var(16); + GetServerVariable(m_lpECB, "REMOTE_USER", var, 32, false); + if (!var.empty()) + m_remote_user = var; + } + return m_remote_user; + } + string getRemoteAddr() const { + m_remote_addr = AbstractSPRequest::getRemoteAddr(); + if (m_remote_addr.empty()) { + dynabuf var(16); + GetServerVariable(m_lpECB, "REMOTE_ADDR", var, 16, false); + if (!var.empty()) + m_remote_addr = var; + } + return m_remote_addr; + } + void log(SPLogLevel level, const string& msg) const { + AbstractSPRequest::log(level,msg); + if (level >= SPCrit) + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str()); + } + string getHeader(const char* name) const { + string hdr("HTTP_"); + for (; *name; ++name) { + if (*name=='-') + hdr += '_'; + else + hdr += toupper(*name); + } + dynabuf buf(128); + GetServerVariable(m_lpECB, const_cast(hdr.c_str()), buf, 128, false); + return buf.empty() ? "" : buf; + } + void setResponseHeader(const char* name, const char* value) { + // Set for later. + if (value) + m_headers.insert(make_pair(name,value)); + else + m_headers.erase(name); + } + const char* getQueryString() const { + return m_lpECB->lpszQueryString; + } + const char* getRequestBody() const { + if (m_gotBody) + return m_body.c_str(); + if (m_lpECB->cbTotalBytes > 1024*1024) // 1MB? + throw opensaml::SecurityPolicyException("Size of request body exceeded 1M size limit."); + else if (m_lpECB->cbTotalBytes > m_lpECB->cbAvailable) { + m_gotBody=true; + DWORD datalen=m_lpECB->cbTotalBytes; + if (m_lpECB->cbAvailable > 0) { + m_body.assign(reinterpret_cast(m_lpECB->lpbData),m_lpECB->cbAvailable); + datalen-=m_lpECB->cbAvailable; + } + char buf[8192]; + while (datalen) { + DWORD buflen=8192; + BOOL ret = m_lpECB->ReadClient(m_lpECB->ConnID, buf, &buflen); + if (!ret) + throw IOException("Error reading request body from browser."); + else if (!buflen) + throw IOException("Socket closed while reading request body from browser."); + m_body.append(buf, buflen); + datalen-=buflen; + } + } + else if (m_lpECB->cbAvailable) { + m_gotBody=true; + m_body.assign(reinterpret_cast(m_lpECB->lpbData),m_lpECB->cbAvailable); + } + return m_body.c_str(); + } + long sendResponse(istream& in, long status) { + string hdr = string("Connection: close\r\n"); + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + const char* codestr="200 OK"; + switch (status) { + case XMLTOOLING_HTTP_STATUS_UNAUTHORIZED: codestr="401 Authorization Required"; break; + case XMLTOOLING_HTTP_STATUS_FORBIDDEN: codestr="403 Forbidden"; break; + case XMLTOOLING_HTTP_STATUS_NOTFOUND: codestr="404 Not Found"; break; + case XMLTOOLING_HTTP_STATUS_ERROR: codestr="500 Server Error"; break; + } + m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, (void*)codestr, 0, (LPDWORD)hdr.c_str()); + char buf[1024]; + while (in) { + in.read(buf,1024); + DWORD resplen = in.gcount(); + m_lpECB->WriteClient(m_lpECB->ConnID, buf, &resplen, HSE_IO_SYNC); + } + return HSE_STATUS_SUCCESS; + } + long sendRedirect(const char* url) { + string hdr=string("Location: ") + url + "\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 40\r\n" + "Expires: 01-Jan-1997 12:00:00 GMT\r\n" + "Cache-Control: private,no-store,no-cache\r\n"; + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, "302 Moved", 0, (LPDWORD)hdr.c_str()); + static const char* redmsg="Redirecting..."; + DWORD resplen=40; + m_lpECB->WriteClient(m_lpECB->ConnID, (LPVOID)redmsg, &resplen, HSE_IO_SYNC); + return HSE_STATUS_SUCCESS; + } + // Decline happens in the POST processor if this isn't the shire url + // Note that it can also happen with HTAccess, but we don't support that, yet. + long returnDecline() { + return WriteClientError( + m_lpECB, + "ISAPI extension can only be invoked to process Shibboleth protocol requests." + "Make sure the mapped file extension doesn't match actual content." + ); + } + long returnOK() { + return HSE_STATUS_SUCCESS; + } + + const vector& getClientCertificates() const { + if (m_certs.empty()) { + char CertificateBuf[8192]; + CERT_CONTEXT_EX ccex; + ccex.cbAllocated = sizeof(CertificateBuf); + ccex.CertContext.pbCertEncoded = (BYTE*)CertificateBuf; + DWORD dwSize = sizeof(ccex); + + if (m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_GET_CERT_INFO_EX, (LPVOID)&ccex, (LPDWORD)dwSize, NULL)) { + if (ccex.CertContext.cbCertEncoded) { + xsecsize_t outlen; + XMLByte* serialized = Base64::encode(reinterpret_cast(CertificateBuf), ccex.CertContext.cbCertEncoded, &outlen); + m_certs.push_back(reinterpret_cast(serialized)); +#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&serialized); +#else + XMLString::release((char**)&serialized); +#endif + } + } + } + return m_certs; + } + + // Not used in the extension. + void clearHeader(const char* rawname, const char* cginame) { throw runtime_error("clearHeader not implemented"); } + void setHeader(const char* name, const char* value) { throw runtime_error("setHeader not implemented"); } + void setRemoteUser(const char* user) { throw runtime_error("setRemoteUser not implemented"); } +}; + +extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) +{ + try { + ostringstream threadid; + threadid << "[" << getpid() << "] isapi_shib_extension" << '\0'; + xmltooling::NDC ndc(threadid.str().c_str()); + + // Determine web site number. This can't really fail, I don't think. + dynabuf buf(128); + GetServerVariable(lpECB,"INSTANCE_ID",buf,10); + + // Match site instance to host name, skip if no match. + map::const_iterator map_i=g_Sites.find(static_cast(buf)); + if (map_i==g_Sites.end()) + return WriteClientError(lpECB, "Shibboleth Extension not configured for web site (check mappings in configuration)."); + + ShibTargetIsapiE ste(lpECB, map_i->second); + pair res = ste.getServiceProvider().doHandler(ste); + if (res.first) return res.second; + + return WriteClientError(lpECB, "Shibboleth Extension failed to process request"); + + } + catch(bad_alloc) { + return WriteClientError(lpECB,"Out of Memory"); + } + catch(long e) { + if (e==ERROR_NO_DATA) + return WriteClientError(lpECB,"A required variable or header was empty."); + else + return WriteClientError(lpECB,"Server detected unexpected IIS error."); + } + catch (exception& e) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, e.what()); + return WriteClientError(lpECB,"Shibboleth Extension caught an exception, check Event Log for details."); + } + catch(...) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Shibboleth Extension threw an unknown exception."); + if (g_catchAll) + return WriteClientError(lpECB,"Shibboleth Extension threw an unknown exception."); + throw; + } + + // If we get here we've got an error. + return HSE_STATUS_ERROR; +} diff --git a/isapi_shib/isapi_shib.cpp.r3059 b/isapi_shib/isapi_shib.cpp.r3059 new file mode 100644 index 0000000..73fb7a0 --- /dev/null +++ b/isapi_shib/isapi_shib.cpp.r3059 @@ -0,0 +1,950 @@ +/* + * 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. + */ + +/** + * isapi_shib.cpp + * + * Shibboleth ISAPI filter + */ + +#define SHIBSP_LITE +#include "config_win32.h" + +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#define _CRT_SECURE_NO_DEPRECATE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace shibsp; +using namespace xmltooling; +using namespace xercesc; +using namespace std; + +// globals +namespace { + static const XMLCh path[] = UNICODE_LITERAL_4(p,a,t,h); + static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e); + static const XMLCh name[] = UNICODE_LITERAL_4(n,a,m,e); + static const XMLCh port[] = UNICODE_LITERAL_4(p,o,r,t); + static const XMLCh sslport[] = UNICODE_LITERAL_7(s,s,l,p,o,r,t); + static const XMLCh scheme[] = UNICODE_LITERAL_6(s,c,h,e,m,e); + static const XMLCh id[] = UNICODE_LITERAL_2(i,d); + static const XMLCh Alias[] = UNICODE_LITERAL_5(A,l,i,a,s); + static const XMLCh Site[] = UNICODE_LITERAL_4(S,i,t,e); + + struct site_t { + site_t(const DOMElement* e) + { + auto_ptr_char n(e->getAttributeNS(NULL,name)); + auto_ptr_char s(e->getAttributeNS(NULL,scheme)); + auto_ptr_char p(e->getAttributeNS(NULL,port)); + auto_ptr_char p2(e->getAttributeNS(NULL,sslport)); + if (n.get()) m_name=n.get(); + if (s.get()) m_scheme=s.get(); + if (p.get()) m_port=p.get(); + if (p2.get()) m_sslport=p2.get(); + e = XMLHelper::getFirstChildElement(e, Alias); + while (e) { + if (e->hasChildNodes()) { + auto_ptr_char alias(e->getFirstChild()->getNodeValue()); + m_aliases.insert(alias.get()); + } + e = XMLHelper::getNextSiblingElement(e, Alias); + } + } + string m_scheme,m_port,m_sslport,m_name; + set m_aliases; + }; + + struct context_t { + char* m_user; + bool m_checked; + }; + + HINSTANCE g_hinstDLL; + SPConfig* g_Config = NULL; + map g_Sites; + bool g_bNormalizeRequest = true; + string g_unsetHeaderValue; + bool g_checkSpoofing = true; + bool g_catchAll = false; + vector g_NoCerts; +} + +BOOL LogEvent( + LPCSTR lpUNCServerName, + WORD wType, + DWORD dwEventID, + PSID lpUserSid, + LPCSTR message) +{ + LPCSTR messages[] = {message, NULL}; + + HANDLE hElog = RegisterEventSource(lpUNCServerName, "Shibboleth ISAPI Filter"); + BOOL res = ReportEvent(hElog, wType, 0, dwEventID, lpUserSid, 1, 0, messages, NULL); + return (DeregisterEventSource(hElog) && res); +} + +extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID) +{ + if (fdwReason==DLL_PROCESS_ATTACH) + g_hinstDLL=hinstDLL; + return TRUE; +} + +extern "C" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO* pVer) +{ + if (!pVer) + return FALSE; + + if (!g_Config) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Extension mode startup not possible, is the DLL loaded as a filter?"); + return FALSE; + } + + pVer->dwExtensionVersion=HSE_VERSION; + strncpy(pVer->lpszExtensionDesc,"Shibboleth ISAPI Extension",HSE_MAX_EXT_DLL_NAME_LEN-1); + return TRUE; +} + +extern "C" BOOL WINAPI TerminateExtension(DWORD) +{ + return TRUE; // cleanup should happen when filter unloads +} + +extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer) +{ + if (!pVer) + return FALSE; + else if (g_Config) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Reentrant filter initialization, ignoring..."); + return TRUE; + } + + g_Config=&SPConfig::getConfig(); + g_Config->setFeatures( + SPConfig::Listener | + SPConfig::Caching | + SPConfig::RequestMapping | + SPConfig::InProcess | + SPConfig::Logging | + SPConfig::Handlers + ); + if (!g_Config->init()) { + g_Config=NULL; + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Filter startup failed during library initialization, check native log for help."); + return FALSE; + } + + try { + if (!g_Config->instantiate(NULL, true)) + throw runtime_error("unknown error"); + } + catch (exception& ex) { + g_Config->term(); + g_Config=NULL; + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, ex.what()); + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Filter startup failed to load configuration, check native log for details."); + return FALSE; + } + + // Access implementation-specifics and site mappings. + ServiceProvider* sp=g_Config->getServiceProvider(); + Locker locker(sp); + const PropertySet* props=sp->getPropertySet("InProcess"); + if (props) { + pair unsetValue=props->getString("unsetHeaderValue"); + if (unsetValue.first) + g_unsetHeaderValue = unsetValue.second; + pair flag=props->getBool("checkSpoofing"); + g_checkSpoofing = !flag.first || flag.second; + flag=props->getBool("catchAll"); + g_catchAll = flag.first && flag.second; + + props = props->getPropertySet("ISAPI"); + if (props) { + flag = props->getBool("normalizeRequest"); + g_bNormalizeRequest = !flag.first || flag.second; + const DOMElement* child = XMLHelper::getFirstChildElement(props->getElement(),Site); + while (child) { + auto_ptr_char id(child->getAttributeNS(NULL,id)); + if (id.get()) + g_Sites.insert(pair(id.get(),site_t(child))); + child=XMLHelper::getNextSiblingElement(child,Site); + } + } + } + + pVer->dwFilterVersion=HTTP_FILTER_REVISION; + strncpy(pVer->lpszFilterDesc,"Shibboleth ISAPI Filter",SF_MAX_FILTER_DESC_LEN); + pVer->dwFlags=(SF_NOTIFY_ORDER_HIGH | + SF_NOTIFY_SECURE_PORT | + SF_NOTIFY_NONSECURE_PORT | + SF_NOTIFY_PREPROC_HEADERS | + SF_NOTIFY_LOG); + LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter initialized..."); + return TRUE; +} + +extern "C" BOOL WINAPI TerminateFilter(DWORD) +{ + if (g_Config) + g_Config->term(); + g_Config = NULL; + LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter shut down..."); + return TRUE; +} + +/* Next up, some suck-free versions of various APIs. + + You DON'T require people to guess the buffer size and THEN tell them the right size. + Returning an LPCSTR is apparently way beyond their ken. Not to mention the fact that + constant strings aren't typed as such, making it just that much harder. These versions + are now updated to use a special growable buffer object, modeled after the standard + string class. The standard string won't work because they left out the option to + pre-allocate a non-constant buffer. +*/ + +class dynabuf +{ +public: + dynabuf() { bufptr=NULL; buflen=0; } + dynabuf(size_t s) { bufptr=new char[buflen=s]; *bufptr=0; } + ~dynabuf() { delete[] bufptr; } + size_t length() const { return bufptr ? strlen(bufptr) : 0; } + size_t size() const { return buflen; } + bool empty() const { return length()==0; } + void reserve(size_t s, bool keep=false); + void erase() { if (bufptr) memset(bufptr,0,buflen); } + operator char*() { return bufptr; } + bool operator ==(const char* s) const; + bool operator !=(const char* s) const { return !(*this==s); } +private: + char* bufptr; + size_t buflen; +}; + +void dynabuf::reserve(size_t s, bool keep) +{ + if (s<=buflen) + return; + char* p=new char[s]; + if (keep) + while (buflen--) + p[buflen]=bufptr[buflen]; + buflen=s; + delete[] bufptr; + bufptr=p; +} + +bool dynabuf::operator==(const char* s) const +{ + if (buflen==NULL || s==NULL) + return (buflen==NULL && s==NULL); + else + return strcmp(bufptr,s)==0; +} + +void GetServerVariable(PHTTP_FILTER_CONTEXT pfc, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true) +{ + s.reserve(size); + s.erase(); + size=s.size(); + + while (!pfc->GetServerVariable(pfc,lpszVariable,s,&size)) { + // Grumble. Check the error. + DWORD e=GetLastError(); + if (e==ERROR_INSUFFICIENT_BUFFER) + s.reserve(size); + else + break; + } + if (bRequired && s.empty()) + throw ERROR_NO_DATA; +} + +void GetServerVariable(LPEXTENSION_CONTROL_BLOCK lpECB, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true) +{ + s.reserve(size); + s.erase(); + size=s.size(); + + while (!lpECB->GetServerVariable(lpECB->ConnID,lpszVariable,s,&size)) { + // Grumble. Check the error. + DWORD e=GetLastError(); + if (e==ERROR_INSUFFICIENT_BUFFER) + s.reserve(size); + else + break; + } + if (bRequired && s.empty()) + throw ERROR_NO_DATA; +} + +void GetHeader(PHTTP_FILTER_PREPROC_HEADERS pn, PHTTP_FILTER_CONTEXT pfc, + LPSTR lpszName, dynabuf& s, DWORD size=80, bool bRequired=true) +{ + s.reserve(size); + s.erase(); + size=s.size(); + + while (!pn->GetHeader(pfc,lpszName,s,&size)) { + // Grumble. Check the error. + DWORD e=GetLastError(); + if (e==ERROR_INSUFFICIENT_BUFFER) + s.reserve(size); + else + break; + } + if (bRequired && s.empty()) + throw ERROR_NO_DATA; +} + +/****************************************************************************/ +// ISAPI Filter + +class ShibTargetIsapiF : public AbstractSPRequest +{ + PHTTP_FILTER_CONTEXT m_pfc; + PHTTP_FILTER_PREPROC_HEADERS m_pn; + multimap m_headers; + int m_port; + string m_scheme,m_hostname; + mutable string m_remote_addr,m_content_type,m_method; + dynabuf m_allhttp; + +public: + ShibTargetIsapiF(PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, const site_t& site) + : AbstractSPRequest(SHIBSP_LOGCAT".ISAPI"), m_pfc(pfc), m_pn(pn), m_allhttp(4096) { + + // URL path always come from IIS. + dynabuf var(256); + GetHeader(pn,pfc,"url",var,256,false); + setRequestURI(var); + + // Port may come from IIS or from site def. + if (!g_bNormalizeRequest || (pfc->fIsSecurePort && site.m_sslport.empty()) || (!pfc->fIsSecurePort && site.m_port.empty())) { + GetServerVariable(pfc,"SERVER_PORT",var,10); + m_port = atoi(var); + } + else if (pfc->fIsSecurePort) { + m_port = atoi(site.m_sslport.c_str()); + } + else { + m_port = atoi(site.m_port.c_str()); + } + + // Scheme may come from site def or be derived from IIS. + m_scheme=site.m_scheme; + if (m_scheme.empty() || !g_bNormalizeRequest) + m_scheme=pfc->fIsSecurePort ? "https" : "http"; + + GetServerVariable(pfc,"SERVER_NAME",var,32); + + // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name. + m_hostname = var; + if (site.m_name!=m_hostname && site.m_aliases.find(m_hostname)==site.m_aliases.end()) + m_hostname=site.m_name; + + if (!pfc->pFilterContext) { + pfc->pFilterContext = pfc->AllocMem(pfc, sizeof(context_t), NULL); + if (static_cast(pfc->pFilterContext)) { + static_cast(pfc->pFilterContext)->m_user = NULL; + static_cast(pfc->pFilterContext)->m_checked = false; + } + } + } + ~ShibTargetIsapiF() { } + + const char* getScheme() const { + return m_scheme.c_str(); + } + const char* getHostname() const { + return m_hostname.c_str(); + } + int getPort() const { + return m_port; + } + const char* getQueryString() const { + const char* uri = getRequestURI(); + uri = (uri ? strchr(uri, '?') : NULL); + return uri ? (uri + 1) : NULL; + } + const char* getMethod() const { + if (m_method.empty()) { + dynabuf var(5); + GetServerVariable(m_pfc,"HTTP_METHOD",var,5,false); + if (!var.empty()) + m_method = var; + } + return m_method.c_str(); + } + string getContentType() const { + if (m_content_type.empty()) { + dynabuf var(32); + GetServerVariable(m_pfc,"HTTP_CONTENT_TYPE",var,32,false); + if (!var.empty()) + m_content_type = var; + } + return m_content_type; + } + string getRemoteAddr() const { + m_remote_addr = AbstractSPRequest::getRemoteAddr(); + if (m_remote_addr.empty()) { + dynabuf var(16); + GetServerVariable(m_pfc,"REMOTE_ADDR",var,16,false); + if (!var.empty()) + m_remote_addr = var; + } + return m_remote_addr; + } + void log(SPLogLevel level, const string& msg) { + AbstractSPRequest::log(level,msg); + if (level >= SPError) + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str()); + } + void clearHeader(const char* rawname, const char* cginame) { + if (g_checkSpoofing && m_pfc->pFilterContext && !static_cast(m_pfc->pFilterContext)->m_checked) { + if (m_allhttp.empty()) + GetServerVariable(m_pfc,"ALL_HTTP",m_allhttp,4096); + if (strstr(m_allhttp, cginame)) + throw opensaml::SecurityPolicyException("Attempt to spoof header ($1) was detected.", params(1, rawname)); + } + string hdr(!strcmp(rawname,"REMOTE_USER") ? "remote-user" : rawname); + hdr += ':'; + m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(g_unsetHeaderValue.c_str())); + } + void setHeader(const char* name, const char* value) { + string hdr(name); + hdr += ':'; + m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(value)); + } + string getHeader(const char* name) const { + string hdr(name); + hdr += ':'; + dynabuf buf(256); + GetHeader(m_pn, m_pfc, const_cast(hdr.c_str()), buf, 256, false); + return string(buf); + } + void setRemoteUser(const char* user) { + setHeader("remote-user", user); + if (m_pfc->pFilterContext) { + if (!user || !*user) + static_cast(m_pfc->pFilterContext)->m_user = NULL; + else if (static_cast(m_pfc->pFilterContext)->m_user = (char*)m_pfc->AllocMem(m_pfc, sizeof(char) * (strlen(user) + 1), NULL)) + strcpy(static_cast(m_pfc->pFilterContext)->m_user, user); + } + } + string getRemoteUser() const { + return getHeader("remote-user"); + } + void setResponseHeader(const char* name, const char* value) { + // Set for later. + if (value) + m_headers.insert(make_pair(name,value)); + else + m_headers.erase(name); + } + long sendResponse(istream& in, long status) { + string hdr = string("Connection: close\r\n"); + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + const char* codestr="200 OK"; + switch (status) { + case XMLTOOLING_HTTP_STATUS_UNAUTHORIZED: codestr="401 Authorization Required"; break; + case XMLTOOLING_HTTP_STATUS_FORBIDDEN: codestr="403 Forbidden"; break; + case XMLTOOLING_HTTP_STATUS_NOTFOUND: codestr="404 Not Found"; break; + case XMLTOOLING_HTTP_STATUS_ERROR: codestr="500 Server Error"; break; + } + m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, (void*)codestr, (DWORD)hdr.c_str(), 0); + char buf[1024]; + while (in) { + in.read(buf,1024); + DWORD resplen = in.gcount(); + m_pfc->WriteClient(m_pfc, buf, &resplen, 0); + } + return SF_STATUS_REQ_FINISHED; + } + long sendRedirect(const char* url) { + // XXX: Don't support the httpRedirect option, yet. + string hdr=string("Location: ") + url + "\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 40\r\n" + "Expires: 01-Jan-1997 12:00:00 GMT\r\n" + "Cache-Control: private,no-store,no-cache\r\n"; + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, "302 Please Wait", (DWORD)hdr.c_str(), 0); + static const char* redmsg="Redirecting..."; + DWORD resplen=40; + m_pfc->WriteClient(m_pfc, (LPVOID)redmsg, &resplen, 0); + return SF_STATUS_REQ_FINISHED; + } + long returnDecline() { + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + long returnOK() { + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + + const vector& getClientCertificates() const { + return g_NoCerts; + } + + // The filter never processes the POST, so stub these methods. + long getContentLength() const { throw IOException("The request's Content-Length is not available to an ISAPI filter."); } + const char* getRequestBody() const { throw IOException("The request body is not available to an ISAPI filter."); } +}; + +DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const char* msg) +{ + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg); + static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n"; + pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)ctype,0); + static const char* xmsg="Shibboleth Filter Error" + "

Shibboleth Filter Error

"; + DWORD resplen=strlen(xmsg); + pfc->WriteClient(pfc,(LPVOID)xmsg,&resplen,0); + resplen=strlen(msg); + pfc->WriteClient(pfc,(LPVOID)msg,&resplen,0); + static const char* xmsg2=""; + resplen=strlen(xmsg2); + pfc->WriteClient(pfc,(LPVOID)xmsg2,&resplen,0); + return SF_STATUS_REQ_FINISHED; +} + +extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification) +{ + // Is this a log notification? + if (notificationType==SF_NOTIFY_LOG) { + if (pfc->pFilterContext && static_cast(pfc->pFilterContext)->m_user) + ((PHTTP_FILTER_LOG)pvNotification)->pszClientUserName=static_cast(pfc->pFilterContext)->m_user; + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + + PHTTP_FILTER_PREPROC_HEADERS pn=(PHTTP_FILTER_PREPROC_HEADERS)pvNotification; + try + { + // Determine web site number. This can't really fail, I don't think. + dynabuf buf(128); + GetServerVariable(pfc,"INSTANCE_ID",buf,10); + + // Match site instance to host name, skip if no match. + map::const_iterator map_i=g_Sites.find(static_cast(buf)); + if (map_i==g_Sites.end()) + return SF_STATUS_REQ_NEXT_NOTIFICATION; + + ostringstream threadid; + threadid << "[" << getpid() << "] isapi_shib" << '\0'; + xmltooling::NDC ndc(threadid.str().c_str()); + + ShibTargetIsapiF stf(pfc, pn, map_i->second); + + // "false" because we don't override the Shib settings + pair res = stf.getServiceProvider().doAuthentication(stf); + if (pfc->pFilterContext) + static_cast(pfc->pFilterContext)->m_checked = true; + if (res.first) return res.second; + + // "false" because we don't override the Shib settings + res = stf.getServiceProvider().doExport(stf); + if (res.first) return res.second; + + res = stf.getServiceProvider().doAuthorization(stf); + if (res.first) return res.second; + + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + catch(bad_alloc) { + return WriteClientError(pfc,"Out of Memory"); + } + catch(long e) { + if (e==ERROR_NO_DATA) + return WriteClientError(pfc,"A required variable or header was empty."); + else + return WriteClientError(pfc,"Shibboleth Filter detected unexpected IIS error."); + } + catch (exception& e) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, e.what()); + return WriteClientError(pfc,"Shibboleth Filter caught an exception, check Event Log for details."); + } + catch(...) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Shibboleth Filter threw an unknown exception."); + if (g_catchAll) + return WriteClientError(pfc,"Shibboleth Filter threw an unknown exception."); + throw; + } + + return WriteClientError(pfc,"Shibboleth Filter reached unreachable code, save my walrus!"); +} + + +/****************************************************************************/ +// ISAPI Extension + +DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const char* msg) +{ + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg); + static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n"; + lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)ctype); + static const char* xmsg="Shibboleth Error

Shibboleth Error

"; + DWORD resplen=strlen(xmsg); + lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg,&resplen,HSE_IO_SYNC); + resplen=strlen(msg); + lpECB->WriteClient(lpECB->ConnID,(LPVOID)msg,&resplen,HSE_IO_SYNC); + static const char* xmsg2=""; + resplen=strlen(xmsg2); + lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg2,&resplen,HSE_IO_SYNC); + return HSE_STATUS_SUCCESS; +} + + +class ShibTargetIsapiE : public AbstractSPRequest +{ + LPEXTENSION_CONTROL_BLOCK m_lpECB; + multimap m_headers; + mutable vector m_certs; + mutable string m_body; + mutable bool m_gotBody; + int m_port; + string m_scheme,m_hostname,m_uri; + mutable string m_remote_addr,m_remote_user; + +public: + ShibTargetIsapiE(LPEXTENSION_CONTROL_BLOCK lpECB, const site_t& site) + : AbstractSPRequest(SHIBSP_LOGCAT".ISAPI"), m_lpECB(lpECB), m_gotBody(false) { + dynabuf ssl(5); + GetServerVariable(lpECB,"HTTPS",ssl,5); + bool SSL=(ssl=="on" || ssl=="ON"); + + // Scheme may come from site def or be derived from IIS. + m_scheme=site.m_scheme; + if (m_scheme.empty() || !g_bNormalizeRequest) + m_scheme = SSL ? "https" : "http"; + + // URL path always come from IIS. + dynabuf url(256); + GetServerVariable(lpECB,"URL",url,255); + + // Port may come from IIS or from site def. + dynabuf port(11); + if (!g_bNormalizeRequest || (SSL && site.m_sslport.empty()) || (!SSL && site.m_port.empty())) + GetServerVariable(lpECB,"SERVER_PORT",port,10); + else if (SSL) { + strncpy(port,site.m_sslport.c_str(),10); + static_cast(port)[10]=0; + } + else { + strncpy(port,site.m_port.c_str(),10); + static_cast(port)[10]=0; + } + m_port = atoi(port); + + dynabuf var(32); + GetServerVariable(lpECB, "SERVER_NAME", var, 32); + + // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name. + m_hostname=var; + if (site.m_name!=m_hostname && site.m_aliases.find(m_hostname)==site.m_aliases.end()) + m_hostname=site.m_name; + + /* + * IIS screws us over on PATH_INFO (the hits keep on coming). We need to figure out if + * the server is set up for proper PATH_INFO handling, or "IIS sucks rabid weasels mode", + * which is the default. No perfect way to tell, but we can take a good guess by checking + * whether the URL is a substring of the PATH_INFO: + * + * e.g. for /Shibboleth.sso/SAML/POST + * + * Bad mode (default): + * URL: /Shibboleth.sso + * PathInfo: /Shibboleth.sso/SAML/POST + * + * Good mode: + * URL: /Shibboleth.sso + * PathInfo: /SAML/POST + */ + + string uri; + + // Clearly we're only in bad mode if path info exists at all. + if (lpECB->lpszPathInfo && *(lpECB->lpszPathInfo)) { + if (strstr(lpECB->lpszPathInfo,url)) + // Pretty good chance we're in bad mode, unless the PathInfo repeats the path itself. + uri = lpECB->lpszPathInfo; + else { + uri = url; + uri += lpECB->lpszPathInfo; + } + } + else { + uri = url; + } + + // For consistency with Apache, let's add the query string. + if (lpECB->lpszQueryString && *(lpECB->lpszQueryString)) { + uri += '?'; + uri += lpECB->lpszQueryString; + } + + setRequestURI(uri.c_str()); + } + ~ShibTargetIsapiE() { } + + const char* getScheme() const { + return m_scheme.c_str(); + } + const char* getHostname() const { + return m_hostname.c_str(); + } + int getPort() const { + return m_port; + } + const char* getMethod() const { + return m_lpECB->lpszMethod; + } + string getContentType() const { + return m_lpECB->lpszContentType ? m_lpECB->lpszContentType : ""; + } + long getContentLength() const { + return m_lpECB->cbTotalBytes; + } + string getRemoteUser() const { + if (m_remote_user.empty()) { + dynabuf var(16); + GetServerVariable(m_lpECB, "REMOTE_USER", var, 32, false); + if (!var.empty()) + m_remote_user = var; + } + return m_remote_user; + } + string getRemoteAddr() const { + m_remote_addr = AbstractSPRequest::getRemoteAddr(); + if (m_remote_addr.empty()) { + dynabuf var(16); + GetServerVariable(m_lpECB, "REMOTE_ADDR", var, 16, false); + if (!var.empty()) + m_remote_addr = var; + } + return m_remote_addr; + } + void log(SPLogLevel level, const string& msg) const { + AbstractSPRequest::log(level,msg); + if (level >= SPError) + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str()); + } + string getHeader(const char* name) const { + string hdr("HTTP_"); + for (; *name; ++name) { + if (*name=='-') + hdr += '_'; + else + hdr += toupper(*name); + } + dynabuf buf(128); + GetServerVariable(m_lpECB, const_cast(hdr.c_str()), buf, 128, false); + return buf.empty() ? "" : buf; + } + void setResponseHeader(const char* name, const char* value) { + // Set for later. + if (value) + m_headers.insert(make_pair(name,value)); + else + m_headers.erase(name); + } + const char* getQueryString() const { + return m_lpECB->lpszQueryString; + } + const char* getRequestBody() const { + if (m_gotBody) + return m_body.c_str(); + if (m_lpECB->cbTotalBytes > 1024*1024) // 1MB? + throw opensaml::SecurityPolicyException("Size of request body exceeded 1M size limit."); + else if (m_lpECB->cbTotalBytes > m_lpECB->cbAvailable) { + m_gotBody=true; + DWORD datalen=m_lpECB->cbTotalBytes; + if (m_lpECB->cbAvailable > 0) { + m_body.assign(reinterpret_cast(m_lpECB->lpbData),m_lpECB->cbAvailable); + datalen-=m_lpECB->cbAvailable; + } + char buf[8192]; + while (datalen) { + DWORD buflen=8192; + BOOL ret = m_lpECB->ReadClient(m_lpECB->ConnID, buf, &buflen); + if (!ret) + throw IOException("Error reading request body from browser."); + else if (!buflen) + throw IOException("Socket closed while reading request body from browser."); + m_body.append(buf, buflen); + datalen-=buflen; + } + } + else if (m_lpECB->cbAvailable) { + m_gotBody=true; + m_body.assign(reinterpret_cast(m_lpECB->lpbData),m_lpECB->cbAvailable); + } + return m_body.c_str(); + } + long sendResponse(istream& in, long status) { + string hdr = string("Connection: close\r\n"); + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + const char* codestr="200 OK"; + switch (status) { + case XMLTOOLING_HTTP_STATUS_UNAUTHORIZED: codestr="401 Authorization Required"; break; + case XMLTOOLING_HTTP_STATUS_FORBIDDEN: codestr="403 Forbidden"; break; + case XMLTOOLING_HTTP_STATUS_NOTFOUND: codestr="404 Not Found"; break; + case XMLTOOLING_HTTP_STATUS_ERROR: codestr="500 Server Error"; break; + } + m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, (void*)codestr, 0, (LPDWORD)hdr.c_str()); + char buf[1024]; + while (in) { + in.read(buf,1024); + DWORD resplen = in.gcount(); + m_lpECB->WriteClient(m_lpECB->ConnID, buf, &resplen, HSE_IO_SYNC); + } + return HSE_STATUS_SUCCESS; + } + long sendRedirect(const char* url) { + string hdr=string("Location: ") + url + "\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 40\r\n" + "Expires: 01-Jan-1997 12:00:00 GMT\r\n" + "Cache-Control: private,no-store,no-cache\r\n"; + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, "302 Moved", 0, (LPDWORD)hdr.c_str()); + static const char* redmsg="Redirecting..."; + DWORD resplen=40; + m_lpECB->WriteClient(m_lpECB->ConnID, (LPVOID)redmsg, &resplen, HSE_IO_SYNC); + return HSE_STATUS_SUCCESS; + } + // Decline happens in the POST processor if this isn't the shire url + // Note that it can also happen with HTAccess, but we don't support that, yet. + long returnDecline() { + return WriteClientError( + m_lpECB, + "ISAPI extension can only be invoked to process Shibboleth protocol requests." + "Make sure the mapped file extension doesn't match actual content." + ); + } + long returnOK() { + return HSE_STATUS_SUCCESS; + } + + const vector& getClientCertificates() const { + if (m_certs.empty()) { + char CertificateBuf[8192]; + CERT_CONTEXT_EX ccex; + ccex.cbAllocated = sizeof(CertificateBuf); + ccex.CertContext.pbCertEncoded = (BYTE*)CertificateBuf; + DWORD dwSize = sizeof(ccex); + + if (m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_GET_CERT_INFO_EX, (LPVOID)&ccex, (LPDWORD)dwSize, NULL)) { + if (ccex.CertContext.cbCertEncoded) { + xsecsize_t outlen; + XMLByte* serialized = Base64::encode(reinterpret_cast(CertificateBuf), ccex.CertContext.cbCertEncoded, &outlen); + m_certs.push_back(reinterpret_cast(serialized)); +#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&serialized); +#else + XMLString::release((char**)&serialized); +#endif + } + } + } + return m_certs; + } + + // Not used in the extension. + void clearHeader(const char* rawname, const char* cginame) { throw runtime_error("clearHeader not implemented"); } + void setHeader(const char* name, const char* value) { throw runtime_error("setHeader not implemented"); } + void setRemoteUser(const char* user) { throw runtime_error("setRemoteUser not implemented"); } +}; + +extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) +{ + try { + ostringstream threadid; + threadid << "[" << getpid() << "] isapi_shib_extension" << '\0'; + xmltooling::NDC ndc(threadid.str().c_str()); + + // Determine web site number. This can't really fail, I don't think. + dynabuf buf(128); + GetServerVariable(lpECB,"INSTANCE_ID",buf,10); + + // Match site instance to host name, skip if no match. + map::const_iterator map_i=g_Sites.find(static_cast(buf)); + if (map_i==g_Sites.end()) + return WriteClientError(lpECB, "Shibboleth Extension not configured for web site (check mappings in configuration)."); + + ShibTargetIsapiE ste(lpECB, map_i->second); + pair res = ste.getServiceProvider().doHandler(ste); + if (res.first) return res.second; + + return WriteClientError(lpECB, "Shibboleth Extension failed to process request"); + + } + catch(bad_alloc) { + return WriteClientError(lpECB,"Out of Memory"); + } + catch(long e) { + if (e==ERROR_NO_DATA) + return WriteClientError(lpECB,"A required variable or header was empty."); + else + return WriteClientError(lpECB,"Server detected unexpected IIS error."); + } + catch (exception& e) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, e.what()); + return WriteClientError(lpECB,"Shibboleth Extension caught an exception, check Event Log for details."); + } + catch(...) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Shibboleth Extension threw an unknown exception."); + if (g_catchAll) + return WriteClientError(lpECB,"Shibboleth Extension threw an unknown exception."); + throw; + } + + // If we get here we've got an error. + return HSE_STATUS_ERROR; +} diff --git a/isapi_shib/isapi_shib.cpp.r3097 b/isapi_shib/isapi_shib.cpp.r3097 new file mode 100644 index 0000000..2a1130d --- /dev/null +++ b/isapi_shib/isapi_shib.cpp.r3097 @@ -0,0 +1,1008 @@ +/* + * 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. + */ + +/** + * isapi_shib.cpp + * + * Shibboleth ISAPI filter + */ + +#define SHIBSP_LITE +#include "config_win32.h" + +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_RAND_S + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace shibsp; +using namespace xmltooling; +using namespace xercesc; +using namespace std; + +// globals +namespace { + static const XMLCh path[] = UNICODE_LITERAL_4(p,a,t,h); + static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e); + static const XMLCh name[] = UNICODE_LITERAL_4(n,a,m,e); + static const XMLCh port[] = UNICODE_LITERAL_4(p,o,r,t); + static const XMLCh sslport[] = UNICODE_LITERAL_7(s,s,l,p,o,r,t); + static const XMLCh scheme[] = UNICODE_LITERAL_6(s,c,h,e,m,e); + static const XMLCh id[] = UNICODE_LITERAL_2(i,d); + static const XMLCh Alias[] = UNICODE_LITERAL_5(A,l,i,a,s); + static const XMLCh Site[] = UNICODE_LITERAL_4(S,i,t,e); + + struct site_t { + site_t(const DOMElement* e) + { + auto_ptr_char n(e->getAttributeNS(NULL,name)); + auto_ptr_char s(e->getAttributeNS(NULL,scheme)); + auto_ptr_char p(e->getAttributeNS(NULL,port)); + auto_ptr_char p2(e->getAttributeNS(NULL,sslport)); + if (n.get()) m_name=n.get(); + if (s.get()) m_scheme=s.get(); + if (p.get()) m_port=p.get(); + if (p2.get()) m_sslport=p2.get(); + e = XMLHelper::getFirstChildElement(e, Alias); + while (e) { + if (e->hasChildNodes()) { + auto_ptr_char alias(e->getFirstChild()->getNodeValue()); + m_aliases.insert(alias.get()); + } + e = XMLHelper::getNextSiblingElement(e, Alias); + } + } + string m_scheme,m_port,m_sslport,m_name; + set m_aliases; + }; + + HINSTANCE g_hinstDLL; + SPConfig* g_Config = NULL; + map g_Sites; + bool g_bNormalizeRequest = true; + string g_unsetHeaderValue,g_spoofKey; + bool g_checkSpoofing = true; + bool g_catchAll = false; + bool g_bSafeHeaderNames = false; + vector g_NoCerts; +} + +BOOL LogEvent( + LPCSTR lpUNCServerName, + WORD wType, + DWORD dwEventID, + PSID lpUserSid, + LPCSTR message) +{ + LPCSTR messages[] = {message, NULL}; + + HANDLE hElog = RegisterEventSource(lpUNCServerName, "Shibboleth ISAPI Filter"); + BOOL res = ReportEvent(hElog, wType, 0, dwEventID, lpUserSid, 1, 0, messages, NULL); + return (DeregisterEventSource(hElog) && res); +} + +void _my_invalid_parameter_handler( + const wchar_t * expression, + const wchar_t * function, + const wchar_t * file, + unsigned int line, + uintptr_t pReserved + ) +{ + return; +} + +extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID) +{ + if (fdwReason==DLL_PROCESS_ATTACH) + g_hinstDLL=hinstDLL; + return TRUE; +} + +extern "C" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO* pVer) +{ + if (!pVer) + return FALSE; + + if (!g_Config) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Extension mode startup not possible, is the DLL loaded as a filter?"); + return FALSE; + } + + pVer->dwExtensionVersion=HSE_VERSION; + strncpy(pVer->lpszExtensionDesc,"Shibboleth ISAPI Extension",HSE_MAX_EXT_DLL_NAME_LEN-1); + return TRUE; +} + +extern "C" BOOL WINAPI TerminateExtension(DWORD) +{ + return TRUE; // cleanup should happen when filter unloads +} + +extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer) +{ + if (!pVer) + return FALSE; + else if (g_Config) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Reentrant filter initialization, ignoring..."); + return TRUE; + } + + g_Config=&SPConfig::getConfig(); + g_Config->setFeatures( + SPConfig::Listener | + SPConfig::Caching | + SPConfig::RequestMapping | + SPConfig::InProcess | + SPConfig::Logging | + SPConfig::Handlers + ); + if (!g_Config->init()) { + g_Config=NULL; + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Filter startup failed during library initialization, check native log for help."); + return FALSE; + } + + try { + if (!g_Config->instantiate(NULL, true)) + throw runtime_error("unknown error"); + } + catch (exception& ex) { + g_Config->term(); + g_Config=NULL; + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, ex.what()); + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Filter startup failed to load configuration, check native log for details."); + return FALSE; + } + + // Access implementation-specifics and site mappings. + ServiceProvider* sp=g_Config->getServiceProvider(); + Locker locker(sp); + const PropertySet* props=sp->getPropertySet("InProcess"); + if (props) { + pair flag=props->getBool("checkSpoofing"); + g_checkSpoofing = !flag.first || flag.second; + flag=props->getBool("catchAll"); + g_catchAll = flag.first && flag.second; + + pair unsetValue=props->getString("unsetHeaderValue"); + if (unsetValue.first) + g_unsetHeaderValue = unsetValue.second; + if (g_checkSpoofing) { + unsetValue = props->getString("spoofKey"); + if (unsetValue.first) + g_spoofKey = unsetValue.second; + else { + _invalid_parameter_handler old = _set_invalid_parameter_handler(_my_invalid_parameter_handler); + unsigned int randkey=0,randkey2=0,randkey3=0,randkey4=0; + if (rand_s(&randkey) == 0 && rand_s(&randkey2) == 0 && rand_s(&randkey3) == 0 && rand_s(&randkey4) == 0) { + _set_invalid_parameter_handler(old); + ostringstream keystr; + keystr << randkey << randkey2 << randkey3 << randkey4; + g_spoofKey = keystr.str(); + } + else { + _set_invalid_parameter_handler(old); + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, + "Filter failed to generate a random anti-spoofing key (if this is Windows 2000 set one manually)."); + locker.assign(); // pops lock on SP config + g_Config->term(); + g_Config=NULL; + return FALSE; + } + } + } + + props = props->getPropertySet("ISAPI"); + if (props) { + flag = props->getBool("normalizeRequest"); + g_bNormalizeRequest = !flag.first || flag.second; + flag = props->getBool("safeHeaderNames"); + g_bSafeHeaderNames = flag.first && flag.second; + const DOMElement* child = XMLHelper::getFirstChildElement(props->getElement(),Site); + while (child) { + auto_ptr_char id(child->getAttributeNS(NULL,id)); + if (id.get()) + g_Sites.insert(pair(id.get(),site_t(child))); + child=XMLHelper::getNextSiblingElement(child,Site); + } + } + } + + pVer->dwFilterVersion=HTTP_FILTER_REVISION; + strncpy(pVer->lpszFilterDesc,"Shibboleth ISAPI Filter",SF_MAX_FILTER_DESC_LEN); + pVer->dwFlags=(SF_NOTIFY_ORDER_HIGH | + SF_NOTIFY_SECURE_PORT | + SF_NOTIFY_NONSECURE_PORT | + SF_NOTIFY_PREPROC_HEADERS | + SF_NOTIFY_LOG); + LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter initialized..."); + return TRUE; +} + +extern "C" BOOL WINAPI TerminateFilter(DWORD) +{ + if (g_Config) + g_Config->term(); + g_Config = NULL; + LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter shut down..."); + return TRUE; +} + +/* Next up, some suck-free versions of various APIs. + + You DON'T require people to guess the buffer size and THEN tell them the right size. + Returning an LPCSTR is apparently way beyond their ken. Not to mention the fact that + constant strings aren't typed as such, making it just that much harder. These versions + are now updated to use a special growable buffer object, modeled after the standard + string class. The standard string won't work because they left out the option to + pre-allocate a non-constant buffer. +*/ + +class dynabuf +{ +public: + dynabuf() { bufptr=NULL; buflen=0; } + dynabuf(size_t s) { bufptr=new char[buflen=s]; *bufptr=0; } + ~dynabuf() { delete[] bufptr; } + size_t length() const { return bufptr ? strlen(bufptr) : 0; } + size_t size() const { return buflen; } + bool empty() const { return length()==0; } + void reserve(size_t s, bool keep=false); + void erase() { if (bufptr) memset(bufptr,0,buflen); } + operator char*() { return bufptr; } + bool operator ==(const char* s) const; + bool operator !=(const char* s) const { return !(*this==s); } +private: + char* bufptr; + size_t buflen; +}; + +void dynabuf::reserve(size_t s, bool keep) +{ + if (s<=buflen) + return; + char* p=new char[s]; + if (keep) + while (buflen--) + p[buflen]=bufptr[buflen]; + buflen=s; + delete[] bufptr; + bufptr=p; +} + +bool dynabuf::operator==(const char* s) const +{ + if (buflen==NULL || s==NULL) + return (buflen==NULL && s==NULL); + else + return strcmp(bufptr,s)==0; +} + +void GetServerVariable(PHTTP_FILTER_CONTEXT pfc, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true) +{ + s.reserve(size); + s.erase(); + size=s.size(); + + while (!pfc->GetServerVariable(pfc,lpszVariable,s,&size)) { + // Grumble. Check the error. + DWORD e=GetLastError(); + if (e==ERROR_INSUFFICIENT_BUFFER) + s.reserve(size); + else + break; + } + if (bRequired && s.empty()) + throw ERROR_NO_DATA; +} + +void GetServerVariable(LPEXTENSION_CONTROL_BLOCK lpECB, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true) +{ + s.reserve(size); + s.erase(); + size=s.size(); + + while (!lpECB->GetServerVariable(lpECB->ConnID,lpszVariable,s,&size)) { + // Grumble. Check the error. + DWORD e=GetLastError(); + if (e==ERROR_INSUFFICIENT_BUFFER) + s.reserve(size); + else + break; + } + if (bRequired && s.empty()) + throw ERROR_NO_DATA; +} + +void GetHeader(PHTTP_FILTER_PREPROC_HEADERS pn, PHTTP_FILTER_CONTEXT pfc, + LPSTR lpszName, dynabuf& s, DWORD size=80, bool bRequired=true) +{ + s.reserve(size); + s.erase(); + size=s.size(); + + while (!pn->GetHeader(pfc,lpszName,s,&size)) { + // Grumble. Check the error. + DWORD e=GetLastError(); + if (e==ERROR_INSUFFICIENT_BUFFER) + s.reserve(size); + else + break; + } + if (bRequired && s.empty()) + throw ERROR_NO_DATA; +} + +/****************************************************************************/ +// ISAPI Filter + +class ShibTargetIsapiF : public AbstractSPRequest +{ + PHTTP_FILTER_CONTEXT m_pfc; + PHTTP_FILTER_PREPROC_HEADERS m_pn; + multimap m_headers; + int m_port; + string m_scheme,m_hostname; + mutable string m_remote_addr,m_content_type,m_method; + dynabuf m_allhttp; + bool m_firsttime; + +public: + ShibTargetIsapiF(PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, const site_t& site) + : AbstractSPRequest(SHIBSP_LOGCAT".ISAPI"), m_pfc(pfc), m_pn(pn), m_allhttp(4096), m_firsttime(true) { + + // URL path always come from IIS. + dynabuf var(256); + GetHeader(pn,pfc,"url",var,256,false); + setRequestURI(var); + + // Port may come from IIS or from site def. + if (!g_bNormalizeRequest || (pfc->fIsSecurePort && site.m_sslport.empty()) || (!pfc->fIsSecurePort && site.m_port.empty())) { + GetServerVariable(pfc,"SERVER_PORT",var,10); + m_port = atoi(var); + } + else if (pfc->fIsSecurePort) { + m_port = atoi(site.m_sslport.c_str()); + } + else { + m_port = atoi(site.m_port.c_str()); + } + + // Scheme may come from site def or be derived from IIS. + m_scheme=site.m_scheme; + if (m_scheme.empty() || !g_bNormalizeRequest) + m_scheme=pfc->fIsSecurePort ? "https" : "http"; + + GetServerVariable(pfc,"SERVER_NAME",var,32); + + // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name. + m_hostname = var; + if (site.m_name!=m_hostname && site.m_aliases.find(m_hostname)==site.m_aliases.end()) + m_hostname=site.m_name; + + if (!g_spoofKey.empty()) { + GetHeader(pn, pfc, "ShibSpoofCheck:", var, 32, false); + if (!var.empty() && g_spoofKey == (char*)var) + m_firsttime = false; + } + + if (!m_firsttime) + log(SPDebug, "ISAPI filter running more than once"); + } + ~ShibTargetIsapiF() { } + + const char* getScheme() const { + return m_scheme.c_str(); + } + const char* getHostname() const { + return m_hostname.c_str(); + } + int getPort() const { + return m_port; + } + const char* getQueryString() const { + const char* uri = getRequestURI(); + uri = (uri ? strchr(uri, '?') : NULL); + return uri ? (uri + 1) : NULL; + } + const char* getMethod() const { + if (m_method.empty()) { + dynabuf var(5); + GetServerVariable(m_pfc,"HTTP_METHOD",var,5,false); + if (!var.empty()) + m_method = var; + } + return m_method.c_str(); + } + string getContentType() const { + if (m_content_type.empty()) { + dynabuf var(32); + GetServerVariable(m_pfc,"HTTP_CONTENT_TYPE",var,32,false); + if (!var.empty()) + m_content_type = var; + } + return m_content_type; + } + string getRemoteAddr() const { + m_remote_addr = AbstractSPRequest::getRemoteAddr(); + if (m_remote_addr.empty()) { + dynabuf var(16); + GetServerVariable(m_pfc,"REMOTE_ADDR",var,16,false); + if (!var.empty()) + m_remote_addr = var; + } + return m_remote_addr; + } + void log(SPLogLevel level, const string& msg) { + AbstractSPRequest::log(level,msg); + if (level >= SPCrit) + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str()); + } + string makeSafeHeader(const char* rawname) const { + string hdr; + for (; *rawname; ++rawname) { + if (isalnum(*rawname)) + hdr += *rawname; + } + return (hdr + ':'); + } + void clearHeader(const char* rawname, const char* cginame) { + if (g_checkSpoofing && m_firsttime) { + if (m_allhttp.empty()) + GetServerVariable(m_pfc, "ALL_HTTP", m_allhttp, 4096); + string hdr = g_bSafeHeaderNames ? ("HTTP_" + makeSafeHeader(cginame + 5)) : (string(cginame) + ':'); + if (strstr(m_allhttp, hdr.c_str())) + throw opensaml::SecurityPolicyException("Attempt to spoof header ($1) was detected.", params(1, hdr.c_str())); + } + if (g_bSafeHeaderNames) { + string hdr = makeSafeHeader(rawname); + m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(g_unsetHeaderValue.c_str())); + } + else if (!strcmp(rawname,"REMOTE_USER")) { + m_pn->SetHeader(m_pfc, "remote-user:", const_cast(g_unsetHeaderValue.c_str())); + m_pn->SetHeader(m_pfc, "remote_user:", const_cast(g_unsetHeaderValue.c_str())); + } + else { + string hdr = string(rawname) + ':'; + m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(g_unsetHeaderValue.c_str())); + } + } + void setHeader(const char* name, const char* value) { + string hdr = g_bSafeHeaderNames ? makeSafeHeader(name) : (string(name) + ':'); + m_pn->SetHeader(m_pfc, const_cast(hdr.c_str()), const_cast(value)); + } + string getSecureHeader(const char* name) const { + string hdr = g_bSafeHeaderNames ? makeSafeHeader(name) : (string(name) + ':'); + dynabuf buf(256); + GetHeader(m_pn, m_pfc, const_cast(hdr.c_str()), buf, 256, false); + return string(buf.empty() ? "" : buf); + } + string getHeader(const char* name) const { + string hdr(name); + hdr += ':'; + dynabuf buf(256); + GetHeader(m_pn, m_pfc, const_cast(hdr.c_str()), buf, 256, false); + return string(buf.empty() ? "" : buf); + } + void setRemoteUser(const char* user) { + setHeader("remote-user", user); + if (!user || !*user) + m_pfc->pFilterContext = NULL; + else if (m_pfc->pFilterContext = m_pfc->AllocMem(m_pfc, sizeof(char) * (strlen(user) + 1), NULL)) + strcpy(reinterpret_cast(m_pfc->pFilterContext), user); + } + string getRemoteUser() const { + return getSecureHeader("remote-user"); + } + void setResponseHeader(const char* name, const char* value) { + // Set for later. + if (value) + m_headers.insert(make_pair(name,value)); + else + m_headers.erase(name); + } + long sendResponse(istream& in, long status) { + string hdr = string("Connection: close\r\n"); + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + const char* codestr="200 OK"; + switch (status) { + case XMLTOOLING_HTTP_STATUS_UNAUTHORIZED: codestr="401 Authorization Required"; break; + case XMLTOOLING_HTTP_STATUS_FORBIDDEN: codestr="403 Forbidden"; break; + case XMLTOOLING_HTTP_STATUS_NOTFOUND: codestr="404 Not Found"; break; + case XMLTOOLING_HTTP_STATUS_ERROR: codestr="500 Server Error"; break; + } + m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, (void*)codestr, (DWORD)hdr.c_str(), 0); + char buf[1024]; + while (in) { + in.read(buf,1024); + DWORD resplen = in.gcount(); + m_pfc->WriteClient(m_pfc, buf, &resplen, 0); + } + return SF_STATUS_REQ_FINISHED; + } + long sendRedirect(const char* url) { + // XXX: Don't support the httpRedirect option, yet. + string hdr=string("Location: ") + url + "\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 40\r\n" + "Expires: 01-Jan-1997 12:00:00 GMT\r\n" + "Cache-Control: private,no-store,no-cache\r\n"; + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, "302 Please Wait", (DWORD)hdr.c_str(), 0); + static const char* redmsg="Redirecting..."; + DWORD resplen=40; + m_pfc->WriteClient(m_pfc, (LPVOID)redmsg, &resplen, 0); + return SF_STATUS_REQ_FINISHED; + } + long returnDecline() { + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + long returnOK() { + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + + const vector& getClientCertificates() const { + return g_NoCerts; + } + + // The filter never processes the POST, so stub these methods. + long getContentLength() const { throw IOException("The request's Content-Length is not available to an ISAPI filter."); } + const char* getRequestBody() const { throw IOException("The request body is not available to an ISAPI filter."); } +}; + +DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const char* msg) +{ + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg); + static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n"; + pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)ctype,0); + static const char* xmsg="Shibboleth Filter Error" + "

Shibboleth Filter Error

"; + DWORD resplen=strlen(xmsg); + pfc->WriteClient(pfc,(LPVOID)xmsg,&resplen,0); + resplen=strlen(msg); + pfc->WriteClient(pfc,(LPVOID)msg,&resplen,0); + static const char* xmsg2=""; + resplen=strlen(xmsg2); + pfc->WriteClient(pfc,(LPVOID)xmsg2,&resplen,0); + return SF_STATUS_REQ_FINISHED; +} + +extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification) +{ + // Is this a log notification? + if (notificationType==SF_NOTIFY_LOG) { + if (pfc->pFilterContext) + ((PHTTP_FILTER_LOG)pvNotification)->pszClientUserName=reinterpret_cast(pfc->pFilterContext); + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + + PHTTP_FILTER_PREPROC_HEADERS pn=(PHTTP_FILTER_PREPROC_HEADERS)pvNotification; + try + { + // Determine web site number. This can't really fail, I don't think. + dynabuf buf(128); + GetServerVariable(pfc,"INSTANCE_ID",buf,10); + + // Match site instance to host name, skip if no match. + map::const_iterator map_i=g_Sites.find(static_cast(buf)); + if (map_i==g_Sites.end()) + return SF_STATUS_REQ_NEXT_NOTIFICATION; + + ostringstream threadid; + threadid << "[" << getpid() << "] isapi_shib" << '\0'; + xmltooling::NDC ndc(threadid.str().c_str()); + + ShibTargetIsapiF stf(pfc, pn, map_i->second); + + // "false" because we don't override the Shib settings + pair res = stf.getServiceProvider().doAuthentication(stf); + if (!g_spoofKey.empty()) + pn->SetHeader(pfc, "ShibSpoofCheck:", const_cast(g_spoofKey.c_str())); + if (res.first) return res.second; + + // "false" because we don't override the Shib settings + res = stf.getServiceProvider().doExport(stf); + if (res.first) return res.second; + + res = stf.getServiceProvider().doAuthorization(stf); + if (res.first) return res.second; + + return SF_STATUS_REQ_NEXT_NOTIFICATION; + } + catch(bad_alloc) { + return WriteClientError(pfc,"Out of Memory"); + } + catch(long e) { + if (e==ERROR_NO_DATA) + return WriteClientError(pfc,"A required variable or header was empty."); + else + return WriteClientError(pfc,"Shibboleth Filter detected unexpected IIS error."); + } + catch (exception& e) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, e.what()); + return WriteClientError(pfc,"Shibboleth Filter caught an exception, check Event Log for details."); + } + catch(...) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Shibboleth Filter threw an unknown exception."); + if (g_catchAll) + return WriteClientError(pfc,"Shibboleth Filter threw an unknown exception."); + throw; + } + + return WriteClientError(pfc,"Shibboleth Filter reached unreachable code, save my walrus!"); +} + + +/****************************************************************************/ +// ISAPI Extension + +DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const char* msg) +{ + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg); + static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n"; + lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)ctype); + static const char* xmsg="Shibboleth Error

Shibboleth Error

"; + DWORD resplen=strlen(xmsg); + lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg,&resplen,HSE_IO_SYNC); + resplen=strlen(msg); + lpECB->WriteClient(lpECB->ConnID,(LPVOID)msg,&resplen,HSE_IO_SYNC); + static const char* xmsg2=""; + resplen=strlen(xmsg2); + lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg2,&resplen,HSE_IO_SYNC); + return HSE_STATUS_SUCCESS; +} + + +class ShibTargetIsapiE : public AbstractSPRequest +{ + LPEXTENSION_CONTROL_BLOCK m_lpECB; + multimap m_headers; + mutable vector m_certs; + mutable string m_body; + mutable bool m_gotBody; + int m_port; + string m_scheme,m_hostname,m_uri; + mutable string m_remote_addr,m_remote_user; + +public: + ShibTargetIsapiE(LPEXTENSION_CONTROL_BLOCK lpECB, const site_t& site) + : AbstractSPRequest(SHIBSP_LOGCAT".ISAPI"), m_lpECB(lpECB), m_gotBody(false) { + dynabuf ssl(5); + GetServerVariable(lpECB,"HTTPS",ssl,5); + bool SSL=(ssl=="on" || ssl=="ON"); + + // Scheme may come from site def or be derived from IIS. + m_scheme=site.m_scheme; + if (m_scheme.empty() || !g_bNormalizeRequest) + m_scheme = SSL ? "https" : "http"; + + // URL path always come from IIS. + dynabuf url(256); + GetServerVariable(lpECB,"URL",url,255); + + // Port may come from IIS or from site def. + dynabuf port(11); + if (!g_bNormalizeRequest || (SSL && site.m_sslport.empty()) || (!SSL && site.m_port.empty())) + GetServerVariable(lpECB,"SERVER_PORT",port,10); + else if (SSL) { + strncpy(port,site.m_sslport.c_str(),10); + static_cast(port)[10]=0; + } + else { + strncpy(port,site.m_port.c_str(),10); + static_cast(port)[10]=0; + } + m_port = atoi(port); + + dynabuf var(32); + GetServerVariable(lpECB, "SERVER_NAME", var, 32); + + // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name. + m_hostname=var; + if (site.m_name!=m_hostname && site.m_aliases.find(m_hostname)==site.m_aliases.end()) + m_hostname=site.m_name; + + /* + * IIS screws us over on PATH_INFO (the hits keep on coming). We need to figure out if + * the server is set up for proper PATH_INFO handling, or "IIS sucks rabid weasels mode", + * which is the default. No perfect way to tell, but we can take a good guess by checking + * whether the URL is a substring of the PATH_INFO: + * + * e.g. for /Shibboleth.sso/SAML/POST + * + * Bad mode (default): + * URL: /Shibboleth.sso + * PathInfo: /Shibboleth.sso/SAML/POST + * + * Good mode: + * URL: /Shibboleth.sso + * PathInfo: /SAML/POST + */ + + string uri; + + // Clearly we're only in bad mode if path info exists at all. + if (lpECB->lpszPathInfo && *(lpECB->lpszPathInfo)) { + if (strstr(lpECB->lpszPathInfo,url)) + // Pretty good chance we're in bad mode, unless the PathInfo repeats the path itself. + uri = lpECB->lpszPathInfo; + else { + uri = url; + uri += lpECB->lpszPathInfo; + } + } + else { + uri = url; + } + + // For consistency with Apache, let's add the query string. + if (lpECB->lpszQueryString && *(lpECB->lpszQueryString)) { + uri += '?'; + uri += lpECB->lpszQueryString; + } + + setRequestURI(uri.c_str()); + } + ~ShibTargetIsapiE() { } + + const char* getScheme() const { + return m_scheme.c_str(); + } + const char* getHostname() const { + return m_hostname.c_str(); + } + int getPort() const { + return m_port; + } + const char* getMethod() const { + return m_lpECB->lpszMethod; + } + string getContentType() const { + return m_lpECB->lpszContentType ? m_lpECB->lpszContentType : ""; + } + long getContentLength() const { + return m_lpECB->cbTotalBytes; + } + string getRemoteUser() const { + if (m_remote_user.empty()) { + dynabuf var(16); + GetServerVariable(m_lpECB, "REMOTE_USER", var, 32, false); + if (!var.empty()) + m_remote_user = var; + } + return m_remote_user; + } + string getRemoteAddr() const { + m_remote_addr = AbstractSPRequest::getRemoteAddr(); + if (m_remote_addr.empty()) { + dynabuf var(16); + GetServerVariable(m_lpECB, "REMOTE_ADDR", var, 16, false); + if (!var.empty()) + m_remote_addr = var; + } + return m_remote_addr; + } + void log(SPLogLevel level, const string& msg) const { + AbstractSPRequest::log(level,msg); + if (level >= SPCrit) + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str()); + } + string getHeader(const char* name) const { + string hdr("HTTP_"); + for (; *name; ++name) { + if (*name=='-') + hdr += '_'; + else + hdr += toupper(*name); + } + dynabuf buf(128); + GetServerVariable(m_lpECB, const_cast(hdr.c_str()), buf, 128, false); + return buf.empty() ? "" : buf; + } + void setResponseHeader(const char* name, const char* value) { + // Set for later. + if (value) + m_headers.insert(make_pair(name,value)); + else + m_headers.erase(name); + } + const char* getQueryString() const { + return m_lpECB->lpszQueryString; + } + const char* getRequestBody() const { + if (m_gotBody) + return m_body.c_str(); + if (m_lpECB->cbTotalBytes > 1024*1024) // 1MB? + throw opensaml::SecurityPolicyException("Size of request body exceeded 1M size limit."); + else if (m_lpECB->cbTotalBytes > m_lpECB->cbAvailable) { + m_gotBody=true; + DWORD datalen=m_lpECB->cbTotalBytes; + if (m_lpECB->cbAvailable > 0) { + m_body.assign(reinterpret_cast(m_lpECB->lpbData),m_lpECB->cbAvailable); + datalen-=m_lpECB->cbAvailable; + } + char buf[8192]; + while (datalen) { + DWORD buflen=8192; + BOOL ret = m_lpECB->ReadClient(m_lpECB->ConnID, buf, &buflen); + if (!ret) + throw IOException("Error reading request body from browser."); + else if (!buflen) + throw IOException("Socket closed while reading request body from browser."); + m_body.append(buf, buflen); + datalen-=buflen; + } + } + else if (m_lpECB->cbAvailable) { + m_gotBody=true; + m_body.assign(reinterpret_cast(m_lpECB->lpbData),m_lpECB->cbAvailable); + } + return m_body.c_str(); + } + long sendResponse(istream& in, long status) { + string hdr = string("Connection: close\r\n"); + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + const char* codestr="200 OK"; + switch (status) { + case XMLTOOLING_HTTP_STATUS_UNAUTHORIZED: codestr="401 Authorization Required"; break; + case XMLTOOLING_HTTP_STATUS_FORBIDDEN: codestr="403 Forbidden"; break; + case XMLTOOLING_HTTP_STATUS_NOTFOUND: codestr="404 Not Found"; break; + case XMLTOOLING_HTTP_STATUS_ERROR: codestr="500 Server Error"; break; + } + m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, (void*)codestr, 0, (LPDWORD)hdr.c_str()); + char buf[1024]; + while (in) { + in.read(buf,1024); + DWORD resplen = in.gcount(); + m_lpECB->WriteClient(m_lpECB->ConnID, buf, &resplen, HSE_IO_SYNC); + } + return HSE_STATUS_SUCCESS; + } + long sendRedirect(const char* url) { + string hdr=string("Location: ") + url + "\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 40\r\n" + "Expires: 01-Jan-1997 12:00:00 GMT\r\n" + "Cache-Control: private,no-store,no-cache\r\n"; + for (multimap::const_iterator i=m_headers.begin(); i!=m_headers.end(); ++i) + hdr += i->first + ": " + i->second + "\r\n"; + hdr += "\r\n"; + m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, "302 Moved", 0, (LPDWORD)hdr.c_str()); + static const char* redmsg="Redirecting..."; + DWORD resplen=40; + m_lpECB->WriteClient(m_lpECB->ConnID, (LPVOID)redmsg, &resplen, HSE_IO_SYNC); + return HSE_STATUS_SUCCESS; + } + // Decline happens in the POST processor if this isn't the shire url + // Note that it can also happen with HTAccess, but we don't support that, yet. + long returnDecline() { + return WriteClientError( + m_lpECB, + "ISAPI extension can only be invoked to process Shibboleth protocol requests." + "Make sure the mapped file extension doesn't match actual content." + ); + } + long returnOK() { + return HSE_STATUS_SUCCESS; + } + + const vector& getClientCertificates() const { + if (m_certs.empty()) { + char CertificateBuf[8192]; + CERT_CONTEXT_EX ccex; + ccex.cbAllocated = sizeof(CertificateBuf); + ccex.CertContext.pbCertEncoded = (BYTE*)CertificateBuf; + DWORD dwSize = sizeof(ccex); + + if (m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_GET_CERT_INFO_EX, (LPVOID)&ccex, (LPDWORD)dwSize, NULL)) { + if (ccex.CertContext.cbCertEncoded) { + xsecsize_t outlen; + XMLByte* serialized = Base64::encode(reinterpret_cast(CertificateBuf), ccex.CertContext.cbCertEncoded, &outlen); + m_certs.push_back(reinterpret_cast(serialized)); +#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&serialized); +#else + XMLString::release((char**)&serialized); +#endif + } + } + } + return m_certs; + } + + // Not used in the extension. + void clearHeader(const char* rawname, const char* cginame) { throw runtime_error("clearHeader not implemented"); } + void setHeader(const char* name, const char* value) { throw runtime_error("setHeader not implemented"); } + void setRemoteUser(const char* user) { throw runtime_error("setRemoteUser not implemented"); } +}; + +extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) +{ + try { + ostringstream threadid; + threadid << "[" << getpid() << "] isapi_shib_extension" << '\0'; + xmltooling::NDC ndc(threadid.str().c_str()); + + // Determine web site number. This can't really fail, I don't think. + dynabuf buf(128); + GetServerVariable(lpECB,"INSTANCE_ID",buf,10); + + // Match site instance to host name, skip if no match. + map::const_iterator map_i=g_Sites.find(static_cast(buf)); + if (map_i==g_Sites.end()) + return WriteClientError(lpECB, "Shibboleth Extension not configured for web site (check mappings in configuration)."); + + ShibTargetIsapiE ste(lpECB, map_i->second); + pair res = ste.getServiceProvider().doHandler(ste); + if (res.first) return res.second; + + return WriteClientError(lpECB, "Shibboleth Extension failed to process request"); + + } + catch(bad_alloc) { + return WriteClientError(lpECB,"Out of Memory"); + } + catch(long e) { + if (e==ERROR_NO_DATA) + return WriteClientError(lpECB,"A required variable or header was empty."); + else + return WriteClientError(lpECB,"Server detected unexpected IIS error."); + } + catch (exception& e) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, e.what()); + return WriteClientError(lpECB,"Shibboleth Extension caught an exception, check Event Log for details."); + } + catch(...) { + LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Shibboleth Extension threw an unknown exception."); + if (g_catchAll) + return WriteClientError(lpECB,"Shibboleth Extension threw an unknown exception."); + throw; + } + + // If we get here we've got an error. + return HSE_STATUS_ERROR; +} diff --git a/isapi_shib/isapi_shib.rc b/isapi_shib/isapi_shib.rc index 26733a8..4a6f709 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,1,0,0 - PRODUCTVERSION 2,1,0,0 + FILEVERSION 2,2,1,0 + PRODUCTVERSION 2,2,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, 1, 0, 0\0" + VALUE "FileVersion", "2, 2, 1, 0\0" VALUE "InternalName", "isapi_shib\0" - VALUE "LegalCopyright", "Copyright © 2008 Internet2\0" + VALUE "LegalCopyright", "Copyright © 2009 Internet2\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "isapi_shib.dll\0" VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Shibboleth 2.1\0" - VALUE "ProductVersion", "2, 1, 0, 0\0" + VALUE "ProductName", "Shibboleth 2.2.1\0" + VALUE "ProductVersion", "2, 2, 1, 0\0" VALUE "SpecialBuild", "\0" END END diff --git a/isapi_shib/isapi_shib.vcproj b/isapi_shib/isapi_shib.vcproj index 3f1bad4..f82279a 100644 --- a/isapi_shib/isapi_shib.vcproj +++ b/isapi_shib/isapi_shib.vcproj @@ -1,9 +1,10 @@ - - - @@ -349,11 +347,13 @@ - diff --git a/memcache-store/memcache-store.cpp b/memcache-store/memcache-store.cpp index d1fb373..c208108 100644 --- a/memcache-store/memcache-store.cpp +++ b/memcache-store/memcache-store.cpp @@ -26,17 +26,26 @@ # include "config.h" #endif +#ifdef WIN32 +# define _CRT_NONSTDC_NO_DEPRECATE 1 +# define _CRT_SECURE_NO_DEPRECATE 1 +# define MCEXT_EXPORTS __declspec(dllexport) +#else +# define MCEXT_EXPORTS +#endif + +#include + +#include +#include #include #include - #include #include #include #include -#include - using namespace xmltooling::logging; using namespace xmltooling; using namespace xercesc; @@ -100,7 +109,7 @@ namespace xmltooling { memcached_st *memc; string m_memcacheHosts; string m_prefix; - + Mutex* m_lock; }; class MemcacheStorageService : public StorageService, public MemcacheBase { @@ -151,15 +160,19 @@ bool MemcacheBase::addLock(string what, bool use_prefix) { string set_val = "1"; unsigned tries = 5; while (!addMemcache(lock_name.c_str(), set_val, 5, 0, use_prefix)) { - if (tries-- < 0) { + if (tries-- == 0) { log.debug("Unable to get lock %s... FAILED.", lock_name.c_str()); return false; } log.debug("Unable to get lock %s... Retrying.", lock_name.c_str()); // sleep 100ms +#ifdef WIN32 + Sleep(100); +#else struct timeval tv = { 0, 100000 }; - select(0, 0, 0, 0, &tv); + select(0, 0, 0, 0, &tv); +#endif } return true; } @@ -264,7 +277,6 @@ bool MemcacheBase::deleteMemcache(const char *key, bool use_prefix) { memcached_return rv; string final_key; - memcached_st clone; bool success; if (use_prefix) { @@ -273,23 +285,25 @@ bool MemcacheBase::deleteMemcache(const char *key, final_key = key; } - if (memcached_clone(&clone, memc) == NULL) { - throw IOException("MemcacheBase::deleteMemcache(): memcached_clone() failed"); - } + m_lock->lock(); + rv = memcached_delete(memc, (char *)final_key.c_str(), final_key.length(), timeout); + m_lock->unlock(); - rv = memcached_delete(&clone, (char *)final_key.c_str(), final_key.length(), timeout); if (rv == MEMCACHED_SUCCESS) { success = true; } else if (rv == MEMCACHED_NOTFOUND) { // Key wasn't there... No biggie. success = false; + } else if (rv == MEMCACHED_ERRNO) { + // System error + log.error(string("Memcache::deleteMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno))); + success = false; } else { - log.error(string("Memcache::deleteMemcache() Problems: ") + memcached_strerror(&clone, rv)); + log.error(string("Memcache::deleteMemcache() Problems: ") + memcached_strerror(memc, rv)); // shouldn't be here success = false; } - memcached_free(&clone); return success; } @@ -301,7 +315,6 @@ bool MemcacheBase::getMemcache(const char *key, size_t len; char *result; string final_key; - memcached_st clone; bool success; if (use_prefix) { @@ -310,11 +323,10 @@ bool MemcacheBase::getMemcache(const char *key, final_key = key; } - if (memcached_clone(&clone, memc) == NULL) { - throw IOException("MemcacheBase::getMemcache(): memcached_clone() failed"); - } + m_lock->lock(); + result = memcached_get(memc, (char *)final_key.c_str(), final_key.length(), &len, flags, &rv); + m_lock->unlock(); - result = memcached_get(&clone, (char *)final_key.c_str(), final_key.length(), &len, flags, &rv); if (rv == MEMCACHED_SUCCESS) { dest = result; free(result); @@ -322,12 +334,15 @@ bool MemcacheBase::getMemcache(const char *key, } else if (rv == MEMCACHED_NOTFOUND) { log.debug("Key %s not found in memcache...", key); success = false; + } else if (rv == MEMCACHED_ERRNO) { + // System error + log.error(string("Memcache::getMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno))); + success = false; } else { - log.error(string("Memcache::getMemcache() Problems: ") + memcached_strerror(&clone, rv)); + log.error(string("Memcache::getMemcache() Problems: ") + memcached_strerror(memc, rv)); success = false; } - memcached_free(&clone); return success; } @@ -339,7 +354,6 @@ bool MemcacheBase::addMemcache(const char *key, memcached_return rv; string final_key; - memcached_st clone; bool success; if (use_prefix) { @@ -348,23 +362,25 @@ bool MemcacheBase::addMemcache(const char *key, final_key = key; } - if (memcached_clone(&clone, memc) == NULL) { - throw IOException("MemcacheBase::addMemcache(): memcached_clone() failed"); - } + m_lock->lock(); + rv = memcached_add(memc, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags); + m_lock->unlock(); - rv = memcached_add(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags); if (rv == MEMCACHED_SUCCESS) { success = true; } else if (rv == MEMCACHED_NOTSTORED) { // already there success = false; + } else if (rv == MEMCACHED_ERRNO) { + // System error + log.error(string("Memcache::addMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno))); + success = false; } else { // shouldn't be here - log.error(string("Memcache::addMemcache() Problems: ") + memcached_strerror(&clone, rv)); + log.error(string("Memcache::addMemcache() Problems: ") + memcached_strerror(memc, rv)); success = false; } - memcached_free(&clone); return success; } @@ -376,7 +392,6 @@ bool MemcacheBase::setMemcache(const char *key, memcached_return rv; string final_key; - memcached_st clone; bool success; if (use_prefix) { @@ -385,20 +400,22 @@ bool MemcacheBase::setMemcache(const char *key, final_key = key; } - if (memcached_clone(&clone, memc) == NULL) { - throw IOException("MemcacheBase::setMemcache(): memcached_clone() failed"); - } + m_lock->lock(); + rv = memcached_set(memc, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags); + m_lock->unlock(); - rv = memcached_set(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags); if (rv == MEMCACHED_SUCCESS) { success = true; + } else if (rv == MEMCACHED_ERRNO) { + // System error + log.error(string("Memcache::setMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno))); + success = false; } else { // shouldn't be here - log.error(string("Memcache::setMemcache() Problems: ") + memcached_strerror(&clone, rv)); + log.error(string("Memcache::setMemcache() Problems: ") + memcached_strerror(memc, rv)); success = false; } - memcached_free(&clone); return success; } @@ -410,7 +427,6 @@ bool MemcacheBase::replaceMemcache(const char *key, memcached_return rv; string final_key; - memcached_st clone; bool success; if (use_prefix) { @@ -419,23 +435,25 @@ bool MemcacheBase::replaceMemcache(const char *key, final_key = key; } - if (memcached_clone(&clone, memc) == NULL) { - throw IOException("MemcacheBase::replaceMemcache(): memcached_clone() failed"); - } + m_lock->lock(); + rv = memcached_replace(memc, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags); + m_lock->unlock(); - rv = memcached_replace(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags); if (rv == MEMCACHED_SUCCESS) { success = true; } else if (rv == MEMCACHED_NOTSTORED) { // not there success = false; + } else if (rv == MEMCACHED_ERRNO) { + // System error + log.error(string("Memcache::replaceMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno))); + success = false; } else { // shouldn't be here - log.error(string("Memcache::replaceMemcache() Problems: ") + memcached_strerror(&clone, rv)); + log.error(string("Memcache::replaceMemcache() Problems: ") + memcached_strerror(memc, rv)); success = false; } - memcached_free(&clone); return success; } @@ -456,6 +474,9 @@ MemcacheBase::MemcacheBase(const DOMElement* e) : m_root(e), log(Category::getIn log.debug("INIT: GOT Hosts: %s", h.get()); m_memcacheHosts = h.get(); + m_lock = Mutex::create(); + log.debug("Lock created"); + memc = memcached_create(NULL); if (memc == NULL) { throw XMLToolingException("MemcacheBase::Memcache(): memcached_create() failed"); @@ -463,10 +484,23 @@ MemcacheBase::MemcacheBase(const DOMElement* e) : m_root(e), log(Category::getIn log.debug("Memcache created"); - unsigned int set = MEMCACHED_HASH_CRC; - memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, set); + unsigned int hash = MEMCACHED_HASH_CRC; + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, hash); log.debug("CRC hash set"); + int32_t timeout = 1000000; + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SND_TIMEOUT, timeout); + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RCV_TIMEOUT, timeout); + + int32_t poll_timeout = 1000; + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_POLL_TIMEOUT, poll_timeout); + + int32_t fail_limit = 5; + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT, fail_limit); + + int32_t retry_timeout = 30; + memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, retry_timeout); + memcached_server_st *servers; servers = memcached_servers_parse((char *)m_memcacheHosts.c_str()); log.debug("Got %u hosts.", memcached_server_list_count(servers)); @@ -480,6 +514,7 @@ MemcacheBase::MemcacheBase(const DOMElement* e) : m_root(e), log(Category::getIn MemcacheBase::~MemcacheBase() { memcached_free(memc); + delete m_lock; log.debug("Base object destroyed"); } @@ -564,6 +599,19 @@ int MemcacheStorageService::readString(const char* context, const char* key, str uint32_t rec_version; string value; + if (m_buildMap) { + log.debug("Checking context"); + + string map_name = context; + string ser_arr; + uint32_t flags; + bool ctx_found = getMemcache(map_name.c_str(), ser_arr, &flags); + + if (!ctx_found) { + return 0; + } + } + bool found = getMemcache(final_key.c_str(), value, &rec_version); if (!found) { return 0; @@ -645,12 +693,6 @@ void MemcacheStorageService::updateContext(const char* context, time_t expiratio } string map_name = context; - - if (! addLock(map_name)) { - log.error("Unable to get lock for context %s!", context); - return; - } - string ser_arr; uint32_t flags; bool result = getMemcache(map_name.c_str(), ser_arr, &flags); @@ -681,8 +723,6 @@ void MemcacheStorageService::updateContext(const char* context, time_t expiratio replaceMemcache(map_name.c_str(), ser_arr, expiration, flags); } - deleteLock(map_name); - } void MemcacheStorageService::deleteContext(const char* context) { @@ -695,12 +735,6 @@ void MemcacheStorageService::deleteContext(const char* context) { } string map_name = context; - - if (! addLock(map_name)) { - log.error("Unable to get lock for context %s!", context); - return; - } - string ser_arr; uint32_t flags; bool result = getMemcache(map_name.c_str(), ser_arr, &flags); @@ -723,16 +757,14 @@ void MemcacheStorageService::deleteContext(const char* context) { deleteMemcache(map_name.c_str(), 0); } - deleteLock(map_name); - } -extern "C" int xmltooling_extension_init(void*) { +extern "C" int MCEXT_EXPORTS xmltooling_extension_init(void*) { // Register this SS type XMLToolingConfig::getConfig().StorageServiceManager.registerFactory("MEMCACHE", MemcacheStorageServiceFactory); return 0; } -extern "C" void xmltooling_extension_term() { +extern "C" void MCEXT_EXPORTS xmltooling_extension_term() { XMLToolingConfig::getConfig().StorageServiceManager.deregisterFactory("MEMCACHE"); } diff --git a/memcache-store/memcache-store.rc b/memcache-store/memcache-store.rc index ee39660..ffd78ff 100644 --- a/memcache-store/memcache-store.rc +++ b/memcache-store/memcache-store.rc @@ -53,8 +53,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,1,0,0 - PRODUCTVERSION 2,1,0,0 + FILEVERSION 2,2,1,0 + PRODUCTVERSION 2,2,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, 1, 0, 0\0" + VALUE "FileVersion", "2, 2, 1, 0\0" VALUE "InternalName", "memcache-store\0" - VALUE "LegalCopyright", "Copyright © 2008 Internet2\0" + VALUE "LegalCopyright", "Copyright © 2009 Internet2\0" VALUE "OriginalFilename", "memcache-store.so\0" - VALUE "ProductName", "Shibboleth 2.1\0" - VALUE "ProductVersion", "2, 1, 0, 0\0" + VALUE "ProductName", "Shibboleth 2.2.1\0" + VALUE "ProductVersion", "2, 2, 1, 0\0" END END BLOCK "VarFileInfo" diff --git a/memcache-store/memcache-store.vcproj b/memcache-store/memcache-store.vcproj index 854aad3..245333c 100644 --- a/memcache-store/memcache-store.vcproj +++ b/memcache-store/memcache-store.vcproj @@ -1,11 +1,12 @@ - - - @@ -282,7 +280,7 @@ /> - diff --git a/nsapi_shib/nsapi_shib.cpp b/nsapi_shib/nsapi_shib.cpp index ea38feb..960409d 100644 --- a/nsapi_shib/nsapi_shib.cpp +++ b/nsapi_shib/nsapi_shib.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -31,6 +31,7 @@ #ifdef WIN32 # define _CRT_NONSTDC_NO_DEPRECATE 1 # define _CRT_SECURE_NO_DEPRECATE 1 +# define _CRT_RAND_S #endif #include @@ -75,13 +76,23 @@ using namespace std; namespace { SPConfig* g_Config=NULL; string g_ServerName; - string g_ServerScheme; string g_unsetHeaderValue; + string g_spoofKey; bool g_checkSpoofing = true; bool g_catchAll = false; static const XMLCh path[] = UNICODE_LITERAL_4(p,a,t,h); static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e); + + void _my_invalid_parameter_handler( + const wchar_t * expression, + const wchar_t * function, + const wchar_t * file, + unsigned int line, + uintptr_t pReserved + ) { + return; + } } PluginManager::Factory SunRequestMapFactory; @@ -115,9 +126,6 @@ extern "C" NSAPI_PUBLIC int nsapi_shib_init(pblock* pb, ::Session* sn, Request* } } } - name=pblock_findval("server-scheme",pb); - if (name) - g_ServerScheme=name; log_error(LOG_INFORM,"nsapi_shib_init",sn,rq,"nsapi_shib loaded for host (%s)",g_ServerName.c_str()); @@ -139,7 +147,7 @@ extern "C" NSAPI_PUBLIC int nsapi_shib_init(pblock* pb, ::Session* sn, Request* return REQ_ABORTED; } - g_Config->RequestMapperManager.registerFactory(XML_REQUEST_MAPPER,&SunRequestMapFactory); + g_Config->RequestMapperManager.registerFactory(NATIVE_REQUEST_MAPPER,&SunRequestMapFactory); try { if (!g_Config->instantiate(pblock_findval("shib-config",pb), true)) @@ -156,15 +164,41 @@ extern "C" NSAPI_PUBLIC int nsapi_shib_init(pblock* pb, ::Session* sn, Request* ServiceProvider* sp=g_Config->getServiceProvider(); Locker locker(sp); - const PropertySet* props=sp->getPropertySet("Local"); + const PropertySet* props=sp->getPropertySet("InProcess"); if (props) { - pair unsetValue=props->getString("unsetHeaderValue"); - if (unsetValue.first) - g_unsetHeaderValue = unsetValue.second; pair flag=props->getBool("checkSpoofing"); g_checkSpoofing = !flag.first || flag.second; flag=props->getBool("catchAll"); g_catchAll = flag.first && flag.second; + + pair unsetValue=props->getString("unsetHeaderValue"); + if (unsetValue.first) + g_unsetHeaderValue = unsetValue.second; + if (g_checkSpoofing) { + unsetValue=props->getString("spoofKey"); + if (unsetValue.first) + g_spoofKey = unsetValue.second; +#ifdef WIN32 + else { + _invalid_parameter_handler old = _set_invalid_parameter_handler(_my_invalid_parameter_handler); + unsigned int randkey=0,randkey2=0,randkey3=0,randkey4=0; + if (rand_s(&randkey) == 0 && rand_s(&randkey2) == 0 && rand_s(&randkey3) == 0 && rand_s(&randkey4) == 0) { + _set_invalid_parameter_handler(old); + ostringstream keystr; + keystr << randkey << randkey2 << randkey3 << randkey4; + g_spoofKey = keystr.str(); + } + else { + _set_invalid_parameter_handler(old); + pblock_nvinsert("error", "module failed to generate a random anti-spoofing key (if this is Windows 2000 set one manually)", pb); + locker.assign(); // pops lock on SP config + g_Config->term(); + g_Config=NULL; + return REQ_ABORTED; + } + } +#endif + } } return REQ_PROCEED; } @@ -176,6 +210,8 @@ class ShibTargetNSAPI : public AbstractSPRequest { mutable string m_body; mutable bool m_gotBody,m_firsttime; + bool m_security_active; + int m_server_portnum; mutable vector m_certs; set m_allhttp; @@ -185,10 +221,34 @@ public: Request* m_rq; ShibTargetNSAPI(pblock* pb, ::Session* sn, Request* rq) - : AbstractSPRequest(SHIBSP_LOGCAT".NSAPI"), m_gotBody(false), m_firsttime(true), m_pb(pb), m_sn(sn), m_rq(rq) { + : AbstractSPRequest(SHIBSP_LOGCAT".NSAPI"), + m_gotBody(false), m_firsttime(true), m_security_active(false), m_server_portnum(0), m_pb(pb), m_sn(sn), m_rq(rq) { + + // To determine whether SSL is active or not, we're supposed to rely + // on the security_active macro. For iPlanet 4.x, this works. + // For Sun 7.x, it's useless and appears to be on or off based + // on whether ANY SSL support is enabled for a vhost. Sun 6.x is unknown. + // As a fix, there's a conf variable called $security that can be mapped + // into a function parameter: security_active="$security" + // We check for this parameter, and rely on the macro if it isn't set. + // This doubles as a scheme virtualizer for load balanced scenarios + // since you can set the parameter to 1 or 0 as needed. + const char* sa = pblock_findval("security_active", m_pb); + if (sa) + m_security_active = (*sa == '1'); + else if (security_active) + m_security_active = true; + else + m_security_active = false; + + // A similar issue exists for the port. server_portnum is no longer + // working on at least Sun 7.x, and returns the first listener's port + // rather than whatever port is actually used for the request. Nice job, Sun. + sa = pblock_findval("server_portnum", m_pb); + m_server_portnum = (sa && *sa) ? atoi(sa) : server_portnum; - const char* uri=pblock_findval("uri", rq->reqpb); - const char* qstr=pblock_findval("query", rq->reqpb); + const char* uri = pblock_findval("uri", rq->reqpb); + const char* qstr = pblock_findval("query", rq->reqpb); if (qstr) { string temp = string(uri) + '?' + qstr; @@ -199,16 +259,18 @@ public: } // See if this is the first time we've run. - qstr = pblock_findval("auth-type", rq->vars); - if (qstr && !strcmp(qstr, "shibboleth")) - m_firsttime = false; + if (!g_spoofKey.empty()) { + qstr = pblock_findval("Shib-Spoof-Check", rq->headers); + if (qstr && g_spoofKey == qstr) + m_firsttime = false; + } if (!m_firsttime || rq->orig_rq) log(SPDebug, "nsapi_shib function running more than once"); } ~ShibTargetNSAPI() { } const char* getScheme() const { - return security_active ? "https" : "http"; + return m_security_active ? "https" : "http"; } const char* getHostname() const { #ifdef vs_is_default_vs @@ -223,25 +285,28 @@ public: return g_ServerName.c_str(); } int getPort() const { - return server_portnum; + return m_server_portnum; } const char* getMethod() const { return pblock_findval("method", m_rq->reqpb); } string getContentType() const { - char* content_type = ""; - request_header("content-type", &content_type, m_sn, m_rq); - return content_type; + char* content_type = NULL; + if (request_header("content-type", &content_type, m_sn, m_rq) != REQ_PROCEED) + return ""; + return content_type ? content_type : ""; } long getContentLength() const { if (m_gotBody) return m_body.length(); - char* content_length=""; - request_header("content-length", &content_length, m_sn, m_rq); - return atoi(content_length); + char* content_length=NULL; + if (request_header("content-length", &content_length, m_sn, m_rq) != REQ_PROCEED) + return 0; + return content_length ? atoi(content_length) : 0; } string getRemoteAddr() const { - return pblock_findval("ip", m_sn->client); + string ret = AbstractSPRequest::getRemoteAddr(); + return ret.empty() ? pblock_findval("ip", m_sn->client) : ret; } void log(SPLogLevel level, const string& msg) const { AbstractSPRequest::log(level,msg); @@ -255,7 +320,11 @@ public: if (m_gotBody) return m_body.c_str(); char* content_length=NULL; - if (request_header("content-length", &content_length, m_sn, m_rq)!=REQ_PROCEED || atoi(content_length) > 1024*1024) // 1MB? + if (request_header("content-length", &content_length, m_sn, m_rq) != REQ_PROCEED || !content_length) { + m_gotBody = true; + return NULL; + } + else if (atoi(content_length) > 1024*1024) // 1MB? throw opensaml::SecurityPolicyException("Blocked request body exceeding 1M size limit."); else { char ch=IO_EOF+1; @@ -296,8 +365,14 @@ public: if (m_allhttp.count(cginame) > 0) throw opensaml::SecurityPolicyException("Attempt to spoof header ($1) was detected.", params(1, rawname)); } - param_free(pblock_remove(rawname, m_rq->headers)); - pblock_nvinsert(rawname, g_unsetHeaderValue.c_str(), m_rq->headers); + if (strcmp(rawname, "REMOTE_USER") == 0) { + param_free(pblock_remove("remote-user", m_rq->headers)); + pblock_nvinsert("remote-user", g_unsetHeaderValue.c_str(), m_rq->headers); + } + else { + param_free(pblock_remove(rawname, m_rq->headers)); + pblock_nvinsert(rawname, g_unsetHeaderValue.c_str(), m_rq->headers); + } } void setHeader(const char* name, const char* value) { param_free(pblock_remove(name, m_rq->headers)); @@ -323,11 +398,27 @@ public: } void setRemoteUser(const char* user) { pblock_nvinsert("auth-user", user, m_rq->vars); + param_free(pblock_remove("remote-user", m_rq->headers)); + pblock_nvinsert("remote-user", user, m_rq->headers); } string getRemoteUser() const { const char* ru = pblock_findval("auth-user", m_rq->vars); return ru ? ru : ""; } + void setAuthType(const char* authtype) { + param_free(pblock_remove("auth-type", m_rq->vars)); + if (authtype) + pblock_nvinsert("auth-type", authtype, m_rq->vars); + } + string getAuthType() const { + const char* at = pblock_findval("auth-type", m_rq->vars); + return at ? at : ""; + } + void setContentType(const char* type) { + // iPlanet seems to have a case folding problem. + param_free(pblock_remove("content-type", m_rq->srvhdrs)); + setResponseHeader("Content-Type", type); + } void setResponseHeader(const char* name, const char* value) { pblock_nvinsert(name, value, m_rq->srvhdrs); } @@ -391,13 +482,16 @@ extern "C" NSAPI_PUBLIC int nsapi_shib(pblock* pb, ::Session* sn, Request* rq) // Check user authentication pair res = stn.getServiceProvider().doAuthentication(stn); + // If directed, install a spoof key to recognize when we've already cleared headers. + if (!g_spoofKey.empty()) { + param_free(pblock_remove("Shib-Spoof-Check", rq->headers)); + pblock_nvinsert("Shib-Spoof-Check", g_spoofKey.c_str(), rq->headers); + } if (res.first) return (int)res.second; // user authN was okay -- export the assertions now param_free(pblock_remove("auth-user",rq->vars)); - // This seems to be required in order to eventually set - // the auth-user var. - pblock_nvinsert("auth-type","shibboleth",rq->vars); + res = stn.getServiceProvider().doExport(stn); if (res.first) return (int)res.second; @@ -442,6 +536,7 @@ extern "C" NSAPI_PUBLIC int shib_handler(pblock* pb, ::Session* sn, Request* rq) return WriteClientError(sn, rq, FUNC, "Shibboleth handler threw an exception, see web server log for error."); } catch (...) { + log_error(LOG_FAILURE,FUNC,sn,rq,"unknown exception caught in Shibboleth handler"); if (g_catchAll) return WriteClientError(sn, rq, FUNC, "Shibboleth handler threw an unknown exception."); throw; diff --git a/nsapi_shib/nsapi_shib.rc b/nsapi_shib/nsapi_shib.rc index 6af4d84..ba31800 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,1,0,0 - PRODUCTVERSION 2,1,0,0 + FILEVERSION 2,2,1,0 + PRODUCTVERSION 2,2,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, 1, 0, 0\0" + VALUE "FileVersion", "2, 2, 1, 0\0" VALUE "InternalName", "nsapi_shib\0" - VALUE "LegalCopyright", "Copyright © 2008 Internet2\0" + VALUE "LegalCopyright", "Copyright © 2009 Internet2\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "nsapi_shib.dll\0" VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Shibboleth 2.1\0" - VALUE "ProductVersion", "2, 1, 0, 0\0" + VALUE "ProductName", "Shibboleth 2.2.1\0" + VALUE "ProductVersion", "2, 2, 1, 0\0" VALUE "SpecialBuild", "\0" END END diff --git a/nsapi_shib/nsapi_shib.vcproj b/nsapi_shib/nsapi_shib.vcproj index a1c457b..ad271b0 100644 --- a/nsapi_shib/nsapi_shib.vcproj +++ b/nsapi_shib/nsapi_shib.vcproj @@ -1,9 +1,10 @@ - - - @@ -343,11 +341,13 @@ /> - diff --git a/odbc-store/odbc-store.rc b/odbc-store/odbc-store.rc index ef40f0e..1a9eb39 100644 --- a/odbc-store/odbc-store.rc +++ b/odbc-store/odbc-store.rc @@ -53,8 +53,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,1,0,0 - PRODUCTVERSION 2,1,0,0 + FILEVERSION 2,2,1,0 + PRODUCTVERSION 2,2,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, 1, 0, 0\0" + VALUE "FileVersion", "2, 2, 1, 0\0" VALUE "InternalName", "odbc-store\0" - VALUE "LegalCopyright", "Copyright © 2008 Internet2\0" + VALUE "LegalCopyright", "Copyright © 2009 Internet2\0" VALUE "OriginalFilename", "odbc-store.so\0" - VALUE "ProductName", "Shibboleth 2.1\0" - VALUE "ProductVersion", "2, 1, 0, 0\0" + VALUE "ProductName", "Shibboleth 2.2.1\0" + VALUE "ProductVersion", "2, 2, 1, 0\0" END END BLOCK "VarFileInfo" diff --git a/odbc-store/odbc-store.vcproj b/odbc-store/odbc-store.vcproj index 1b6a307..b15cf98 100644 --- a/odbc-store/odbc-store.vcproj +++ b/odbc-store/odbc-store.vcproj @@ -1,11 +1,12 @@ - - - @@ -300,7 +298,7 @@ /> - diff --git a/pkginfo b/pkginfo index 67f161a..cd040c3 100644 --- a/pkginfo +++ b/pkginfo @@ -1,5 +1,5 @@ PKG=SHIBsp -VERSION=2.1 +VERSION=2.2.1 BASEDIR=/ NAME=shibboleth-sp CATEGORY=application,security diff --git a/pkginfo.in b/pkginfo.in index 53f2469..9df027f 100644 --- a/pkginfo.in +++ b/pkginfo.in @@ -1,5 +1,5 @@ PKG=SHIBsp -VERSION=@-VERSION-@ +VERSION=@PACKAGE_VERSION@ BASEDIR=/ NAME=shibboleth-sp CATEGORY=application,security diff --git a/schemas/shibboleth-2.0-attribute-map.xsd b/schemas/shibboleth-2.0-attribute-map.xsd index 3a72cda..caf5e1e 100644 --- a/schemas/shibboleth-2.0-attribute-map.xsd +++ b/schemas/shibboleth-2.0-attribute-map.xsd @@ -3,7 +3,7 @@ xmlns="http://www.w3.org/2001/XMLSchema" xmlns:am="urn:mace:shibboleth:2.0:attribute-map" elementFormDefault="qualified" - version="2.1"> + version="2.2"> @@ -26,6 +26,14 @@ + + + + + + + + @@ -33,8 +41,12 @@ + + + + @@ -73,13 +85,18 @@ Decodes a SAML attribute into its Shibboleth-internal representation. - + Flag controlling case sensitivity when comparisons to the attribute's values are done. + + + Flag controlling whether the resulting attribute should be exported for CGI use. + + @@ -127,6 +144,13 @@ + + + + Flag controlling whether to default in values for NameQualifier/SPNameQualifier if not set. + + + @@ -147,6 +171,13 @@ + + + + Flag controlling whether to default in values for NameQualifier/SPNameQualifier if not set. + + + diff --git a/schemas/shibboleth-2.0-native-sp-config.xsd b/schemas/shibboleth-2.0-native-sp-config.xsd index f9950db..88befab 100644 --- a/schemas/shibboleth-2.0-native-sp-config.xsd +++ b/schemas/shibboleth-2.0-native-sp-config.xsd @@ -8,7 +8,7 @@ elementFormDefault="qualified" attributeFormDefault="unqualified" blockDefault="substitution" - version="2.1"> + version="2.2"> @@ -87,6 +87,7 @@ + @@ -199,9 +200,10 @@ - + - + + @@ -224,6 +226,7 @@ + @@ -293,6 +296,7 @@ + @@ -536,7 +540,7 @@ - + @@ -550,6 +554,10 @@ + + + + @@ -586,6 +594,7 @@ + @@ -678,9 +687,10 @@ Specifies a set of SecurityPolicyRule plugins - + - + + diff --git a/shibboleth.spec b/shibboleth.spec index 89a86a1..393507a 100644 --- a/shibboleth.spec +++ b/shibboleth.spec @@ -1,19 +1,32 @@ Name: shibboleth -Summary: Open source system for attribute-based Web SSO -Version: 2.1 +Version: 2.2.1 Release: 1 -#Copyright: Internet2 +Summary: Open source system for attribute-based Web SSO Group: System Environment/Libraries -License: Apache style +Vendor: Internet2 +License: Apache 2.0 URL: http://shibboleth.internet2.edu/ -Source0: http://shibboleth.internet2.edu/downloads/%{name}-%{version}.tar.gz +Source: %{name}-sp-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-root -BuildRequires: openssl-devel -BuildRequires: xerces%{?xercesver}-c-devel >= 2.8.0 +%if 0%{?suse_version} > 1030 +BuildRequires: libXerces-c-devel >= 2.8.0 +BuildRequires: libxml-security-c-devel >= 1.4.0 +BuildRequires: libxmltooling-devel >= 1.2 +BuildRequires: libsaml-devel >= 2.2 +%{?_with_log4cpp:BuildRequires: liblog4cpp-devel >= 1.0} +%{!?_with_log4cpp:BuildRequires: liblog4shib-devel} +%else +BuildRequires: xerces%{?xercesver}-c-devel >= 2.8.0 BuildRequires: xml-security-c-devel >= 1.4.0 -BuildRequires: zlib-devel, opensaml-devel >= 2.1 -%{?_with_log4cpp:BuildRequires: log4cpp-devel >= 1.0} +BuildRequires: xmltooling-devel >= 1.2 +BuildRequires: opensaml-devel >= 2.2 +%{?_with_log4cpp:BuildRequires: log4cpp-devel >= 1.0} %{!?_with_log4cpp:BuildRequires: log4shib-devel} +%endif +BuildRequires: gcc-c++ +%{!?_without_doxygen:BuildRequires: doxygen} +%{!?_without_odbc:BuildRequires:unixODBC-devel} +BuildRequires: zlib-devel %{?_with_fastcgi:BuildRequires: fcgi-devel} %if "%{_vendor}" == "redhat" %{!?_without_builtinapache:BuildRequires: httpd-devel} @@ -22,68 +35,76 @@ BuildRequires: zlib-devel, opensaml-devel >= 2.1 %{!?_without_builtinapache:BuildRequires: apache2-devel} %endif +%if "%{_vendor}" == "suse" +%define pkgdocdir %{_docdir}/%{name} +%else +%define pkgdocdir %{_docdir}/%{name}-%{version} +%endif %description -Shibboleth, a project of Internet2/MACE, is developing architectures, -policy structures, practical technologies, and an open source -implementation to support inter-institutional sharing of web resources -subject to access controls. In addition, Shibboleth will develop a -policy framework that will allow inter-operation within the higher -education community. +Shibboleth is a Web Single Sign-On implementations based on OpenSAML +that supports multiple protocols, federated identity, and the extensible +exchange of rich attributes subject to privacy controls. -This package contains the shibboleth runtime library and apache module. +This package contains the Shibboleth Service Provider runtime libraries +and Apache module(s). %package devel Summary: Shibboleth development Headers Group: Development/Libraries Requires: %{name} = %{version} +%if 0%{?suse_version} > 1030 +Requires: libXerces-c-devel >= 2.8.0 +Requires: libxml-security-c-devel >= 1.4.0 +Requires: libxmltooling-devel >= 1.2 +Requires: libsaml-devel >= 2.2 +%{?_with_log4cpp:Requires: liblog4cpp-devel >= 1.0} +%{!?_with_log4cpp:Requires: liblog4shib-devel} +%else +Requires: xerces%{?xercesver}-c-devel >= 2.8.0 +Requires: xml-security-c-devel >= 1.4.0 +Requires: xmltooling-devel >= 1.2 +Requires: opensaml-devel >= 2.2 +%{?_with_log4cpp:Requires: log4cpp-devel >= 1.0} +%{!?_with_log4cpp:Requires: log4shib-devel} +%endif %description devel -Shibboleth, a project of Internet2/MACE, is developing architectures, -policy structures, practical technologies, and an open source -implementation to support inter-institutional sharing of web resources -subject to access controls. In addition, Shibboleth will develop a -policy framework that will allow inter-operation within the higher -education community. - -This package contains the headers and other necessary files to build -applications that use the shibboleth library. - -%package docs -Summary: Shibboleth API Documentation -Group: Development/Libraries -Requires: %{name} = %{version} +Shibboleth is a Web Single Sign-On implementations based on OpenSAML +that supports multiple protocols, federated identity, and the extensible +exchange of rich attributes subject to privacy controls. + +This package includes files needed for development with Shibboleth. -%description docs -Shibboleth Library API documentation generated by doxygen. %prep %setup -q %build -%configure %{?_without_odbc:--disable-odbc} %{?_without_adfs:--disable-adfs} %{?_with_fastcgi} %{?shib_options} -%{__make} +%configure %{?_without_odbc:--disable-odbc} %{?_without_adfs:--disable-adfs} %{?_with_fastcgi} %{?_with_memcached} %{?shib_options} +%{__make} pkgdocdir=%{pkgdocdir} %install -[ "$RPM_BUILD_ROOT" != "/" ] && %{__rm} -rf $RPM_BUILD_ROOT -%{__make} install NOKEYGEN=1 DESTDIR=$RPM_BUILD_ROOT +%{__make} install NOKEYGEN=1 DESTDIR=$RPM_BUILD_ROOT pkgdocdir=%{pkgdocdir} %if "%{_vendor}" == "suse" %{__sed} -i "s/\/var\/log\/httpd/\/var\/log\/apache2/g" \ - $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/native.logger + $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/native.logger %endif -# Plug the SP into Apache on a recognized system. +# Plug the SP into the built-in Apache on a recognized system. +touch rpm.filelist APACHE_CONFIG="no" -if [ -f $RPM_BUILD_ROOT/%{_libdir}/%{name}/mod_shib_13.so ] ; then +if [ -f $RPM_BUILD_ROOT%{_libdir}/%{name}/mod_shib_13.so ] ; then APACHE_CONFIG="apache.config" fi -if [ -f $RPM_BUILD_ROOT/%{_libdir}/%{name}/mod_shib_20.so ] ; then +if [ -f $RPM_BUILD_ROOT%{_libdir}/%{name}/mod_shib_20.so ] ; then APACHE_CONFIG="apache2.config" fi -if [ -f $RPM_BUILD_ROOT/%{_libdir}/%{name}/mod_shib_22.so ] ; then +if [ -f $RPM_BUILD_ROOT%{_libdir}/%{name}/mod_shib_22.so ] ; then APACHE_CONFIG="apache22.config" fi +%{?_without_builtinapache:APACHE_CONFIG="no"} if [ "$APACHE_CONFIG" != "no" ] ; then APACHE_CONFD="no" if [ -d %{_sysconfdir}/httpd/conf.d ] ; then @@ -94,19 +115,19 @@ if [ "$APACHE_CONFIG" != "no" ] ; then fi if [ "$APACHE_CONFD" != "no" ] ; then %{__mkdir} -p $RPM_BUILD_ROOT$APACHE_CONFD -%if "%{_vendor}" == "suse" - %{__sed} "s/\/usr\/doc\/%{name}/\/usr\/share\/doc\/packages\/%{name}/g" \ - $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/$APACHE_CONFIG \ - > $RPM_BUILD_ROOT$APACHE_CONFD/shib.conf -%else - %{__sed} "s/\/usr\/doc\/%{name}/\/usr\/share\/doc\/%{name}-2.1/g" \ - $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/$APACHE_CONFIG \ - > $RPM_BUILD_ROOT$APACHE_CONFD/shib.conf -%endif + %{__cp} -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/$APACHE_CONFIG $RPM_BUILD_ROOT$APACHE_CONFD/shib.conf + echo "%config $APACHE_CONFD/shib.conf" > rpm.filelist fi fi -%check || : +%if "%{_vendor}" == "redhat" || "%{_vendor}" == "suse" + # %{_initddir} not yet in RHEL5, use deprecated %{_initrddir} + mkdir -p $RPM_BUILD_ROOT%{_initrddir} + %{__cp} -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/shibd-%{_vendor} $RPM_BUILD_ROOT%{_initrddir}/shibd + %{__chmod} 755 $RPM_BUILD_ROOT%{_initrddir}/shibd +%endif + +%check %{__make} check %clean @@ -117,34 +138,62 @@ fi /sbin/ldconfig %endif -# Install the shibd init.d scripts and service -%if "%{_vendor}" == "redhat" - if [ -d %{_sysconfdir}/init.d ] ; then - if [ ! -f %{_sysconfdir}/init.d/shibd ] ; then - %{__cp} -p %{_sysconfdir}/%{name}/shibd-%{_vendor} %{_sysconfdir}/init.d/shibd - %{__chmod} 755 %{_sysconfdir}/init.d/shibd - chkconfig --add shibd - fi - fi -%endif - # Key generation cd %{_sysconfdir}/%{name} sh ./keygen.sh -b -%postun +%if "%{_vendor}" == "redhat" + # This adds the proper /etc/rc*.d links for the script + /sbin/chkconfig --add shibd + # On upgrade, restart components if they're already running. + if [ "$1" -gt "1" ] ; then + /etc/init.d/shibd status 1>/dev/null && /etc/init.d/shibd restart 1>/dev/null + %{!?_without_builtinapache:/etc/init.d/httpd status 1>/dev/null && /etc/init.d/httpd restart 1>/dev/null} + fi +%endif +%if "%{_vendor}" == "suse" + # This adds the proper /etc/rc*.d links for the script + /sbin/chkconfig --add shibd + cd /usr/sbin && ln -s /etc/init.d/shibd rcshibd + # On upgrade, restart components if they're already running. + if [ "$1" -gt "1" ] ; then + /etc/init.d/shibd status 1>/dev/null && /etc/init.d/shibd restart 1>/dev/null + %{!?_without_builtinapache:/etc/init.d/apache2 status 1>/dev/null && /etc/init.d/apache2 restart 1>/dev/null} + fi +%endif + +%preun +%if "%{_vendor}" == "redhat" + if [ "$1" = 0 ] ; then + /sbin/service shibd stop >/dev/null 2>&1 + /sbin/chkconfig --del shibd + fi +%endif +%if "%{_vendor}" == "suse" + if [ "$1" = 0 ] ; then + /sbin/service shibd stop >/dev/null 2>&1 + /sbin/chkconfig --del shibd + cd /usr/sbin && %{__rm} -f rcshibd + fi +%endif + %ifnos solaris2.8 solaris2.9 solaris2.10 -/sbin/ldconfig +%postun -p /sbin/ldconfig %endif -# clear init.d state +%posttrans +# ugly hack if init script got removed during %postun by upgraded (buggy/2.1) package %if "%{_vendor}" == "redhat" - chkconfig --del shibd - [ -f %{_sysconfdir}/init.d/shibd ] && \ - %{__rm} -f %{_sysconfdir}/init.d/shibd + if [ ! -f %{_initrddir}/shibd ] ; then + if [ -f %{_sysconfdir}/%{name}/shibd-%{_vendor} ] ; then + %{__cp} -p %{_sysconfdir}/%{name}/shibd-%{_vendor} %{_initrddir}/shibd + %{__chmod} 755 %{_initrddir}/shibd + /sbin/chkconfig --add shibd + fi + fi %endif -%files +%files -f rpm.filelist %defattr(-,root,root,-) %{_sbindir}/shibd %{_bindir}/mdquery @@ -162,41 +211,46 @@ sh ./keygen.sh -b %config(noreplace) %{_sysconfdir}/%{name}/*.xml %config(noreplace) %{_sysconfdir}/%{name}/*.html %config(noreplace) %{_sysconfdir}/%{name}/*.logger -%if "%{_vendor}" == "suse" -%config %{_sysconfdir}/apache2/conf.d/shib.conf -%else -%config %{_sysconfdir}/httpd/conf.d/shib.conf +%if "%{_vendor}" == "redhat" || "%{_vendor}" == "suse" +%attr(755, root, root) %{_initrddir}/shibd %endif %{_sysconfdir}/%{name}/*.dist %{_sysconfdir}/%{name}/apache*.config -%{_sysconfdir}/%{name}/shibd-redhat -%{_sysconfdir}/%{name}/shibd-debian -%{_sysconfdir}/%{name}/shibd-osx.plist -%{_sysconfdir}/%{name}/keygen.sh +%{_sysconfdir}/%{name}/shibd-* +%attr(755, root, root) %{_sysconfdir}/%{name}/keygen.sh +%attr(755, root, root) %{_sysconfdir}/%{name}/metagen.sh %{_sysconfdir}/%{name}/*.xsl -%docdir %{_datadir}/doc/%{name} -%{_datadir}/doc/%{name}/CREDITS.txt -%{_datadir}/doc/%{name}/FASTCGI.LICENSE -%{_datadir}/doc/%{name}/LICENSE.txt -%{_datadir}/doc/%{name}/LOG4CPP.LICENSE -%{_datadir}/doc/%{name}/logo.jpg -%{_datadir}/doc/%{name}/main.css -%{_datadir}/doc/%{name}/NOTICE.txt -%{_datadir}/doc/%{name}/OPENSSL.LICENSE -%{_datadir}/doc/%{name}/README.txt -%{_datadir}/doc/%{name}/RELEASE.txt +%doc %{pkgdocdir} +%exclude %{pkgdocdir}/api %files devel %defattr(-,root,root,-) -%{_includedir} +%{_includedir}/* %{_libdir}/libshibsp.so %{_libdir}/libshibsp-lite.so - -%files docs -%defattr(644,root,root,755) -%doc %{_datadir}/doc/%{name}/api +%doc %{pkgdocdir}/api %changelog +* Mon Aug 10 2009 Scott Cantor - 2.2.1-1 +- Doc handling changes +- SuSE init script + +* Tue Aug 4 2009 Scott Cantor - 2.2.1-1 +- Initial version for 2.2.1, with shibd/httpd restart on upgrade + +* Thu Jun 25 2009 Scott Cantor - 2.2-3 +- Add additional cleanup to posttrans fix + +* Tue Jun 23 2009 Scott Cantor - 2.2-2 +- Reverse without_builtinapache macro test +- Fix init script handling on Red Hat to handle upgrades + +* Wed Dec 3 2008 Scott Cantor - 2.2-1 +- Bump minor version. +- Make keygen.sh executable. +- Fixing SUSE Xerces dependency name. +- Optionally package shib.conf. + * Tue Jun 10 2008 Scott Cantor - 2.1-1 - Change shib.conf handling to treat as config file. diff --git a/shibboleth.spec.in b/shibboleth.spec.in index 2a9f809..b6f89d8 100644 --- a/shibboleth.spec.in +++ b/shibboleth.spec.in @@ -1,19 +1,32 @@ -Name: shibboleth -Summary: Open source system for attribute-based Web SSO -Version: @-VERSION-@ +Name: @PACKAGE@ +Version: @PACKAGE_VERSION@ Release: 1 -#Copyright: Internet2 +Summary: Open source system for attribute-based Web SSO Group: System Environment/Libraries -License: Apache style +Vendor: Internet2 +License: Apache 2.0 URL: http://shibboleth.internet2.edu/ -Source0: http://shibboleth.internet2.edu/downloads/%{name}-%{version}.tar.gz +Source: %{name}-sp-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-root -BuildRequires: openssl-devel -BuildRequires: xerces%{?xercesver}-c-devel >= 2.8.0 +%if 0%{?suse_version} > 1030 +BuildRequires: libXerces-c-devel >= 2.8.0 +BuildRequires: libxml-security-c-devel >= 1.4.0 +BuildRequires: libxmltooling-devel >= 1.2 +BuildRequires: libsaml-devel >= 2.2 +%{?_with_log4cpp:BuildRequires: liblog4cpp-devel >= 1.0} +%{!?_with_log4cpp:BuildRequires: liblog4shib-devel} +%else +BuildRequires: xerces%{?xercesver}-c-devel >= 2.8.0 BuildRequires: xml-security-c-devel >= 1.4.0 -BuildRequires: zlib-devel, opensaml-devel >= 2.1 -%{?_with_log4cpp:BuildRequires: log4cpp-devel >= 1.0} +BuildRequires: xmltooling-devel >= 1.2 +BuildRequires: opensaml-devel >= 2.2 +%{?_with_log4cpp:BuildRequires: log4cpp-devel >= 1.0} %{!?_with_log4cpp:BuildRequires: log4shib-devel} +%endif +BuildRequires: gcc-c++ +%{!?_without_doxygen:BuildRequires: doxygen} +%{!?_without_odbc:BuildRequires:unixODBC-devel} +BuildRequires: zlib-devel %{?_with_fastcgi:BuildRequires: fcgi-devel} %if "%{_vendor}" == "redhat" %{!?_without_builtinapache:BuildRequires: httpd-devel} @@ -22,68 +35,76 @@ BuildRequires: zlib-devel, opensaml-devel >= 2.1 %{!?_without_builtinapache:BuildRequires: apache2-devel} %endif +%if "%{_vendor}" == "suse" +%define pkgdocdir %{_docdir}/%{name} +%else +%define pkgdocdir %{_docdir}/%{name}-%{version} +%endif %description -Shibboleth, a project of Internet2/MACE, is developing architectures, -policy structures, practical technologies, and an open source -implementation to support inter-institutional sharing of web resources -subject to access controls. In addition, Shibboleth will develop a -policy framework that will allow inter-operation within the higher -education community. +Shibboleth is a Web Single Sign-On implementations based on OpenSAML +that supports multiple protocols, federated identity, and the extensible +exchange of rich attributes subject to privacy controls. -This package contains the shibboleth runtime library and apache module. +This package contains the Shibboleth Service Provider runtime libraries +and Apache module(s). %package devel Summary: Shibboleth development Headers Group: Development/Libraries Requires: %{name} = %{version} +%if 0%{?suse_version} > 1030 +Requires: libXerces-c-devel >= 2.8.0 +Requires: libxml-security-c-devel >= 1.4.0 +Requires: libxmltooling-devel >= 1.2 +Requires: libsaml-devel >= 2.2 +%{?_with_log4cpp:Requires: liblog4cpp-devel >= 1.0} +%{!?_with_log4cpp:Requires: liblog4shib-devel} +%else +Requires: xerces%{?xercesver}-c-devel >= 2.8.0 +Requires: xml-security-c-devel >= 1.4.0 +Requires: xmltooling-devel >= 1.2 +Requires: opensaml-devel >= 2.2 +%{?_with_log4cpp:Requires: log4cpp-devel >= 1.0} +%{!?_with_log4cpp:Requires: log4shib-devel} +%endif %description devel -Shibboleth, a project of Internet2/MACE, is developing architectures, -policy structures, practical technologies, and an open source -implementation to support inter-institutional sharing of web resources -subject to access controls. In addition, Shibboleth will develop a -policy framework that will allow inter-operation within the higher -education community. - -This package contains the headers and other necessary files to build -applications that use the shibboleth library. - -%package docs -Summary: Shibboleth API Documentation -Group: Development/Libraries -Requires: %{name} = %{version} +Shibboleth is a Web Single Sign-On implementations based on OpenSAML +that supports multiple protocols, federated identity, and the extensible +exchange of rich attributes subject to privacy controls. + +This package includes files needed for development with Shibboleth. -%description docs -Shibboleth Library API documentation generated by doxygen. %prep %setup -q %build -%configure %{?_without_odbc:--disable-odbc} %{?_without_adfs:--disable-adfs} %{?_with_fastcgi} %{?shib_options} -%{__make} +%configure %{?_without_odbc:--disable-odbc} %{?_without_adfs:--disable-adfs} %{?_with_fastcgi} %{?_with_memcached} %{?shib_options} +%{__make} pkgdocdir=%{pkgdocdir} %install -[ "$RPM_BUILD_ROOT" != "/" ] && %{__rm} -rf $RPM_BUILD_ROOT -%{__make} install NOKEYGEN=1 DESTDIR=$RPM_BUILD_ROOT +%{__make} install NOKEYGEN=1 DESTDIR=$RPM_BUILD_ROOT pkgdocdir=%{pkgdocdir} %if "%{_vendor}" == "suse" %{__sed} -i "s/\/var\/log\/httpd/\/var\/log\/apache2/g" \ - $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/native.logger + $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/native.logger %endif -# Plug the SP into Apache on a recognized system. +# Plug the SP into the built-in Apache on a recognized system. +touch rpm.filelist APACHE_CONFIG="no" -if [ -f $RPM_BUILD_ROOT/%{_libdir}/%{name}/mod_shib_13.so ] ; then +if [ -f $RPM_BUILD_ROOT%{_libdir}/%{name}/mod_shib_13.so ] ; then APACHE_CONFIG="apache.config" fi -if [ -f $RPM_BUILD_ROOT/%{_libdir}/%{name}/mod_shib_20.so ] ; then +if [ -f $RPM_BUILD_ROOT%{_libdir}/%{name}/mod_shib_20.so ] ; then APACHE_CONFIG="apache2.config" fi -if [ -f $RPM_BUILD_ROOT/%{_libdir}/%{name}/mod_shib_22.so ] ; then +if [ -f $RPM_BUILD_ROOT%{_libdir}/%{name}/mod_shib_22.so ] ; then APACHE_CONFIG="apache22.config" fi +%{?_without_builtinapache:APACHE_CONFIG="no"} if [ "$APACHE_CONFIG" != "no" ] ; then APACHE_CONFD="no" if [ -d %{_sysconfdir}/httpd/conf.d ] ; then @@ -94,19 +115,19 @@ if [ "$APACHE_CONFIG" != "no" ] ; then fi if [ "$APACHE_CONFD" != "no" ] ; then %{__mkdir} -p $RPM_BUILD_ROOT$APACHE_CONFD -%if "%{_vendor}" == "suse" - %{__sed} "s/\/usr\/doc\/%{name}/\/usr\/share\/doc\/packages\/%{name}/g" \ - $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/$APACHE_CONFIG \ - > $RPM_BUILD_ROOT$APACHE_CONFD/shib.conf -%else - %{__sed} "s/\/usr\/doc\/%{name}/\/usr\/share\/doc\/%{name}-@-VERSION-@/g" \ - $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/$APACHE_CONFIG \ - > $RPM_BUILD_ROOT$APACHE_CONFD/shib.conf -%endif + %{__cp} -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/$APACHE_CONFIG $RPM_BUILD_ROOT$APACHE_CONFD/shib.conf + echo "%config $APACHE_CONFD/shib.conf" > rpm.filelist fi fi -%check || : +%if "%{_vendor}" == "redhat" || "%{_vendor}" == "suse" + # %{_initddir} not yet in RHEL5, use deprecated %{_initrddir} + mkdir -p $RPM_BUILD_ROOT%{_initrddir} + %{__cp} -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/shibd-%{_vendor} $RPM_BUILD_ROOT%{_initrddir}/shibd + %{__chmod} 755 $RPM_BUILD_ROOT%{_initrddir}/shibd +%endif + +%check %{__make} check %clean @@ -117,34 +138,62 @@ fi /sbin/ldconfig %endif -# Install the shibd init.d scripts and service -%if "%{_vendor}" == "redhat" - if [ -d %{_sysconfdir}/init.d ] ; then - if [ ! -f %{_sysconfdir}/init.d/shibd ] ; then - %{__cp} -p %{_sysconfdir}/%{name}/shibd-%{_vendor} %{_sysconfdir}/init.d/shibd - %{__chmod} 755 %{_sysconfdir}/init.d/shibd - chkconfig --add shibd - fi - fi -%endif - # Key generation cd %{_sysconfdir}/%{name} sh ./keygen.sh -b -%postun +%if "%{_vendor}" == "redhat" + # This adds the proper /etc/rc*.d links for the script + /sbin/chkconfig --add shibd + # On upgrade, restart components if they're already running. + if [ "$1" -gt "1" ] ; then + /etc/init.d/shibd status 1>/dev/null && /etc/init.d/shibd restart 1>/dev/null + %{!?_without_builtinapache:/etc/init.d/httpd status 1>/dev/null && /etc/init.d/httpd restart 1>/dev/null} + fi +%endif +%if "%{_vendor}" == "suse" + # This adds the proper /etc/rc*.d links for the script + /sbin/chkconfig --add shibd + cd /usr/sbin && ln -s /etc/init.d/shibd rcshibd + # On upgrade, restart components if they're already running. + if [ "$1" -gt "1" ] ; then + /etc/init.d/shibd status 1>/dev/null && /etc/init.d/shibd restart 1>/dev/null + %{!?_without_builtinapache:/etc/init.d/apache2 status 1>/dev/null && /etc/init.d/apache2 restart 1>/dev/null} + fi +%endif + +%preun +%if "%{_vendor}" == "redhat" + if [ "$1" = 0 ] ; then + /sbin/service shibd stop >/dev/null 2>&1 + /sbin/chkconfig --del shibd + fi +%endif +%if "%{_vendor}" == "suse" + if [ "$1" = 0 ] ; then + /sbin/service shibd stop >/dev/null 2>&1 + /sbin/chkconfig --del shibd + cd /usr/sbin && %{__rm} -f rcshibd + fi +%endif + %ifnos solaris2.8 solaris2.9 solaris2.10 -/sbin/ldconfig +%postun -p /sbin/ldconfig %endif -# clear init.d state +%posttrans +# ugly hack if init script got removed during %postun by upgraded (buggy/2.1) package %if "%{_vendor}" == "redhat" - chkconfig --del shibd - [ -f %{_sysconfdir}/init.d/shibd ] && \ - %{__rm} -f %{_sysconfdir}/init.d/shibd + if [ ! -f %{_initrddir}/shibd ] ; then + if [ -f %{_sysconfdir}/%{name}/shibd-%{_vendor} ] ; then + %{__cp} -p %{_sysconfdir}/%{name}/shibd-%{_vendor} %{_initrddir}/shibd + %{__chmod} 755 %{_initrddir}/shibd + /sbin/chkconfig --add shibd + fi + fi %endif -%files +%files -f rpm.filelist %defattr(-,root,root,-) %{_sbindir}/shibd %{_bindir}/mdquery @@ -162,41 +211,46 @@ sh ./keygen.sh -b %config(noreplace) %{_sysconfdir}/%{name}/*.xml %config(noreplace) %{_sysconfdir}/%{name}/*.html %config(noreplace) %{_sysconfdir}/%{name}/*.logger -%if "%{_vendor}" == "suse" -%config %{_sysconfdir}/apache2/conf.d/shib.conf -%else -%config %{_sysconfdir}/httpd/conf.d/shib.conf +%if "%{_vendor}" == "redhat" || "%{_vendor}" == "suse" +%attr(755, root, root) %{_initrddir}/shibd %endif %{_sysconfdir}/%{name}/*.dist %{_sysconfdir}/%{name}/apache*.config -%{_sysconfdir}/%{name}/shibd-redhat -%{_sysconfdir}/%{name}/shibd-debian -%{_sysconfdir}/%{name}/shibd-osx.plist -%{_sysconfdir}/%{name}/keygen.sh +%{_sysconfdir}/%{name}/shibd-* +%attr(755, root, root) %{_sysconfdir}/%{name}/keygen.sh +%attr(755, root, root) %{_sysconfdir}/%{name}/metagen.sh %{_sysconfdir}/%{name}/*.xsl -%docdir %{_datadir}/doc/%{name} -%{_datadir}/doc/%{name}/CREDITS.txt -%{_datadir}/doc/%{name}/FASTCGI.LICENSE -%{_datadir}/doc/%{name}/LICENSE.txt -%{_datadir}/doc/%{name}/LOG4CPP.LICENSE -%{_datadir}/doc/%{name}/logo.jpg -%{_datadir}/doc/%{name}/main.css -%{_datadir}/doc/%{name}/NOTICE.txt -%{_datadir}/doc/%{name}/OPENSSL.LICENSE -%{_datadir}/doc/%{name}/README.txt -%{_datadir}/doc/%{name}/RELEASE.txt +%doc %{pkgdocdir} +%exclude %{pkgdocdir}/api %files devel %defattr(-,root,root,-) -%{_includedir} +%{_includedir}/* %{_libdir}/libshibsp.so %{_libdir}/libshibsp-lite.so - -%files docs -%defattr(644,root,root,755) -%doc %{_datadir}/doc/%{name}/api +%doc %{pkgdocdir}/api %changelog +* Mon Aug 10 2009 Scott Cantor - 2.2.1-1 +- Doc handling changes +- SuSE init script + +* Tue Aug 4 2009 Scott Cantor - 2.2.1-1 +- Initial version for 2.2.1, with shibd/httpd restart on upgrade + +* Thu Jun 25 2009 Scott Cantor - 2.2-3 +- Add additional cleanup to posttrans fix + +* Tue Jun 23 2009 Scott Cantor - 2.2-2 +- Reverse without_builtinapache macro test +- Fix init script handling on Red Hat to handle upgrades + +* Wed Dec 3 2008 Scott Cantor - 2.2-1 +- Bump minor version. +- Make keygen.sh executable. +- Fixing SUSE Xerces dependency name. +- Optionally package shib.conf. + * Tue Jun 10 2008 Scott Cantor - 2.1-1 - Change shib.conf handling to treat as config file. diff --git a/shibd/shibd.cpp b/shibd/shibd.cpp index b7a6956..4c86dae 100644 --- a/shibd/shibd.cpp +++ b/shibd/shibd.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -61,7 +61,7 @@ const char* shar_schemadir = NULL; const char* shar_prefix = NULL; bool shar_checkonly = false; bool shar_version = false; -static int unlink_socket = 0; +static bool unlink_socket = false; const char* pidfile = NULL; #ifdef WIN32 @@ -141,14 +141,21 @@ int real_main(int preinit) //_CrtSetAllocHook(MyAllocHook); - // Run the listener if (!shar_checkonly) { - // Run the listener. - if (!conf.getServiceProvider()->getListenerService()->run(&shibd_shutdown)) { - fprintf(stderr, "listener failed to enter listen loop\n"); + ListenerService* listener = conf.getServiceProvider()->getListenerService(); + if (!listener->init(unlink_socket)) { + fprintf(stderr, "listener failed to initialize\n"); + conf.term(); + return -3; + } + else if (!listener->run(&shibd_shutdown)) { + fprintf(stderr, "listener failed during service\n"); + listener->term(); + conf.term(); return -3; } + listener->term(); } conf.term(); @@ -158,11 +165,25 @@ int real_main(int preinit) #else +int daemon_wait = 3; +bool shibd_running = false; +bool daemonize = true; + static void term_handler(int arg) { shibd_shutdown = true; } +static void run_handler(int arg) +{ + shibd_running = true; +} + +static void child_handler(int arg) +{ + // Terminate the parent's wait/sleep if the newly born daemon dies early. +} + static int setup_signals(void) { struct sigaction sa; @@ -190,6 +211,23 @@ static int setup_signals(void) if (sigaction(SIGTERM, &sa, NULL) < 0) { return -1; } + + if (daemonize) { + memset(&sa, 0, sizeof (sa)); + sa.sa_handler = run_handler; + + if (sigaction(SIGUSR1, &sa, NULL) < 0) { + return -1; + } + + memset(&sa, 0, sizeof (sa)); + sa.sa_handler = child_handler; + + if (sigaction(SIGCHLD, &sa, NULL) < 0) { + return -1; + } + } + return 0; } @@ -199,9 +237,11 @@ static void usage(char* whoami) fprintf(stderr, " -d\tinstallation prefix to use.\n"); fprintf(stderr, " -c\tconfig file to use.\n"); fprintf(stderr, " -x\tXML schema catalogs to use.\n"); - fprintf(stderr, " -t\tcheck configuration file for problems.\n"); + fprintf(stderr, " -t\ttest configuration file for problems.\n"); fprintf(stderr, " -f\tforce removal of listener socket.\n"); + fprintf(stderr, " -F\tstay in the foreground.\n"); fprintf(stderr, " -p\tpid file to use.\n"); + fprintf(stderr, " -w\tseconds to wait for successful daemonization.\n"); fprintf(stderr, " -v\tprint software version.\n"); fprintf(stderr, " -h\tprint this help message.\n"); exit(1); @@ -211,7 +251,7 @@ static int parse_args(int argc, char* argv[]) { int opt; - while ((opt = getopt(argc, argv, "d:c:x:p:ftvh")) > 0) { + while ((opt = getopt(argc, argv, "d:c:x:p:w:fFtvh")) > 0) { switch (opt) { case 'd': shar_prefix=optarg; @@ -223,10 +263,14 @@ static int parse_args(int argc, char* argv[]) shar_schemadir=optarg; break; case 'f': - unlink_socket = 1; + unlink_socket = true; + break; + case 'F': + daemonize = false; break; case 't': shar_checkonly=true; + daemonize=false; break; case 'v': shar_version=true; @@ -234,6 +278,12 @@ static int parse_args(int argc, char* argv[]) case 'p': pidfile=optarg; break; + case 'w': + if (optarg) + daemon_wait = atoi(optarg); + if (daemon_wait <= 0) + daemon_wait = 3; + break; default: return -1; } @@ -271,6 +321,21 @@ int main(int argc, char *argv[]) return -1; } + if (daemonize) { + // We must fork() early, while we're single threaded. + // StorageService cleanup thread is about to start. + switch (fork()) { + case 0: + break; + case -1: + perror("forking"); + exit(EXIT_FAILURE); + default: + sleep(daemon_wait); + exit(shibd_running ? EXIT_SUCCESS : EXIT_FAILURE); + } + } + if (!conf.instantiate(shar_config)) { fprintf(stderr, "configuration is invalid, check console for specific problems\n"); conf.term(); @@ -280,23 +345,53 @@ int main(int argc, char *argv[]) if (shar_checkonly) fprintf(stderr, "overall configuration is loadable, check console for non-fatal problems\n"); else { + // Init the listener. + ListenerService* listener = conf.getServiceProvider()->getListenerService(); + if (!listener->init(unlink_socket)) { + fprintf(stderr, "listener failed to initialize\n"); + conf.term(); + return -3; + } - // Write the pid file - if (pidfile) { - FILE* pidf = fopen(pidfile, "w"); - if (pidf) { - fprintf(pidf, "%d\n", getpid()); - fclose(pidf); - } else { - perror(pidfile); // keep running though + if (daemonize) { + if (setsid() == -1) { + perror("setsid"); + exit(EXIT_FAILURE); } + if (chdir("/") == -1) { + perror("chdir to root"); + exit(EXIT_FAILURE); + } + + if (pidfile) { + FILE* pidf = fopen(pidfile, "w"); + if (pidf) { + fprintf(pidf, "%d\n", getpid()); + fclose(pidf); + } + else { + perror(pidfile); + } + } + + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); + + // Signal our parent that we are A-OK. + kill(getppid(), SIGUSR1); } - // Run the listener - if (!conf.getServiceProvider()->getListenerService()->run(&shibd_shutdown)) { - fprintf(stderr, "listener failed to enter listen loop\n"); + // Run the listener. + if (!listener->run(&shibd_shutdown)) { + fprintf(stderr, "listener failure during service\n"); + listener->term(); + conf.term(); + if (pidfile) + unlink(pidfile); return -3; } + listener->term(); } conf.term(); diff --git a/shibd/shibd.rc b/shibd/shibd.rc index 6f21f45..4f11667 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,1,0,0 - PRODUCTVERSION 2,1,0,0 + FILEVERSION 2,2,1,0 + PRODUCTVERSION 2,2,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, 1, 0, 0\0" + VALUE "FileVersion", "2, 2, 1, 0\0" VALUE "InternalName", "shibd\0" - VALUE "LegalCopyright", "Copyright © 2008 Internet2\0" + VALUE "LegalCopyright", "Copyright © 2009 Internet2\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "shibd.exe\0" VALUE "PrivateBuild", "\0" - VALUE "ProductName", "Shibboleth 2.1\0" - VALUE "ProductVersion", "2, 1, 0, 0\0" + VALUE "ProductName", "Shibboleth 2.2.1\0" + VALUE "ProductVersion", "2, 2, 1, 0\0" VALUE "SpecialBuild", "\0" END END diff --git a/shibd/shibd.vcproj b/shibd/shibd.vcproj index f86cfad..c921764 100644 --- a/shibd/shibd.vcproj +++ b/shibd/shibd.vcproj @@ -1,10 +1,11 @@ - - - @@ -334,6 +332,8 @@ AdditionalLibraryDirectories="..\..\cpp-opensaml2\$(PlatformName)\$(ConfigurationName);..\..\cpp-xmltooling\$(PlatformName)\$(ConfigurationName)" GenerateDebugInformation="true" SubSystem="1" + RandomizedBaseAddress="1" + DataExecutionPrevention="0" TargetMachine="17" /> - diff --git a/shibsp/AbstractSPRequest.cpp b/shibsp/AbstractSPRequest.cpp index ff5c98c..c18d826 100644 --- a/shibsp/AbstractSPRequest.cpp +++ b/shibsp/AbstractSPRequest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -52,14 +52,19 @@ AbstractSPRequest::~AbstractSPRequest() RequestMapper::Settings AbstractSPRequest::getRequestSettings() const { - if (m_mapper) - return m_settings; - - // Map request to application and content settings. - m_mapper=m_sp->getRequestMapper(); - m_mapper->lock(); - return m_settings = m_mapper->getSettings(*this); - + if (!m_mapper) { + // Map request to application and content settings. + m_mapper=m_sp->getRequestMapper(); + m_mapper->lock(); + m_settings = m_mapper->getSettings(*this); + + if (reinterpret_cast(m_log)->isDebugEnabled()) { + reinterpret_cast(m_log)->debug( + "mapped %s to %s", getRequestURL(), m_settings.first->getString("applicationId").second + ); + } + } + return m_settings; } const Application& AbstractSPRequest::getApplication() const @@ -68,7 +73,7 @@ const Application& AbstractSPRequest::getApplication() const // Now find the application from the URL settings m_app=m_sp->getApplication(getRequestSettings().first->getString("applicationId").second); if (!m_app) - throw ConfigurationException("Unable to map request to application settings, check configuration."); + throw ConfigurationException("Unable to map request to ApplicationOverride settings, check configuration."); } return *m_app; } @@ -127,17 +132,6 @@ void AbstractSPRequest::setRequestURI(const char* uri) m_uri += uri; break; } - else if (*uri == ';') { - // If this is Java being stupid, skip everything up to the query string, if any. - if (!strncmp(uri, ";jsessionid=", 12)) { - if (uri = strchr(uri, '?')) - m_uri += uri; - break; - } - else { - m_uri += *uri; - } - } else if (*uri != '%') { m_uri += *uri; } @@ -170,6 +164,12 @@ const char* AbstractSPRequest::getRequestURL() const return m_url.c_str(); } +string AbstractSPRequest::getRemoteAddr() const +{ + pair addr = getRequestSettings().first->getString("REMOTE_ADDR"); + return addr.first ? getHeader(addr.second) : ""; +} + const char* AbstractSPRequest::getParameter(const char* name) const { if (!m_parser) @@ -209,7 +209,7 @@ const char* AbstractSPRequest::getHandlerURL(const char* resource) const bool ssl_only=true; const char* handler=NULL; - const PropertySet* props=m_app->getPropertySet("Sessions"); + const PropertySet* props=getApplication().getPropertySet("Sessions"); if (props) { pair p=props->getBool("handlerSSL"); if (p.first) @@ -222,7 +222,7 @@ const char* AbstractSPRequest::getHandlerURL(const char* resource) const // Should never happen... if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6))) throw ConfigurationException( - "Invalid handlerURL property ($1) in Application ($2)", + "Invalid handlerURL property ($1) in element for Application ($2)", params(2, handler ? handler : "null", m_app->getId()) ); diff --git a/shibsp/AbstractSPRequest.h b/shibsp/AbstractSPRequest.h index 6faf9ee..1253256 100644 --- a/shibsp/AbstractSPRequest.h +++ b/shibsp/AbstractSPRequest.h @@ -74,6 +74,8 @@ namespace shibsp { const char* getRequestURL() const; + std::string getRemoteAddr() const; + const char* getParameter(const char* name) const; std::vector::size_type getParameters(const char* name, std::vector& values) const; diff --git a/shibsp/AccessControl.h b/shibsp/AccessControl.h index 77774b6..59178aa 100644 --- a/shibsp/AccessControl.h +++ b/shibsp/AccessControl.h @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * @file shibsp/AccessControl.h - * + * * Interface to an access control plugin */ @@ -33,7 +33,7 @@ namespace shibsp { /** * Interface to an access control plugin - * + * * Access control plugins return authorization decisions based on the intersection * of the resource request and the active session. They can be implemented through * cross-platform or platform-specific mechanisms. @@ -54,10 +54,10 @@ namespace shibsp { shib_acl_false, shib_acl_indeterminate }; - + /** * Perform an authorization check. - * + * * @param request SP request information * @param session active user session, if any * @return true iff access should be granted @@ -70,11 +70,14 @@ namespace shibsp { */ void SHIBSP_API registerAccessControls(); + /** Chains together multiple plugins. */ + #define CHAINING_ACCESS_CONTROL "Chaining" + /** AccessControl based on rudimentary XML syntax. */ - #define XML_ACCESS_CONTROL "XML" + #define XML_ACCESS_CONTROL "XML" /** Reserved for Apache-style .htaccess support. */ - #define HT_ACCESS_CONTROL "htaccess" + #define HT_ACCESS_CONTROL "htaccess" }; #endif /* __shibsp_acl_h__ */ diff --git a/shibsp/Application.cpp b/shibsp/Application.cpp index 8e41cab..6ee7919 100644 --- a/shibsp/Application.cpp +++ b/shibsp/Application.cpp @@ -68,6 +68,21 @@ pair Application::getCookieNameProps(const char* prefix, tim return pair(prefix,defProps); } +void Application::clearHeader(SPRequest& request, const char* rawname, const char* cginame) const +{ + request.clearHeader(rawname, cginame); +} + +void Application::setHeader(SPRequest& request, const char* name, const char* value) const +{ + request.setHeader(name, value); +} + +string Application::getSecureHeader(const SPRequest& request, const char* name) const +{ + return request.getSecureHeader(name); +} + void Application::clearAttributeHeaders(SPRequest& request) const { if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { diff --git a/shibsp/Application.h b/shibsp/Application.h index c7352a2..cfd38e7 100644 --- a/shibsp/Application.h +++ b/shibsp/Application.h @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * @file shibsp/Application.h - * + * * Interface to a Shibboleth Application instance. */ @@ -36,7 +36,7 @@ #include namespace shibsp { - + #ifndef SHIBSP_LITE class SHIBSP_API AttributeExtractor; class SHIBSP_API AttributeFilter; @@ -55,7 +55,7 @@ namespace shibsp { /** * Interface to a Shibboleth Application instance. - * + * *

An Application is a logical set of resources that act as a unit * of session management and policy. */ @@ -72,7 +72,7 @@ namespace shibsp { * @param sp parent ServiceProvider instance */ Application(const ServiceProvider* sp); - + /** Pointer to parent SP instance. */ const ServiceProvider* m_sp; @@ -96,23 +96,23 @@ namespace shibsp { /** * Returns the Application's ID. - * + * * @return the ID - */ + */ virtual const char* getId() const { return getString("id").second; } /** * Returns a unique hash for the Application. - * + * * @return a value resulting from a computation over the Application's configuration */ virtual const char* getHash() const=0; /** * Returns the name and cookie properties to use for this Application. - * + * * @param prefix a value to prepend to the base cookie name * @param lifetime if non-null, will be populated with a suggested lifetime for the cookie, or 0 if session-bound * @return a pair containing the cookie name and the string to append to the cookie value @@ -122,15 +122,15 @@ namespace shibsp { #ifndef SHIBSP_LITE /** * Returns a MetadataProvider for use with this Application. - * + * * @param required true iff an exception should be thrown if no MetadataProvider is available * @return a MetadataProvider instance, or NULL */ virtual opensaml::saml2md::MetadataProvider* getMetadataProvider(bool required=true) const=0; - + /** * Returns a TrustEngine for use with this Application. - * + * * @param required true iff an exception should be thrown if no TrustEngine is available * @return a TrustEngine instance, or NULL */ @@ -138,35 +138,35 @@ namespace shibsp { /** * Returns an AttributeExtractor for use with this Application. - * + * * @return an AttributeExtractor, or NULL */ virtual AttributeExtractor* getAttributeExtractor() const=0; /** * Returns an AttributeFilter for use with this Application. - * + * * @return an AttributeFilter, or NULL */ virtual AttributeFilter* getAttributeFilter() const=0; /** * Returns an AttributeResolver for use with this Application. - * + * * @return an AttributeResolver, or NULL */ virtual AttributeResolver* getAttributeResolver() const=0; /** * Returns the CredentialResolver instance associated with this Application. - * + * * @return a CredentialResolver, or NULL */ virtual xmltooling::CredentialResolver* getCredentialResolver() const=0; /** * Returns configuration properties governing security interactions with a peer. - * + * * @param provider a peer entity's metadata * @return the applicable PropertySet */ @@ -174,15 +174,16 @@ namespace shibsp { /** * Returns configuration properties governing security interactions with a named peer. - * + * * @param entityID a peer name * @return the applicable PropertySet */ virtual const PropertySet* getRelyingParty(const XMLCh* entityID) const=0; /** + * @deprecated * Returns any additional audience values associated with this Application. - * + * * @return additional audience values associated with the Application, or NULL */ virtual const std::vector* getAudiences() const=0; @@ -206,6 +207,33 @@ namespace shibsp { virtual const std::vector& getRemoteUserAttributeIds() const=0; /** + * Ensures no value exists for a request header, allowing for application-specific customization. + * + * @param request SP request to modify + * @param rawname raw name of header to clear + * @param cginame CGI-equivalent name of header, MUST begin with "HTTP_". + */ + virtual void clearHeader(SPRequest& request, const char* rawname, const char* cginame) const; + + /** + * Sets a value for a request header allowing for application-specific customization. + * + * @param request SP request to modify + * @param name name of header to set + * @param value value to set + */ + virtual void setHeader(SPRequest& request, const char* name, const char* value) const; + + /** + * Returns a non-spoofable request header value allowing for application-specific customization. + * + * @param request SP request to access + * @param name the name of the secure header to return + * @return the header's value, or an empty string + */ + virtual std::string getSecureHeader(const SPRequest& request, const char* name) const; + + /** * Clears any headers that may be used to hold attributes after export. * * @param request SP request to clear @@ -214,14 +242,14 @@ namespace shibsp { /** * Returns the default SessionInitiator when automatically requesting a session. - * + * * @return the default SessionInitiator, or NULL */ virtual const SessionInitiator* getDefaultSessionInitiator() const=0; - + /** * Returns a SessionInitiator with a particular ID when automatically requesting a session. - * + * * @param id an identifier unique to the Application * @return the designated SessionInitiator, or NULL */ @@ -230,7 +258,7 @@ namespace shibsp { /** * Returns the default AssertionConsumerService Handler * for use in AuthnRequest messages. - * + * * @return the default AssertionConsumerService, or NULL */ virtual const Handler* getDefaultAssertionConsumerService() const=0; @@ -238,7 +266,7 @@ namespace shibsp { /** * Returns an AssertionConsumerService Handler with a particular index * for use in AuthnRequest messages. - * + * * @param index an index unique to an application * @return the designated AssertionConsumerService, or NULL */ @@ -247,18 +275,18 @@ namespace shibsp { /** * Returns one or more AssertionConsumerService Handlers that support * a particular protocol binding. - * + * * @param binding a protocol binding identifier * @return a set of qualifying AssertionConsumerServices */ virtual const std::vector& getAssertionConsumerServicesByBinding(const XMLCh* binding) const=0; - + /** * Returns the Handler associated with a particular path/location. - * + * * @param path the PATH_INFO appended to the end of a base Handler location * that invokes the Handler - * @return the mapped Handler, or NULL + * @return the mapped Handler, or NULL */ virtual const Handler* getHandler(const char* path) const=0; diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am index 703da9d..811d4ed 100644 --- a/shibsp/Makefile.am +++ b/shibsp/Makefile.am @@ -1,8 +1,5 @@ AUTOMAKE_OPTIONS = foreign -pkgsysconfdir = $(sysconfdir)/@PACKAGE@ -pkgxmldir = $(datadir)/xml/@PACKAGE@ - lib_LTLIBRARIES = libshibsp.la libshibsp-lite.la libshibspincludedir = $(includedir)/shibsp @@ -46,9 +43,11 @@ libshibspinclude_HEADERS = \ attrinclude_HEADERS = \ attribute/Attribute.h \ attribute/AttributeDecoder.h \ + attribute/ExtensibleAttribute.h \ attribute/NameIDAttribute.h \ attribute/ScopedAttribute.h \ - attribute/SimpleAttribute.h + attribute/SimpleAttribute.h \ + attribute/XMLAttribute.h attrfiltinclude_HEADERS = \ attribute/filtering/AttributeFilter.h \ @@ -107,6 +106,7 @@ common_sources = \ ServiceProvider.cpp \ SPConfig.cpp \ attribute/Attribute.cpp \ + attribute/ExtensibleAttribute.cpp \ handler/impl/AbstractHandler.cpp \ handler/impl/AssertionConsumerService.cpp \ handler/impl/AssertionLookup.cpp \ @@ -132,6 +132,7 @@ common_sources = \ handler/impl/Shib1SessionInitiator.cpp \ handler/impl/TransformSessionInitiator.cpp \ handler/impl/WAYFSessionInitiator.cpp \ + impl/ChainingAccessControl.cpp \ impl/StorageServiceSessionCache.cpp \ impl/XMLAccessControl.cpp \ impl/XMLRequestMapper.cpp \ @@ -153,10 +154,13 @@ libshibsp_lite_la_SOURCES = \ libshibsp_la_SOURCES = \ ${common_sources} \ + attribute/DOMAttributeDecoder.cpp \ + attribute/KeyInfoAttributeDecoder.cpp \ attribute/NameIDAttributeDecoder.cpp \ attribute/NameIDFromScopedAttributeDecoder.cpp \ attribute/ScopedAttributeDecoder.cpp \ attribute/StringAttributeDecoder.cpp \ + attribute/XMLAttributeDecoder.cpp \ attribute/filtering/impl/AttributeFilter.cpp \ attribute/filtering/impl/ChainingAttributeFilter.cpp \ attribute/filtering/impl/XMLAttributeFilter.cpp \ @@ -181,7 +185,10 @@ libshibsp_la_SOURCES = \ attribute/filtering/impl/AttributeScopeMatchesShibMDScopeFunctor.cpp \ attribute/resolver/impl/ChainingAttributeResolver.cpp \ attribute/resolver/impl/QueryAttributeResolver.cpp \ + attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp \ attribute/resolver/impl/ChainingAttributeExtractor.cpp \ + attribute/resolver/impl/DelegationAttributeExtractor.cpp \ + attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp \ attribute/resolver/impl/XMLAttributeExtractor.cpp \ binding/impl/ArtifactResolver.cpp \ binding/impl/SOAPClient.cpp \ @@ -193,14 +200,25 @@ 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 2:0:0 -libshibsp_lite_la_LDFLAGS = $(LITE_LIBS) -version-info 2:0:0 +libshibsp_la_LDFLAGS = $(XMLSEC_LIBS) -version-info 3:1:0 +libshibsp_lite_la_LDFLAGS = $(LITE_LIBS) -version-info 3:1:0 libshibsp_lite_la_CPPFLAGS = -DSHIBSP_LITE +pkgsysconfdir = $(sysconfdir)/@PACKAGE@ +pkgxmldir = $(datadir)/xml/@PACKAGE@ +logdir = ${localstatedir}/log +rundir = $(localstatedir)/run +xmldir = $(datadir)/xml + paths.h: ${srcdir}/paths.h.in Makefile ${top_builddir}/config.status rm -f $@.tmp sed < ${srcdir}/$@.in > $@.tmp \ -e 's:@-PREFIX-@:${prefix}:g' \ + -e 's:@-LIBDIR-@:${libdir}:g' \ + -e 's:@-SYSCONFDIR-@:${sysconfdir}:g' \ + -e 's:@-LOGDIR-@:${logdir}:g' \ + -e 's:@-RUNDIR-@:${rundir}:g' \ + -e 's:@-XMLDIR-@:${xmldir}:g' \ -e 's:@-PKGSYSCONFDIR-@:${pkgsysconfdir}:g' \ -e 's:@-PKGXMLDIR-@:${pkgxmldir}:g' \ -e 's:@-XMLTOOLINGXMLDIR-@:${XMLTOOLINGXMLDIR}:g' \ diff --git a/shibsp/Makefile.in b/shibsp/Makefile.in index 7a04a6c..43015a5 100644 --- a/shibsp/Makefile.in +++ b/shibsp/Makefile.in @@ -75,6 +75,7 @@ am__objects_1 = libshibsp_lite_la-AbstractSPRequest.lo \ libshibsp_lite_la-Application.lo \ libshibsp_lite_la-ServiceProvider.lo \ libshibsp_lite_la-SPConfig.lo libshibsp_lite_la-Attribute.lo \ + libshibsp_lite_la-ExtensibleAttribute.lo \ libshibsp_lite_la-AbstractHandler.lo \ libshibsp_lite_la-AssertionConsumerService.lo \ libshibsp_lite_la-AssertionLookup.lo \ @@ -100,6 +101,7 @@ am__objects_1 = libshibsp_lite_la-AbstractSPRequest.lo \ libshibsp_lite_la-Shib1SessionInitiator.lo \ libshibsp_lite_la-TransformSessionInitiator.lo \ libshibsp_lite_la-WAYFSessionInitiator.lo \ + libshibsp_lite_la-ChainingAccessControl.lo \ libshibsp_lite_la-StorageServiceSessionCache.lo \ libshibsp_lite_la-XMLAccessControl.lo \ libshibsp_lite_la-XMLRequestMapper.lo \ @@ -118,28 +120,30 @@ am_libshibsp_lite_la_OBJECTS = $(am__objects_1) \ libshibsp_lite_la_OBJECTS = $(am_libshibsp_lite_la_OBJECTS) libshibsp_la_LIBADD = am__objects_2 = AbstractSPRequest.lo Application.lo ServiceProvider.lo \ - SPConfig.lo Attribute.lo AbstractHandler.lo \ - AssertionConsumerService.lo AssertionLookup.lo \ - ChainingLogoutInitiator.lo ChainingSessionInitiator.lo \ - CookieSessionInitiator.lo FormSessionInitiator.lo \ - LocalLogoutInitiator.lo LogoutHandler.lo MetadataGenerator.lo \ - RemotedHandler.lo StatusHandler.lo SessionHandler.lo \ - SAML1Consumer.lo SAML2Consumer.lo SAML2ArtifactResolution.lo \ - SAML2Logout.lo SAML2LogoutInitiator.lo SAML2NameIDMgmt.lo \ + SPConfig.lo Attribute.lo ExtensibleAttribute.lo \ + AbstractHandler.lo AssertionConsumerService.lo \ + AssertionLookup.lo ChainingLogoutInitiator.lo \ + ChainingSessionInitiator.lo CookieSessionInitiator.lo \ + FormSessionInitiator.lo LocalLogoutInitiator.lo \ + LogoutHandler.lo MetadataGenerator.lo RemotedHandler.lo \ + StatusHandler.lo SessionHandler.lo SAML1Consumer.lo \ + SAML2Consumer.lo SAML2ArtifactResolution.lo SAML2Logout.lo \ + SAML2LogoutInitiator.lo SAML2NameIDMgmt.lo \ SAML2SessionInitiator.lo SAMLDSSessionInitiator.lo \ SessionInitiator.lo Shib1SessionInitiator.lo \ TransformSessionInitiator.lo WAYFSessionInitiator.lo \ - StorageServiceSessionCache.lo XMLAccessControl.lo \ - XMLRequestMapper.lo XMLServiceProvider.lo ddf.lo \ - ListenerService.lo SocketListener.lo TCPListener.lo \ + ChainingAccessControl.lo StorageServiceSessionCache.lo \ + XMLAccessControl.lo XMLRequestMapper.lo XMLServiceProvider.lo \ + ddf.lo ListenerService.lo SocketListener.lo TCPListener.lo \ UnixListener.lo CGIParser.lo DOMPropertySet.lo SPConstants.lo \ TemplateParameters.lo -am_libshibsp_la_OBJECTS = $(am__objects_2) NameIDAttributeDecoder.lo \ +am_libshibsp_la_OBJECTS = $(am__objects_2) DOMAttributeDecoder.lo \ + KeyInfoAttributeDecoder.lo NameIDAttributeDecoder.lo \ NameIDFromScopedAttributeDecoder.lo ScopedAttributeDecoder.lo \ - StringAttributeDecoder.lo AttributeFilter.lo \ - ChainingAttributeFilter.lo XMLAttributeFilter.lo \ - MatchFunctor.lo AndMatchFunctor.lo AnyMatchFunctor.lo \ - NotMatchFunctor.lo OrMatchFunctor.lo \ + StringAttributeDecoder.lo XMLAttributeDecoder.lo \ + AttributeFilter.lo ChainingAttributeFilter.lo \ + XMLAttributeFilter.lo MatchFunctor.lo AndMatchFunctor.lo \ + AnyMatchFunctor.lo NotMatchFunctor.lo OrMatchFunctor.lo \ AttributeIssuerStringFunctor.lo \ AttributeRequesterStringFunctor.lo \ AttributeScopeStringFunctor.lo AttributeValueStringFunctor.lo \ @@ -153,7 +157,9 @@ am_libshibsp_la_OBJECTS = $(am__objects_2) NameIDAttributeDecoder.lo \ AttributeRequesterInEntityGroupFunctor.lo \ AttributeScopeMatchesShibMDScopeFunctor.lo \ ChainingAttributeResolver.lo QueryAttributeResolver.lo \ - ChainingAttributeExtractor.lo XMLAttributeExtractor.lo \ + SimpleAggregationAttributeResolver.lo \ + ChainingAttributeExtractor.lo DelegationAttributeExtractor.lo \ + KeyDescriptorAttributeExtractor.lo XMLAttributeExtractor.lo \ ArtifactResolver.lo SOAPClient.lo DynamicMetadataProvider.lo \ MetadataExtImpl.lo MetadataExtSchemaValidators.lo \ PKIXTrustEngine.lo SecurityPolicy.lo @@ -389,8 +395,6 @@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ xs = @xs@ AUTOMAKE_OPTIONS = foreign -pkgsysconfdir = $(sysconfdir)/@PACKAGE@ -pkgxmldir = $(datadir)/xml/@PACKAGE@ lib_LTLIBRARIES = libshibsp.la libshibsp-lite.la libshibspincludedir = $(includedir)/shibsp attrincludedir = $(includedir)/shibsp/attribute @@ -422,9 +426,11 @@ libshibspinclude_HEADERS = \ attrinclude_HEADERS = \ attribute/Attribute.h \ attribute/AttributeDecoder.h \ + attribute/ExtensibleAttribute.h \ attribute/NameIDAttribute.h \ attribute/ScopedAttribute.h \ - attribute/SimpleAttribute.h + attribute/SimpleAttribute.h \ + attribute/XMLAttribute.h attrfiltinclude_HEADERS = \ attribute/filtering/AttributeFilter.h \ @@ -483,6 +489,7 @@ common_sources = \ ServiceProvider.cpp \ SPConfig.cpp \ attribute/Attribute.cpp \ + attribute/ExtensibleAttribute.cpp \ handler/impl/AbstractHandler.cpp \ handler/impl/AssertionConsumerService.cpp \ handler/impl/AssertionLookup.cpp \ @@ -508,6 +515,7 @@ common_sources = \ handler/impl/Shib1SessionInitiator.cpp \ handler/impl/TransformSessionInitiator.cpp \ handler/impl/WAYFSessionInitiator.cpp \ + impl/ChainingAccessControl.cpp \ impl/StorageServiceSessionCache.cpp \ impl/XMLAccessControl.cpp \ impl/XMLRequestMapper.cpp \ @@ -529,10 +537,13 @@ libshibsp_lite_la_SOURCES = \ libshibsp_la_SOURCES = \ ${common_sources} \ + attribute/DOMAttributeDecoder.cpp \ + attribute/KeyInfoAttributeDecoder.cpp \ attribute/NameIDAttributeDecoder.cpp \ attribute/NameIDFromScopedAttributeDecoder.cpp \ attribute/ScopedAttributeDecoder.cpp \ attribute/StringAttributeDecoder.cpp \ + attribute/XMLAttributeDecoder.cpp \ attribute/filtering/impl/AttributeFilter.cpp \ attribute/filtering/impl/ChainingAttributeFilter.cpp \ attribute/filtering/impl/XMLAttributeFilter.cpp \ @@ -557,7 +568,10 @@ libshibsp_la_SOURCES = \ attribute/filtering/impl/AttributeScopeMatchesShibMDScopeFunctor.cpp \ attribute/resolver/impl/ChainingAttributeResolver.cpp \ attribute/resolver/impl/QueryAttributeResolver.cpp \ + attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp \ attribute/resolver/impl/ChainingAttributeExtractor.cpp \ + attribute/resolver/impl/DelegationAttributeExtractor.cpp \ + attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp \ attribute/resolver/impl/XMLAttributeExtractor.cpp \ binding/impl/ArtifactResolver.cpp \ binding/impl/SOAPClient.cpp \ @@ -570,9 +584,14 @@ 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 2:0:0 -libshibsp_lite_la_LDFLAGS = $(LITE_LIBS) -version-info 2:0:0 +libshibsp_la_LDFLAGS = $(XMLSEC_LIBS) -version-info 3:1:0 +libshibsp_lite_la_LDFLAGS = $(LITE_LIBS) -version-info 3:1:0 libshibsp_lite_la_CPPFLAGS = -DSHIBSP_LITE +pkgsysconfdir = $(sysconfdir)/@PACKAGE@ +pkgxmldir = $(datadir)/xml/@PACKAGE@ +logdir = ${localstatedir}/log +rundir = $(localstatedir)/run +xmldir = $(datadir)/xml EXTRA_DIST = shibsp.vcproj shibsp-lite.vcproj paths.h.in resource.h shibsp.rc BUILT_SOURCES = paths.h all: $(BUILT_SOURCES) @@ -671,15 +690,21 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AuthenticationMethodRegexFunctor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/AuthenticationMethodStringFunctor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CGIParser.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ChainingAccessControl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ChainingAttributeExtractor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ChainingAttributeFilter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ChainingAttributeResolver.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ChainingLogoutInitiator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ChainingSessionInitiator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieSessionInitiator.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DOMAttributeDecoder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DOMPropertySet.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DelegationAttributeExtractor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DynamicMetadataProvider.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ExtensibleAttribute.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FormSessionInitiator.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/KeyDescriptorAttributeExtractor.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/KeyInfoAttributeDecoder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ListenerService.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LocalLogoutInitiator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LogoutHandler.Plo@am__quote@ @@ -712,6 +737,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SessionHandler.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SessionInitiator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Shib1SessionInitiator.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SimpleAggregationAttributeResolver.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SocketListener.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StatusHandler.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StorageServiceSessionCache.Plo@am__quote@ @@ -722,6 +748,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UnixListener.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WAYFSessionInitiator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XMLAccessControl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XMLAttributeDecoder.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XMLAttributeExtractor.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XMLAttributeFilter.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XMLRequestMapper.Plo@am__quote@ @@ -734,11 +761,13 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-AssertionLookup.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-Attribute.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-CGIParser.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-ChainingAccessControl.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-ChainingLogoutInitiator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-ChainingSessionInitiator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-CommonDomainCookie.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-CookieSessionInitiator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-DOMPropertySet.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-ExtensibleAttribute.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-FormSessionInitiator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-ListenerService.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libshibsp_lite_la-LocalLogoutInitiator.Plo@am__quote@ @@ -829,6 +858,13 @@ libshibsp_lite_la-Attribute.lo: attribute/Attribute.cpp @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshibsp_lite_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libshibsp_lite_la-Attribute.lo `test -f 'attribute/Attribute.cpp' || echo '$(srcdir)/'`attribute/Attribute.cpp +libshibsp_lite_la-ExtensibleAttribute.lo: attribute/ExtensibleAttribute.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshibsp_lite_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libshibsp_lite_la-ExtensibleAttribute.lo -MD -MP -MF "$(DEPDIR)/libshibsp_lite_la-ExtensibleAttribute.Tpo" -c -o libshibsp_lite_la-ExtensibleAttribute.lo `test -f 'attribute/ExtensibleAttribute.cpp' || echo '$(srcdir)/'`attribute/ExtensibleAttribute.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/libshibsp_lite_la-ExtensibleAttribute.Tpo" "$(DEPDIR)/libshibsp_lite_la-ExtensibleAttribute.Plo"; else rm -f "$(DEPDIR)/libshibsp_lite_la-ExtensibleAttribute.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='attribute/ExtensibleAttribute.cpp' object='libshibsp_lite_la-ExtensibleAttribute.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshibsp_lite_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libshibsp_lite_la-ExtensibleAttribute.lo `test -f 'attribute/ExtensibleAttribute.cpp' || echo '$(srcdir)/'`attribute/ExtensibleAttribute.cpp + libshibsp_lite_la-AbstractHandler.lo: handler/impl/AbstractHandler.cpp @am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshibsp_lite_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libshibsp_lite_la-AbstractHandler.lo -MD -MP -MF "$(DEPDIR)/libshibsp_lite_la-AbstractHandler.Tpo" -c -o libshibsp_lite_la-AbstractHandler.lo `test -f 'handler/impl/AbstractHandler.cpp' || echo '$(srcdir)/'`handler/impl/AbstractHandler.cpp; \ @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/libshibsp_lite_la-AbstractHandler.Tpo" "$(DEPDIR)/libshibsp_lite_la-AbstractHandler.Plo"; else rm -f "$(DEPDIR)/libshibsp_lite_la-AbstractHandler.Tpo"; exit 1; fi @@ -1004,6 +1040,13 @@ libshibsp_lite_la-WAYFSessionInitiator.lo: handler/impl/WAYFSessionInitiator.cpp @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshibsp_lite_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libshibsp_lite_la-WAYFSessionInitiator.lo `test -f 'handler/impl/WAYFSessionInitiator.cpp' || echo '$(srcdir)/'`handler/impl/WAYFSessionInitiator.cpp +libshibsp_lite_la-ChainingAccessControl.lo: impl/ChainingAccessControl.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshibsp_lite_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libshibsp_lite_la-ChainingAccessControl.lo -MD -MP -MF "$(DEPDIR)/libshibsp_lite_la-ChainingAccessControl.Tpo" -c -o libshibsp_lite_la-ChainingAccessControl.lo `test -f 'impl/ChainingAccessControl.cpp' || echo '$(srcdir)/'`impl/ChainingAccessControl.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/libshibsp_lite_la-ChainingAccessControl.Tpo" "$(DEPDIR)/libshibsp_lite_la-ChainingAccessControl.Plo"; else rm -f "$(DEPDIR)/libshibsp_lite_la-ChainingAccessControl.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='impl/ChainingAccessControl.cpp' object='libshibsp_lite_la-ChainingAccessControl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshibsp_lite_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libshibsp_lite_la-ChainingAccessControl.lo `test -f 'impl/ChainingAccessControl.cpp' || echo '$(srcdir)/'`impl/ChainingAccessControl.cpp + libshibsp_lite_la-StorageServiceSessionCache.lo: impl/StorageServiceSessionCache.cpp @am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshibsp_lite_la_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libshibsp_lite_la-StorageServiceSessionCache.lo -MD -MP -MF "$(DEPDIR)/libshibsp_lite_la-StorageServiceSessionCache.Tpo" -c -o libshibsp_lite_la-StorageServiceSessionCache.lo `test -f 'impl/StorageServiceSessionCache.cpp' || echo '$(srcdir)/'`impl/StorageServiceSessionCache.cpp; \ @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/libshibsp_lite_la-StorageServiceSessionCache.Tpo" "$(DEPDIR)/libshibsp_lite_la-StorageServiceSessionCache.Plo"; else rm -f "$(DEPDIR)/libshibsp_lite_la-StorageServiceSessionCache.Tpo"; exit 1; fi @@ -1116,6 +1159,13 @@ Attribute.lo: attribute/Attribute.cpp @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Attribute.lo `test -f 'attribute/Attribute.cpp' || echo '$(srcdir)/'`attribute/Attribute.cpp +ExtensibleAttribute.lo: attribute/ExtensibleAttribute.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ExtensibleAttribute.lo -MD -MP -MF "$(DEPDIR)/ExtensibleAttribute.Tpo" -c -o ExtensibleAttribute.lo `test -f 'attribute/ExtensibleAttribute.cpp' || echo '$(srcdir)/'`attribute/ExtensibleAttribute.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ExtensibleAttribute.Tpo" "$(DEPDIR)/ExtensibleAttribute.Plo"; else rm -f "$(DEPDIR)/ExtensibleAttribute.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='attribute/ExtensibleAttribute.cpp' object='ExtensibleAttribute.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ExtensibleAttribute.lo `test -f 'attribute/ExtensibleAttribute.cpp' || echo '$(srcdir)/'`attribute/ExtensibleAttribute.cpp + AbstractHandler.lo: handler/impl/AbstractHandler.cpp @am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT AbstractHandler.lo -MD -MP -MF "$(DEPDIR)/AbstractHandler.Tpo" -c -o AbstractHandler.lo `test -f 'handler/impl/AbstractHandler.cpp' || echo '$(srcdir)/'`handler/impl/AbstractHandler.cpp; \ @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/AbstractHandler.Tpo" "$(DEPDIR)/AbstractHandler.Plo"; else rm -f "$(DEPDIR)/AbstractHandler.Tpo"; exit 1; fi @@ -1291,6 +1341,13 @@ WAYFSessionInitiator.lo: handler/impl/WAYFSessionInitiator.cpp @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o WAYFSessionInitiator.lo `test -f 'handler/impl/WAYFSessionInitiator.cpp' || echo '$(srcdir)/'`handler/impl/WAYFSessionInitiator.cpp +ChainingAccessControl.lo: impl/ChainingAccessControl.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ChainingAccessControl.lo -MD -MP -MF "$(DEPDIR)/ChainingAccessControl.Tpo" -c -o ChainingAccessControl.lo `test -f 'impl/ChainingAccessControl.cpp' || echo '$(srcdir)/'`impl/ChainingAccessControl.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ChainingAccessControl.Tpo" "$(DEPDIR)/ChainingAccessControl.Plo"; else rm -f "$(DEPDIR)/ChainingAccessControl.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='impl/ChainingAccessControl.cpp' object='ChainingAccessControl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ChainingAccessControl.lo `test -f 'impl/ChainingAccessControl.cpp' || echo '$(srcdir)/'`impl/ChainingAccessControl.cpp + StorageServiceSessionCache.lo: impl/StorageServiceSessionCache.cpp @am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT StorageServiceSessionCache.lo -MD -MP -MF "$(DEPDIR)/StorageServiceSessionCache.Tpo" -c -o StorageServiceSessionCache.lo `test -f 'impl/StorageServiceSessionCache.cpp' || echo '$(srcdir)/'`impl/StorageServiceSessionCache.cpp; \ @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/StorageServiceSessionCache.Tpo" "$(DEPDIR)/StorageServiceSessionCache.Plo"; else rm -f "$(DEPDIR)/StorageServiceSessionCache.Tpo"; exit 1; fi @@ -1382,6 +1439,20 @@ TemplateParameters.lo: util/TemplateParameters.cpp @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o TemplateParameters.lo `test -f 'util/TemplateParameters.cpp' || echo '$(srcdir)/'`util/TemplateParameters.cpp +DOMAttributeDecoder.lo: attribute/DOMAttributeDecoder.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT DOMAttributeDecoder.lo -MD -MP -MF "$(DEPDIR)/DOMAttributeDecoder.Tpo" -c -o DOMAttributeDecoder.lo `test -f 'attribute/DOMAttributeDecoder.cpp' || echo '$(srcdir)/'`attribute/DOMAttributeDecoder.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/DOMAttributeDecoder.Tpo" "$(DEPDIR)/DOMAttributeDecoder.Plo"; else rm -f "$(DEPDIR)/DOMAttributeDecoder.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='attribute/DOMAttributeDecoder.cpp' object='DOMAttributeDecoder.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o DOMAttributeDecoder.lo `test -f 'attribute/DOMAttributeDecoder.cpp' || echo '$(srcdir)/'`attribute/DOMAttributeDecoder.cpp + +KeyInfoAttributeDecoder.lo: attribute/KeyInfoAttributeDecoder.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT KeyInfoAttributeDecoder.lo -MD -MP -MF "$(DEPDIR)/KeyInfoAttributeDecoder.Tpo" -c -o KeyInfoAttributeDecoder.lo `test -f 'attribute/KeyInfoAttributeDecoder.cpp' || echo '$(srcdir)/'`attribute/KeyInfoAttributeDecoder.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/KeyInfoAttributeDecoder.Tpo" "$(DEPDIR)/KeyInfoAttributeDecoder.Plo"; else rm -f "$(DEPDIR)/KeyInfoAttributeDecoder.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='attribute/KeyInfoAttributeDecoder.cpp' object='KeyInfoAttributeDecoder.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o KeyInfoAttributeDecoder.lo `test -f 'attribute/KeyInfoAttributeDecoder.cpp' || echo '$(srcdir)/'`attribute/KeyInfoAttributeDecoder.cpp + NameIDAttributeDecoder.lo: attribute/NameIDAttributeDecoder.cpp @am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT NameIDAttributeDecoder.lo -MD -MP -MF "$(DEPDIR)/NameIDAttributeDecoder.Tpo" -c -o NameIDAttributeDecoder.lo `test -f 'attribute/NameIDAttributeDecoder.cpp' || echo '$(srcdir)/'`attribute/NameIDAttributeDecoder.cpp; \ @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/NameIDAttributeDecoder.Tpo" "$(DEPDIR)/NameIDAttributeDecoder.Plo"; else rm -f "$(DEPDIR)/NameIDAttributeDecoder.Tpo"; exit 1; fi @@ -1410,6 +1481,13 @@ StringAttributeDecoder.lo: attribute/StringAttributeDecoder.cpp @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o StringAttributeDecoder.lo `test -f 'attribute/StringAttributeDecoder.cpp' || echo '$(srcdir)/'`attribute/StringAttributeDecoder.cpp +XMLAttributeDecoder.lo: attribute/XMLAttributeDecoder.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT XMLAttributeDecoder.lo -MD -MP -MF "$(DEPDIR)/XMLAttributeDecoder.Tpo" -c -o XMLAttributeDecoder.lo `test -f 'attribute/XMLAttributeDecoder.cpp' || echo '$(srcdir)/'`attribute/XMLAttributeDecoder.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/XMLAttributeDecoder.Tpo" "$(DEPDIR)/XMLAttributeDecoder.Plo"; else rm -f "$(DEPDIR)/XMLAttributeDecoder.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='attribute/XMLAttributeDecoder.cpp' object='XMLAttributeDecoder.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o XMLAttributeDecoder.lo `test -f 'attribute/XMLAttributeDecoder.cpp' || echo '$(srcdir)/'`attribute/XMLAttributeDecoder.cpp + AttributeFilter.lo: attribute/filtering/impl/AttributeFilter.cpp @am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT AttributeFilter.lo -MD -MP -MF "$(DEPDIR)/AttributeFilter.Tpo" -c -o AttributeFilter.lo `test -f 'attribute/filtering/impl/AttributeFilter.cpp' || echo '$(srcdir)/'`attribute/filtering/impl/AttributeFilter.cpp; \ @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/AttributeFilter.Tpo" "$(DEPDIR)/AttributeFilter.Plo"; else rm -f "$(DEPDIR)/AttributeFilter.Tpo"; exit 1; fi @@ -1578,6 +1656,13 @@ QueryAttributeResolver.lo: attribute/resolver/impl/QueryAttributeResolver.cpp @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o QueryAttributeResolver.lo `test -f 'attribute/resolver/impl/QueryAttributeResolver.cpp' || echo '$(srcdir)/'`attribute/resolver/impl/QueryAttributeResolver.cpp +SimpleAggregationAttributeResolver.lo: attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT SimpleAggregationAttributeResolver.lo -MD -MP -MF "$(DEPDIR)/SimpleAggregationAttributeResolver.Tpo" -c -o SimpleAggregationAttributeResolver.lo `test -f 'attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp' || echo '$(srcdir)/'`attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/SimpleAggregationAttributeResolver.Tpo" "$(DEPDIR)/SimpleAggregationAttributeResolver.Plo"; else rm -f "$(DEPDIR)/SimpleAggregationAttributeResolver.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp' object='SimpleAggregationAttributeResolver.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o SimpleAggregationAttributeResolver.lo `test -f 'attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp' || echo '$(srcdir)/'`attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp + ChainingAttributeExtractor.lo: attribute/resolver/impl/ChainingAttributeExtractor.cpp @am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ChainingAttributeExtractor.lo -MD -MP -MF "$(DEPDIR)/ChainingAttributeExtractor.Tpo" -c -o ChainingAttributeExtractor.lo `test -f 'attribute/resolver/impl/ChainingAttributeExtractor.cpp' || echo '$(srcdir)/'`attribute/resolver/impl/ChainingAttributeExtractor.cpp; \ @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ChainingAttributeExtractor.Tpo" "$(DEPDIR)/ChainingAttributeExtractor.Plo"; else rm -f "$(DEPDIR)/ChainingAttributeExtractor.Tpo"; exit 1; fi @@ -1585,6 +1670,20 @@ ChainingAttributeExtractor.lo: attribute/resolver/impl/ChainingAttributeExtracto @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ChainingAttributeExtractor.lo `test -f 'attribute/resolver/impl/ChainingAttributeExtractor.cpp' || echo '$(srcdir)/'`attribute/resolver/impl/ChainingAttributeExtractor.cpp +DelegationAttributeExtractor.lo: attribute/resolver/impl/DelegationAttributeExtractor.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT DelegationAttributeExtractor.lo -MD -MP -MF "$(DEPDIR)/DelegationAttributeExtractor.Tpo" -c -o DelegationAttributeExtractor.lo `test -f 'attribute/resolver/impl/DelegationAttributeExtractor.cpp' || echo '$(srcdir)/'`attribute/resolver/impl/DelegationAttributeExtractor.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/DelegationAttributeExtractor.Tpo" "$(DEPDIR)/DelegationAttributeExtractor.Plo"; else rm -f "$(DEPDIR)/DelegationAttributeExtractor.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='attribute/resolver/impl/DelegationAttributeExtractor.cpp' object='DelegationAttributeExtractor.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o DelegationAttributeExtractor.lo `test -f 'attribute/resolver/impl/DelegationAttributeExtractor.cpp' || echo '$(srcdir)/'`attribute/resolver/impl/DelegationAttributeExtractor.cpp + +KeyDescriptorAttributeExtractor.lo: attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp +@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT KeyDescriptorAttributeExtractor.lo -MD -MP -MF "$(DEPDIR)/KeyDescriptorAttributeExtractor.Tpo" -c -o KeyDescriptorAttributeExtractor.lo `test -f 'attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp' || echo '$(srcdir)/'`attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/KeyDescriptorAttributeExtractor.Tpo" "$(DEPDIR)/KeyDescriptorAttributeExtractor.Plo"; else rm -f "$(DEPDIR)/KeyDescriptorAttributeExtractor.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp' object='KeyDescriptorAttributeExtractor.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o KeyDescriptorAttributeExtractor.lo `test -f 'attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp' || echo '$(srcdir)/'`attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp + XMLAttributeExtractor.lo: attribute/resolver/impl/XMLAttributeExtractor.cpp @am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT XMLAttributeExtractor.lo -MD -MP -MF "$(DEPDIR)/XMLAttributeExtractor.Tpo" -c -o XMLAttributeExtractor.lo `test -f 'attribute/resolver/impl/XMLAttributeExtractor.cpp' || echo '$(srcdir)/'`attribute/resolver/impl/XMLAttributeExtractor.cpp; \ @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/XMLAttributeExtractor.Tpo" "$(DEPDIR)/XMLAttributeExtractor.Plo"; else rm -f "$(DEPDIR)/XMLAttributeExtractor.Tpo"; exit 1; fi @@ -2042,6 +2141,11 @@ paths.h: ${srcdir}/paths.h.in Makefile ${top_builddir}/config.status rm -f $@.tmp sed < ${srcdir}/$@.in > $@.tmp \ -e 's:@-PREFIX-@:${prefix}:g' \ + -e 's:@-LIBDIR-@:${libdir}:g' \ + -e 's:@-SYSCONFDIR-@:${sysconfdir}:g' \ + -e 's:@-LOGDIR-@:${logdir}:g' \ + -e 's:@-RUNDIR-@:${rundir}:g' \ + -e 's:@-XMLDIR-@:${xmldir}:g' \ -e 's:@-PKGSYSCONFDIR-@:${pkgsysconfdir}:g' \ -e 's:@-PKGXMLDIR-@:${pkgxmldir}:g' \ -e 's:@-XMLTOOLINGXMLDIR-@:${XMLTOOLINGXMLDIR}:g' \ diff --git a/shibsp/SPConfig.cpp b/shibsp/SPConfig.cpp index 7aaaf98..cbc9f8c 100644 --- a/shibsp/SPConfig.cpp +++ b/shibsp/SPConfig.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -55,7 +55,6 @@ # include "metadata/MetadataExt.h" # include "security/PKIXTrustEngine.h" # include -# include #else # include #endif @@ -124,6 +123,10 @@ bool SPConfig::init(const char* catalog_path, const char* inst_prefix) std::string ll(loglevel); PathResolver localpr; localpr.setDefaultPrefix(inst_prefix2.c_str()); + inst_prefix = getenv("SHIBSP_CFGDIR"); + if (!inst_prefix) + inst_prefix = SHIBSP_CFGDIR; + localpr.setCfgDir(inst_prefix); XMLToolingConfig::getConfig().log_config(localpr.resolve(ll, PathResolver::XMLTOOLING_CFG_FILE, PACKAGE_NAME).c_str()); Category& log=Category::getInstance(SHIBSP_LOGCAT".Config"); @@ -140,15 +143,33 @@ bool SPConfig::init(const char* catalog_path, const char* inst_prefix) log.fatal("failed to initialize OpenSAML library"); return false; } - XMLPlatformUtils::fgNetAccessor = new CurlNetAccessor(); #else if (!XMLToolingConfig::getConfig().init()) { log.fatal("failed to initialize XMLTooling library"); return false; } #endif - XMLToolingConfig::getConfig().getPathResolver()->setDefaultPackageName(PACKAGE_NAME); - XMLToolingConfig::getConfig().getPathResolver()->setDefaultPrefix(inst_prefix2.c_str()); + PathResolver* pr = XMLToolingConfig::getConfig().getPathResolver(); + pr->setDefaultPackageName(PACKAGE_NAME); + pr->setDefaultPrefix(inst_prefix2.c_str()); + pr->setCfgDir(inst_prefix); + inst_prefix = getenv("SHIBSP_LIBDIR"); + if (!inst_prefix) + inst_prefix = SHIBSP_LIBDIR; + pr->setLibDir(inst_prefix); + inst_prefix = getenv("SHIBSP_LOGDIR"); + if (!inst_prefix) + inst_prefix = SHIBSP_LOGDIR; + pr->setLogDir(inst_prefix); + inst_prefix = getenv("SHIBSP_RUNDIR"); + if (!inst_prefix) + inst_prefix = SHIBSP_RUNDIR; + pr->setRunDir(inst_prefix); + inst_prefix = getenv("SHIBSP_XMLDIR"); + if (!inst_prefix) + inst_prefix = SHIBSP_XMLDIR; + pr->setXMLDir(inst_prefix); + XMLToolingConfig::getConfig().setTemplateEngine(new TemplateEngine()); XMLToolingConfig::getConfig().getTemplateEngine()->setTagPrefix("shibmlp"); @@ -220,6 +241,9 @@ void SPConfig::term() log.info("%s library shutting down", PACKAGE_STRING); setServiceProvider(NULL); + if (m_configDoc) + m_configDoc->release(); + m_configDoc = NULL; #ifndef SHIBSP_LITE setArtifactResolver(NULL); #endif @@ -289,6 +313,9 @@ bool SPConfig::instantiate(const char* config, bool rethrow) dummydoc = XMLToolingConfig::getConfig().getParser().parse(snippet); XercesJanitor docjanitor(dummydoc); setServiceProvider(ServiceProviderManager.newPlugin(XML_SERVICE_PROVIDER, dummydoc->getDocumentElement())); + if (m_configDoc) + m_configDoc->release(); + m_configDoc = docjanitor.release(); } else { stringstream snippet(config); @@ -300,6 +327,9 @@ bool SPConfig::instantiate(const char* config, bool rethrow) setServiceProvider(ServiceProviderManager.newPlugin(type.get(), dummydoc->getDocumentElement())); else throw ConfigurationException("The supplied XML bootstrapping configuration did not include a type attribute."); + if (m_configDoc) + m_configDoc->release(); + m_configDoc = docjanitor.release(); } getServiceProvider()->init(); diff --git a/shibsp/SPConfig.h b/shibsp/SPConfig.h index d58c3a4..99f1a22 100644 --- a/shibsp/SPConfig.h +++ b/shibsp/SPConfig.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -69,7 +69,8 @@ namespace shibsp { #ifndef SHIBSP_LITE m_artifactResolver(NULL), #endif - m_features(0) {} + m_features(0), m_configDoc(NULL) { + } virtual ~SPConfig() {} @@ -185,7 +186,7 @@ namespace shibsp { * * @return global ArtifactResolver or NULL */ - opensaml::MessageDecoder::ArtifactResolver* getArtifactResolver() const { + const opensaml::MessageDecoder::ArtifactResolver* getArtifactResolver() const { return m_artifactResolver; } #endif @@ -291,6 +292,7 @@ namespace shibsp { private: unsigned long m_features; + xercesc::DOMDocument* m_configDoc; }; #if defined (_MSC_VER) diff --git a/shibsp/SPRequest.h b/shibsp/SPRequest.h index cf2b7ed..255e8b5 100644 --- a/shibsp/SPRequest.h +++ b/shibsp/SPRequest.h @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 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 @@ -16,8 +16,8 @@ /** * @file shibsp/SPRequest.h - * - * Interface to server request being processed + * + * Interface to server request being processed */ #ifndef __shibsp_req_h__ @@ -28,18 +28,18 @@ #include namespace shibsp { - + class SHIBSP_API Application; class SHIBSP_API ServiceProvider; class SHIBSP_API Session; - + /** * Interface to server request being processed - * + * *

To supply information from the surrounding web server environment, * a shim must be supplied in the form of this interface to adapt the * library to different proprietary server APIs. - * + * *

This interface need not be threadsafe. */ class SHIBSP_API SPRequest : public virtual xmltooling::HTTPRequest, public virtual xmltooling::HTTPResponse @@ -48,10 +48,10 @@ namespace shibsp { SPRequest() {} public: virtual ~SPRequest() {} - + /** * Returns the locked ServiceProvider processing the request. - * + * * @return reference to ServiceProvider */ virtual const ServiceProvider& getServiceProvider() const=0; @@ -59,14 +59,14 @@ namespace shibsp { /** * Returns RequestMapper Settings associated with the request, guaranteed * to be valid for the request's duration. - * + * * @return copy of settings */ virtual RequestMapper::Settings getRequestSettings() const=0; - + /** * Returns the Application governing the request. - * + * * @return reference to Application */ virtual const Application& getApplication() const=0; @@ -84,7 +84,7 @@ namespace shibsp { /** * Returns the effective base Handler URL for a resource, * or the current request URL. - * + * * @param resource resource URL to compute handler for * @return base location of handler */ @@ -94,7 +94,7 @@ namespace shibsp { * Returns a non-spoofable request header value, if possible. * Platforms that support environment export can redirect header * lookups by overriding this method. - * + * * @param name the name of the secure header to return * @return the header's value, or an empty string */ @@ -104,7 +104,7 @@ namespace shibsp { /** * Ensures no value exists for a request header. - * + * * @param rawname raw name of header to clear * @param cginame CGI-equivalent name of header */ @@ -112,7 +112,7 @@ namespace shibsp { /** * Sets a value for a request header. - * + * * @param name name of header to set * @param value value to set */ @@ -120,11 +120,19 @@ namespace shibsp { /** * Establish REMOTE_USER identity in request. - * + * * @param user REMOTE_USER value to set or NULL to clear */ virtual void setRemoteUser(const char* user)=0; - + + /** + * Establish AUTH_TYPE for request. + * + * @param authtype AUTH_TYPE value to set or NULL to clear + */ + virtual void setAuthType(const char* authtype) { + } + /** Portable logging levels. */ enum SPLogLevel { SPDebug, @@ -136,7 +144,7 @@ namespace shibsp { /** * Log to native server environment. - * + * * @param level logging level * @param msg message to log */ @@ -144,7 +152,7 @@ namespace shibsp { /** * Test logging level. - * + * * @param level logging level * @return true iff logging level is enabled */ @@ -152,16 +160,16 @@ namespace shibsp { /** * Indicates that processing was declined, meaning no action is required during this phase of processing. - * + * * @return a status code to pass back to the server-specific layer - */ + */ virtual long returnDecline()=0; /** * Indicates that processing was completed. - * + * * @return a status code to pass back to the server-specific layer - */ + */ virtual long returnOK()=0; }; }; diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp index 91a8ab9..0006dbc 100644 --- a/shibsp/ServiceProvider.cpp +++ b/shibsp/ServiceProvider.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -55,7 +55,12 @@ namespace shibsp { bool mderror = dynamic_cast(tp.getRichException())!=NULL; pair redirectErrors = pair(false,NULL); pair pathname = pair(false,NULL); - const PropertySet* props=app ? app->getPropertySet("Errors") : NULL; + + // Strictly for error handling, detect a NULL application and point at the default. + if (!app) + app = request.getServiceProvider().getApplication("default"); + + const PropertySet* props=app->getPropertySet("Errors"); try { RequestMapper::Settings settings = request.getRequestSettings(); @@ -101,13 +106,13 @@ namespace shibsp { tp.setPropertySet(props); stringstream str; XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, tp, tp.getRichException()); - return request.sendResponse(str); + return request.sendError(str); } } if (!strcmp(page,"access")) { istringstream msg("Access Denied"); - return request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_UNAUTHORIZED); + return request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN); } log.error("sendError could not process error template (%s)", page); @@ -116,16 +121,16 @@ namespace shibsp { } void SHIBSP_DLLLOCAL clearHeaders(SPRequest& request) { - request.clearHeader("Shib-Session-ID", "HTTP_SHIB_SESSION_ID"); - request.clearHeader("Shib-Identity-Provider", "HTTP_SHIB_IDENTITY_PROVIDER"); - request.clearHeader("Shib-Authentication-Method", "HTTP_SHIB_AUTHENTICATION_METHOD"); - request.clearHeader("Shib-Authentication-Instant", "HTTP_SHIB_AUTHENTICATION_INSTANT"); - request.clearHeader("Shib-AuthnContext-Class", "HTTP_SHIB_AUTHNCONTEXT_CLASS"); - request.clearHeader("Shib-AuthnContext-Decl", "HTTP_SHIB_AUTHNCONTEXT_DECL"); - request.clearHeader("Shib-Assertion-Count", "HTTP_SHIB_ASSERTION_COUNT"); + const Application& app = request.getApplication(); + app.clearHeader(request, "Shib-Session-ID", "HTTP_SHIB_SESSION_ID"); + app.clearHeader(request, "Shib-Identity-Provider", "HTTP_SHIB_IDENTITY_PROVIDER"); + app.clearHeader(request, "Shib-Authentication-Method", "HTTP_SHIB_AUTHENTICATION_METHOD"); + app.clearHeader(request, "Shib-Authentication-Instant", "HTTP_SHIB_AUTHENTICATION_INSTANT"); + app.clearHeader(request, "Shib-AuthnContext-Class", "HTTP_SHIB_AUTHNCONTEXT_CLASS"); + app.clearHeader(request, "Shib-AuthnContext-Decl", "HTTP_SHIB_AUTHNCONTEXT_DECL"); + app.clearHeader(request, "Shib-Assertion-Count", "HTTP_SHIB_ASSERTION_COUNT"); + app.clearAttributeHeaders(request); request.clearHeader("REMOTE_USER", "HTTP_REMOTE_USER"); - //request.clearHeader("Shib-Application-ID"); handle inside app method - request.getApplication().clearAttributeHeaders(request); } }; @@ -240,12 +245,15 @@ pair ServiceProvider::doAuthentication(SPRequest& request, bool handl return initiator->run(request,false); } + request.setAuthType("shibboleth"); + // We're done. Everything is okay. Nothing to report. Nothing to do.. // Let the caller decide how to proceed. log.debug("doAuthentication succeeded"); return make_pair(false,0L); } catch (exception& e) { + request.log(SPRequest::SPError, e.what()); TemplateParameters tp(&e); tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); return make_pair(true,sendError(log, request, app, "session", tp)); @@ -316,6 +324,7 @@ pair ServiceProvider::doAuthorization(SPRequest& request) const } } catch (exception& e) { + request.log(SPRequest::SPError, e.what()); TemplateParameters tp(&e); tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); return make_pair(true,sendError(log, request, app, "access", tp)); @@ -355,24 +364,24 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio return make_pair(false,0L); // just bail silently } - request.setHeader("Shib-Application-ID", app->getId()); - request.setHeader("Shib-Session-ID", session->getID()); + app->setHeader(request, "Shib-Application-ID", app->getId()); + app->setHeader(request, "Shib-Session-ID", session->getID()); // Export the IdP name and Authn method/context info. const char* hval = session->getEntityID(); if (hval) - request.setHeader("Shib-Identity-Provider", hval); + app->setHeader(request, "Shib-Identity-Provider", hval); hval = session->getAuthnInstant(); if (hval) - request.setHeader("Shib-Authentication-Instant", hval); + app->setHeader(request, "Shib-Authentication-Instant", hval); hval = session->getAuthnContextClassRef(); if (hval) { - request.setHeader("Shib-Authentication-Method", hval); - request.setHeader("Shib-AuthnContext-Class", hval); + app->setHeader(request, "Shib-Authentication-Method", hval); + app->setHeader(request, "Shib-AuthnContext-Class", hval); } hval = session->getAuthnContextDeclRef(); if (hval) - request.setHeader("Shib-AuthnContext-Decl", hval); + app->setHeader(request, "Shib-AuthnContext-Decl", hval); // Maybe export the assertion keys. pair exp=settings.first->getBool("exportAssertion"); @@ -397,16 +406,18 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio *(exportName.rbegin()) = '0' + (count%10); *(++exportName.rbegin()) = '0' + (count/10); string fullURL = baseURL + encoder->encode(*tokenids); - request.setHeader(exportName.c_str(), fullURL.c_str()); + app->setHeader(request, exportName.c_str(), fullURL.c_str()); } - request.setHeader("Shib-Assertion-Count", exportName.c_str() + 15); + app->setHeader(request, "Shib-Assertion-Count", exportName.c_str() + 15); } } // Export the attributes. const multimap& attributes = session->getIndexedAttributes(); for (multimap::const_iterator a = attributes.begin(); a!=attributes.end(); ++a) { - string header(request.getSecureHeader(a->first.c_str())); + if (a->second->isInternal()) + continue; + string header(app->getSecureHeader(request, a->first.c_str())); const vector& vals = a->second->getSerializedValues(); for (vector::const_iterator v = vals.begin(); v!=vals.end(); ++v) { if (!header.empty()) @@ -424,7 +435,7 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio header += (*v); } } - request.setHeader(a->first.c_str(), header.c_str()); + app->setHeader(request, a->first.c_str(), header.c_str()); } // Check for REMOTE_USER. @@ -433,7 +444,7 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio for (vector::const_iterator rmid = rmids.begin(); !remoteUserSet && rmid != rmids.end(); ++rmid) { pair::const_iterator,multimap::const_iterator> matches = attributes.equal_range(*rmid); - while (matches.first != matches.second) { + for (; matches.first != matches.second; ++matches.first) { const vector& vals = matches.first->second->getSerializedValues(); if (!vals.empty()) { request.setRemoteUser(vals.front().c_str()); @@ -446,6 +457,7 @@ pair ServiceProvider::doExport(SPRequest& request, bool requireSessio return make_pair(false,0L); } catch (exception& e) { + request.log(SPRequest::SPError, e.what()); TemplateParameters tp(&e); tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); return make_pair(true,sendError(log, request, app, "session", tp)); @@ -525,6 +537,7 @@ pair ServiceProvider::doHandler(SPRequest& request) const throw ConfigurationException("Configured Shibboleth handler failed to process the request."); } catch (exception& e) { + request.log(SPRequest::SPError, e.what()); TemplateParameters tp(&e); tp.m_map["requestURL"] = targetURL.substr(0,targetURL.find('?')); tp.m_request = &request; diff --git a/shibsp/attribute/Attribute.cpp b/shibsp/attribute/Attribute.cpp index d4301e4..36c32bc 100644 --- a/shibsp/attribute/Attribute.cpp +++ b/shibsp/attribute/Attribute.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -28,8 +28,11 @@ #include "attribute/SimpleAttribute.h" #include "attribute/ScopedAttribute.h" #include "attribute/NameIDAttribute.h" +#include "attribute/ExtensibleAttribute.h" +#include "attribute/XMLAttribute.h" #include "util/SPConstants.h" +#include #include using namespace shibsp; @@ -50,26 +53,44 @@ namespace shibsp { return new NameIDAttribute(in); } + SHIBSP_DLLLOCAL Attribute* ExtensibleAttributeFactory(DDF& in) { + return new ExtensibleAttribute(in); + } + + SHIBSP_DLLLOCAL Attribute* XMLAttributeFactory(DDF& in) { + return new XMLAttribute(in); + } + #ifndef SHIBSP_LITE - SHIBSP_DLLLOCAL PluginManager::Factory StringAttributeDecoderFactory; - SHIBSP_DLLLOCAL PluginManager::Factory ScopedAttributeDecoderFactory; - SHIBSP_DLLLOCAL PluginManager::Factory NameIDAttributeDecoderFactory; - SHIBSP_DLLLOCAL PluginManager::Factory NameIDFromScopedAttributeDecoderFactory; + SHIBSP_DLLLOCAL PluginManager::Factory StringAttributeDecoderFactory; + SHIBSP_DLLLOCAL PluginManager::Factory ScopedAttributeDecoderFactory; + SHIBSP_DLLLOCAL PluginManager::Factory NameIDAttributeDecoderFactory; + SHIBSP_DLLLOCAL PluginManager::Factory NameIDFromScopedAttributeDecoderFactory; + SHIBSP_DLLLOCAL PluginManager::Factory KeyInfoAttributeDecoderFactory; + SHIBSP_DLLLOCAL PluginManager::Factory DOMAttributeDecoderFactory; + SHIBSP_DLLLOCAL PluginManager::Factory XMLAttributeDecoderFactory; static const XMLCh _StringAttributeDecoder[] = UNICODE_LITERAL_22(S,t,r,i,n,g,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); static const XMLCh _ScopedAttributeDecoder[] = UNICODE_LITERAL_22(S,c,o,p,e,d,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); static const XMLCh _NameIDAttributeDecoder[] = UNICODE_LITERAL_22(N,a,m,e,I,D,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); static const XMLCh _NameIDFromScopedAttributeDecoder[] = UNICODE_LITERAL_32(N,a,m,e,I,D,F,r,o,m,S,c,o,p,e,d,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); + static const XMLCh _KeyInfoAttributeDecoder[] =UNICODE_LITERAL_23(K,e,y,I,n,f,o,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); + static const XMLCh _DOMAttributeDecoder[] = UNICODE_LITERAL_19(D,O,M,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); + static const XMLCh _XMLAttributeDecoder[] = UNICODE_LITERAL_19(X,M,L,A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r); static const XMLCh caseSensitive[] = UNICODE_LITERAL_13(c,a,s,e,S,e,n,s,i,t,i,v,e); + static const XMLCh internal[] = UNICODE_LITERAL_8(i,n,t,e,r,n,a,l); #endif }; #ifndef SHIBSP_LITE -QName shibsp::StringAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _StringAttributeDecoder); -QName shibsp::ScopedAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _ScopedAttributeDecoder); -QName shibsp::NameIDAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _NameIDAttributeDecoder); -QName shibsp::NameIDFromScopedAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _NameIDFromScopedAttributeDecoder); +xmltooling::QName shibsp::StringAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _StringAttributeDecoder); +xmltooling::QName shibsp::ScopedAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _ScopedAttributeDecoder); +xmltooling::QName shibsp::NameIDAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _NameIDAttributeDecoder); +xmltooling::QName shibsp::NameIDFromScopedAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _NameIDFromScopedAttributeDecoder); +xmltooling::QName shibsp::KeyInfoAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _KeyInfoAttributeDecoder); +xmltooling::QName shibsp::DOMAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _DOMAttributeDecoder); +xmltooling::QName shibsp::XMLAttributeDecoderType(shibspconstants::SHIB2ATTRIBUTEMAP_NS, _XMLAttributeDecoder); void shibsp::registerAttributeDecoders() { @@ -78,14 +99,21 @@ void shibsp::registerAttributeDecoders() conf.AttributeDecoderManager.registerFactory(ScopedAttributeDecoderType, ScopedAttributeDecoderFactory); conf.AttributeDecoderManager.registerFactory(NameIDAttributeDecoderType, NameIDAttributeDecoderFactory); conf.AttributeDecoderManager.registerFactory(NameIDFromScopedAttributeDecoderType, NameIDFromScopedAttributeDecoderFactory); + conf.AttributeDecoderManager.registerFactory(KeyInfoAttributeDecoderType, KeyInfoAttributeDecoderFactory); + conf.AttributeDecoderManager.registerFactory(DOMAttributeDecoderType, DOMAttributeDecoderFactory); + conf.AttributeDecoderManager.registerFactory(XMLAttributeDecoderType, XMLAttributeDecoderFactory); } -AttributeDecoder::AttributeDecoder(const DOMElement *e) : m_caseSensitive(true) +AttributeDecoder::AttributeDecoder(const DOMElement *e) : m_caseSensitive(true), m_internal(false) { if (e) { - const XMLCh* flag = e->getAttributeNS(NULL,caseSensitive); + const XMLCh* flag = e->getAttributeNS(NULL, caseSensitive); if (flag && (*flag == chLatin_f || *flag == chDigit_0)) m_caseSensitive = false; + + flag = e->getAttributeNS(NULL, internal); + if (flag && (*flag == chLatin_t || *flag == chDigit_1)) + m_internal = true; } } #endif @@ -96,14 +124,76 @@ void shibsp::registerAttributeFactories() Attribute::registerFactory("Simple", SimpleAttributeFactory); Attribute::registerFactory("Scoped", ScopedAttributeFactory); Attribute::registerFactory("NameID", NameIDAttributeFactory); + Attribute::registerFactory("Extensible", ExtensibleAttributeFactory); + Attribute::registerFactory("XML", XMLAttributeFactory); +} + +map Attribute::m_factoryMap; + +Attribute::Attribute(DDF& in) : m_caseSensitive(in["case_insensitive"].isnull()), m_internal(!in["internal"].isnull()) +{ + const char* id = in.first().name(); + if (id && *id) + m_id.push_back(id); + else + throw AttributeException("No id found in marshalled attribute content."); + DDF aliases = in["aliases"]; + if (aliases.islist()) { + DDF alias = aliases.first(); + while (alias.isstring()) { + m_id.push_back(alias.string()); + alias = aliases.next(); + } + } } -std::map Attribute::m_factoryMap; +DDF Attribute::marshall() const +{ + DDF ddf(NULL); + ddf.structure().addmember(m_id.front().c_str()).list(); + if (!m_caseSensitive) + ddf.addmember("case_insensitive"); + if (m_internal) + ddf.addmember("internal"); + if (m_id.size() > 1) { + DDF alias; + DDF aliases = ddf.addmember("aliases").list(); + for (std::vector::const_iterator a = m_id.begin() + 1; a != m_id.end(); ++a) { + alias = DDF(NULL).string(a->c_str()); + aliases.add(alias); + } + } + return ddf; +} Attribute* Attribute::unmarshall(DDF& in) { map::const_iterator i = m_factoryMap.find(in.name() ? in.name() : ""); if (i == m_factoryMap.end()) - throw AttributeException("No registered factory for Attribute of type ($1).", xmltooling::params(1,in.name())); + throw AttributeException("No registered factory for Attribute of type ($1).", params(1,in.name())); return (i->second)(in); } + +const vector& XMLAttribute::getSerializedValues() const +{ + xsecsize_t len; + XMLByte *pos, *pos2; + if (m_serialized.empty()) { + for (vector::const_iterator i=m_values.begin(); i!=m_values.end(); ++i) { + XMLByte* enc = Base64::encode(reinterpret_cast(i->data()), i->size(), &len); + if (enc) { + for (pos=enc, pos2=enc; *pos2; pos2++) + if (isgraph(*pos2)) + *pos++=*pos2; + *pos=0; + m_serialized.push_back(reinterpret_cast(enc)); +#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE + XMLString::release(&enc); +#else + XMLString::release((char**)&enc); +#endif + } + } + } + return Attribute::getSerializedValues(); +} diff --git a/shibsp/attribute/Attribute.h b/shibsp/attribute/Attribute.h index 1219681..37e1d39 100644 --- a/shibsp/attribute/Attribute.h +++ b/shibsp/attribute/Attribute.h @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * @file shibsp/attribute/Attribute.h - * + * * A resolved attribute. */ @@ -39,16 +39,16 @@ namespace shibsp { /** * A resolved attribute. - * + * *

Resolved attributes are a neutral construct that represent both simple and * complex attribute data structures that might be found in SAML assertions * or obtained from other sources. - * + * *

Attributes consist of an id/name that is locally unique (that is, unique to a * configuration at any given point in time) and zero or more values. Values can * be of any type or structure, but will generally be made available to applications * only if a serialized string form exists. More complex values can be used with - * access control plugins that understand them, however. + * access control plugins and other components that understand them, however. */ class SHIBSP_API Attribute { @@ -56,50 +56,36 @@ namespace shibsp { protected: /** * Constructor - * + * * @param ids array with primary identifier in first position, followed by any aliases */ - Attribute(const std::vector& ids) : m_id(ids), m_caseSensitive(true) { + Attribute(const std::vector& ids) : m_id(ids), m_caseSensitive(true), m_internal(false) { } /** * Constructs based on a remoted Attribute. - * + * *

This allows Attribute objects to be recreated after marshalling. * The DDF supplied must be a struct containing a single list member named * with the Attribute's "id" and containing the values. - * + * * @param in input object containing marshalled Attribute */ - Attribute(DDF& in) : m_caseSensitive(in["case_insensitive"].isnull()) { - const char* id = in.first().name(); - if (id && *id) - m_id.push_back(id); - else - throw AttributeException("No id found in marshalled attribute content."); - DDF aliases = in["aliases"]; - if (aliases.islist()) { - DDF alias = aliases.first(); - while (alias.isstring()) { - m_id.push_back(alias.string()); - alias = aliases.next(); - } - } - } - + Attribute(DDF& in); + /** * Maintains a copy of serialized attribute values, when possible. - * + * *

Implementations should maintain the array when values are added or removed. */ mutable std::vector m_serialized; public: virtual ~Attribute() {} - + /** * Returns the Attribute identifier. - * + * * @return the Attribute identifier */ const char* getId() const { @@ -134,6 +120,15 @@ namespace shibsp { } /** + * Sets whether the attribute should be exported for CGI use. + * + * @param export true iff the attribute should NOT be exported + */ + void setInternal(bool internal) { + m_internal = internal; + } + + /** * Indicates whether case sensitivity should apply to basic value comparisons. * * @return true iff value comparisons should be case sensitive @@ -141,31 +136,40 @@ namespace shibsp { bool isCaseSensitive() const { return m_caseSensitive; } - + + /** + * Indicates whether the attribute should be exported for CGI use. + * + * @return true iff the attribute should NOT be exported + */ + bool isInternal() const { + return m_internal; + } + /** * Returns the number of values. - * + * * @return number of values */ virtual size_t valueCount() const { return m_serialized.size(); } - + /** * Returns serialized Attribute values encoded as UTF-8 strings. - * + * * @return an immutable vector of values */ virtual const std::vector& getSerializedValues() const { return m_serialized; } - + /** * Informs the Attribute that values have changed and any serializations - * must be cleared. + * must be cleared. */ virtual void clearSerializedValues()=0; - + /** * Gets the string equivalent of the value at the specified position (starting from zero). * @@ -198,54 +202,40 @@ namespace shibsp { /** * Marshalls an Attribute for remoting. - * + * *

This allows Attribute objects to be communicated across process boundaries * without excess XML parsing. The DDF returned must be a struct containing * a single list member named with the Attribute's "id". The name of the struct * should contain the registered name of the Attribute implementation. */ - virtual DDF marshall() const { - DDF ddf(NULL); - ddf.structure().addmember(m_id.front().c_str()).list(); - if (!m_caseSensitive) - ddf.addmember("case_insensitive"); - if (m_id.size() > 1) { - DDF alias; - DDF aliases = ddf.addmember("aliases").list(); - for (std::vector::const_iterator a = m_id.begin() + 1; a != m_id.end(); ++a) { - alias = DDF(NULL).string(a->c_str()); - aliases.add(alias); - } - } - return ddf; - } - + virtual DDF marshall() const; + /** * Unmarshalls a remoted Attribute. - * + * * @param in remoted Attribute data - * @return a resolved Attribute of the proper subclass + * @return a resolved Attribute of the proper subclass */ static Attribute* unmarshall(DDF& in); - + /** A function that unmarshalls remoted data into the proper Attribute subclass. */ typedef Attribute* AttributeFactory(DDF& in); /** * Registers an AttributeFactory function for a given attribute "type". - * + * * @param type string used at the root of remoted Attribute structures * @param factory factory function - */ + */ static void registerFactory(const char* type, AttributeFactory* factory) { m_factoryMap[type] = factory; } /** * Deregisters an AttributeFactory function for a given attribute "type". - * + * * @param type string used at the root of remoted Attribute structures - */ + */ static void deregisterFactory(const char* type) { m_factoryMap.erase(type); } @@ -256,11 +246,11 @@ namespace shibsp { static void deregisterFactories() { m_factoryMap.clear(); } - + private: static std::map m_factoryMap; std::vector m_id; - bool m_caseSensitive; + bool m_caseSensitive,m_internal; }; #if defined (_MSC_VER) @@ -269,7 +259,7 @@ namespace shibsp { /** Registers built-in Attribute types into the runtime. */ void registerAttributeFactories(); - + }; #endif /* __shibsp_attribute_h__ */ diff --git a/shibsp/attribute/AttributeDecoder.h b/shibsp/attribute/AttributeDecoder.h index 7063ceb..5b0ad48 100644 --- a/shibsp/attribute/AttributeDecoder.h +++ b/shibsp/attribute/AttributeDecoder.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -45,6 +45,21 @@ namespace shibsp { /** Flag for case sensitivity of decoded attributes. */ bool m_caseSensitive; + /** Flag for hiding attributes from CGI export. */ + bool m_internal; + + /** + * Helper method to handle base class decoding housekeeping. + * + * @param attr the new Attribute object being created + * @return the attr parameter + */ + virtual Attribute* _decode(Attribute* attr) const { + attr->setCaseSensitive(m_caseSensitive); + attr->setInternal(m_internal); + return attr; + } + public: virtual ~AttributeDecoder() {} @@ -78,6 +93,15 @@ namespace shibsp { /** Decodes scoped attributes into a NameIDAttribute. */ extern SHIBSP_API xmltooling::QName NameIDFromScopedAttributeDecoderType; + /** Decodes KeyInfo information into a SimpleAttribute. */ + extern SHIBSP_API xmltooling::QName KeyInfoAttributeDecoderType; + + /** Decodes arbitrary DOM information into an ExtensibleAttribute. */ + extern SHIBSP_API xmltooling::QName DOMAttributeDecoderType; + + /** Decodes arbitrary XML into an XMLAttribute. */ + extern SHIBSP_API xmltooling::QName XMLAttributeDecoderType; + /** Registers built-in AttributeDecoders into the runtime. */ void registerAttributeDecoders(); }; diff --git a/shibsp/attribute/DOMAttributeDecoder.cpp b/shibsp/attribute/DOMAttributeDecoder.cpp new file mode 100644 index 0000000..98cb87c --- /dev/null +++ b/shibsp/attribute/DOMAttributeDecoder.cpp @@ -0,0 +1,226 @@ +/* + * Copyright 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. + */ + +/** + * DOMAttributeDecoder.cpp + * + * Decodes a DOM into an ExtensibleAttribute. + */ + +#include "internal.h" +#include "attribute/AttributeDecoder.h" +#include "attribute/ExtensibleAttribute.h" + +#include +#include +#include + +using namespace shibsp; +using namespace opensaml; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + class SHIBSP_DLLLOCAL DOMAttributeDecoder : virtual public AttributeDecoder + { + public: + DOMAttributeDecoder(const DOMElement* e); + ~DOMAttributeDecoder() {} + + Attribute* decode( + const vector& ids, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL + ) const; + + private: + DDF convert(DOMElement* e, bool nameit=true) const; + auto_ptr_char m_formatter; + map,string> m_tagMap; + }; + + AttributeDecoder* SHIBSP_DLLLOCAL DOMAttributeDecoderFactory(const DOMElement* const & e) + { + return new DOMAttributeDecoder(e); + } + + static const XMLCh Mapping[] = UNICODE_LITERAL_7(M,a,p,p,i,n,g); + static const XMLCh _from[] = UNICODE_LITERAL_4(f,r,o,m); + static const XMLCh _to[] = UNICODE_LITERAL_2(t,o); + static const XMLCh formatter[] = UNICODE_LITERAL_9(f,o,r,m,a,t,t,e,r); +}; + +DOMAttributeDecoder::DOMAttributeDecoder(const DOMElement* e) + : AttributeDecoder(e), m_formatter(e ? e->getAttributeNS(NULL,formatter) : NULL) +{ + Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder.DOM"); + + e = e ? XMLHelper::getFirstChildElement(e, Mapping) : NULL; + while (e) { + if (e->hasAttributeNS(NULL, _from) && e->hasAttributeNS(NULL, _to)) { + auto_ptr f(XMLHelper::getNodeValueAsQName(e->getAttributeNodeNS(NULL, _from))); + auto_ptr_char t(e->getAttributeNS(NULL, _to)); + if (f.get() && t.get() && *t.get()) { + if (log.isDebugEnabled()) + log.debug("mapping (%s) to (%s)", f->toString().c_str(), t.get()); + m_tagMap.insert( + pair< const pair,string>( + pair(f->getLocalPart(), f->hasNamespaceURI() ? f->getNamespaceURI() : &chNull), + t.get() + ) + ); + } + } + e = XMLHelper::getNextSiblingElement(e, Mapping); + } +} + +Attribute* DOMAttributeDecoder::decode( + const vector& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty + ) const +{ + Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder.DOM"); + + if (!xmlObject || !XMLString::equals(saml1::Attribute::LOCAL_NAME, xmlObject->getElementQName().getLocalPart())) { + log.warn("XMLObject type not recognized by DOMAttributeDecoder, no values returned"); + return NULL; + } + + auto_ptr attr(new ExtensibleAttribute(ids, m_formatter.get())); + DDF dest = attr->getValues(); + vector::const_iterator v,stop; + + const saml2::Attribute* saml2attr = dynamic_cast(xmlObject); + if (saml2attr) { + const vector& values = saml2attr->getAttributeValues(); + v = values.begin(); + stop = values.end(); + if (log.isDebugEnabled()) { + auto_ptr_char n(saml2attr->getName()); + log.debug( + "decoding ExtensibleAttribute (%s) from SAML 2 Attribute (%s) with %lu value(s)", + ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size() + ); + } + } + else { + const saml1::Attribute* saml1attr = dynamic_cast(xmlObject); + if (saml1attr) { + const vector& values = saml1attr->getAttributeValues(); + v = values.begin(); + stop = values.end(); + if (log.isDebugEnabled()) { + auto_ptr_char n(saml1attr->getAttributeName()); + log.debug( + "decoding ExtensibleAttribute (%s) from SAML 1 Attribute (%s) with %lu value(s)", + ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size() + ); + } + } + else { + log.warn("XMLObject type not recognized by DOMAttributeDecoder, no values returned"); + return NULL; + } + } + + for (; v!=stop; ++v) { + DOMElement* e = (*v)->getDOM(); + if (e) { + DDF converted = convert(e, false); + if (!converted.isnull()) + dest.add(converted); + } + else + log.warn("skipping AttributeValue without a backing DOM"); + } + + return dest.integer() ? _decode(attr.release()) : NULL; +} + +DDF DOMAttributeDecoder::convert(DOMElement* e, bool nameit) const +{ + const XMLCh* nsURI; + const XMLCh* local; + map,string>::const_iterator mapping; + DDF obj = DDF(NULL).structure(); + + if (nameit) { + // Name this structure. + nsURI = e->getNamespaceURI(); + local = e->getLocalName(); + mapping = m_tagMap.find(pair(local,nsURI)); + if (mapping == m_tagMap.end()) { + auto_ptr_char temp(local); + obj.name(temp.get()); + } + else { + obj.name(mapping->second.c_str()); + } + } + + // Process non-xmlns attributes. + DOMNamedNodeMap* attrs = e->getAttributes(); + for (XMLSize_t a = attrs->getLength(); a > 0; --a) { + DOMNode* attr = attrs->item(a-1); + nsURI = attr->getNamespaceURI(); + if (XMLString::equals(nsURI, xmlconstants::XMLNS_NS)) + continue; + local = attr->getLocalName(); + mapping = m_tagMap.find(pair(local, nsURI ? nsURI : &chNull)); + if (mapping == m_tagMap.end()) { + auto_ptr_char temp(local); + obj.addmember(temp.get()).string(toUTF8(attr->getNodeValue(), true), false); + } + else { + obj.addmember(mapping->second.c_str()).string(toUTF8(attr->getNodeValue(), true), false); + } + } + + DOMElement* child = XMLHelper::getFirstChildElement(e); + if (!child && e->hasChildNodes() && e->getFirstChild()->getNodeType() == DOMNode::TEXT_NODE) { + // Attach a _text member if a text node is present. + obj.addmember("_string").string(toUTF8(e->getFirstChild()->getNodeValue(), true), false); + } + else { + while (child) { + // Convert the child element. + DDF converted = convert(child); + if (!converted.isnull()) { + // Now identify it and attach it. + if (obj[converted.name()].isnull()) { + // We're a new child, so just attach as a structure member. + obj.add(converted); + } + else if (obj[converted.name()].islist()) { + // We're already a repeating child, so add it to the list. + obj[converted.name()].add(converted); + } + else if (obj[converted.name()].isstruct()) { + // This is the complex case where we see a child for the second + // time and have to convert a structure member into a named list. + DDF newlist = DDF(converted.name()).list(); + newlist.add(obj[converted.name()].remove()); + newlist.add(converted); + obj.add(newlist); + } + } + child = XMLHelper::getNextSiblingElement(child); + } + } + + // If we're empty, just delete. + if (obj.integer() == 0) + obj.destroy(); + return obj; +} diff --git a/shibsp/attribute/ExtensibleAttribute.cpp b/shibsp/attribute/ExtensibleAttribute.cpp new file mode 100644 index 0000000..5602c55 --- /dev/null +++ b/shibsp/attribute/ExtensibleAttribute.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 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. + */ + +/** + * shibsp/attribute/ExtensibleAttribute.cpp + * + * An Attribute whose values are arbitrary structures. + */ + +#include "internal.h" +#include "SPConfig.h" +#include "attribute/ExtensibleAttribute.h" +#include "util/SPConstants.h" + +using namespace shibsp; +using namespace xmltooling; +using namespace std; + +const vector& ExtensibleAttribute::getSerializedValues() const +{ + if (m_serialized.empty()) { + const char* formatter = m_obj["_formatter"].string(); + if (formatter) { + string msg = formatter; + DDF val = m_obj.first().first(); + while (!val.isnull()) { + + static const char* legal="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_.[]"; + + m_serialized.push_back(string()); + string& processed = m_serialized.back(); + + string::size_type i=0,start=0; + while (start!=string::npos && startstart) + processed += msg.substr(start,i-start); // append everything in between + start=i+1; // move start to the beginning of the token name + i=msg.find_first_not_of(legal,start); // find token delimiter + if (i==start) { // append a non legal character + processed+=msg[start++]; + continue; + } + + string tag = msg.substr(start,(i==string::npos) ? i : i-start); + if (tag == "_string" && val.string()) { + processed += val.string(); + start=i; + } + else { + DDF child = val.getmember(tag.c_str()); + if (child.string()) + processed += child.string(); + else if (child.isstruct() && child["_string"].string()) + processed += child["_string"].string(); + start=i; + } + } + if (start!=string::npos && start +#include + +namespace shibsp { + +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4251 ) +#endif + + /** + * An Attribute whose values are arbitrary structures. + */ + class SHIBSP_API ExtensibleAttribute : public Attribute + { + public: + /** + * Constructor. + * + * @param ids array with primary identifier in first position, followed by any aliases + * @param formatter template for serialization of values + */ + ExtensibleAttribute(const std::vector& ids, const char* formatter) : Attribute(ids) { + m_obj = Attribute::marshall(); + m_obj.name("Extensible"); + m_obj.addmember("_formatter").string(formatter); + } + + /** + * Constructs based on a remoted ExtensibleAttribute. + * + * @param in input object containing marshalled ExtensibleAttribute + */ + ExtensibleAttribute(DDF& in) : Attribute(in), m_obj(in.copy()) { + } + + virtual ~ExtensibleAttribute() { + m_obj.destroy(); + } + + /** + * Returns the set of values in a DDF list. + * + * @return a mutable list object containing the values + */ + DDF getValues() { + return m_obj.first(); + } + + size_t valueCount() const { + return m_obj.first().integer(); + } + + void clearSerializedValues() { + m_serialized.clear(); + } + + const char* getString(size_t index) const { + return m_obj.first()[static_cast(index)].string(); + } + + const char* getScope(size_t index) const { + return NULL; + } + + void removeValue(size_t index) { + Attribute::removeValue(index); + DDF vals = m_obj.first(); + if (index < static_cast(vals.integer())) + vals[static_cast(index)].remove().destroy(); + } + + const std::vector& getSerializedValues() const; + + DDF marshall() const { + if (!isCaseSensitive()) + m_obj.addmember("case_insensitive"); + if (isInternal()) + m_obj.addmember("internal"); + return m_obj.copy(); + } + + private: + mutable DDF m_obj; + }; + +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + +}; + +#endif /* __shibsp_nameidattr_h__ */ diff --git a/shibsp/attribute/KeyInfoAttributeDecoder.cpp b/shibsp/attribute/KeyInfoAttributeDecoder.cpp new file mode 100644 index 0000000..3ab80f2 --- /dev/null +++ b/shibsp/attribute/KeyInfoAttributeDecoder.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 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. + */ + +/** + * KeyInfoAttributeDecoder.cpp + * + * Decodes KeyInfo information into a SimpleAttribute. + */ + +#include "internal.h" +#include "attribute/AttributeDecoder.h" +#include "attribute/SimpleAttribute.h" + +#include +#include +#include +#include + +using namespace shibsp; +using namespace opensaml; +using namespace xmlsignature; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + class SHIBSP_DLLLOCAL KeyInfoAttributeDecoder : virtual public AttributeDecoder + { + public: + KeyInfoAttributeDecoder(const DOMElement* e); + ~KeyInfoAttributeDecoder() { + delete m_keyInfoResolver; + } + + Attribute* decode( + const vector& ids, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL + ) const; + + private: + void extract(const KeyInfo* k, vector& dest) const { + auto_ptr cred (getKeyInfoResolver()->resolve(k, Credential::RESOLVE_KEYS)); + if (cred.get()) { + dest.push_back(string()); + dest.back() = SecurityHelper::getDEREncoding(*cred.get(), m_hash); + if (dest.back().empty()) + dest.pop_back(); + } + } + + const KeyInfoResolver* getKeyInfoResolver() const { + return m_keyInfoResolver ? m_keyInfoResolver : XMLToolingConfig::getConfig().getKeyInfoResolver(); + } + + bool m_hash; + KeyInfoResolver* m_keyInfoResolver; + }; + + AttributeDecoder* SHIBSP_DLLLOCAL KeyInfoAttributeDecoderFactory(const DOMElement* const & e) + { + return new KeyInfoAttributeDecoder(e); + } + + static const XMLCh _KeyInfoResolver[] = UNICODE_LITERAL_15(K,e,y,I,n,f,o,R,e,s,o,l,v,e,r); + static const XMLCh _hash[] = UNICODE_LITERAL_4(h,a,s,h); + static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e); +}; + +KeyInfoAttributeDecoder::KeyInfoAttributeDecoder(const DOMElement* e) : AttributeDecoder(e), m_hash(false), m_keyInfoResolver(NULL) { + const XMLCh* flag = e ? e->getAttributeNS(NULL, _hash) : NULL; + m_hash = (flag && (*flag == chLatin_t || *flag == chDigit_1)); + e = e ? XMLHelper::getFirstChildElement(e,_KeyInfoResolver) : NULL; + if (e) { + auto_ptr_char t(e->getAttributeNS(NULL, _type)); + if (t.get() && *t.get()) + m_keyInfoResolver = XMLToolingConfig::getConfig().KeyInfoResolverManager.newPlugin(t.get(), e); + else + throw UnknownExtensionException(" element found with no type attribute"); + } +} + +Attribute* KeyInfoAttributeDecoder::decode( + const vector& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty + ) const +{ + Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder.KeyInfo"); + + if (!xmlObject || !XMLString::equals(saml1::Attribute::LOCAL_NAME, xmlObject->getElementQName().getLocalPart())) { + log.warn("XMLObject type not recognized by KeyInfoAttributeDecoder, no values returned"); + return NULL; + } + + auto_ptr attr(new SimpleAttribute(ids)); + vector& dest = attr->getValues(); + vector::const_iterator v,stop; + + const saml2::Attribute* saml2attr = dynamic_cast(xmlObject); + if (saml2attr) { + const vector& values = saml2attr->getAttributeValues(); + v = values.begin(); + stop = values.end(); + if (log.isDebugEnabled()) { + auto_ptr_char n(saml2attr->getName()); + log.debug( + "decoding KeyInfo information (%s) from SAML 2 Attribute (%s) with %lu value(s)", + ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size() + ); + } + } + else { + const saml1::Attribute* saml1attr = dynamic_cast(xmlObject); + if (saml1attr) { + const vector& values = saml1attr->getAttributeValues(); + v = values.begin(); + stop = values.end(); + if (log.isDebugEnabled()) { + auto_ptr_char n(saml1attr->getAttributeName()); + log.debug( + "decoding KeyInfo information (%s) from SAML 1 Attribute (%s) with %lu value(s)", + ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size() + ); + } + } + else { + log.warn("XMLObject type not recognized by KeyInfoAttributeDecoder, no values returned"); + return NULL; + } + } + + for (; v!=stop; ++v) { + const KeyInfo* k = dynamic_cast(*v); + if (k) + extract(k, dest); + else if ((*v)->hasChildren()) { + const list& children = (*v)->getOrderedChildren(); + for (list::const_iterator vv = children.begin(); vv!=children.end(); ++vv) { + if (k=dynamic_cast(*vv)) + extract(k, dest); + else + log.warn("skipping AttributeValue without a recognizable KeyInfo"); + } + } + } + + return dest.empty() ? NULL : _decode(attr.release()); +} diff --git a/shibsp/attribute/NameIDAttribute.h b/shibsp/attribute/NameIDAttribute.h index bd292ce..1b8d247 100644 --- a/shibsp/attribute/NameIDAttribute.h +++ b/shibsp/attribute/NameIDAttribute.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -17,7 +17,7 @@ /** * @file shibsp/attribute/NameIDAttribute.h * - * An Attribute whose values are relations of a value and a scope. + * An Attribute whose values are derived from or mappable to a SAML NameID. */ #ifndef __shibsp_nameidattr_h__ @@ -102,14 +102,21 @@ namespace shibsp { /** * Returns the set of values encoded as UTF-8 strings. * - *

Each compound value is a pair containing the simple value and the scope. - * * @return a mutable vector of the values */ std::vector& getValues() { return m_values; } - + + /** + * Returns the set of values encoded as UTF-8 strings. + * + * @return an immutable vector of the values + */ + const std::vector& getValues() const { + return m_values; + } + size_t valueCount() const { return m_values.size(); } diff --git a/shibsp/attribute/NameIDAttributeDecoder.cpp b/shibsp/attribute/NameIDAttributeDecoder.cpp index 440fa57..903bc33 100644 --- a/shibsp/attribute/NameIDAttributeDecoder.cpp +++ b/shibsp/attribute/NameIDAttributeDecoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -34,12 +34,18 @@ using namespace xmltooling; using namespace std; namespace shibsp { - static XMLCh formatter[] = UNICODE_LITERAL_9(f,o,r,m,a,t,t,e,r); + static const XMLCh formatter[] = UNICODE_LITERAL_9(f,o,r,m,a,t,t,e,r); + static const XMLCh defaultQualifiers[] = UNICODE_LITERAL_17(d,e,f,a,u,l,t,Q,u,a,l,i,f,i,e,r,s); class SHIBSP_DLLLOCAL NameIDAttributeDecoder : virtual public AttributeDecoder { public: - NameIDAttributeDecoder(const DOMElement* e) : AttributeDecoder(e), m_formatter(e ? e->getAttributeNS(NULL,formatter) : NULL) {} + NameIDAttributeDecoder(const DOMElement* e) + : AttributeDecoder(e), m_formatter(e ? e->getAttributeNS(NULL, formatter) : NULL), m_defaultQualifiers(false) { + const XMLCh* flag = e ? e->getAttributeNS(NULL, defaultQualifiers) : NULL; + if (flag && (*flag == chLatin_t || *flag == chDigit_1)) + m_defaultQualifiers = true; + } ~NameIDAttributeDecoder() {} shibsp::Attribute* decode( @@ -54,6 +60,7 @@ namespace shibsp { const NameIdentifier* n, vector& dest, const char* assertingParty, const char* relyingParty ) const; auto_ptr_char m_formatter; + bool m_defaultQualifiers; }; AttributeDecoder* SHIBSP_DLLLOCAL NameIDAttributeDecoderFactory(const DOMElement* const & e) @@ -69,7 +76,6 @@ shibsp::Attribute* NameIDAttributeDecoder::decode( auto_ptr nameid( new NameIDAttribute(ids, (m_formatter.get() && *m_formatter.get()) ? m_formatter.get() : DEFAULT_NAMEID_FORMATTER) ); - nameid->setCaseSensitive(m_caseSensitive); vector& dest = nameid->getValues(); vector::const_iterator v,stop; @@ -131,7 +137,7 @@ shibsp::Attribute* NameIDAttributeDecoder::decode( } } - return dest.empty() ? NULL : nameid.release(); + return dest.empty() ? NULL : _decode(nameid.release()); } const NameIDType* saml2name = dynamic_cast(xmlObject); @@ -160,7 +166,7 @@ shibsp::Attribute* NameIDAttributeDecoder::decode( } } - return dest.empty() ? NULL : nameid.release(); + return dest.empty() ? NULL : _decode(nameid.release()); } void NameIDAttributeDecoder::extract( @@ -181,14 +187,14 @@ void NameIDAttributeDecoder::extract( str = toUTF8(n->getNameQualifier()); if (str && *str) val.m_NameQualifier = str; - else if (assertingParty) + else if (m_defaultQualifiers && assertingParty) val.m_NameQualifier = assertingParty; delete[] str; str = toUTF8(n->getSPNameQualifier()); if (str && *str) val.m_SPNameQualifier = str; - else if (relyingParty) + else if (m_defaultQualifiers && relyingParty) val.m_SPNameQualifier = relyingParty; delete[] str; @@ -218,11 +224,11 @@ void NameIDAttributeDecoder::extract( str = toUTF8(n->getNameQualifier()); if (str && *str) val.m_NameQualifier = str; - else if (assertingParty) + else if (m_defaultQualifiers && assertingParty) val.m_NameQualifier = assertingParty; delete[] str; - if (relyingParty) + if (m_defaultQualifiers && relyingParty) val.m_SPNameQualifier = relyingParty; } } diff --git a/shibsp/attribute/NameIDFromScopedAttributeDecoder.cpp b/shibsp/attribute/NameIDFromScopedAttributeDecoder.cpp index 7e807b9..ab72dfb 100644 --- a/shibsp/attribute/NameIDFromScopedAttributeDecoder.cpp +++ b/shibsp/attribute/NameIDFromScopedAttributeDecoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -34,20 +34,28 @@ using namespace xmltooling; using namespace std; namespace shibsp { - static XMLCh format[] = UNICODE_LITERAL_6(f,o,r,m,a,t); - static XMLCh formatter[] = UNICODE_LITERAL_9(f,o,r,m,a,t,t,e,r); + static const XMLCh defaultQualifiers[] =UNICODE_LITERAL_17(d,e,f,a,u,l,t,Q,u,a,l,i,f,i,e,r,s); + static const XMLCh format[] = UNICODE_LITERAL_6(f,o,r,m,a,t); + static const XMLCh formatter[] = UNICODE_LITERAL_9(f,o,r,m,a,t,t,e,r); static const XMLCh Scope[] = UNICODE_LITERAL_5(S,c,o,p,e); static const XMLCh scopeDelimeter[] = UNICODE_LITERAL_14(s,c,o,p,e,D,e,l,i,m,e,t,e,r); class SHIBSP_DLLLOCAL NameIDFromScopedAttributeDecoder : virtual public AttributeDecoder { public: - NameIDFromScopedAttributeDecoder(const DOMElement* e) : AttributeDecoder(e), m_delimeter('@'), - m_format(e ? e->getAttributeNS(NULL,format) : NULL), m_formatter(e ? e->getAttributeNS(NULL,formatter) : NULL) { + NameIDFromScopedAttributeDecoder(const DOMElement* e) + : AttributeDecoder(e), + m_delimeter('@'), + m_format(e ? e->getAttributeNS(NULL,format) : NULL), + m_formatter(e ? e->getAttributeNS(NULL,formatter) : NULL), + m_defaultQualifiers(false) { if (e && e->hasAttributeNS(NULL,scopeDelimeter)) { auto_ptr_char d(e->getAttributeNS(NULL,scopeDelimeter)); m_delimeter = *(d.get()); } + const XMLCh* flag = e ? e->getAttributeNS(NULL, defaultQualifiers) : NULL; + if (flag && (*flag == chLatin_t || *flag == chDigit_1)) + m_defaultQualifiers = true; } ~NameIDFromScopedAttributeDecoder() {} @@ -59,6 +67,7 @@ namespace shibsp { char m_delimeter; auto_ptr_char m_format; auto_ptr_char m_formatter; + bool m_defaultQualifiers; }; AttributeDecoder* SHIBSP_DLLLOCAL NameIDFromScopedAttributeDecoderFactory(const DOMElement* const & e) @@ -75,11 +84,10 @@ shibsp::Attribute* NameIDFromScopedAttributeDecoder::decode( char* val; char* scope; const XMLCh* xmlscope; - QName scopeqname(NULL,Scope); + xmltooling::QName scopeqname(NULL,Scope); auto_ptr nameid( new NameIDAttribute(ids, (m_formatter.get() && *m_formatter.get()) ? m_formatter.get() : DEFAULT_NAMEID_FORMATTER) ); - nameid->setCaseSensitive(m_caseSensitive); vector& dest = nameid->getValues(); vector::const_iterator v,stop; @@ -135,9 +143,9 @@ shibsp::Attribute* NameIDFromScopedAttributeDecoder::decode( destval.m_Name = val; if (m_format.get() && *m_format.get()) destval.m_Format = m_format.get(); - if (assertingParty) + if (m_defaultQualifiers && assertingParty) destval.m_NameQualifier = assertingParty; - if (relyingParty) + if (m_defaultQualifiers && relyingParty) destval.m_SPNameQualifier = relyingParty; } else { @@ -150,7 +158,7 @@ shibsp::Attribute* NameIDFromScopedAttributeDecoder::decode( } } - return dest.empty() ? NULL : nameid.release(); + return dest.empty() ? NULL : _decode(nameid.release()); } log.warn("XMLObject type not recognized by NameIDFromScopedAttributeDecoder, no values returned"); diff --git a/shibsp/attribute/ScopedAttribute.h b/shibsp/attribute/ScopedAttribute.h index d72cd0c..a8acac5 100644 --- a/shibsp/attribute/ScopedAttribute.h +++ b/shibsp/attribute/ScopedAttribute.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -84,6 +84,17 @@ namespace shibsp { return m_values; } + /** + * Returns the set of values encoded as UTF-8 strings. + * + *

Each compound value is a pair containing the simple value and the scope. + * + * @return an immutable vector of the values + */ + const std::vector< std::pair >& getValues() const { + return m_values; + } + size_t valueCount() const { return m_values.size(); } diff --git a/shibsp/attribute/ScopedAttributeDecoder.cpp b/shibsp/attribute/ScopedAttributeDecoder.cpp index 2044d6b..c3054c8 100644 --- a/shibsp/attribute/ScopedAttributeDecoder.cpp +++ b/shibsp/attribute/ScopedAttributeDecoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -69,9 +69,8 @@ shibsp::Attribute* ScopedAttributeDecoder::decode( char* val; char* scope; const XMLCh* xmlscope; - QName scopeqname(NULL,Scope); + xmltooling::QName scopeqname(NULL,Scope); auto_ptr scoped(new ScopedAttribute(ids, m_delimeter)); - scoped->setCaseSensitive(m_caseSensitive); vector< pair >& dest = scoped->getValues(); vector::const_iterator v,stop; @@ -146,7 +145,7 @@ shibsp::Attribute* ScopedAttributeDecoder::decode( } } - return dest.empty() ? NULL : scoped.release(); + return dest.empty() ? NULL : _decode(scoped.release()); } const NameID* saml2name = dynamic_cast(xmlObject); @@ -192,5 +191,5 @@ shibsp::Attribute* ScopedAttributeDecoder::decode( log.warn("ignoring empty NameID"); } delete[] val; - return dest.empty() ? NULL : scoped.release(); + return dest.empty() ? NULL : _decode(scoped.release()); } diff --git a/shibsp/attribute/StringAttributeDecoder.cpp b/shibsp/attribute/StringAttributeDecoder.cpp index ca86390..f2a9d6f 100644 --- a/shibsp/attribute/StringAttributeDecoder.cpp +++ b/shibsp/attribute/StringAttributeDecoder.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -57,7 +57,6 @@ shibsp::Attribute* StringAttributeDecoder::decode( { char* val; auto_ptr simple(new SimpleAttribute(ids)); - simple->setCaseSensitive(m_caseSensitive); vector& dest = simple->getValues(); vector::const_iterator v,stop; @@ -111,7 +110,7 @@ shibsp::Attribute* StringAttributeDecoder::decode( } } - return dest.empty() ? NULL : simple.release(); + return dest.empty() ? NULL : _decode(simple.release()); } const NameID* saml2name = dynamic_cast(xmlObject); @@ -145,5 +144,5 @@ shibsp::Attribute* StringAttributeDecoder::decode( else log.warn("ignoring empty NameID"); delete[] val; - return dest.empty() ? NULL : simple.release(); + return dest.empty() ? NULL : _decode(simple.release()); } diff --git a/shibsp/attribute/XMLAttribute.h b/shibsp/attribute/XMLAttribute.h new file mode 100644 index 0000000..3c9a4f6 --- /dev/null +++ b/shibsp/attribute/XMLAttribute.h @@ -0,0 +1,111 @@ +/* + * Copyright 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. + */ + +/** + * @file shibsp/attribute/XMLAttribute.h + * + * An Attribute whose values are serialized XML. + */ + +#ifndef __shibsp_xmlattr_h__ +#define __shibsp_xmlattr_h__ + +#include + +namespace shibsp { + + /** + * An Attribute whose values are serialized XML. + */ + class SHIBSP_API XMLAttribute : public Attribute + { + public: + /** + * Constructor. + * + * @param ids array with primary identifier in first position, followed by any aliases + */ + XMLAttribute(const std::vector& ids) : Attribute(ids) {} + + /** + * Constructs based on a remoted XMLAttribute. + * + * @param in input object containing marshalled XMLAttribute + */ + XMLAttribute(DDF& in) : Attribute(in) { + DDF val = in.first().first(); + while (val.string()) { + m_values.push_back(val.string()); + val = in.first().next(); + } + } + + virtual ~XMLAttribute() {} + + /** + * Returns the set of values encoded as XML. + * + * @return a mutable vector of the values + */ + std::vector& getValues() { + return m_values; + } + + /** + * Returns the set of values encoded as XML. + * + * @return an immutable vector of the values + */ + const std::vector& getValues() const { + return m_values; + } + + size_t valueCount() const { + return m_values.size(); + } + + void clearSerializedValues() { + m_serialized.clear(); + } + + const char* getString(size_t index) const { + return m_values[index].c_str(); + } + + void removeValue(size_t index) { + Attribute::removeValue(index); + if (index < m_values.size()) + m_values.erase(m_values.begin() + index); + } + + const std::vector& getSerializedValues() const; + + DDF marshall() const { + DDF ddf = Attribute::marshall(); + ddf.name("XML"); + DDF vlist = ddf.first(); + for (std::vector::const_iterator i=m_values.begin(); i!=m_values.end(); ++i) + vlist.add(DDF(NULL).string(i->c_str())); + return ddf; + } + + private: + std::vector m_values; + }; + +}; + +#endif /* __shibsp_xmlattr_h__ */ diff --git a/shibsp/attribute/XMLAttributeDecoder.cpp b/shibsp/attribute/XMLAttributeDecoder.cpp new file mode 100644 index 0000000..2d59528 --- /dev/null +++ b/shibsp/attribute/XMLAttributeDecoder.cpp @@ -0,0 +1,138 @@ +/* + * Copyright 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. + */ + +/** + * XMLAttributeDecoder.cpp + * + * Decodes arbitrary XML into an XMLAttribute. + */ + +#include "internal.h" +#include "attribute/AttributeDecoder.h" +#include "attribute/XMLAttribute.h" + +#include +#include +#include + +using namespace shibsp; +using namespace opensaml; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + class SHIBSP_DLLLOCAL XMLAttributeDecoder : virtual public AttributeDecoder + { + public: + XMLAttributeDecoder(const DOMElement* e) : AttributeDecoder(e) {} + ~XMLAttributeDecoder() {} + + Attribute* decode( + const vector& ids, const XMLObject* xmlObject, const char* assertingParty=NULL, const char* relyingParty=NULL + ) const; + + private: + DDF convert(DOMElement* e, bool nameit=true) const; + auto_ptr_char m_formatter; + map,string> m_tagMap; + }; + + AttributeDecoder* SHIBSP_DLLLOCAL XMLAttributeDecoderFactory(const DOMElement* const & e) + { + return new XMLAttributeDecoder(e); + } +}; + + +Attribute* XMLAttributeDecoder::decode( + const vector& ids, const XMLObject* xmlObject, const char* assertingParty, const char* relyingParty + ) const +{ + if (!xmlObject) + return NULL; + + Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder.XML"); + + auto_ptr attr(new XMLAttribute(ids)); + vector& dest = attr->getValues(); + + // Handle any non-Attribute object directly. + if (!xmlObject || !XMLString::equals(saml1::Attribute::LOCAL_NAME, xmlObject->getElementQName().getLocalPart())) { + DOMElement* e = xmlObject->getDOM(); + if (e) { + if (log.isDebugEnabled()) { + log.debug( + "decoding XMLAttribute (%s) from XMLObject (%s)", + ids.front().c_str(), + (xmlObject->getSchemaType() ? xmlObject->getSchemaType()->toString() : xmlObject->getElementQName().toString()).c_str() + ); + } + dest.push_back(string()); + XMLHelper::serialize(e, dest.back()); + } + else { + log.warn("skipping XMLObject without a backing DOM"); + } + return dest.empty() ? NULL : _decode(attr.release()); + } + + vector::const_iterator v,stop; + + const saml2::Attribute* saml2attr = dynamic_cast(xmlObject); + if (saml2attr) { + const vector& values = saml2attr->getAttributeValues(); + v = values.begin(); + stop = values.end(); + if (log.isDebugEnabled()) { + auto_ptr_char n(saml2attr->getName()); + log.debug( + "decoding XMLAttribute (%s) from SAML 2 Attribute (%s) with %lu value(s)", + ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size() + ); + } + } + else { + const saml1::Attribute* saml1attr = dynamic_cast(xmlObject); + if (saml1attr) { + const vector& values = saml1attr->getAttributeValues(); + v = values.begin(); + stop = values.end(); + if (log.isDebugEnabled()) { + auto_ptr_char n(saml1attr->getAttributeName()); + log.debug( + "decoding XMLAttribute (%s) from SAML 1 Attribute (%s) with %lu value(s)", + ids.front().c_str(), n.get() ? n.get() : "unnamed", values.size() + ); + } + } + else { + log.warn("XMLObject type not recognized by XMLAttributeDecoder, no values returned"); + return NULL; + } + } + + for (; v!=stop; ++v) { + DOMElement* e = (*v)->getDOM(); + if (e) { + dest.push_back(string()); + XMLHelper::serialize(e, dest.back()); + } + else + log.warn("skipping AttributeValue without a backing DOM"); + } + + return dest.empty() ? NULL : _decode(attr.release()); +} diff --git a/shibsp/attribute/filtering/impl/AndMatchFunctor.cpp b/shibsp/attribute/filtering/impl/AndMatchFunctor.cpp index 857050d..35cea2c 100644 --- a/shibsp/attribute/filtering/impl/AndMatchFunctor.cpp +++ b/shibsp/attribute/filtering/impl/AndMatchFunctor.cpp @@ -109,7 +109,7 @@ MatchFunctor* AndMatchFunctor::buildFunctor(const DOMElement* e, const FilterPol if (*id && functorMap->getMatchFunctors().count(id)) id = ""; - auto_ptr type(XMLHelper::getXSIType(e)); + auto_ptr type(XMLHelper::getXSIType(e)); if (!type.get()) throw ConfigurationException("Child Rule found with no xsi:type."); diff --git a/shibsp/attribute/filtering/impl/MatchFunctor.cpp b/shibsp/attribute/filtering/impl/MatchFunctor.cpp index 06fd7b2..1ba0ec6 100644 --- a/shibsp/attribute/filtering/impl/MatchFunctor.cpp +++ b/shibsp/attribute/filtering/impl/MatchFunctor.cpp @@ -31,13 +31,13 @@ using namespace xmltooling; using namespace std; #define DECL_FACTORY(name) \ - SHIBSP_DLLLOCAL PluginManager< MatchFunctor,QName,pair >::Factory name##Factory + SHIBSP_DLLLOCAL PluginManager< MatchFunctor,xmltooling::QName,pair >::Factory name##Factory #define DECL_BASIC_QNAME(name,lit) \ - QName shibsp::name##Type(shibspconstants::SHIB2ATTRIBUTEFILTER_MF_BASIC_NS, lit) + xmltooling::QName shibsp::name##Type(shibspconstants::SHIB2ATTRIBUTEFILTER_MF_BASIC_NS, lit) #define DECL_SAML_QNAME(name,lit) \ - QName shibsp::name##Type(shibspconstants::SHIB2ATTRIBUTEFILTER_MF_SAML_NS, lit) + xmltooling::QName shibsp::name##Type(shibspconstants::SHIB2ATTRIBUTEFILTER_MF_SAML_NS, lit) #define REGISTER_FACTORY(name) \ mgr.registerFactory(name##Type, name##Factory) @@ -104,7 +104,7 @@ DECL_SAML_QNAME(AttributeScopeMatchesShibMDScope, AttributeScopeMatchesShibMDSco void SHIBSP_API shibsp::registerMatchFunctors() { - PluginManager< MatchFunctor,QName,pair >& mgr = + PluginManager< MatchFunctor,xmltooling::QName,pair >& mgr = SPConfig::getConfig().MatchFunctorManager; REGISTER_FACTORY(AnyMatchFunctor); REGISTER_FACTORY(AndMatchFunctor); diff --git a/shibsp/attribute/filtering/impl/NotMatchFunctor.cpp b/shibsp/attribute/filtering/impl/NotMatchFunctor.cpp index 0f79a84..9a22474 100644 --- a/shibsp/attribute/filtering/impl/NotMatchFunctor.cpp +++ b/shibsp/attribute/filtering/impl/NotMatchFunctor.cpp @@ -99,7 +99,7 @@ MatchFunctor* NotMatchFunctor::buildFunctor(const DOMElement* e, const FilterPol if (*id && functorMap->getMatchFunctors().count(id)) id = ""; - auto_ptr type(XMLHelper::getXSIType(e)); + auto_ptr type(XMLHelper::getXSIType(e)); if (!type.get()) throw ConfigurationException("Child Rule found with no xsi:type."); diff --git a/shibsp/attribute/filtering/impl/OrMatchFunctor.cpp b/shibsp/attribute/filtering/impl/OrMatchFunctor.cpp index cd29548..2649f42 100644 --- a/shibsp/attribute/filtering/impl/OrMatchFunctor.cpp +++ b/shibsp/attribute/filtering/impl/OrMatchFunctor.cpp @@ -105,7 +105,7 @@ MatchFunctor* OrMatchFunctor::buildFunctor(const DOMElement* e, const FilterPoli if (*id && functorMap->getMatchFunctors().count(id)) id = ""; - auto_ptr type(XMLHelper::getXSIType(e)); + auto_ptr type(XMLHelper::getXSIType(e)); if (!type.get()) throw ConfigurationException("Child Rule found with no xsi:type."); diff --git a/shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp b/shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp index b01cf1f..32fab83 100644 --- a/shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp +++ b/shibsp/attribute/filtering/impl/XMLAttributeFilter.cpp @@ -228,7 +228,7 @@ MatchFunctor* XMLFilterImpl::buildFunctor( id = ""; } - auto_ptr type(XMLHelper::getXSIType(e)); + auto_ptr type(XMLHelper::getXSIType(e)); if (type.get()) { try { MatchFunctor* func = SPConfig::getConfig().MatchFunctorManager.newPlugin(*type.get(), make_pair(&functorMap,e)); @@ -300,10 +300,13 @@ pair< string,pair > XMLFilterImpl::buil } if (perm || deny) { - if (*id) - return m_attrRules[id] = pair< string,pair >(attrID.get(), pair(perm,deny)); - else + if (*id) { + m_attrRules[id] = pair< string,pair >(attrID.get(), pair(perm,deny)); + return m_attrRules[id]; + } + else { return pair< string,pair >(attrID.get(), pair(perm,deny)); + } } m_log.warn("skipping AttributeRule (%s), permit and denial rule(s) invalid or missing", id); @@ -430,18 +433,19 @@ void XMLFilterImpl::filterAttributes(const FilteringContext& context, vectorremoveValue(index-1); } + } - // Check for no values. - if (attr->valueCount() == 0) { - m_log.warn( - "no values left, removing attribute (%s) from (%s)", - attr->getId(), issuer.get() ? issuer.get() : "unknown source" - ); - delete attr; - attributes.erase(attributes.begin() + a); - continue; - } + // Check for no values. + if (attr->valueCount() == 0) { + m_log.warn( + "no values left, removing attribute (%s) from (%s)", + attr->getId(), issuer.get() ? issuer.get() : "unknown source" + ); + delete attr; + attributes.erase(attributes.begin() + a); + continue; } + ++a; } } diff --git a/shibsp/attribute/resolver/AttributeExtractor.h b/shibsp/attribute/resolver/AttributeExtractor.h index a0b0a14..63644ca 100644 --- a/shibsp/attribute/resolver/AttributeExtractor.h +++ b/shibsp/attribute/resolver/AttributeExtractor.h @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * @file shibsp/attribute/resolver/AttributeExtractor.h - * + * * A service that extracts and decodes attributes from XML objects. */ @@ -45,12 +45,12 @@ namespace shibsp { /** * Extracts the attributes found in an XMLObject. - * + * * @param application Application performing the extraction * @param issuer source of object, if known * @param xmlObject object to extract * @param attributes an array to populate with the extracted attributes - * + * * @throws AttributeExtractionException thrown if there is a problem extracting attributes */ virtual void extractAttributes( @@ -76,6 +76,12 @@ namespace shibsp { /** AttributeExtractor based on an XML mapping schema. */ #define XML_ATTRIBUTE_EXTRACTOR "XML" + /** AttributeExtractor for DelegationRestriction information. */ + #define DELEGATION_ATTRIBUTE_EXTRACTOR "Delegation" + + /** AttributeExtractor for KeyInfo information. */ + #define KEYDESCRIPTOR_ATTRIBUTE_EXTRACTOR "KeyDescriptor" + /** AttributeExtractor based on chaining together other extractors. */ #define CHAINING_ATTRIBUTE_EXTRACTOR "Chaining" }; diff --git a/shibsp/attribute/resolver/AttributeResolver.h b/shibsp/attribute/resolver/AttributeResolver.h index 73adab1..524dde8 100644 --- a/shibsp/attribute/resolver/AttributeResolver.h +++ b/shibsp/attribute/resolver/AttributeResolver.h @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * @file shibsp/attribute/resolver/AttributeResolver.h - * + * * A service that transforms or resolves additional attributes for a particular subject. */ @@ -54,10 +54,10 @@ namespace shibsp { /** * Creates a ResolutionContext based on session bootstrap material. - * + * *

This enables resolution to occur ahead of session creation so that * Attributes can be supplied while creating the session. - * + * * @param application reference to Application that owns the eventual Session * @param issuer issuing metadata of assertion issuer, if known * @param protocol protocol used to establish Session @@ -81,19 +81,19 @@ namespace shibsp { /** * Creates a ResolutionContext for an existing Session. - * + * * @param application reference to Application that owns the Session * @param session reference to Session * @return newly created ResolutionContext, owned by caller */ virtual ResolutionContext* createResolutionContext(const Application& application, const Session& session) const=0; - + /** * Resolves attributes for a given subject and returns them in the supplied context. - * + * * @param ctx resolution context to use to resolve attributes - * + * * @throws AttributeResolutionException thrown if there is a problem resolving the attributes for the subject */ virtual void resolveAttributes(ResolutionContext& ctx) const=0; @@ -118,6 +118,9 @@ namespace shibsp { /** AttributeResolver based on SAML queries to an IdP during SSO. */ #define QUERY_ATTRIBUTE_RESOLVER "Query" + /** AttributeResolver based on free-standing SAML queries to additional AAs. */ + #define SIMPLEAGGREGATION_ATTRIBUTE_RESOLVER "SimpleAggregation" + /** AttributeResolver based on chaining together other resolvers. */ #define CHAINING_ATTRIBUTE_RESOLVER "Chaining" }; diff --git a/shibsp/attribute/resolver/impl/ChainingAttributeExtractor.cpp b/shibsp/attribute/resolver/impl/ChainingAttributeExtractor.cpp index de77496..523cdc3 100644 --- a/shibsp/attribute/resolver/impl/ChainingAttributeExtractor.cpp +++ b/shibsp/attribute/resolver/impl/ChainingAttributeExtractor.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -71,6 +71,8 @@ namespace shibsp { 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 _type[] = UNICODE_LITERAL_4(t,y,p,e); + SHIBSP_DLLLOCAL PluginManager::Factory DelegationAttributeExtractorFactory; + SHIBSP_DLLLOCAL PluginManager::Factory KeyDescriptorAttributeExtractorFactory; SHIBSP_DLLLOCAL PluginManager::Factory XMLAttributeExtractorFactory; AttributeExtractor* SHIBSP_DLLLOCAL ChainingExtractorFactory(const DOMElement* const & e) { @@ -80,6 +82,8 @@ namespace shibsp { void SHIBSP_API shibsp::registerAttributeExtractors() { + SPConfig::getConfig().AttributeExtractorManager.registerFactory(DELEGATION_ATTRIBUTE_EXTRACTOR, DelegationAttributeExtractorFactory); + SPConfig::getConfig().AttributeExtractorManager.registerFactory(KEYDESCRIPTOR_ATTRIBUTE_EXTRACTOR, KeyDescriptorAttributeExtractorFactory); SPConfig::getConfig().AttributeExtractorManager.registerFactory(XML_ATTRIBUTE_EXTRACTOR, XMLAttributeExtractorFactory); SPConfig::getConfig().AttributeExtractorManager.registerFactory(CHAINING_ATTRIBUTE_EXTRACTOR, ChainingExtractorFactory); } diff --git a/shibsp/attribute/resolver/impl/ChainingAttributeResolver.cpp b/shibsp/attribute/resolver/impl/ChainingAttributeResolver.cpp index 90e3776..d0b63f1 100644 --- a/shibsp/attribute/resolver/impl/ChainingAttributeResolver.cpp +++ b/shibsp/attribute/resolver/impl/ChainingAttributeResolver.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -134,6 +134,8 @@ namespace shibsp { static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e); SHIBSP_DLLLOCAL PluginManager::Factory QueryResolverFactory; + SHIBSP_DLLLOCAL PluginManager::Factory SimpleAggregationResolverFactory; + AttributeResolver* SHIBSP_DLLLOCAL ChainingResolverFactory(const DOMElement* const & e) { return new ChainingAttributeResolver(e); @@ -143,6 +145,7 @@ namespace shibsp { void SHIBSP_API shibsp::registerAttributeResolvers() { SPConfig::getConfig().AttributeResolverManager.registerFactory(QUERY_ATTRIBUTE_RESOLVER, QueryResolverFactory); + SPConfig::getConfig().AttributeResolverManager.registerFactory(SIMPLEAGGREGATION_ATTRIBUTE_RESOLVER, SimpleAggregationResolverFactory); SPConfig::getConfig().AttributeResolverManager.registerFactory(CHAINING_ATTRIBUTE_RESOLVER, ChainingResolverFactory); } @@ -184,7 +187,7 @@ void ChainingAttributeResolver::resolveAttributes(ResolutionContext& ctx) const (*i)->resolveAttributes(*context.get()); chain.m_attributes.insert(chain.m_attributes.end(), context->getResolvedAttributes().begin(), context->getResolvedAttributes().end()); - chain.m_ownedAttributes.insert(chain.m_attributes.end(), context->getResolvedAttributes().begin(), context->getResolvedAttributes().end()); + chain.m_ownedAttributes.insert(chain.m_ownedAttributes.end(), context->getResolvedAttributes().begin(), context->getResolvedAttributes().end()); context->getResolvedAttributes().clear(); chain.m_tokens.insert(chain.m_tokens.end(), context->getResolvedAssertions().begin(), context->getResolvedAssertions().end()); diff --git a/shibsp/attribute/resolver/impl/DelegationAttributeExtractor.cpp b/shibsp/attribute/resolver/impl/DelegationAttributeExtractor.cpp new file mode 100644 index 0000000..d44311e --- /dev/null +++ b/shibsp/attribute/resolver/impl/DelegationAttributeExtractor.cpp @@ -0,0 +1,205 @@ +/* + * Copyright 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. + */ + +/** + * DelegationAttributeExtractor.cpp + * + * AttributeExtractor for DelegationRestriction information. + */ + +#include "internal.h" +#include "Application.h" +#include "ServiceProvider.h" +#include "attribute/ExtensibleAttribute.h" +#include "attribute/resolver/AttributeExtractor.h" +#include "util/SPConstants.h" + +#include +#include +#include +#include + +using namespace shibsp; +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class DelegationExtractor : public AttributeExtractor + { + public: + DelegationExtractor(const DOMElement* e); + ~DelegationExtractor() {} + + Lockable* lock() { + return this; + } + + void unlock() { + } + + void extractAttributes( + const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, vector& attributes + ) const; + + void getAttributeIds(std::vector& attributes) const { + attributes.push_back(m_attributeId); + } + + private: + string m_attributeId,m_formatter; + }; + +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + + AttributeExtractor* SHIBSP_DLLLOCAL DelegationAttributeExtractorFactory(const DOMElement* const & e) + { + return new DelegationExtractor(e); + } + + static const XMLCh attributeId[] = UNICODE_LITERAL_11(a,t,t,r,i,b,u,t,e,I,d); + static const XMLCh formatter[] = UNICODE_LITERAL_9(f,o,r,m,a,t,t,e,r); +}; + +DelegationExtractor::DelegationExtractor(const DOMElement* e) : m_attributeId("delegate"), m_formatter("$Name") +{ + if (e) { + const XMLCh* a = e->getAttributeNS(NULL, attributeId); + if (a && *a) { + auto_ptr_char temp(a); + m_attributeId = temp.get(); + } + a = e->getAttributeNS(NULL, formatter); + if (a && *a) { + auto_ptr_char temp(a); + m_formatter = temp.get(); + } + } +} + +void DelegationExtractor::extractAttributes( + const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, vector& attributes + ) const +{ + const saml2::Assertion* assertion = dynamic_cast(&xmlObject); + if (!assertion || !assertion->getConditions()) + return; + + Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeExtractor.Delegation"); + + const vector& conditions = const_cast(assertion->getConditions())->getConditions(); + for (vector::const_iterator c = conditions.begin(); c != conditions.end(); ++c) { + const saml2::DelegationRestrictionType* drt = dynamic_cast(*c); + if (drt) { + auto_ptr attr(new ExtensibleAttribute(vector(1,m_attributeId), m_formatter.c_str())); + + const vector& dels = drt->getDelegates(); + for (vector::const_iterator d = dels.begin(); d != dels.end(); ++d) { + if ((*d)->getBaseID()) { + log.error("delegate identified by saml:BaseID cannot be processed into an attribute value"); + continue; + } + + saml2::NameID* n = NULL; + if ((*d)->getEncryptedID()) { + CredentialResolver* cr = application.getCredentialResolver(); + if (!cr) { + log.warn("found encrypted Delegate, but no CredentialResolver was available"); + } + + try { + const XMLCh* recipient = application.getRelyingParty( + issuer ? dynamic_cast(issuer->getParent()) : NULL + )->getXMLString("entityID").second; + Locker credlocker(cr); + if (issuer) { + MetadataCredentialCriteria mcc(*issuer); + auto_ptr decrypted((*d)->getEncryptedID()->decrypt(*cr, recipient, &mcc)); + n = dynamic_cast(decrypted.release()); + } + else { + auto_ptr decrypted((*d)->getEncryptedID()->decrypt(*cr, recipient)); + n = dynamic_cast(decrypted.release()); + } + if (n && log.isDebugEnabled()) + log.debugStream() << "decrypted Delegate: " << *n << logging::eol; + } + catch (exception& ex) { + log.error("caught exception decrypting Delegate: %s", ex.what()); + } + } + else { + n = (*d)->getNameID(); + } + + if (n) { + DDF val = DDF(NULL).structure(); + if ((*d)->getConfirmationMethod()) { + auto_ptr_char temp((*d)->getConfirmationMethod()); + val.addmember("ConfirmationMethod").string(temp.get()); + } + if ((*d)->getDelegationInstant()) { + auto_ptr_char temp((*d)->getDelegationInstant()->getRawData()); + val.addmember("DelegationInstant").string(temp.get()); + } + + auto_arrayptr name(toUTF8(n->getName())); + if (name.get() && *name.get()) { + val.addmember("Name").string(name.get()); + char* str = toUTF8(n->getFormat()); + if (str && *str) + val.addmember("Format").string(str); + delete[] str; + + str = toUTF8(n->getNameQualifier()); + if (str && *str) + val.addmember("NameQualifier").string(str); + delete[] str; + + str = toUTF8(n->getSPNameQualifier()); + if (str && *str) + val.addmember("SPNameQualifier").string(str); + delete[] str; + + str = toUTF8(n->getSPProvidedID()); + if (str && *str) + val.addmember("SPProvidedID").string(str); + delete[] str; + } + + if (n != (*d)->getNameID()) + delete n; + + if (val.integer()) + attr->getValues().add(val); + else + val.destroy(); + } + } + + attributes.push_back(attr.release()); + } + } +} diff --git a/shibsp/attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp b/shibsp/attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp new file mode 100644 index 0000000..d730b35 --- /dev/null +++ b/shibsp/attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp @@ -0,0 +1,177 @@ +/* + * Copyright 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. + */ + +/** + * KeyDescriptorAttributeExtractor.cpp + * + * AttributeExtractor for KeyDescriptor information. + */ + +#include "internal.h" +#include "Application.h" +#include "attribute/AttributeDecoder.h" +#include "attribute/SimpleAttribute.h" +#include "attribute/resolver/AttributeExtractor.h" + +#include +#include +#include +#include +#include + +using namespace shibsp; +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + +#if defined (_MSC_VER) + #pragma warning( push ) + #pragma warning( disable : 4250 ) +#endif + + class KeyDescriptorExtractor : public AttributeExtractor + { + public: + KeyDescriptorExtractor(const DOMElement* e); + ~KeyDescriptorExtractor() {} + + Lockable* lock() { + return this; + } + + void unlock() { + } + + void extractAttributes( + const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, vector& attributes + ) const; + + void getAttributeIds(std::vector& attributes) const { + if (!m_hashId.empty()) + attributes.push_back(m_hashId.front()); + if (!m_signingId.empty()) + attributes.push_back(m_signingId.front()); + if (!m_encryptionId.empty()) + attributes.push_back(m_encryptionId.front()); + } + + private: + vector m_hashId; + vector m_signingId; + vector m_encryptionId; + }; + +#if defined (_MSC_VER) + #pragma warning( pop ) +#endif + + AttributeExtractor* SHIBSP_DLLLOCAL KeyDescriptorAttributeExtractorFactory(const DOMElement* const & e) + { + return new KeyDescriptorExtractor(e); + } + + static const XMLCh encryptionId[] = UNICODE_LITERAL_12(e,n,c,r,y,p,t,i,o,n,I,d); + static const XMLCh hashId[] = UNICODE_LITERAL_6(h,a,s,h,I,d); + static const XMLCh signingId[] = UNICODE_LITERAL_9(s,i,g,n,i,n,g,I,d); +}; + +KeyDescriptorExtractor::KeyDescriptorExtractor(const DOMElement* e) +{ + if (e) { + const XMLCh* a = e->getAttributeNS(NULL, hashId); + if (a && *a) { + auto_ptr_char temp(a); + m_hashId.push_back(temp.get()); + } + a = e->getAttributeNS(NULL, signingId); + if (a && *a) { + auto_ptr_char temp(a); + m_signingId.push_back(temp.get()); + } + a = e->getAttributeNS(NULL, encryptionId); + if (a && *a) { + auto_ptr_char temp(a); + m_encryptionId.push_back(temp.get()); + } + } + if (m_hashId.empty() && m_signingId.empty() && m_encryptionId.empty()) + throw ConfigurationException("KeyDescriptor AttributeExtractor requires hashId, signingId, or encryptionId property."); +} + +void KeyDescriptorExtractor::extractAttributes( + const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, vector& attributes + ) const +{ + const RoleDescriptor* role = dynamic_cast(&xmlObject); + if (!role) + return; + + vector creds; + MetadataCredentialCriteria mcc(*role); + + if (!m_signingId.empty() || !m_hashId.empty()) { + mcc.setUsage(Credential::SIGNING_CREDENTIAL); + if (application.getMetadataProvider()->resolve(creds, &mcc)) { + if (!m_hashId.empty()) { + auto_ptr attr(new SimpleAttribute(m_hashId)); + vector& vals = attr->getValues(); + for (vector::const_iterator c = creds.begin(); c != creds.end(); ++c) { + if (vals.empty() || !vals.back().empty()) + vals.push_back(string()); + vals.back() = SecurityHelper::getDEREncoding(*(*c), true); + } + if (vals.back().empty()) + vals.pop_back(); + if (!vals.empty()) + attributes.push_back(attr.release()); + } + if (!m_signingId.empty()) { + auto_ptr attr(new SimpleAttribute(m_signingId)); + vector& vals = attr->getValues(); + for (vector::const_iterator c = creds.begin(); c != creds.end(); ++c) { + if (vals.empty() || !vals.back().empty()) + vals.push_back(string()); + vals.back() = SecurityHelper::getDEREncoding(*(*c)); + } + if (vals.back().empty()) + vals.pop_back(); + if (!vals.empty()) + attributes.push_back(attr.release()); + } + creds.clear(); + } + } + + if (!m_encryptionId.empty()) { + mcc.setUsage(Credential::ENCRYPTION_CREDENTIAL); + if (application.getMetadataProvider()->resolve(creds, &mcc)) { + auto_ptr attr(new SimpleAttribute(m_encryptionId)); + vector& vals = attr->getValues(); + for (vector::const_iterator c = creds.begin(); c != creds.end(); ++c) { + if (vals.empty() || !vals.back().empty()) + vals.push_back(string()); + vals.back() = SecurityHelper::getDEREncoding(*(*c)); + } + if (vals.back().empty()) + vals.pop_back(); + if (!vals.empty()) + attributes.push_back(attr.release()); + } + } +} diff --git a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp index e6e231e..ceff508 100644 --- a/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp +++ b/shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -39,12 +39,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include #include @@ -78,8 +76,7 @@ namespace shibsp { const NameID* nameid=NULL, const XMLCh* authncontext_class=NULL, const XMLCh* authncontext_decl=NULL, - const vector* tokens=NULL, - const vector* attributes=NULL + const vector* tokens=NULL ) : m_query(true), m_app(application), m_session(NULL), m_metadata(NULL), m_entity(issuer), m_protocol(protocol), m_nameid(nameid), m_class(authncontext_class), m_decl(authncontext_decl) { @@ -188,7 +185,7 @@ namespace shibsp { const vector* tokens=NULL, const vector* attributes=NULL ) const { - return new QueryContext(application,issuer,protocol,nameid,authncontext_class,authncontext_decl,tokens,attributes); + return new QueryContext(application,issuer,protocol,nameid,authncontext_class,authncontext_decl,tokens); } ResolutionContext* createResolutionContext(const Application& application, const Session& session) const { @@ -206,6 +203,7 @@ namespace shibsp { bool SAML2Query(QueryContext& ctx) const; Category& m_log; + string m_policyId; vector m_SAML1Designators; vector m_SAML2Designators; }; @@ -215,6 +213,7 @@ namespace shibsp { return new QueryResolver(e); } + static const XMLCh _policyId[] = UNICODE_LITERAL_8(p,o,l,i,c,y,I,d); }; QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance(SHIBSP_LOGCAT".AttributeResolver.Query")) @@ -223,10 +222,16 @@ QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance( xmltooling::NDC ndc("QueryResolver"); #endif + const XMLCh* pid = e ? e->getAttributeNS(NULL, _policyId) : NULL; + if (pid && *pid) { + auto_ptr_char temp(pid); + m_policyId = temp.get(); + } + DOMElement* child = XMLHelper::getFirstChildElement(e); while (child) { try { - if (XMLHelper::isNodeNamed(e, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME)) { + if (XMLHelper::isNodeNamed(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME)) { auto_ptr obj(saml2::AttributeBuilder::buildOneFromElement(child)); saml2::Attribute* down = dynamic_cast(obj.get()); if (down) { @@ -234,7 +239,7 @@ QueryResolver::QueryResolver(const DOMElement* e) : m_log(Category::getInstance( obj.release(); } } - else if (XMLHelper::isNodeNamed(e, samlconstants::SAML1P_NS, AttributeDesignator::LOCAL_NAME)) { + else if (XMLHelper::isNodeNamed(child, samlconstants::SAML1P_NS, AttributeDesignator::LOCAL_NAME)) { auto_ptr obj(AttributeDesignatorBuilder::buildOneFromElement(child)); AttributeDesignator* down = dynamic_cast(obj.get()); if (down) { @@ -266,7 +271,16 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const const Application& application = ctx.getApplication(); const PropertySet* relyingParty = application.getRelyingParty(ctx.getEntityDescriptor()); - shibsp::SecurityPolicy policy(application); + + // Locate policy key. + const char* policyId = m_policyId.empty() ? application.getString("policyId").second : m_policyId.c_str(); + + // Access policy properties. + const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId); + pair validate = settings->getBool("validate"); + + shibsp::SecurityPolicy policy(application, NULL, validate.first && validate.second, policyId); + policy.getAudiences().push_back(relyingParty->getXMLString("entityID").second); MetadataCredentialCriteria mcc(*AA); shibsp::SOAPClient soaper(policy); @@ -274,7 +288,7 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const saml1p::Response* response=NULL; const vector& endpoints=AA->getAttributeServices(); for (vector::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) { - if (!XMLString::equals((*ep)->getBinding(),binding.get())) + if (!XMLString::equals((*ep)->getBinding(),binding.get()) || !(*ep)->getLocation()) continue; auto_ptr_char loc((*ep)->getLocation()); try { @@ -344,13 +358,9 @@ bool QueryResolver::SAML1Query(QueryContext& ctx) const // Now we can check the security status of the policy. if (!policy.isAuthenticated()) throw SecurityPolicyException("Security of SAML 1.x query result not established."); - - // Lastly, check it over. - saml1::AssertionValidator tokval(relyingParty->getXMLString("entityID").second, application.getAudiences(), time(NULL)); - tokval.validateAssertion(*newtoken); } catch (exception& ex) { - m_log.error("assertion failed policy/validation: %s", ex.what()); + m_log.error("assertion failed policy validation: %s", ex.what()); return true; } @@ -396,19 +406,28 @@ bool QueryResolver::SAML2Query(QueryContext& ctx) const } const Application& application = ctx.getApplication(); - shibsp::SecurityPolicy policy(application); - MetadataCredentialCriteria mcc(*AA); - shibsp::SOAPClient soaper(policy); - const PropertySet* relyingParty = application.getRelyingParty(ctx.getEntityDescriptor()); + + // Locate policy key. + const char* policyId = m_policyId.empty() ? application.getString("policyId").second : m_policyId.c_str(); + + // Access policy properties. + const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId); + pair validate = settings->getBool("validate"); + pair signedAssertions = relyingParty->getBool("requireSignedAssertions"); pair encryption = relyingParty->getString("encryption"); + shibsp::SecurityPolicy policy(application, NULL, validate.first && validate.second, policyId); + policy.getAudiences().push_back(relyingParty->getXMLString("entityID").second); + MetadataCredentialCriteria mcc(*AA); + shibsp::SOAPClient soaper(policy); + auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP); saml2p::StatusResponseType* srt=NULL; const vector& endpoints=AA->getAttributeServices(); for (vector::const_iterator ep=endpoints.begin(); !srt && ep!=endpoints.end(); ++ep) { - if (!XMLString::equals((*ep)->getBinding(),binding.get())) + if (!XMLString::equals((*ep)->getBinding(),binding.get()) || !(*ep)->getLocation()) continue; auto_ptr_char loc((*ep)->getLocation()); try { @@ -495,13 +514,9 @@ 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."); - - // Lastly, check it over. - saml2::AssertionValidator tokval(relyingParty->getXMLString("entityID").second, application.getAudiences(), time(NULL)); - tokval.validateAssertion(*newtoken); } catch (exception& ex) { - m_log.error("assertion failed policy/validation: %s", ex.what()); + m_log.error("assertion failed policy validation: %s", ex.what()); return true; } diff --git a/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp b/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp new file mode 100644 index 0000000..abd1cad --- /dev/null +++ b/shibsp/attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp @@ -0,0 +1,603 @@ +/* + * Copyright 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. + */ + +/** + * SimpleAggregationAttributeResolver.cpp + * + * AttributeResolver based on SAML queries to third-party AA sources. + */ + +#include "internal.h" +#include "Application.h" +#include "ServiceProvider.h" +#include "SessionCache.h" +#include "attribute/NameIDAttribute.h" +#include "attribute/filtering/AttributeFilter.h" +#include "attribute/filtering/BasicFilteringContext.h" +#include "attribute/resolver/AttributeExtractor.h" +#include "attribute/resolver/AttributeResolver.h" +#include "attribute/resolver/ResolutionContext.h" +#include "binding/SOAPClient.h" +#include "metadata/MetadataProviderCriteria.h" +#include "util/SPConstants.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace shibsp; +using namespace opensaml::saml2; +using namespace opensaml::saml2p; +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + + class SHIBSP_DLLLOCAL SimpleAggregationContext : public ResolutionContext + { + public: + SimpleAggregationContext(const Application& application, const Session& session) + : m_app(application), + m_session(&session), + m_nameid(NULL), + m_class(XMLString::transcode(session.getAuthnContextClassRef())), + m_decl(XMLString::transcode(session.getAuthnContextDeclRef())), + m_inputTokens(NULL), + m_inputAttributes(NULL) { + } + + SimpleAggregationContext( + const Application& application, + const NameID* nameid=NULL, + const XMLCh* authncontext_class=NULL, + const XMLCh* authncontext_decl=NULL, + const vector* tokens=NULL, + const vector* attributes=NULL + ) : m_app(application), + m_session(NULL), + m_nameid(nameid), + m_class(const_cast(authncontext_class)), + m_decl(const_cast(authncontext_decl)), + m_inputTokens(tokens), + m_inputAttributes(attributes) { + } + + ~SimpleAggregationContext() { + for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup()); + for_each(m_assertions.begin(), m_assertions.end(), xmltooling::cleanup()); + if (m_session) { + XMLString::release(&m_class); + XMLString::release(&m_decl); + } + } + + const Application& getApplication() const { + return m_app; + } + const NameID* getNameID() const { + return m_session ? m_session->getNameID() : m_nameid; + } + const XMLCh* getClassRef() const { + return m_class; + } + const XMLCh* getDeclRef() const { + return m_decl; + } + const Session* getSession() const { + return m_session; + } + const vector* getInputAttributes() const { + return m_inputAttributes; + } + const vector* getInputTokens() const { + return m_inputTokens; + } + vector& getResolvedAttributes() { + return m_attributes; + } + vector& getResolvedAssertions() { + return m_assertions; + } + + private: + const Application& m_app; + const Session* m_session; + const NameID* m_nameid; + XMLCh* m_class; + XMLCh* m_decl; + const vector* m_inputTokens; + const vector* m_inputAttributes; + vector m_attributes; + vector m_assertions; + }; + + class SHIBSP_DLLLOCAL SimpleAggregationResolver : public AttributeResolver + { + public: + SimpleAggregationResolver(const DOMElement* e); + ~SimpleAggregationResolver() { + delete m_trust; + delete m_metadata; + for_each(m_designators.begin(), m_designators.end(), xmltooling::cleanup()); + } + + Lockable* lock() {return this;} + void unlock() {} + + ResolutionContext* createResolutionContext( + const Application& application, + const EntityDescriptor* issuer, + const XMLCh* protocol, + const NameID* nameid=NULL, + const XMLCh* authncontext_class=NULL, + const XMLCh* authncontext_decl=NULL, + const vector* tokens=NULL, + const vector* attributes=NULL + ) const { + return new SimpleAggregationContext(application,nameid,authncontext_class,authncontext_decl,tokens,attributes); + } + + ResolutionContext* createResolutionContext(const Application& application, const Session& session) const { + return new SimpleAggregationContext(application,session); + } + + void resolveAttributes(ResolutionContext& ctx) const; + + void getAttributeIds(vector& attributes) const { + // Nothing to do, only the extractor would actually generate them. + } + + private: + bool doQuery(SimpleAggregationContext& ctx, const char* entityID, const NameID* name) const; + + Category& m_log; + string m_policyId; + vector m_attributeIds; + xstring m_format; + MetadataProvider* m_metadata; + TrustEngine* m_trust; + vector m_designators; + vector< pair > m_sources; + }; + + AttributeResolver* SHIBSP_DLLLOCAL SimpleAggregationResolverFactory(const DOMElement* const & e) + { + return new SimpleAggregationResolver(e); + } + + static const XMLCh attributeId[] = UNICODE_LITERAL_11(a,t,t,r,i,b,u,t,e,I,d); + static const XMLCh Entity[] = UNICODE_LITERAL_6(E,n,t,i,t,y); + static const XMLCh EntityReference[] = UNICODE_LITERAL_15(E,n,t,i,t,y,R,e,f,e,r,e,n,c,e); + static const XMLCh 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 _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) +{ +#ifdef _DEBUG + xmltooling::NDC ndc("SimpleAggregationResolver"); +#endif + + 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, attributeId) : NULL; + if (pid && *pid) { + char* dup = XMLString::transcode(pid); + char* pos; + char* start = dup; + while (start && *start) { + while (*start && isspace(*start)) + start++; + if (!*start) + break; + pos = strchr(start,' '); + if (pos) + *pos=0; + m_attributeIds.push_back(start); + start = pos ? pos+1 : NULL; + } + XMLString::release(&dup); + + pid = e->getAttributeNS(NULL, format); + if (pid && *pid) + m_format = pid; + + } + + DOMElement* child = XMLHelper::getFirstChildElement(e, _MetadataProvider); + if (child) { + auto_ptr_char type(child->getAttributeNS(NULL, _type)); + if (!type.get() || !*type.get()) + throw ConfigurationException("MetadataProvider element missing type attribute."); + m_log.info("building MetadataProvider of type %s...", type.get()); + auto_ptr mp(SAMLConfig::getConfig().MetadataProviderManager.newPlugin(type.get(), child)); + mp->init(); + m_metadata = mp.release(); + } + + child = XMLHelper::getFirstChildElement(e, _TrustEngine); + if (child) { + try { + auto_ptr_char type(child->getAttributeNS(NULL, _type)); + if (!type.get() || !*type.get()) + throw ConfigurationException("TrustEngine element missing type attribute."); + m_log.info("building TrustEngine of type %s...", type.get()); + m_trust = XMLToolingConfig::getConfig().TrustEngineManager.newPlugin(type.get(), child); + } + catch (exception&) { + delete m_metadata; + throw; + } + } + + child = XMLHelper::getFirstChildElement(e); + while (child) { + if (child->hasChildNodes() && XMLString::equals(child->getLocalName(), Entity)) { + pid = child->getFirstChild()->getNodeValue(); + if (pid && *pid) { + auto_ptr_char tpid(pid); + m_sources.push_back(pair(tpid.get(),true)); + } + } + else if (child->hasChildNodes() && XMLString::equals(child->getLocalName(), EntityReference)) { + pid = child->getFirstChild()->getNodeValue(); + if (pid && *pid) { + auto_ptr_char tpid(pid); + m_sources.push_back(pair(tpid.get(),false)); + } + } + else if (XMLHelper::isNodeNamed(child, samlconstants::SAML20_NS, saml2::Attribute::LOCAL_NAME)) { + try { + auto_ptr obj(saml2::AttributeBuilder::buildOneFromElement(child)); + saml2::Attribute* down = dynamic_cast(obj.get()); + if (down) { + m_designators.push_back(down); + obj.release(); + } + } + catch (exception& ex) { + m_log.error("exception loading attribute designator: %s", ex.what()); + } + } + child = XMLHelper::getNextSiblingElement(child); + } + +} + +bool SimpleAggregationResolver::doQuery(SimpleAggregationContext& ctx, const char* entityID, const NameID* name) const +{ +#ifdef _DEBUG + xmltooling::NDC ndc("doQuery"); +#endif + const Application& application = ctx.getApplication(); + MetadataProviderCriteria mc(application, entityID, &AttributeAuthorityDescriptor::ELEMENT_QNAME, samlconstants::SAML20P_NS); + Locker mlocker(m_metadata); + const AttributeAuthorityDescriptor* AA=NULL; + pair mdresult = + (m_metadata ? m_metadata : application.getMetadataProvider())->getEntityDescriptor(mc); + if (!mdresult.first) { + m_log.warn("unable to locate metadata for provider (%s)", entityID); + return false; + } + else if (!(AA=dynamic_cast(mdresult.second))) { + m_log.warn("no SAML 2 AttributeAuthority role found in metadata for (%s)", entityID); + return false; + } + + const PropertySet* relyingParty = application.getRelyingParty(mdresult.first); + + // Locate policy key. + const char* policyId = m_policyId.empty() ? application.getString("policyId").second : m_policyId.c_str(); + + // Access policy properties. + const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId); + pair validate = settings->getBool("validate"); + + pair signedAssertions = relyingParty->getBool("requireSignedAssertions"); + pair encryption = relyingParty->getString("encryption"); + + shibsp::SecurityPolicy policy(application, NULL, validate.first && validate.second, policyId); + if (m_metadata) + policy.setMetadataProvider(m_metadata); + if (m_trust) + policy.setTrustEngine(m_trust); + policy.getAudiences().push_back(relyingParty->getXMLString("entityID").second); + + MetadataCredentialCriteria mcc(*AA); + shibsp::SOAPClient soaper(policy); + + auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP); + saml2p::StatusResponseType* srt=NULL; + const vector& endpoints=AA->getAttributeServices(); + for (vector::const_iterator ep=endpoints.begin(); !srt && ep!=endpoints.end(); ++ep) { + if (!XMLString::equals((*ep)->getBinding(),binding.get()) || !(*ep)->getLocation()) + continue; + auto_ptr_char loc((*ep)->getLocation()); + try { + auto_ptr subject(saml2::SubjectBuilder::buildSubject()); + + // Encrypt the NameID? + if (encryption.first && (!strcmp(encryption.second, "true") || !strcmp(encryption.second, "back"))) { + auto_ptr encrypted(EncryptedIDBuilder::buildEncryptedID()); + MetadataCredentialCriteria mcc(*AA); + encrypted->encrypt( + *name, + *(policy.getMetadataProvider()), + mcc, + false, + relyingParty->getXMLString("encryptionAlg").second + ); + subject->setEncryptedID(encrypted.release()); + } + else { + subject->setNameID(name->cloneNameID()); + } + + saml2p::AttributeQuery* query = saml2p::AttributeQueryBuilder::buildAttributeQuery(); + query->setSubject(subject.release()); + Issuer* iss = IssuerBuilder::buildIssuer(); + iss->setName(relyingParty->getXMLString("entityID").second); + query->setIssuer(iss); + for (vector::const_iterator ad = m_designators.begin(); ad!=m_designators.end(); ++ad) + query->getAttributes().push_back((*ad)->cloneAttribute()); + + SAML2SOAPClient client(soaper, false); + client.sendSAML(query, application.getId(), mcc, loc.get()); + srt = client.receiveSAML(); + } + catch (exception& ex) { + m_log.error("exception during SAML query to %s: %s", loc.get(), ex.what()); + soaper.reset(); + } + } + + if (!srt) { + m_log.error("unable to obtain a SAML response from attribute authority (%s)", entityID); + return false; + } + saml2p::Response* response = dynamic_cast(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; + } + + const vector& assertions = const_cast(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"); + + auto_ptr wrapper(srt); + saml2::Assertion* newtoken = assertions.front(); + + if (!newtoken->getSignature() && signedAssertions.first && signedAssertions.second) { + m_log.error("assertion unsigned, rejecting it based on signedAssertions policy"); + return true; + } + + try { + // We're going to insist that the assertion issuer is the same as the peer. + // Reset the policy's message bits and extract them from the assertion. + policy.reset(true); + policy.setMessageID(newtoken->getID()); + policy.setIssueInstant(newtoken->getIssueInstantEpoch()); + policy.setIssuer(newtoken->getIssuer()); + policy.evaluate(*newtoken); + + // Now we can check the security status of the policy. + if (!policy.isAuthenticated()) + throw SecurityPolicyException("Security of SAML 2.0 query result not established."); + } + catch (exception& ex) { + m_log.error("assertion failed policy validation: %s", ex.what()); + return true; + } + + newtoken->detach(); + wrapper.release(); + ctx.getResolvedAssertions().push_back(newtoken); + + // Finally, extract and filter the result. + try { + AttributeExtractor* extractor = application.getAttributeExtractor(); + if (extractor) { + Locker extlocker(extractor); + extractor->extractAttributes(application, AA, *newtoken, ctx.getResolvedAttributes()); + } + + AttributeFilter* filter = application.getAttributeFilter(); + if (filter) { + BasicFilteringContext fc(application, ctx.getResolvedAttributes(), AA, ctx.getClassRef(), ctx.getDeclRef()); + Locker filtlocker(filter); + filter->filterAttributes(fc, ctx.getResolvedAttributes()); + } + } + catch (exception& ex) { + m_log.error("caught exception extracting/filtering attributes from query result: %s", ex.what()); + for_each(ctx.getResolvedAttributes().begin(), ctx.getResolvedAttributes().end(), xmltooling::cleanup()); + ctx.getResolvedAttributes().clear(); + } + + return true; +} + +void SimpleAggregationResolver::resolveAttributes(ResolutionContext& ctx) const +{ +#ifdef _DEBUG + xmltooling::NDC ndc("resolveAttributes"); +#endif + + SimpleAggregationContext& qctx = dynamic_cast(ctx); + + // First we manufacture the appropriate NameID to use. + NameID* n=NULL; + for (vector::const_iterator a = m_attributeIds.begin(); !n && a != m_attributeIds.end(); ++a) { + const Attribute* attr=NULL; + if (qctx.getSession()) { + // Input attributes should be available via multimap. + pair::const_iterator, multimap::const_iterator> range = + qctx.getSession()->getIndexedAttributes().equal_range(*a); + for (; !attr && range.first != range.second; ++range.first) { + if (range.first->second->valueCount() > 0) + attr = range.first->second; + } + } + else if (qctx.getInputAttributes()) { + // Have to loop over unindexed set. + const vector* matches = qctx.getInputAttributes(); + for (vector::const_iterator match = matches->begin(); !attr && match != matches->end(); ++match) { + if (*a == (*match)->getId() && (*match)->valueCount() > 0) + attr = *match; + } + } + + if (attr) { + m_log.debug("using input attribute (%s) as identifier for queries", attr->getId()); + n = NameIDBuilder::buildNameID(); + const NameIDAttribute* down = dynamic_cast(attr); + if (down) { + // We can create a NameID directly from the source material. + const NameIDAttribute::Value& v = down->getValues().front(); + XMLCh* val = fromUTF8(v.m_Name.c_str()); + n->setName(val); + delete[] val; + if (!v.m_Format.empty()) { + val = fromUTF8(v.m_Format.c_str()); + n->setFormat(val); + delete[] val; + } + if (!v.m_NameQualifier.empty()) { + val = fromUTF8(v.m_NameQualifier.c_str()); + n->setNameQualifier(val); + delete[] val; + } + if (!v.m_SPNameQualifier.empty()) { + val = fromUTF8(v.m_SPNameQualifier.c_str()); + n->setSPNameQualifier(val); + delete[] val; + } + if (!v.m_SPProvidedID.empty()) { + val = fromUTF8(v.m_SPProvidedID.c_str()); + n->setSPProvidedID(val); + delete[] val; + } + } + else { + // We have to mock up the NameID. + XMLCh* val = fromUTF8(attr->getSerializedValues().front().c_str()); + n->setName(val); + delete[] val; + if (!m_format.empty()) + n->setFormat(m_format.c_str()); + } + } + } + + if (!n) { + if (qctx.getNameID() && m_attributeIds.empty()) { + m_log.debug("using authenticated NameID as identifier for queries"); + } + else { + m_log.warn("unable to resolve attributes, no suitable query identifier found"); + return; + } + } + + auto_ptr wrapper(n); + + set history; + + // We have a master loop over all the possible sources of material. + for (vector< pair >::const_iterator source = m_sources.begin(); source != m_sources.end(); ++source) { + if (source->second) { + // A literal entityID to query. + if (history.count(source->first) == 0) { + m_log.debug("issuing SAML query to (%s)", source->first.c_str()); + doQuery(qctx, source->first.c_str(), n ? n : qctx.getNameID()); + history.insert(source->first); + } + else { + m_log.debug("skipping previously queried attribute source (%s)", source->first.c_str()); + } + } + else { + m_log.debug("using attribute sources referenced in attribute (%s)", source->first.c_str()); + if (qctx.getSession()) { + // Input attributes should be available via multimap. + pair::const_iterator, multimap::const_iterator> range = + qctx.getSession()->getIndexedAttributes().equal_range(source->first); + for (; range.first != range.second; ++range.first) { + const vector& links = range.first->second->getSerializedValues(); + for (vector::const_iterator link = links.begin(); link != links.end(); ++link) { + if (history.count(*link) == 0) { + m_log.debug("issuing SAML query to (%s)", link->c_str()); + doQuery(qctx, link->c_str(), n ? n : qctx.getNameID()); + history.insert(*link); + } + else { + m_log.debug("skipping previously queried attribute source (%s)", link->c_str()); + } + } + } + } + else if (qctx.getInputAttributes()) { + // Have to loop over unindexed set. + const vector* matches = qctx.getInputAttributes(); + for (vector::const_iterator match = matches->begin(); match != matches->end(); ++match) { + if (source->first == (*match)->getId()) { + const vector& links = (*match)->getSerializedValues(); + for (vector::const_iterator link = links.begin(); link != links.end(); ++link) { + if (history.count(*link) == 0) { + m_log.debug("issuing SAML query to (%s)", link->c_str()); + doQuery(qctx, link->c_str(), n ? n : qctx.getNameID()); + history.insert(*link); + } + else { + m_log.debug("skipping previously queried attribute source (%s)", link->c_str()); + } + } + } + } + } + } + } +} diff --git a/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp b/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp index eeead9f..c27df80 100644 --- a/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp +++ b/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -24,12 +24,17 @@ #include "Application.h" #include "ServiceProvider.h" #include "attribute/AttributeDecoder.h" +#include "attribute/filtering/AttributeFilter.h" +#include "attribute/filtering/BasicFilteringContext.h" #include "attribute/resolver/AttributeExtractor.h" +#include "security/SecurityPolicy.h" #include "util/SPConstants.h" +#include #include #include #include +#include #include #include #include @@ -51,13 +56,22 @@ namespace shibsp { #pragma warning( disable : 4250 ) #endif - class XMLExtractorImpl + class XMLExtractorImpl : public ObservableMetadataProvider::Observer { public: XMLExtractorImpl(const DOMElement* e, Category& log); ~XMLExtractorImpl() { - for (attrmap_t::iterator i = m_attrMap.begin(); i!=m_attrMap.end(); ++i) - delete i->second.first; + for (map::iterator i=m_decodedMap.begin(); i!=m_decodedMap.end(); ++i) { + i->first->removeObserver(this); + for (decoded_t::iterator attrs = i->second.begin(); attrs!=i->second.end(); ++attrs) + for_each(attrs->second.begin(), attrs->second.end(), mem_fun_ref(&DDF::destroy)); + } + delete m_attrLock; + delete m_trust; + delete m_metadata; + delete m_filter; + for (attrmap_t::iterator j = m_attrMap.begin(); j!=m_attrMap.end(); ++j) + delete j->second.first; if (m_document) m_document->release(); } @@ -66,20 +80,51 @@ namespace shibsp { m_document = doc; } + void onEvent(const ObservableMetadataProvider& metadata) const { + // Destroy attributes we cached from this provider. + m_attrLock->wrlock(); + decoded_t& d = m_decodedMap[&metadata]; + for (decoded_t::iterator a = d.begin(); a!=d.end(); ++a) + for_each(a->second.begin(), a->second.end(), mem_fun_ref(&DDF::destroy)); + d.clear(); + m_attrLock->unlock(); + } + void extractAttributes( - const Application& application, const char* assertingParty, const NameIdentifier& nameid, vector& attributes + const Application& application, + const char* assertingParty, + const char* relyingParty, + const NameIdentifier& nameid, + vector& attributes ) const; void extractAttributes( - const Application& application, const char* assertingParty, const NameID& nameid, vector& attributes + const Application& application, + const char* assertingParty, + const char* relyingParty, + const NameID& nameid, + vector& attributes ) const; void extractAttributes( - const Application& application, const char* assertingParty, const saml1::Attribute& attr, vector& attributes + const Application& application, + const char* assertingParty, + const char* relyingParty, + const saml1::Attribute& attr, + vector& attributes ) const; void extractAttributes( - const Application& application, const char* assertingParty, const saml2::Attribute& attr, vector& attributes + const Application& application, + const char* assertingParty, + const char* relyingParty, + const saml2::Attribute& attr, + vector& attributes ) const; void extractAttributes( - const Application& application, const char* assertingParty, const Extensions& ext, vector& attributes + const Application& application, + const ObservableMetadataProvider* observable, + const XMLCh* entityID, + const char* relyingParty, + const Extensions& ext, + vector& attributes ) const; void getAttributeIds(vector& attributes) const { @@ -96,6 +141,18 @@ namespace shibsp { #endif attrmap_t m_attrMap; vector m_attributeIds; + + // settings for embedded assertions in metadata + auto_ptr_char m_policyId; + MetadataProvider* m_metadata; + TrustEngine* m_trust; + AttributeFilter* m_filter; + bool m_entityAssertions; + + // manages caching of decoded Attributes + mutable RWLock* m_attrLock; + typedef map< const EntityAttributes*,vector > decoded_t; + mutable map m_decodedMap; }; class XMLExtractor : public AttributeExtractor, public ReloadableXMLFile @@ -133,15 +190,28 @@ namespace shibsp { return new XMLExtractor(e); } + static const XMLCh _aliases[] = UNICODE_LITERAL_7(a,l,i,a,s,e,s); static const XMLCh _AttributeDecoder[] = UNICODE_LITERAL_16(A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,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 Attributes[] = UNICODE_LITERAL_10(A,t,t,r,i,b,u,t,e,s); static const XMLCh _id[] = UNICODE_LITERAL_2(i,d); - static const XMLCh _aliases[] = UNICODE_LITERAL_7(a,l,i,a,s,e,s); + 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 _name[] = UNICODE_LITERAL_4(n,a,m,e); static const XMLCh nameFormat[] = UNICODE_LITERAL_10(n,a,m,e,F,o,r,m,a,t); + static const XMLCh metadataPolicyId[] = UNICODE_LITERAL_16(m,e,t,a,d,a,t,a,P,o,l,i,c,y,I,d); + 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); }; -XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) : m_log(log), m_document(NULL) +XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) + : m_log(log), + m_document(NULL), + m_policyId(e ? e->getAttributeNS(NULL, metadataPolicyId) : NULL), + m_metadata(NULL), + m_trust(NULL), + m_filter(NULL), + m_entityAssertions(true), + m_attrLock(NULL) { #ifdef _DEBUG xmltooling::NDC ndc("XMLExtractorImpl"); @@ -150,7 +220,61 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) : m_log(l if (!XMLHelper::isNodeNamed(e, shibspconstants::SHIB2ATTRIBUTEMAP_NS, Attributes)) throw ConfigurationException("XML AttributeExtractor requires am:Attributes at root of configuration."); - DOMElement* child = XMLHelper::getFirstChildElement(e, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME); + DOMElement* child = XMLHelper::getFirstChildElement(e, shibspconstants::SHIB2ATTRIBUTEMAP_NS, _MetadataProvider); + if (child) { + try { + auto_ptr_char type(child->getAttributeNS(NULL, _type)); + if (!type.get() || !*type.get()) + throw ConfigurationException("MetadataProvider element missing type attribute."); + m_log.info("building MetadataProvider of type %s...", type.get()); + auto_ptr mp(SAMLConfig::getConfig().MetadataProviderManager.newPlugin(type.get(), child)); + mp->init(); + m_metadata = mp.release(); + } + catch (exception& ex) { + m_entityAssertions = false; + m_log.crit("error building/initializing dedicated MetadataProvider: %s", ex.what()); + m_log.crit("disabling support for Assertions in EntityAttributes extension"); + } + } + + if (m_entityAssertions) { + child = XMLHelper::getFirstChildElement(e, shibspconstants::SHIB2ATTRIBUTEMAP_NS, _TrustEngine); + if (child) { + try { + auto_ptr_char type(child->getAttributeNS(NULL, _type)); + if (!type.get() || !*type.get()) + throw ConfigurationException("TrustEngine element missing type attribute."); + m_log.info("building TrustEngine of type %s...", type.get()); + m_trust = XMLToolingConfig::getConfig().TrustEngineManager.newPlugin(type.get(), child); + } + catch (exception& ex) { + m_entityAssertions = false; + m_log.crit("error building/initializing dedicated TrustEngine: %s", ex.what()); + m_log.crit("disabling support for Assertions in EntityAttributes extension"); + } + } + } + + if (m_entityAssertions) { + child = XMLHelper::getFirstChildElement(e, shibspconstants::SHIB2ATTRIBUTEMAP_NS, _AttributeFilter); + if (child) { + try { + auto_ptr_char type(child->getAttributeNS(NULL, _type)); + if (!type.get() || !*type.get()) + throw ConfigurationException("AttributeFilter element missing type attribute."); + m_log.info("building AttributeFilter of type %s...", type.get()); + m_filter = SPConfig::getConfig().AttributeFilterManager.newPlugin(type.get(), child); + } + catch (exception& ex) { + m_entityAssertions = false; + m_log.crit("error building/initializing dedicated AttributeFilter: %s", ex.what()); + m_log.crit("disabling support for Assertions in EntityAttributes extension"); + } + } + } + + child = XMLHelper::getFirstChildElement(e, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME); while (child) { // Check for missing name or id. const XMLCh* name = child->getAttributeNS(NULL, _name); @@ -176,7 +300,7 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) : m_log(l try { DOMElement* dchild = XMLHelper::getFirstChildElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, _AttributeDecoder); if (dchild) { - auto_ptr q(XMLHelper::getXSIType(dchild)); + auto_ptr q(XMLHelper::getXSIType(dchild)); if (q.get()) decoder = SPConfig::getConfig().AttributeDecoderManager.newPlugin(*q.get(), dchild); } @@ -251,10 +375,16 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) : m_log(l child = XMLHelper::getNextSiblingElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME); } + + m_attrLock = RWLock::create(); } void XMLExtractorImpl::extractAttributes( - const Application& application, const char* assertingParty, const NameIdentifier& nameid, vector& attributes + const Application& application, + const char* assertingParty, + const char* relyingParty, + const NameIdentifier& nameid, + vector& attributes ) const { #ifdef HAVE_GOOD_STL @@ -272,7 +402,7 @@ void XMLExtractorImpl::extractAttributes( auto_ptr_char temp(format); if ((rule=m_attrMap.find(pair(temp.get(),string()))) != m_attrMap.end()) { #endif - Attribute* a = rule->second.first->decode(rule->second.second, &nameid, assertingParty, application.getString("entityID").second); + Attribute* a = rule->second.first->decode(rule->second.second, &nameid, assertingParty, relyingParty); if (a) attributes.push_back(a); } @@ -285,7 +415,11 @@ void XMLExtractorImpl::extractAttributes( } void XMLExtractorImpl::extractAttributes( - const Application& application, const char* assertingParty, const NameID& nameid, vector& attributes + const Application& application, + const char* assertingParty, + const char* relyingParty, + const NameID& nameid, + vector& attributes ) const { #ifdef HAVE_GOOD_STL @@ -303,7 +437,7 @@ void XMLExtractorImpl::extractAttributes( auto_ptr_char temp(format); if ((rule=m_attrMap.find(pair(temp.get(),string()))) != m_attrMap.end()) { #endif - Attribute* a = rule->second.first->decode(rule->second.second, &nameid, assertingParty, application.getString("entityID").second); + Attribute* a = rule->second.first->decode(rule->second.second, &nameid, assertingParty, relyingParty); if (a) attributes.push_back(a); } @@ -316,7 +450,11 @@ void XMLExtractorImpl::extractAttributes( } void XMLExtractorImpl::extractAttributes( - const Application& application, const char* assertingParty, const saml1::Attribute& attr, vector& attributes + const Application& application, + const char* assertingParty, + const char* relyingParty, + const saml1::Attribute& attr, + vector& attributes ) const { #ifdef HAVE_GOOD_STL @@ -338,7 +476,7 @@ void XMLExtractorImpl::extractAttributes( auto_ptr_char temp2(format); if ((rule=m_attrMap.find(pair(temp1.get(),temp2.get()))) != m_attrMap.end()) { #endif - Attribute* a = rule->second.first->decode(rule->second.second, &attr, assertingParty, application.getString("entityID").second); + Attribute* a = rule->second.first->decode(rule->second.second, &attr, assertingParty, relyingParty); if (a) attributes.push_back(a); } @@ -352,7 +490,11 @@ void XMLExtractorImpl::extractAttributes( } void XMLExtractorImpl::extractAttributes( - const Application& application, const char* assertingParty, const saml2::Attribute& attr, vector& attributes + const Application& application, + const char* assertingParty, + const char* relyingParty, + const saml2::Attribute& attr, + vector& attributes ) const { #ifdef HAVE_GOOD_STL @@ -376,7 +518,7 @@ void XMLExtractorImpl::extractAttributes( auto_ptr_char temp2(format); if ((rule=m_attrMap.find(pair(temp1.get(),temp2.get()))) != m_attrMap.end()) { #endif - Attribute* a = rule->second.first->decode(rule->second.second, &attr, assertingParty, application.getString("entityID").second); + Attribute* a = rule->second.first->decode(rule->second.second, &attr, assertingParty, relyingParty); if (a) attributes.push_back(a); } @@ -390,14 +532,246 @@ void XMLExtractorImpl::extractAttributes( } void XMLExtractorImpl::extractAttributes( - const Application& application, const char* assertingParty, const Extensions& ext, vector& attributes + const Application& application, + const ObservableMetadataProvider* observable, + const XMLCh* entityID, + const char* relyingParty, + const Extensions& ext, + vector& attributes ) const { - const vector exts = ext.getUnknownXMLObjects(); + const vector& exts = ext.getUnknownXMLObjects(); for (vector::const_iterator i = exts.begin(); i!=exts.end(); ++i) { - const saml2::Attribute* attr = dynamic_cast(*i); - if (attr) - extractAttributes(application, assertingParty, *attr, attributes); + const EntityAttributes* container = dynamic_cast(*i); + if (!container) + continue; + + bool useCache = false; + map::iterator cacheEntry; + + // Check for cached result. + if (observable) { + m_attrLock->rdlock(); + cacheEntry = m_decodedMap.find(observable); + if (cacheEntry == m_decodedMap.end()) { + // We need to elevate the lock and retry. + m_attrLock->unlock(); + m_attrLock->wrlock(); + cacheEntry = m_decodedMap.find(observable); + if (cacheEntry==m_decodedMap.end()) { + + // It's still brand new, so hook it for cache activation. + observable->addObserver(this); + + // Prime the map reference with an empty decoded map. + cacheEntry = m_decodedMap.insert(make_pair(observable,decoded_t())).first; + + // Downgrade the lock. + // We don't have to recheck because we never erase the master map entry entirely, even on changes. + m_attrLock->unlock(); + m_attrLock->rdlock(); + } + } + useCache = true; + } + + if (useCache) { + // We're holding a read lock, so check the cache. + decoded_t::iterator d = cacheEntry->second.find(container); + if (d != cacheEntry->second.end()) { + SharedLock locker(m_attrLock, false); // pop the lock when we're done + for (vector::iterator obj = d->second.begin(); obj != d->second.end(); ++obj) { + auto_ptr wrapper(Attribute::unmarshall(*obj)); + m_log.debug("recovered cached metadata attribute (%s)", wrapper->getId()); + attributes.push_back(wrapper.release()); + } + break; + } + } + + // Use a holding area to support caching. + vector holding; + + const vector& attrs = container->getAttributes(); + for (vector::const_iterator attr = attrs.begin(); attr != attrs.end(); ++attr) { + try { + extractAttributes(application, NULL, relyingParty, *(*attr), holding); + } + catch (...) { + if (useCache) + m_attrLock->unlock(); + for_each(holding.begin(), holding.end(), xmltooling::cleanup()); + throw; + } + } + + if (entityID && m_entityAssertions) { + const vector& asserts = container->getAssertions(); + for (vector::const_iterator assert = asserts.begin(); assert != asserts.end(); ++assert) { + if (!(*assert)->getSignature()) { + if (m_log.isDebugEnabled()) { + auto_ptr_char eid(entityID); + m_log.debug("skipping unsigned assertion in metadata extension for entity (%s)", eid.get()); + } + continue; + } + else if ((*assert)->getAttributeStatements().empty()) { + if (m_log.isDebugEnabled()) { + auto_ptr_char eid(entityID); + m_log.debug("skipping assertion with no AttributeStatement in metadata extension for entity (%s)", eid.get()); + } + continue; + } + else { + // Check subject. + const NameID* subject = (*assert)->getSubject() ? (*assert)->getSubject()->getNameID() : NULL; + if (!subject || + !XMLString::equals(subject->getFormat(), NameID::ENTITY) || + !XMLString::equals(subject->getName(), entityID)) { + if (m_log.isDebugEnabled()) { + auto_ptr_char eid(entityID); + m_log.debug("skipping assertion with improper Subject in metadata extension for entity (%s)", eid.get()); + } + continue; + } + } + + // Use a private holding area for filtering purposes. + vector holding2; + + try { + // Set up and evaluate a policy for an AA asserting attributes to us. + shibsp::SecurityPolicy policy(application, &AttributeAuthorityDescriptor::ELEMENT_QNAME, false, m_policyId.get()); + Locker locker(m_metadata); + if (m_metadata) + policy.setMetadataProvider(m_metadata); + if (m_trust) + policy.setTrustEngine(m_trust); + // Populate recipient as audience. + const XMLCh* issuer = (*assert)->getIssuer() ? (*assert)->getIssuer()->getName() : NULL; + policy.getAudiences().push_back(application.getRelyingParty(issuer)->getXMLString("entityID").second); + + // Extract assertion information for policy. + policy.setMessageID((*assert)->getID()); + policy.setIssueInstant((*assert)->getIssueInstantEpoch()); + policy.setIssuer((*assert)->getIssuer()); + + // Look up metadata for issuer. + if (policy.getIssuer() && policy.getMetadataProvider()) { + if (policy.getIssuer()->getFormat() && !XMLString::equals(policy.getIssuer()->getFormat(), saml2::NameIDType::ENTITY)) { + m_log.debug("non-system entity issuer, skipping metadata lookup"); + } + else { + m_log.debug("searching metadata for entity assertion issuer..."); + pair lookup; + MetadataProvider::Criteria& mc = policy.getMetadataProviderCriteria(); + mc.entityID_unicode = policy.getIssuer()->getName(); + mc.role = &AttributeAuthorityDescriptor::ELEMENT_QNAME; + mc.protocol = samlconstants::SAML20P_NS; + lookup = policy.getMetadataProvider()->getEntityDescriptor(mc); + if (!lookup.first) { + auto_ptr_char iname(policy.getIssuer()->getName()); + m_log.debug("no metadata found, can't establish identity of issuer (%s)", iname.get()); + } + else if (!lookup.second) { + m_log.debug("unable to find compatible AA role in metadata"); + } + else { + policy.setIssuerMetadata(lookup.second); + } + } + } + + // Authenticate the assertion. We have to clone and marshall it to establish the signature for verification. + auto_ptr tokencopy((*assert)->cloneAssertion()); + tokencopy->marshall(); + policy.evaluate(*tokencopy); + if (!policy.isAuthenticated()) { + if (m_log.isDebugEnabled()) { + auto_ptr_char tempid(tokencopy->getID()); + auto_ptr_char eid(entityID); + m_log.debug( + "failed to authenticate assertion (%s) in metadata extension for entity (%s)", tempid.get(), eid.get() + ); + } + continue; + } + + // Override the asserting/relying party names based on this new issuer. + const EntityDescriptor* inlineEntity = + policy.getIssuerMetadata() ? dynamic_cast(policy.getIssuerMetadata()->getParent()) : NULL; + auto_ptr_char inlineAssertingParty(inlineEntity ? inlineEntity->getEntityID() : NULL); + relyingParty = application.getRelyingParty(inlineEntity)->getString("entityID").second; + const vector& attrs2 = + const_cast(tokencopy->getAttributeStatements().front())->getAttributes(); + for (vector::const_iterator a = attrs2.begin(); a!=attrs2.end(); ++a) + extractAttributes(application, inlineAssertingParty.get(), relyingParty, *(*a), holding2); + + // Now we locally filter the attributes so that the actual issuer can be properly set. + // If we relied on outside filtering, the attributes couldn't be distinguished from the + // ones that come from the user's IdP. + if (m_filter && !holding2.empty()) { + BasicFilteringContext fc(application, holding2, policy.getIssuerMetadata()); + Locker filtlocker(m_filter); + try { + m_filter->filterAttributes(fc, holding2); + } + catch (exception& ex) { + m_log.error("caught exception filtering attributes: %s", ex.what()); + m_log.error("dumping extracted attributes due to filtering exception"); + for_each(holding2.begin(), holding2.end(), xmltooling::cleanup()); + holding2.clear(); + } + } + + if (!holding2.empty()) { + // Copy them over to the main holding tank. + holding.insert(holding.end(), holding2.begin(), holding2.end()); + } + } + catch (exception& ex) { + // Known exceptions are handled gracefully by skipping the assertion. + if (m_log.isDebugEnabled()) { + auto_ptr_char tempid((*assert)->getID()); + auto_ptr_char eid(entityID); + m_log.debug( + "exception authenticating assertion (%s) in metadata extension for entity (%s): %s", + tempid.get(), + eid.get(), + ex.what() + ); + } + for_each(holding2.begin(), holding2.end(), xmltooling::cleanup()); + continue; + } + catch (...) { + // Unknown exceptions are fatal. + if (useCache) + m_attrLock->unlock(); + for_each(holding.begin(), holding.end(), xmltooling::cleanup()); + for_each(holding2.begin(), holding2.end(), xmltooling::cleanup()); + throw; + } + } + } + + if (!holding.empty()) { + if (useCache) { + m_attrLock->unlock(); + m_attrLock->wrlock(); + SharedLock locker(m_attrLock, false); // pop the lock when we're done + if (cacheEntry->second.count(container) == 0) { + for (vector::const_iterator held = holding.begin(); held != holding.end(); ++held) + cacheEntry->second[container].push_back((*held)->marshall()); + } + } + attributes.insert(attributes.end(), holding.begin(), holding.end()); + } + else if (useCache) { + m_attrLock->unlock(); + } + + break; // only process a single extension element } } @@ -408,16 +782,19 @@ void XMLExtractor::extractAttributes( if (!m_impl) return; + const EntityDescriptor* entity = issuer ? dynamic_cast(issuer->getParent()) : NULL; + const char* relyingParty = application.getRelyingParty(entity)->getString("entityID").second; + // Check for assertions. if (XMLString::equals(xmlObject.getElementQName().getLocalPart(), saml1::Assertion::LOCAL_NAME)) { const saml2::Assertion* token2 = dynamic_cast(&xmlObject); if (token2) { - auto_ptr_char assertingParty(issuer ? dynamic_cast(issuer->getParent())->getEntityID() : NULL); + auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL); const vector& statements = token2->getAttributeStatements(); for (vector::const_iterator s = statements.begin(); s!=statements.end(); ++s) { const vector& attrs = const_cast(*s)->getAttributes(); for (vector::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) - m_impl->extractAttributes(application, assertingParty.get(), *(*a), attributes); + m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *(*a), attributes); const vector& encattrs = const_cast(*s)->getEncryptedAttributes(); for (vector::const_iterator ea = encattrs.begin(); ea!=encattrs.end(); ++ea) @@ -428,12 +805,12 @@ void XMLExtractor::extractAttributes( const saml1::Assertion* token1 = dynamic_cast(&xmlObject); if (token1) { - auto_ptr_char assertingParty(issuer ? dynamic_cast(issuer->getParent())->getEntityID() : NULL); + auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL); const vector& statements = token1->getAttributeStatements(); for (vector::const_iterator s = statements.begin(); s!=statements.end(); ++s) { const vector& attrs = const_cast(*s)->getAttributes(); for (vector::const_iterator a = attrs.begin(); a!=attrs.end(); ++a) - m_impl->extractAttributes(application, assertingParty.get(), *(*a), attributes); + m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *(*a), attributes); } return; } @@ -443,18 +820,34 @@ void XMLExtractor::extractAttributes( // Check for metadata. if (XMLString::equals(xmlObject.getElementQName().getNamespaceURI(), samlconstants::SAML20MD_NS)) { - const EntityDescriptor* entity = dynamic_cast(&xmlObject); - if (!entity) + const RoleDescriptor* roleToExtract = dynamic_cast(&xmlObject); + const EntityDescriptor* entityToExtract = roleToExtract ? dynamic_cast(roleToExtract->getParent()) : NULL; + if (!entityToExtract) throw AttributeExtractionException("Unable to extract attributes, unknown metadata object type."); - auto_ptr_char assertingParty(issuer ? dynamic_cast(issuer->getParent())->getEntityID() : NULL); - const Extensions* ext = entity->getExtensions(); - if (ext) - m_impl->extractAttributes(application, assertingParty.get(), *ext, attributes); - const EntitiesDescriptor* group = dynamic_cast(entity->getParent()); + const Extensions* ext = entityToExtract->getExtensions(); + if (ext) { + m_impl->extractAttributes( + application, + dynamic_cast(application.getMetadataProvider(false)), + entityToExtract->getEntityID(), + relyingParty, + *ext, + attributes + ); + } + const EntitiesDescriptor* group = dynamic_cast(entityToExtract->getParent()); while (group) { ext = group->getExtensions(); - if (ext) - m_impl->extractAttributes(application, assertingParty.get(), *ext, attributes); + if (ext) { + m_impl->extractAttributes( + application, + dynamic_cast(application.getMetadataProvider(false)), + NULL, // not an entity, so inline assertions won't be processed + relyingParty, + *ext, + attributes + ); + } group = dynamic_cast(group->getParent()); } return; @@ -462,15 +855,14 @@ void XMLExtractor::extractAttributes( // Check for attributes. if (XMLString::equals(xmlObject.getElementQName().getLocalPart(), saml1::Attribute::LOCAL_NAME)) { - auto_ptr_char assertingParty(issuer ? dynamic_cast(issuer->getParent())->getEntityID() : NULL); - + auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL); const saml2::Attribute* attr2 = dynamic_cast(&xmlObject); if (attr2) - return m_impl->extractAttributes(application, assertingParty.get(), *attr2, attributes); + return m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *attr2, attributes); const saml1::Attribute* attr1 = dynamic_cast(&xmlObject); if (attr1) - return m_impl->extractAttributes(application, assertingParty.get(), *attr1, attributes); + return m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *attr1, attributes); throw AttributeExtractionException("Unable to extract attributes, unknown object type."); } @@ -511,14 +903,14 @@ void XMLExtractor::extractAttributes( // Check for NameIDs. const NameID* name2 = dynamic_cast(&xmlObject); if (name2) { - auto_ptr_char assertingParty(issuer ? dynamic_cast(issuer->getParent())->getEntityID() : NULL); - return m_impl->extractAttributes(application, assertingParty.get(), *name2, attributes); + auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL); + return m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *name2, attributes); } const NameIdentifier* name1 = dynamic_cast(&xmlObject); if (name1) { - auto_ptr_char assertingParty(issuer ? dynamic_cast(issuer->getParent())->getEntityID() : NULL); - return m_impl->extractAttributes(application, assertingParty.get(), *name1, attributes); + auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL); + return m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *name1, attributes); } throw AttributeExtractionException("Unable to extract attributes, unknown object type."); diff --git a/shibsp/base.h b/shibsp/base.h index 9eb4cf3..7e7def0 100644 --- a/shibsp/base.h +++ b/shibsp/base.h @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * @file shibsp/base.h - * + * * Base header file definitions * Must be included prior to including any other header */ @@ -68,14 +68,10 @@ #ifdef WIN32 -/** - * Default catalog path on Windows. - */ +/** Default catalog path on Windows. */ # define SHIBSP_SCHEMAS "c:/opt/shibboleth-sp/share/xml/xmltooling/catalog.xml;c:/opt/shibboleth-sp/share/xml/opensaml/saml20-catalog.xml;c:/opt/shibboleth-sp/share/xml/opensaml/saml11-catalog.xml;c:/opt/shibboleth-sp/share/xml/shibboleth/catalog.xml" -/** - * Default name of configuration file on Windows. - */ +/** Default name of configuration file on Windows. */ # define SHIBSP_CONFIG "shibboleth2.xml" /** @@ -84,23 +80,32 @@ */ #define SHIBSP_LOGGING "console.logger" -/** - * Default prefix for installation (used to resolve relative paths). - */ +/** Default prefix for installation (used to resolve relative paths). */ #define SHIBSP_PREFIX "c:/opt/shibboleth-sp" +/** Library directory for installation (used to resolve relative paths). */ +#define SHIBSP_LIBDIR "lib" + +/** Log directory for installation (used to resolve relative paths). */ +#define SHIBSP_LOGDIR "var/log" + +/** Configuration directory for installation (used to resolve relative paths). */ +#define SHIBSP_CFGDIR "etc" + +/** Runtime state directory for installation (used to resolve relative paths). */ +#define SHIBSP_RUNDIR "var/run" + +/** XML directory for installation (used to resolve relative paths). */ +#define SHIBSP_XMLDIR "share/xml" + #else # include #endif -/** - * Logging category for Service Provider functions. - */ +/** Logging category for Service Provider functions. */ #define SHIBSP_LOGCAT "Shibboleth" -/** - * Logging category for Service Provider auditing. - */ +/** Logging category for Service Provider auditing. */ #define SHIBSP_TX_LOGCAT "Shibboleth-TRANSACTION" #endif /* __shibsp_base_h__ */ diff --git a/shibsp/binding/SOAPClient.h b/shibsp/binding/SOAPClient.h index 573f3f1..eedebc0 100644 --- a/shibsp/binding/SOAPClient.h +++ b/shibsp/binding/SOAPClient.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -72,9 +72,6 @@ namespace shibsp { /** Application supplied to client. */ const Application& m_app; - /** Properties associated with the Application's security policy. */ - const PropertySet* m_settings; - /** RelyingParty properties, set after transport prep. */ const PropertySet* m_relyingParty; diff --git a/shibsp/binding/impl/ArtifactResolver.cpp b/shibsp/binding/impl/ArtifactResolver.cpp index 332549f..52e68fc 100644 --- a/shibsp/binding/impl/ArtifactResolver.cpp +++ b/shibsp/binding/impl/ArtifactResolver.cpp @@ -87,7 +87,7 @@ saml1p::Response* ArtifactResolver::resolve( throw MetadataException("No compatible endpoint found in issuer's metadata."); else if (!response) throw BindingException("Unable to resolve artifact(s) into a SAML response."); - const QName* code = (response->getStatus() && response->getStatus()->getStatusCode()) ? response->getStatus()->getStatusCode()->getValue() : NULL; + const xmltooling::QName* code = (response->getStatus() && response->getStatus()->getStatusCode()) ? response->getStatus()->getStatusCode()->getValue() : NULL; if (!code || *code != saml1p::StatusCode::SUCCESS) { delete response; throw BindingException("Identity provider returned a SAML error in response to artifact(s)."); diff --git a/shibsp/binding/impl/SOAPClient.cpp b/shibsp/binding/impl/SOAPClient.cpp index ad3d3cb..cc5c6dc 100644 --- a/shibsp/binding/impl/SOAPClient.cpp +++ b/shibsp/binding/impl/SOAPClient.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -38,12 +38,8 @@ using namespace xmltooling; using namespace std; SOAPClient::SOAPClient(SecurityPolicy& policy) - : opensaml::SOAPClient(policy), m_app(policy.getApplication()), m_settings(NULL), m_relyingParty(NULL), m_credResolver(NULL) + : opensaml::SOAPClient(policy), m_app(policy.getApplication()), m_relyingParty(NULL), m_credResolver(NULL) { - m_settings = m_app.getServiceProvider().getPolicySettings(m_app.getString("policyId").second); - pair validate = m_settings->getBool("validate"); - policy.setValidating(validate.first && validate.second); - setValidating(validate.first && validate.second); } void SOAPClient::send(const soap11::Envelope& env, const char* from, MetadataCredentialCriteria& to, const char* endpoint) @@ -114,8 +110,7 @@ void SOAPClient::prepareTransport(SOAPTransport& transport) if ((!flag.first || flag.second) && !transport.isConfidential()) throw opensaml::BindingException("Transport confidentiality required, but not available."); - flag = m_settings->getBool("validate"); - setValidating(flag.first && flag.second); + setValidating(getPolicy().getValidating()); flag = m_relyingParty->getBool("requireTransportAuth"); forceTransportAuthentication(!flag.first || flag.second); diff --git a/shibsp/handler/AbstractHandler.h b/shibsp/handler/AbstractHandler.h index 140fa11..20a480b 100644 --- a/shibsp/handler/AbstractHandler.h +++ b/shibsp/handler/AbstractHandler.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -24,6 +24,7 @@ #define __shibsp_abshandler_h__ #include +#include #include #ifndef SHIBSP_LITE @@ -155,6 +156,56 @@ namespace shibsp { bool clear=true ) const; + /** + * Implements a mechanism to preserve form post data. + * + * @param application the associated Application + * @param request incoming HTTP request + * @param response outgoing HTTP response + * @param relayState relay state information attached to current sequence, if any + */ + virtual void preservePostData( + const Application& application, + const xmltooling::HTTPRequest& request, + xmltooling::HTTPResponse& response, + const char* relayState + ) const; + + /** + * Implements storage service and cookie mechanism to recover PostData. + * + *

If a supported mechanism can be identified, the return value will be + * the recovered state information. + * + * @param application the associated Application + * @param request incoming HTTP request + * @param response outgoing HTTP response + * @param relayState relay state information attached to current sequence, if any + * @return recovered form post data associated with request as a DDF list of string members + */ + virtual DDF recoverPostData( + const Application& application, + const xmltooling::HTTPRequest& request, + xmltooling::HTTPResponse& response, + const char* relayState + ) const; + + /** + * Post a redirect response with post data. + * + * @param application the associated Application + * @param response outgoing HTTP response + * @param request incoming HTTP request + * @param url action url for the form + * @param postData list of parameters to load into the form, as DDF string members + */ + virtual long sendPostResponse( + const Application& application, + xmltooling::HTTPResponse& httpResponse, + const char* url, + DDF& postData + ) const; + /** Logging object. */ xmltooling::logging::Category& m_log; @@ -163,6 +214,10 @@ namespace shibsp { public: virtual ~AbstractHandler() {} + + private: + std::pair getPostCookieNameProps(const Application& app, const char* relayState) const; + DDF getPostData(const Application& application, const xmltooling::HTTPRequest& request) const; }; #if defined (_MSC_VER) diff --git a/shibsp/handler/AssertionConsumerService.h b/shibsp/handler/AssertionConsumerService.h index a7289fe..752e86c 100644 --- a/shibsp/handler/AssertionConsumerService.h +++ b/shibsp/handler/AssertionConsumerService.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -76,6 +76,23 @@ namespace shibsp { void generateMetadata(opensaml::saml2md::SPSSODescriptor& role, const char* handlerURL) const; /** + * Returns a SecurityPolicy instance to use for an incoming request. + * + *

Allows handlers to customize the type of policy object their policy rules might require. + *

The caller MUST lock the application's MetadataProvider for the life + * of the returned object. + * + * @param application reference to application receiving message + * @param role identifies the role (generally IdP or SP) of the policy peer + * @param validate true iff XML parsing should be done with validation + * @param policyId identifies policy rules to auto-attach, defaults to the application's set + * @return a new policy instance, which the caller is responsible for freeing + */ + virtual opensaml::SecurityPolicy* createSecurityPolicy( + const Application& application, const xmltooling::QName* role, bool validate, const char* policyId + ) const; + + /** * Implement protocol-specific handling of the incoming decoded message. * *

The result of implementing the protocol should be an exception or diff --git a/shibsp/handler/impl/AbstractHandler.cpp b/shibsp/handler/impl/AbstractHandler.cpp index cd9039c..e42ef5b 100644 --- a/shibsp/handler/impl/AbstractHandler.cpp +++ b/shibsp/handler/impl/AbstractHandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -28,7 +28,16 @@ #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 +#include +#include +#include +#include + #ifndef SHIBSP_LITE # include @@ -36,6 +45,8 @@ # include # include # include +# include +# include # include using namespace opensaml::saml2md; #else @@ -145,7 +156,7 @@ void AbstractHandler::checkError(const XMLObject* response, const saml2md::RoleD const saml1p::Status* status = r1->getStatus(); if (status) { const saml1p::StatusCode* sc = status->getStatusCode(); - const QName* code = sc ? sc->getValue() : NULL; + 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()); @@ -305,7 +316,7 @@ void AbstractHandler::preserveRelayState(const Application& application, HTTPRes else if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) { DDF out,in = DDF("set::RelayState").structure(); in.addmember("id").string(mech.second); - in.addmember("value").string(relayState.c_str()); + in.addmember("value").unsafe_string(relayState.c_str()); DDFJanitor jin(in),jout(out); out = application.getServiceProvider().getListenerService()->send(in); if (!out.isstring()) @@ -348,9 +359,7 @@ void AbstractHandler::recoverRelayState( relayState.erase(); } else { - Category::getInstance(SHIBSP_LOGCAT".Handler").error( - "Storage-backed RelayState with invalid StorageService ID (%s)", ssid.c_str() - ); + m_log.error("Storage-backed RelayState with invalid StorageService ID (%s)", ssid.c_str()); relayState.erase(); } #endif @@ -402,13 +411,244 @@ void AbstractHandler::recoverRelayState( relayState.erase(); } - // Check for "default" value. - if (relayState.empty() || relayState == "default") { + // Check for "default" value (or the old "cookie" value that might come from stale bookmarks). + if (relayState.empty() || relayState == "default" || relayState == "cookie") { pair homeURL=application.getString("homeURL"); - relayState=homeURL.first ? homeURL.second : "/"; + 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 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 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 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 +{ + const PropertySet* props=application.getPropertySet("Sessions"); + pair 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& collection = respParam.m_collectionMap["PostedData"]; + DDF param = postData.first(); + while (param.isstring()) { + collection.insert(pair(param.name(), (param.string() ? param.string() : ""))); + param = postData.next(); } - if (relayState == "default") - relayState.empty(); + stringstream str; + XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, respParam); + + pair 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 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 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 plimit = props->getUnsignedInt("postLimit"); + if (!plimit.first) + plimit.second = 1024 * 1024; + if (plimit.second == 0 || request.getContentLength() <= plimit.second) { + CGIParser cgi(request); + pair params = cgi.getParameters(NULL); + if (params.first == params.second) + return DDF(); + 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 8f3b008..52f747b 100644 --- a/shibsp/handler/impl/AssertionConsumerService.cpp +++ b/shibsp/handler/impl/AssertionConsumerService.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -147,28 +147,48 @@ pair AssertionConsumerService::processMessage( Locker metadataLocker(application.getMetadataProvider()); // Create the policy. - shibsp::SecurityPolicy policy(application, &m_role, validate.first && validate.second); + auto_ptr policy( + createSecurityPolicy(application, &m_role, validate.first && validate.second, policyId.second) + ); string relayState; - try { // Decode the message and process it in a protocol-specific way. - auto_ptr msg(m_decoder->decode(relayState, httpRequest, policy)); + auto_ptr msg(m_decoder->decode(relayState, httpRequest, *(policy.get()))); if (!msg.get()) throw BindingException("Failed to decode an SSO protocol response."); + DDF postData = recoverPostData(application, httpRequest, httpResponse, relayState.c_str()); + DDFJanitor postjan(postData); recoverRelayState(application, httpRequest, httpResponse, relayState); - implementProtocol(application, httpRequest, httpResponse, policy, settings, *msg.get()); + implementProtocol(application, httpRequest, httpResponse, *(policy.get()), settings, *msg.get()); - auto_ptr_char issuer(policy.getIssuer() ? policy.getIssuer()->getName() : NULL); + auto_ptr_char issuer(policy->getIssuer() ? policy->getIssuer()->getName() : NULL); // History cookie. if (issuer.get() && *issuer.get()) maintainHistory(application, httpRequest, httpResponse, issuer.get()); // Now redirect to the state value. By now, it should be set to *something* usable. - return make_pair(true, httpResponse.sendRedirect(relayState.c_str())); + // First check for POST data. + if (!postData.islist()) { + m_log.debug("ACS returning via redirect to: %s", relayState.c_str()); + return make_pair(true, httpResponse.sendRedirect(relayState.c_str())); + } + else { + m_log.debug("ACS returning via POST to: %s", relayState.c_str()); + return make_pair(true, sendPostResponse(application, httpResponse, relayState.c_str(), postData)); + } } catch (XMLToolingException& ex) { + // Check for isPassive error condition. + const char* sc2 = ex.getProperty("statusCode2"); + if (sc2 && !strcmp(sc2, "urn:oasis:names:tc:SAML:2.0:status:NoPassive")) { + validate = getBool("ignoreNoPassive", m_configNS.get()); // namespace-qualified if inside handler element + if (validate.first && validate.second && !relayState.empty()) { + m_log.debug("ignoring SAML status of NoPassive and redirecting to resource..."); + return make_pair(true, httpResponse.sendRedirect(relayState.c_str())); + } + } if (!relayState.empty()) ex.addProperty("RelayState", relayState.c_str()); throw; @@ -203,7 +223,8 @@ void AssertionConsumerService::checkAddress(const Application& application, cons #ifndef SHIBSP_LITE -void AssertionConsumerService::generateMetadata(SPSSODescriptor& role, const char* handlerURL) const { +void AssertionConsumerService::generateMetadata(SPSSODescriptor& role, const char* handlerURL) const +{ const char* loc = getString("Location").second; string hurl(handlerURL); if (*loc != '/') @@ -226,6 +247,13 @@ void AssertionConsumerService::generateMetadata(SPSSODescriptor& role, const cha role.getAssertionConsumerServices().push_back(ep); } +opensaml::SecurityPolicy* AssertionConsumerService::createSecurityPolicy( + const Application& application, const xmltooling::QName* role, bool validate, const char* policyId + ) const +{ + return new SecurityPolicy(application, role, validate, policyId); +} + class SHIBSP_DLLLOCAL DummyContext : public ResolutionContext { public: @@ -261,19 +289,18 @@ ResolutionContext* AssertionConsumerService::resolveAttributes( const vector* tokens ) const { - const saml2md::EntityDescriptor* entity = issuer ? dynamic_cast(issuer->getParent()) : NULL; - // First we do the extraction of any pushed information, including from metadata. vector resolvedAttributes; AttributeExtractor* extractor = application.getAttributeExtractor(); if (extractor) { Locker extlocker(extractor); - if (entity) { + if (issuer) { pair mprefix = application.getString("metadataAttributePrefix"); if (mprefix.first) { m_log.debug("extracting metadata-derived attributes..."); try { - extractor->extractAttributes(application, issuer, *entity, resolvedAttributes); + // We pass NULL for "issuer" because the IdP isn't the one asserting metadata-based attributes. + extractor->extractAttributes(application, NULL, *issuer, resolvedAttributes); for (vector::iterator a = resolvedAttributes.begin(); a != resolvedAttributes.end(); ++a) { vector& ids = (*a)->getAliases(); for (vector::iterator id = ids.begin(); id != ids.end(); ++id) @@ -338,7 +365,7 @@ ResolutionContext* AssertionConsumerService::resolveAttributes( auto_ptr ctx( resolver->createResolutionContext( application, - entity, + issuer ? dynamic_cast(issuer->getParent()) : NULL, protocol, nameid, authncontext_class, @@ -351,17 +378,6 @@ ResolutionContext* AssertionConsumerService::resolveAttributes( // Copy over any pushed attributes. if (!resolvedAttributes.empty()) ctx->getResolvedAttributes().insert(ctx->getResolvedAttributes().end(), resolvedAttributes.begin(), resolvedAttributes.end()); - - // Attach global prefix if needed. - pair prefix = application.getString("attributePrefix"); - if (prefix.first) { - for (vector::iterator a = ctx->getResolvedAttributes().begin(); a != ctx->getResolvedAttributes().end(); ++a) { - vector& ids = (*a)->getAliases(); - for (vector::iterator id = ids.begin(); id != ids.end(); ++id) - *id = prefix.second + *id; - } - } - return ctx.release(); } } @@ -369,19 +385,8 @@ ResolutionContext* AssertionConsumerService::resolveAttributes( m_log.error("attribute resolution failed: %s", ex.what()); } - if (!resolvedAttributes.empty()) { - // Attach global prefix if needed. - pair prefix = application.getString("attributePrefix"); - if (prefix.first) { - for (vector::iterator a = resolvedAttributes.begin(); a != resolvedAttributes.end(); ++a) { - vector& ids = (*a)->getAliases(); - for (vector::iterator id = ids.begin(); id != ids.end(); ++id) - *id = prefix.second + *id; - } - } - + if (!resolvedAttributes.empty()) return new DummyContext(resolvedAttributes); - } return NULL; } @@ -412,15 +417,11 @@ void AssertionConsumerService::extractMessageDetails(const Assertion& assertion, } m_log.debug("searching metadata for assertion issuer..."); pair entity; - shibsp::SecurityPolicy* sppol = dynamic_cast(&policy); - if (sppol) { - MetadataProviderCriteria mc(sppol->getApplication(), policy.getIssuer()->getName(), &IDPSSODescriptor::ELEMENT_QNAME, protocol); - entity = policy.getMetadataProvider()->getEntityDescriptor(mc); - } - else { - MetadataProvider::Criteria mc(policy.getIssuer()->getName(), &IDPSSODescriptor::ELEMENT_QNAME, protocol); - entity = policy.getMetadataProvider()->getEntityDescriptor(mc); - } + MetadataProvider::Criteria& mc = policy.getMetadataProviderCriteria(); + mc.entityID_unicode = policy.getIssuer()->getName(); + mc.role = &IDPSSODescriptor::ELEMENT_QNAME; + mc.protocol = protocol; + entity = policy.getMetadataProvider()->getEntityDescriptor(mc); if (!entity.first) { auto_ptr_char iname(policy.getIssuer()->getName()); m_log.warn("no metadata found, can't establish identity of issuer (%s)", iname.get()); diff --git a/shibsp/handler/impl/AssertionLookup.cpp b/shibsp/handler/impl/AssertionLookup.cpp index 990d7d7..adeb711 100644 --- a/shibsp/handler/impl/AssertionLookup.cpp +++ b/shibsp/handler/impl/AssertionLookup.cpp @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 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 @@ -16,7 +16,7 @@ /** * AssertionLookup.cpp - * + * * Handler for looking assertions in SessionCache */ @@ -45,7 +45,12 @@ namespace shibsp { class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter { public: - short acceptNode(const DOMNode* node) const { +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { return FILTER_REJECT; } }; @@ -112,10 +117,10 @@ pair AssertionLookup::run(SPRequest& request, bool isHandler) const if (m_acl.count(request.getRemoteAddr()) == 0) { m_log.error("request for assertion lookup blocked from invalid address (%s)", request.getRemoteAddr().c_str()); istringstream msg("Assertion Lookup Blocked"); - return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_UNAUTHORIZED)); + return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN)); } } - + try { if (conf.isEnabled(SPConfig::OutOfProcess)) { // When out of process, we run natively and directly process the message. @@ -125,7 +130,7 @@ pair AssertionLookup::run(SPRequest& request, bool isHandler) const // When not out of process, we remote all the message processing. DDF out,in = wrap(request); DDFJanitor jin(in), jout(out); - + out=request.getServiceProvider().getListenerService()->send(in); return unwrap(request, out); } @@ -147,7 +152,7 @@ void AssertionLookup::receive(DDF& in, ostream& out) m_log.error("couldn't find application (%s) for assertion lookup", aid ? aid : "(missing)"); throw ConfigurationException("Unable to locate application for assertion lookup, deleted?"); } - + // Unpack the request. auto_ptr req(getRequest(in)); //m_log.debug("found %d client certificates", req->getClientCertificates().size()); @@ -156,7 +161,7 @@ void AssertionLookup::receive(DDF& in, ostream& out) DDF ret(NULL); DDFJanitor jout(ret); auto_ptr resp(getResponse(ret)); - + // Since we're remoted, the result should either be a throw, a false/0 return, // which we just return as an empty structure, or a response/redirect, // which we capture in the facade and send back. diff --git a/shibsp/handler/impl/ChainingLogoutInitiator.cpp b/shibsp/handler/impl/ChainingLogoutInitiator.cpp index e8de4d6..4fc6776 100644 --- a/shibsp/handler/impl/ChainingLogoutInitiator.cpp +++ b/shibsp/handler/impl/ChainingLogoutInitiator.cpp @@ -75,7 +75,12 @@ namespace shibsp { class SHIBSP_DLLLOCAL LogoutInitiatorNodeFilter : public DOMNodeFilter { public: - short acceptNode(const DOMNode* node) const { +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { if (XMLHelper::isNodeNamed(node,shibspconstants::SHIB2SPCONFIG_NS,_LogoutInitiator)) return FILTER_REJECT; return FILTER_ACCEPT; diff --git a/shibsp/handler/impl/ChainingSessionInitiator.cpp b/shibsp/handler/impl/ChainingSessionInitiator.cpp index dcd29d6..1cbc45b 100644 --- a/shibsp/handler/impl/ChainingSessionInitiator.cpp +++ b/shibsp/handler/impl/ChainingSessionInitiator.cpp @@ -71,7 +71,12 @@ namespace shibsp { class SHIBSP_DLLLOCAL SessionInitiatorNodeFilter : public DOMNodeFilter { public: - short acceptNode(const DOMNode* node) const { +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { if (XMLHelper::isNodeNamed(node,shibspconstants::SHIB2SPCONFIG_NS,_SessionInitiator)) return FILTER_REJECT; return FILTER_ACCEPT; diff --git a/shibsp/handler/impl/LogoutHandler.cpp b/shibsp/handler/impl/LogoutHandler.cpp index 161dcc3..dfa43ed 100644 --- a/shibsp/handler/impl/LogoutHandler.cpp +++ b/shibsp/handler/impl/LogoutHandler.cpp @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 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 @@ -16,7 +16,7 @@ /** * LogoutHandler.cpp - * + * * Base class for logout-related handlers. */ @@ -71,7 +71,7 @@ pair LogoutHandler::run(SPRequest& request, bool isHandler) const // If we're inside a chain, do nothing. if (getParent()) return make_pair(false,0L); - + // If this isn't a LogoutInitiator, we only "continue" a notification loop, rather than starting one. if (!m_initiator && !request.getParameter("notifying")) return make_pair(false,0L); @@ -172,6 +172,7 @@ pair LogoutHandler::notifyFrontChannel( #include #include #include +#include using namespace soap11; namespace { static const XMLCh LogoutNotification[] = UNICODE_LITERAL_18(L,o,g,o,u,t,N,o,t,i,f,i,c,a,t,i,o,n); @@ -188,6 +189,12 @@ namespace { private: void prepareTransport(SOAPTransport& transport) { transport.setVerifyHost(false); + HTTPSOAPTransport* http = dynamic_cast(&transport); + if (http) { + http->useChunkedEncoding(false); + http->setRequestHeader("User-Agent", PACKAGE_NAME); + http->setRequestHeader(PACKAGE_NAME, PACKAGE_VERSION); + } } }; }; @@ -197,6 +204,11 @@ bool LogoutHandler::notifyBackChannel( const Application& application, const char* requestURL, const vector& sessions, bool local ) const { + if (sessions.empty()) { + Category::getInstance(SHIBSP_LOGCAT".Logout").error("no sessions supplied to back channel notification method"); + return false; + } + unsigned int index = 0; string endpoint = application.getNotificationURL(requestURL, false, index++); if (endpoint.empty()) @@ -209,7 +221,7 @@ bool LogoutHandler::notifyBackChannel( env->setBody(body); ElementProxy* msg = new AnyElementImpl(shibspconstants::SHIB2SPNOTIFY_NS, LogoutNotification); body->getUnknownXMLObjects().push_back(msg); - msg->setAttribute(QName(NULL, _type), local ? _local : _global); + msg->setAttribute(xmltooling::QName(NULL, _type), local ? _local : _global); for (vector::const_iterator s = sessions.begin(); s!=sessions.end(); ++s) { auto_ptr_XMLCh temp(s->c_str()); ElementProxy* child = new AnyElementImpl(shibspconstants::SHIB2SPNOTIFY_NS, SessionID); diff --git a/shibsp/handler/impl/MetadataGenerator.cpp b/shibsp/handler/impl/MetadataGenerator.cpp index 08e73e0..2a847cb 100644 --- a/shibsp/handler/impl/MetadataGenerator.cpp +++ b/shibsp/handler/impl/MetadataGenerator.cpp @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 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 @@ -16,7 +16,7 @@ /** * MetadataGenerator.cpp - * + * * Handler for generating "approximate" metadata based on SP configuration. */ @@ -29,6 +29,7 @@ #ifndef SHIBSP_LITE # include "metadata/MetadataProviderCriteria.h" +# include #endif #include @@ -53,7 +54,12 @@ namespace shibsp { class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter { public: - short acceptNode(const DOMNode* node) const { +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { return FILTER_REJECT; } }; @@ -121,14 +127,14 @@ MetadataGenerator::MetadataGenerator(const DOMElement* e, const char* appId) #ifndef SHIBSP_LITE static XMLCh EndpointBase[] = UNICODE_LITERAL_12(E,n,d,p,o,i,n,t,B,a,s,e); - + pair flag = getBool("http"); if (flag.first) m_http = flag.second ? 1 : -1; flag = getBool("https"); if (flag.first) m_https = flag.second ? 1 : -1; - + e = XMLHelper::getFirstChildElement(e, EndpointBase); while (e) { if (e->hasChildNodes()) { @@ -148,10 +154,10 @@ pair MetadataGenerator::run(SPRequest& request, bool isHandler) const if (!m_acl.empty() && m_acl.count(request.getRemoteAddr()) == 0) { m_log.error("request for metadata blocked from invalid address (%s)", request.getRemoteAddr().c_str()); istringstream msg("Metadata Request Blocked"); - return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_UNAUTHORIZED)); + return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN)); } } - + try { if (conf.isEnabled(SPConfig::OutOfProcess)) { // When out of process, we run natively and directly process the message. @@ -165,7 +171,7 @@ pair MetadataGenerator::run(SPRequest& request, bool isHandler) const if (request.getParameter("entityID")) in.addmember("entity_id").string(request.getParameter("entityID")); DDFJanitor jin(in), jout(out); - + out=request.getServiceProvider().getListenerService()->send(in); return unwrap(request, out); } @@ -191,12 +197,12 @@ void MetadataGenerator::receive(DDF& in, ostream& out) else if (!hurl) { throw ConfigurationException("Missing handler_url parameter in remoted method call."); } - + // Wrap a response shim. DDF ret(NULL); DDFJanitor jout(ret); auto_ptr resp(getResponse(ret)); - + // Since we're remoted, the result should either be a throw, a false/0 return, // which we just return as an empty structure, or a response/redirect, // which we capture in the facade and send back. @@ -226,7 +232,10 @@ pair MetadataGenerator::processMessage( pair prop = getString("template"); if (prop.first) { // Load a template to use for our metadata. - LocalFileInputSource src(getXMLString("template").second); + string templ(prop.second); + XMLToolingConfig::getConfig().getPathResolver()->resolve(templ, PathResolver::XMLTOOLING_CFG_FILE); + auto_ptr_XMLCh widenit(templ.c_str()); + LocalFileInputSource src(widenit.get()); Wrapper4InputSource dsrc(&src,false); DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(dsrc); XercesJanitor docjan(doc); @@ -234,7 +243,7 @@ pair MetadataGenerator::processMessage( docjan.release(); entity = dynamic_cast(xmlobj.get()); if (!entity) - throw ConfigurationException("Template file ($1) did not contain an EntityDescriptor", params(1, prop.second)); + throw ConfigurationException("Template file ($1) did not contain an EntityDescriptor", params(1, templ.c_str())); xmlobj.release(); } else { @@ -243,8 +252,14 @@ pair MetadataGenerator::processMessage( auto_ptr wrapper(entity); pair cache = getUnsignedInt("cacheDuration"); - if (cache.first) - entity->setValidUntil(time(NULL) + cache.second); + if (cache.first) { + entity->setCacheDuration(cache.second); + } + else { + cache = getUnsignedInt("validUntil"); + if (cache.first) + entity->setValidUntil(time(NULL) + cache.second); + } entity->setEntityID(relyingParty->getXMLString("entityID").second); SPSSODescriptor* role; @@ -355,7 +370,7 @@ pair MetadataGenerator::processMessage( XMLHelper::serialize(entity->marshall(), pretty, true); DOMDocument* prettydoc = XMLToolingConfig::getConfig().getParser().parse(pretty); auto_ptr prettyentity(XMLObjectBuilder::buildOneFromElement(prettydoc->getDocumentElement(), true)); - + Signature* sig = SignatureBuilder::buildSignature(); dynamic_cast(prettyentity.get())->setSignature(sig); if (sigalg.first) diff --git a/shibsp/handler/impl/RemotedHandler.cpp b/shibsp/handler/impl/RemotedHandler.cpp index b8580e2..2ee35a1 100644 --- a/shibsp/handler/impl/RemotedHandler.cpp +++ b/shibsp/handler/impl/RemotedHandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -206,7 +206,7 @@ long RemotedResponse::sendRedirect(const char* url) { if (!m_output.isstruct()) m_output.structure(); - m_output.addmember("redirect").string(url); + m_output.addmember("redirect").unsafe_string(url); return HTTPResponse::XMLTOOLING_HTTP_STATUS_MOVED; } @@ -240,7 +240,7 @@ DDF RemotedHandler::wrap(const SPRequest& request, const vector* headers DDF in = DDF(m_address.c_str()).structure(); in.addmember("application_id").string(request.getApplication().getId()); in.addmember("scheme").string(request.getScheme()); - in.addmember("hostname").string(request.getHostname()); + in.addmember("hostname").unsafe_string(request.getHostname()); in.addmember("port").integer(request.getPort()); in.addmember("content_type").string(request.getContentType().c_str()); in.addmember("content_length").integer(request.getContentLength()); @@ -248,8 +248,8 @@ DDF RemotedHandler::wrap(const SPRequest& request, const vector* headers in.addmember("remote_user").string(request.getRemoteUser().c_str()); in.addmember("client_addr").string(request.getRemoteAddr().c_str()); in.addmember("method").string(request.getMethod()); - in.addmember("uri").string(request.getRequestURI()); - in.addmember("url").string(request.getRequestURL()); + in.addmember("uri").unsafe_string(request.getRequestURI()); + in.addmember("url").unsafe_string(request.getRequestURL()); in.addmember("query").string(request.getQueryString()); if (headers) { diff --git a/shibsp/handler/impl/SAML1Consumer.cpp b/shibsp/handler/impl/SAML1Consumer.cpp index d1c46b8..239d7d2 100644 --- a/shibsp/handler/impl/SAML1Consumer.cpp +++ b/shibsp/handler/impl/SAML1Consumer.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,8 +16,8 @@ /** * SAML1Consumer.cpp - * - * SAML 1.x assertion consumer service + * + * SAML 1.x assertion consumer service */ #include "internal.h" @@ -29,9 +29,9 @@ # include "ServiceProvider.h" # include "SessionCache.h" # include "attribute/resolver/ResolutionContext.h" +# include # include # include -# include # include using namespace opensaml::saml1; using namespace opensaml::saml1p; @@ -55,18 +55,25 @@ namespace shibsp { #pragma warning( push ) #pragma warning( disable : 4250 ) #endif - + class SHIBSP_DLLLOCAL SAML1Consumer : public AssertionConsumerService { public: SAML1Consumer(const DOMElement* e, const char* appId) - : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".SSO.SAML1")) { + : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".SSO.SAML1")) { #ifndef SHIBSP_LITE + m_ssoRule = NULL; m_post = XMLString::equals(getString("Binding").second, samlconstants::SAML1_PROFILE_BROWSER_POST); + if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) + m_ssoRule = SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(SAML1BROWSERSSO_POLICY_RULE, e); #endif } - virtual ~SAML1Consumer() {} - + virtual ~SAML1Consumer() { +#ifndef SHIBSP_LITE + delete m_ssoRule; +#endif + } + #ifndef SHIBSP_LITE void generateMetadata(SPSSODescriptor& role, const char* handlerURL) const { AssertionConsumerService::generateMetadata(role, handlerURL); @@ -83,7 +90,9 @@ namespace shibsp { const PropertySet* settings, const XMLObject& xmlObject ) const; + bool m_post; + SecurityPolicyRule* m_ssoRule; #endif }; @@ -95,7 +104,19 @@ namespace shibsp { { return new SAML1Consumer(p.first, p.second); } - + +#ifndef SHIBSP_LITE + class SHIBSP_DLLLOCAL _rulenamed : std::unary_function + { + public: + _rulenamed(const char* name) : m_name(name) {} + bool operator()(const SecurityPolicyRule* rule) const { + return rule ? !strcmp(m_name, rule->getType()) : false; + } + private: + const char* m_name; + }; +#endif }; #ifndef SHIBSP_LITE @@ -125,7 +146,7 @@ void SAML1Consumer::implementProtocol( throw MetadataException("Security of SAML 1.x SSO POST response not established."); throw SecurityPolicyException("Security of SAML 1.x SSO POST response not established."); } - + // Remember whether we already established trust. bool alreadySecured = policy.isAuthenticated(); @@ -158,10 +179,14 @@ void SAML1Consumer::implementProtocol( // Saves off error messages potentially helpful for users. string contextualError; - // Profile validator. - time_t now = time(NULL); - BrowserSSOProfileValidator ssoValidator(application.getRelyingParty(entity)->getXMLString("entityID").second, application.getAudiences(), now); + // Ensure the BrowserSSO rule is in the policy set. + if (find_if(policy.getRules(), _rulenamed(SAML1BROWSERSSO_POLICY_RULE)) == NULL) + policy.getRules().push_back(m_ssoRule); + + // Populate recipient as audience. + policy.getAudiences().push_back(application.getRelyingParty(entity)->getXMLString("entityID").second); + time_t now = time(NULL); for (vector::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) { try { // Skip unsigned assertion? @@ -178,16 +203,14 @@ void SAML1Consumer::implementProtocol( ); // Run the policy over the assertion. Handles replay, freshness, and - // signature verification, assuming the relevant rules are configured. - policy.evaluate(*(*a)); - + // signature verification, assuming the relevant rules are configured, + // along with condition and profile enforcement. + policy.evaluate(*(*a), &httpRequest); + // If no security is in place now, we kick it. if (!alreadySecured && !policy.isAuthenticated()) throw SecurityPolicyException("Unable to establish security of incoming assertion."); - // Now do profile and core semantic validation to ensure we can use it for SSO. - ssoValidator.validateAssertion(*(*a)); - // Track it as a valid token. tokens.push_back(*a); diff --git a/shibsp/handler/impl/SAML2ArtifactResolution.cpp b/shibsp/handler/impl/SAML2ArtifactResolution.cpp index ef85778..6b98392 100644 --- a/shibsp/handler/impl/SAML2ArtifactResolution.cpp +++ b/shibsp/handler/impl/SAML2ArtifactResolution.cpp @@ -90,7 +90,7 @@ namespace shibsp { MessageEncoder* m_encoder; MessageDecoder* m_decoder; - QName m_role; + xmltooling::QName m_role; #endif }; @@ -121,7 +121,7 @@ SAML2ArtifactResolution::SAML2ArtifactResolution(const DOMElement* e, const char getString("Binding").second,pair(e,NULL) ); } - catch (exception& ex) { + catch (exception&) { m_log.error("error building MessageEncoder/Decoder pair for binding (%s)", getString("Binding").second); delete m_encoder; delete m_decoder; diff --git a/shibsp/handler/impl/SAML2Consumer.cpp b/shibsp/handler/impl/SAML2Consumer.cpp index 0a2ebd8..532c0ac 100644 --- a/shibsp/handler/impl/SAML2Consumer.cpp +++ b/shibsp/handler/impl/SAML2Consumer.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,8 +16,8 @@ /** * SAML2Consumer.cpp - * - * SAML 2.0 assertion consumer service + * + * SAML 2.0 assertion consumer service */ #include "internal.h" @@ -29,10 +29,11 @@ # include "ServiceProvider.h" # include "SessionCache.h" # include "attribute/resolver/ResolutionContext.h" +# include # include -# include # include # include +# include using namespace opensaml::saml2; using namespace opensaml::saml2p; using namespace opensaml::saml2md; @@ -52,15 +53,24 @@ namespace shibsp { #pragma warning( push ) #pragma warning( disable : 4250 ) #endif - + class SHIBSP_DLLLOCAL SAML2Consumer : public AssertionConsumerService { public: SAML2Consumer(const DOMElement* e, const char* appId) : AssertionConsumerService(e, appId, Category::getInstance(SHIBSP_LOGCAT".SSO.SAML2")) { +#ifndef SHIBSP_LITE + m_ssoRule = NULL; + if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) + m_ssoRule = SAMLConfig::getConfig().SecurityPolicyRuleManager.newPlugin(BEARER_POLICY_RULE, e); +#endif } - virtual ~SAML2Consumer() {} - + virtual ~SAML2Consumer() { +#ifndef SHIBSP_LITE + delete m_ssoRule; +#endif + } + #ifndef SHIBSP_LITE void generateMetadata(SPSSODescriptor& role, const char* handlerURL) const { AssertionConsumerService::generateMetadata(role, handlerURL); @@ -76,6 +86,8 @@ namespace shibsp { const PropertySet* settings, const XMLObject& xmlObject ) const; + + SecurityPolicyRule* m_ssoRule; #endif }; @@ -87,7 +99,19 @@ namespace shibsp { { return new SAML2Consumer(p.first, p.second); } - + +#ifndef SHIBSP_LITE + class SHIBSP_DLLLOCAL _rulenamed : std::unary_function + { + public: + _rulenamed(const char* name) : m_name(name) {} + bool operator()(const SecurityPolicyRule* rule) const { + return rule ? !strcmp(m_name, rule->getType()) : false; + } + private: + const char* m_name; + }; +#endif }; #ifndef SHIBSP_LITE @@ -109,7 +133,7 @@ void SAML2Consumer::implementProtocol( bool alreadySecured = policy.isAuthenticated(); // Check for errors...this will throw if it's not a successful message. - checkError(&xmlObject); + checkError(&xmlObject, policy.getIssuerMetadata()); const Response* response = dynamic_cast(&xmlObject); if (!response) @@ -140,9 +164,6 @@ void SAML2Consumer::implementProtocol( flag = application.getRelyingParty(entity)->getBool("requireSignedAssertions"); } - time_t now = time(NULL); - string dest = httpRequest.getRequestURL(); - // authnskew allows rejection of SSO if AuthnInstant is too old. const PropertySet* sessionProps = application.getPropertySet("Sessions"); pair authnskew = sessionProps ? sessionProps->getUnsignedInt("maxTimeSinceAuthn") : pair(false,0); @@ -150,6 +171,14 @@ void SAML2Consumer::implementProtocol( // Saves off error messages potentially helpful for users. string contextualError; + // Ensure the Bearer rule is in the policy set. + if (find_if(policy.getRules(), _rulenamed(BEARER_POLICY_RULE)) == NULL) + policy.getRules().push_back(m_ssoRule); + + // Populate recipient as audience. + policy.getAudiences().push_back(application.getRelyingParty(entity)->getXMLString("entityID").second); + + time_t now = time(NULL); for (vector::const_iterator a = assertions.begin(); a!=assertions.end(); ++a) { try { // Skip unsigned assertion? @@ -164,9 +193,10 @@ void SAML2Consumer::implementProtocol( extractMessageDetails(*(*a), samlconstants::SAML20P_NS, policy); // Run the policy over the assertion. Handles replay, freshness, and - // signature verification, assuming the relevant rules are configured. - policy.evaluate(*(*a)); - + // signature verification, assuming the relevant rules are configured, + // along with condition and profile enforcement. + policy.evaluate(*(*a), &httpRequest); + // If no security is in place now, we kick it. if (!alreadySecured && !policy.isAuthenticated()) throw SecurityPolicyException("Unable to establish security of incoming assertion."); @@ -179,14 +209,14 @@ void SAML2Consumer::implementProtocol( throw SecurityPolicyException("The incoming assertion was unsigned, violating local security policy."); } - // Now do profile and core semantic validation to ensure we can use it for SSO. - BrowserSSOProfileValidator ssoValidator( - application.getRelyingParty(entity)->getXMLString("entityID").second, application.getAudiences(), now, dest.substr(0,dest.find('?')).c_str() - ); - ssoValidator.validateAssertion(*(*a)); - // Address checking. - checkAddress(application, httpRequest, ssoValidator.getAddress()); + SubjectConfirmationData* subcondata = dynamic_cast( + dynamic_cast(policy).getSubjectConfirmation()->getSubjectConfirmationData() + ); + if (subcondata && subcondata->getAddress()) { + auto_ptr_char boundip(subcondata->getAddress()); + checkAddress(application, httpRequest, boundip.get()); + } // Track it as a valid token. tokens.push_back(*a); @@ -241,10 +271,6 @@ void SAML2Consumer::implementProtocol( continue; try { - // Skip unsigned assertion? - if (!decrypted->getSignature() && flag.first && flag.second) - throw SecurityPolicyException("The incoming assertion was unsigned, violating local security policy."); - // We clear the security flag, so we can tell whether the token was secured on its own. policy.setAuthenticated(false); policy.reset(true); @@ -253,24 +279,33 @@ void SAML2Consumer::implementProtocol( extractMessageDetails(*decrypted, samlconstants::SAML20P_NS, policy); // Run the policy over the assertion. Handles replay, freshness, and - // signature verification, assuming the relevant rules are configured. + // signature verification, assuming the relevant rules are configured, + // along with condition and profile enforcement. // We have to marshall the object first to ensure signatures can be checked. if (!decrypted->getDOM()) decrypted->marshall(); - policy.evaluate(*decrypted); - + policy.evaluate(*decrypted, &httpRequest); + // If no security is in place now, we kick it. if (!alreadySecured && !policy.isAuthenticated()) throw SecurityPolicyException("Unable to establish security of incoming assertion."); - // Now do profile and core semantic validation to ensure we can use it for SSO. - BrowserSSOProfileValidator ssoValidator( - application.getRelyingParty(entity)->getXMLString("entityID").second, application.getAudiences(), now, dest.substr(0,dest.find('?')).c_str() - ); - ssoValidator.validateAssertion(*decrypted); + // If we hadn't established Issuer yet, redo the signedAssertions check. + if (!entity && policy.getIssuerMetadata()) { + entity = dynamic_cast(policy.getIssuerMetadata()->getParent()); + flag = application.getRelyingParty(entity)->getBool("requireSignedAssertions"); + if (!decrypted->getSignature() && flag.first && flag.second) + throw SecurityPolicyException("The decrypted assertion was unsigned, violating local security policy."); + } // Address checking. - checkAddress(application, httpRequest, ssoValidator.getAddress()); + SubjectConfirmationData* subcondata = dynamic_cast( + dynamic_cast(policy).getSubjectConfirmation()->getSubjectConfirmationData() + ); + if (subcondata && subcondata->getAddress()) { + auto_ptr_char boundip(subcondata->getAddress()); + checkAddress(application, httpRequest, boundip.get()); + } // Track it as a valid token. tokens.push_back(decrypted); @@ -341,7 +376,8 @@ void SAML2Consumer::implementProtocol( // Now we have to extract the authentication details for session setup. // Session expiration for SAML 2.0 is jointly IdP- and SP-driven. - time_t sessionExp = ssoStatement->getSessionNotOnOrAfter() ? ssoStatement->getSessionNotOnOrAfterEpoch() : 0; + time_t sessionExp = ssoStatement->getSessionNotOnOrAfter() ? + (ssoStatement->getSessionNotOnOrAfterEpoch() + XMLToolingConfig::getConfig().clock_skew_secs) : 0; pair lifetime = sessionProps ? sessionProps->getUnsignedInt("lifetime") : pair(true,28800); if (!lifetime.first || lifetime.second == 0) lifetime.second = 28800; diff --git a/shibsp/handler/impl/SAML2Logout.cpp b/shibsp/handler/impl/SAML2Logout.cpp index d917407..9a2e71a 100644 --- a/shibsp/handler/impl/SAML2Logout.cpp +++ b/shibsp/handler/impl/SAML2Logout.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * SAML2Logout.cpp - * + * * Handles SAML 2.0 single logout protocol messages. */ @@ -55,7 +55,7 @@ namespace shibsp { #pragma warning( push ) #pragma warning( disable : 4250 ) #endif - + class SHIBSP_DLLLOCAL SAML2Logout : public AbstractHandler, public LogoutHandler { public: @@ -69,7 +69,7 @@ namespace shibsp { } #endif } - + void receive(DDF& in, ostream& out); pair run(SPRequest& request, bool isHandler=true) const; @@ -109,7 +109,7 @@ namespace shibsp { bool front ) const; - QName m_role; + xmltooling::QName m_role; MessageDecoder* m_decoder; XMLCh* m_outgoing; vector m_bindings; @@ -133,8 +133,8 @@ SAML2Logout::SAML2Logout(const DOMElement* e, const char* appId) ,m_role(samlconstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME), m_decoder(NULL), m_outgoing(NULL) #endif { -#ifndef SHIBSP_LITE m_initiator = false; +#ifndef SHIBSP_LITE m_preserve.push_back("ID"); m_preserve.push_back("entityID"); m_preserve.push_back("RelayState"); @@ -174,8 +174,14 @@ SAML2Logout::SAML2Logout(const DOMElement* e, const char* appId) MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin( b.get(), pair(e,shibspconstants::SHIB2SPCONFIG_NS) ); - m_encoders[start] = encoder; - m_log.debug("supporting outgoing front-channel binding (%s)", b.get()); + if (encoder->isUserAgentPresent()) { + m_encoders[start] = encoder; + m_log.debug("supporting outgoing binding (%s)", b.get()); + } + else { + delete encoder; + m_log.warn("skipping outgoing binding (%s), not a front-channel mechanism", b.get()); + } } catch (exception& ex) { m_log.error("error building MessageEncoder: %s", ex.what()); @@ -233,7 +239,7 @@ void SAML2Logout::receive(DDF& in, ostream& out) m_log.error("couldn't find application (%s) for logout", aid ? aid : "(missing)"); throw ConfigurationException("Unable to locate application for logout, deleted?"); } - + // Unpack the request. auto_ptr req(getRequest(in)); @@ -241,7 +247,7 @@ void SAML2Logout::receive(DDF& in, ostream& out) DDF ret(NULL); DDFJanitor jout(ret); auto_ptr resp(getResponse(ret)); - + // Since we're remoted, the result should either be a throw, which we pass on, // a false/0 return, which we just return as an empty structure, or a response/redirect, // which we capture in the facade and send back. @@ -324,7 +330,7 @@ pair SAML2Logout::doRequest(const Application& application, const HTT pair policyId = getString("policyId", m_configNS.get()); // namespace-qualified if inside handler element if (!policyId.first) policyId = application.getString("policyId"); // unqualified in Application(s) element - + // Access policy properties. const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId.second); pair validate = settings->getBool("validate"); @@ -334,7 +340,7 @@ pair SAML2Logout::doRequest(const Application& application, const HTT // Create the policy. shibsp::SecurityPolicy policy(application, &m_role, validate.first && validate.second); - + // Decode the message. string relayState; auto_ptr msg(m_decoder->decode(relayState, request, policy)); @@ -344,7 +350,7 @@ pair SAML2Logout::doRequest(const Application& application, const HTT throw SecurityPolicyException("Security of LogoutRequest not established."); // Message from IdP to logout one or more sessions. - + // If this is front-channel, we have to have a session_id to use already. if (m_decoder->isUserAgentPresent() && session_id.empty()) { m_log.error("no active session"); @@ -441,6 +447,7 @@ pair SAML2Logout::doRequest(const Application& application, const HTT if (cacheex) { time_t expires = logoutRequest->getNotOnOrAfter() ? logoutRequest->getNotOnOrAfterEpoch() : 0; cacheex->logout(application, entity, *nameid, &indexes, expires, sessions); + m_log.debug("session cache returned %d sessions bound to NameID in logout request", sessions.size()); // Now we actually terminate everything except for the active session, // if this is front-channel, for notification purposes. @@ -482,17 +489,17 @@ pair SAML2Logout::doRequest(const Application& application, const HTT if (result.first) return result; } - + // For back-channel requests, or if no front-channel notification is needed... - bool worked1 = false,worked2 = false; - worked1 = notifyBackChannel(application, request.getRequestURL(), sessions, false); + bool worked1 = notifyBackChannel(application, request.getRequestURL(), sessions, false); + bool worked2 = true; if (!session_id.empty()) { // One last session to yoink... try { cache->remove(application, request, &response); - worked2 = true; } catch (exception& ex) { + worked2 = false; m_log.error("error removing active session (%s): %s", session_id.c_str(), ex.what()); } } diff --git a/shibsp/handler/impl/SAML2LogoutInitiator.cpp b/shibsp/handler/impl/SAML2LogoutInitiator.cpp index e5a6bbb..249eb8f 100644 --- a/shibsp/handler/impl/SAML2LogoutInitiator.cpp +++ b/shibsp/handler/impl/SAML2LogoutInitiator.cpp @@ -139,8 +139,14 @@ SAML2LogoutInitiator::SAML2LogoutInitiator(const DOMElement* e, const char* appI auto_ptr_char b(start); MessageEncoder * encoder = SAMLConfig::getConfig().MessageEncoderManager.newPlugin(b.get(),pair(e,NULL)); - m_encoders[start] = encoder; - m_log.debug("supporting outgoing binding (%s)", b.get()); + if (encoder->isUserAgentPresent()) { + m_encoders[start] = encoder; + m_log.debug("supporting outgoing binding (%s)", b.get()); + } + else { + delete encoder; + m_log.warn("skipping outgoing binding (%s), not a front-channel mechanism", b.get()); + } } catch (exception& ex) { m_log.error("error building MessageEncoder: %s", ex.what()); diff --git a/shibsp/handler/impl/SAML2NameIDMgmt.cpp b/shibsp/handler/impl/SAML2NameIDMgmt.cpp index 4aba9a6..3af13a5 100644 --- a/shibsp/handler/impl/SAML2NameIDMgmt.cpp +++ b/shibsp/handler/impl/SAML2NameIDMgmt.cpp @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 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 @@ -16,7 +16,7 @@ /** * SAML2NameIDMgmt.cpp - * + * * Handles SAML 2.0 NameID management protocol messages. */ @@ -54,7 +54,7 @@ namespace shibsp { #pragma warning( push ) #pragma warning( disable : 4250 ) #endif - + class SHIBSP_DLLLOCAL SAML2NameIDMgmt : public AbstractHandler, public RemotedHandler { public: @@ -68,7 +68,7 @@ namespace shibsp { } #endif } - + void receive(DDF& in, ostream& out); pair run(SPRequest& request, bool isHandler=true) const; @@ -110,7 +110,7 @@ namespace shibsp { bool front ) const; - QName m_role; + xmltooling::QName m_role; MessageDecoder* m_decoder; XMLCh* m_outgoing; vector m_bindings; @@ -170,8 +170,14 @@ SAML2NameIDMgmt::SAML2NameIDMgmt(const DOMElement* e, const char* appId) MessageEncoder * encoder = conf.MessageEncoderManager.newPlugin( b.get(), pair(e,shibspconstants::SHIB2SPCONFIG_NS) ); - m_encoders[start] = encoder; - m_log.debug("supporting outgoing front-channel binding (%s)", b.get()); + if (encoder->isUserAgentPresent()) { + m_encoders[start] = encoder; + m_log.debug("supporting outgoing binding (%s)", b.get()); + } + else { + delete encoder; + m_log.warn("skipping outgoing binding (%s), not a front-channel mechanism", b.get()); + } } catch (exception& ex) { m_log.error("error building MessageEncoder: %s", ex.what()); @@ -223,7 +229,7 @@ void SAML2NameIDMgmt::receive(DDF& in, ostream& out) m_log.error("couldn't find application (%s) for NameID mgmt", aid ? aid : "(missing)"); throw ConfigurationException("Unable to locate application for NameID mgmt, deleted?"); } - + // Unpack the request. auto_ptr req(getRequest(in)); @@ -231,7 +237,7 @@ void SAML2NameIDMgmt::receive(DDF& in, ostream& out) DDF ret(NULL); DDFJanitor jout(ret); auto_ptr resp(getResponse(ret)); - + // Since we're remoted, the result should either be a throw, which we pass on, // a false/0 return, which we just return as an empty structure, or a response/redirect, // which we capture in the facade and send back. @@ -250,7 +256,7 @@ pair SAML2NameIDMgmt::doRequest( pair policyId = getString("policyId", m_configNS.get()); // namespace-qualified if inside handler element if (!policyId.first) policyId = application.getString("policyId"); // unqualified in Application(s) element - + // Access policy properties. const PropertySet* settings = application.getServiceProvider().getPolicySettings(policyId.second); pair validate = settings->getBool("validate"); @@ -260,7 +266,7 @@ pair SAML2NameIDMgmt::doRequest( // Create the policy. shibsp::SecurityPolicy policy(application, &m_role, validate.first && validate.second); - + // Decode the message. string relayState; auto_ptr msg(m_decoder->decode(relayState, request, policy)); @@ -533,6 +539,7 @@ pair SAML2NameIDMgmt::sendResponse( #include #include #include +#include using namespace soap11; namespace { static const XMLCh NameIDNotification[] = UNICODE_LITERAL_18(N,a,m,e,I,D,N,o,t,i,f,i,c,a,t,i,o,n); @@ -545,6 +552,12 @@ namespace { private: void prepareTransport(SOAPTransport& transport) { transport.setVerifyHost(false); + HTTPSOAPTransport* http = dynamic_cast(&transport); + if (http) { + http->useChunkedEncoding(false); + http->setRequestHeader("User-Agent", PACKAGE_NAME); + http->setRequestHeader(PACKAGE_NAME, PACKAGE_VERSION); + } } }; }; diff --git a/shibsp/handler/impl/SAML2SessionInitiator.cpp b/shibsp/handler/impl/SAML2SessionInitiator.cpp index 7121176..ae14e44 100644 --- a/shibsp/handler/impl/SAML2SessionInitiator.cpp +++ b/shibsp/handler/impl/SAML2SessionInitiator.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * SAML2SessionInitiator.cpp - * + * * SAML 2.0 AuthnRequest support. */ @@ -66,20 +66,24 @@ namespace shibsp { XMLString::release(&m_outgoing); for_each(m_encoders.begin(), m_encoders.end(), cleanup_pair()); delete m_requestTemplate; + delete m_ecp; } #endif } - + void setParent(const PropertySet* parent); void receive(DDF& in, ostream& out); + pair unwrap(SPRequest& request, DDF& out) const; pair run(SPRequest& request, string& entityID, bool isHandler=true) const; private: pair doRequest( const Application& application, + const HTTPRequest* httpRequest, HTTPResponse& httpResponse, const char* entityID, const XMLCh* acsIndex, + bool artifactInbound, const char* acsLocation, const XMLCh* acsBinding, bool isPassive, @@ -170,8 +174,14 @@ SAML2SessionInitiator::SAML2SessionInitiator(const DOMElement* e, const char* ap MessageEncoder * encoder = SAMLConfig::getConfig().MessageEncoderManager.newPlugin( b.get(),pair(e,NULL) ); - m_encoders[start] = encoder; - m_log.debug("supporting outgoing binding (%s)", b.get()); + if (encoder->isUserAgentPresent()) { + m_encoders[start] = encoder; + m_log.debug("supporting outgoing binding (%s)", b.get()); + } + else { + delete encoder; + m_log.warn("skipping outgoing binding (%s), not a front-channel mechanism", b.get()); + } } catch (exception& ex) { m_log.error("error building MessageEncoder: %s", ex.what()); @@ -220,6 +230,7 @@ pair SAML2SessionInitiator::run(SPRequest& request, string& entityID, return make_pair(false,0L); string target; + string postData; const Handler* ACS=NULL; const char* option; pair acClass; @@ -245,7 +256,7 @@ pair SAML2SessionInitiator::run(SPRequest& request, string& entityID, option = request.getParameter("target"); if (option) target = option; - + // Always need to recover target URL to compute handler below. recoverRelayState(request.getApplication(), request, request, target, false); @@ -328,15 +339,33 @@ pair SAML2SessionInitiator::run(SPRequest& request, string& entityID, } } + // Validate the ACS for use with this protocol. + if (!ECP) { + pair ACSbinding = ACS ? ACS->getString("Binding") : pair(false,NULL); + if (ACSbinding.first) { + pair compatibleBindings = getString("compatibleBindings"); + if (compatibleBindings.first && strstr(compatibleBindings.second, ACSbinding.second) == NULL) { + m_log.info("configured or requested ACS has non-SAML 2.0 binding"); + return make_pair(false,0L); + } + else if (strcmp(ACSbinding.second, samlconstants::SAML20_BINDING_HTTP_POST) && + strcmp(ACSbinding.second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT) && + strcmp(ACSbinding.second, samlconstants::SAML20_BINDING_HTTP_POST_SIMPLESIGN)) { + m_log.info("configured or requested ACS has non-SAML 2.0 binding"); + return make_pair(false,0L); + } + } + } + // To invoke the request builder, the key requirement is to figure out how // to express the ACS, by index or value, and if by value, where. // We have to compute the handlerURL no matter what, because we may need to // flip the index to an SSL-version. string ACSloc=request.getHandlerURL(target.c_str()); - + SPConfig& conf = SPConfig::getConfig(); if (conf.isEnabled(SPConfig::OutOfProcess)) { - if (!acsByIndex.first || acsByIndex.second) { + if (acsByIndex.first && acsByIndex.second) { // Pass by Index. if (isHandler) { // We may already have RelayState set if we looped back here, @@ -346,7 +375,7 @@ pair SAML2SessionInitiator::run(SPRequest& request, string& entityID, if (option) target = option; } - + // Determine index to use. pair ix = pair(false,NULL); if (ACS) { @@ -359,10 +388,12 @@ pair SAML2SessionInitiator::run(SPRequest& request, string& entityID, ix = ACS->getXMLString("index"); } } - + return doRequest( - app, request, entityID.c_str(), - ix.second, NULL, NULL, + app, &request, request, entityID.c_str(), + ix.second, + ACS ? XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT) : false, + NULL, NULL, isPassive, forceAuthn, acClass.first ? acClass.second : NULL, acComp.first ? acComp.second : NULL, @@ -385,8 +416,10 @@ pair SAML2SessionInitiator::run(SPRequest& request, string& entityID, } return doRequest( - app, request, entityID.c_str(), - NULL, ACSloc.c_str(), ACS ? ACS->getXMLString("Binding").second : NULL, + app, &request, request, entityID.c_str(), + NULL, + ACS ? XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT) : false, + ACSloc.c_str(), ACS ? ACS->getXMLString("Binding").second : NULL, isPassive, forceAuthn, acClass.first ? acClass.second : NULL, acComp.first ? acComp.second : NULL, @@ -408,7 +441,7 @@ pair SAML2SessionInitiator::run(SPRequest& request, string& entityID, in.addmember("authnContextClassRef").string(acClass.second); if (acComp.first) in.addmember("authnContextComparison").string(acComp.second); - if (!acsByIndex.first || acsByIndex.second) { + if (acsByIndex.first && acsByIndex.second) { if (ACS) { // Determine index to use. pair ix = pair(false,NULL); @@ -421,6 +454,8 @@ pair SAML2SessionInitiator::run(SPRequest& request, string& entityID, ix = ACS->getString("index"); } in.addmember("acsIndex").string(ix.second); + if (XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT)) + in.addmember("artifact").integer(1); } } else { @@ -429,8 +464,12 @@ pair SAML2SessionInitiator::run(SPRequest& request, string& entityID, pair loc=ACS ? ACS->getString("Location") : pair(false,NULL); if (loc.first) ACSloc+=loc.second; in.addmember("acsLocation").string(ACSloc.c_str()); - if (ACS) - in.addmember("acsBinding").string(ACS->getString("Binding").second); + if (ACS) { + loc = ACS->getString("Binding"); + in.addmember("acsBinding").string(loc.second); + if (XMLString::equals(loc.second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT)) + in.addmember("artifact").integer(1); + } } if (isHandler) { @@ -442,13 +481,23 @@ pair SAML2SessionInitiator::run(SPRequest& request, string& entityID, target = option; } if (!target.empty()) - in.addmember("RelayState").string(target.c_str()); + in.addmember("RelayState").unsafe_string(target.c_str()); // Remote the processing. out = request.getServiceProvider().getListenerService()->send(in); return unwrap(request, out); } +pair SAML2SessionInitiator::unwrap(SPRequest& request, DDF& out) const +{ + // See if there's any response to send back. + if (!out["redirect"].isnull() || !out["response"].isnull()) { + // If so, we're responsible for handling the POST data, probably by dropping a cookie. + preservePostData(request.getApplication(), request, request, out["RelayState"].string()); + } + return RemotedHandler::unwrap(request, out); +} + void SAML2SessionInitiator::receive(DDF& in, ostream& out) { // Find application. @@ -470,17 +519,23 @@ void SAML2SessionInitiator::receive(DDF& in, ostream& out) auto_ptr_XMLCh bind(in["acsBinding"].string()); string relayState(in["RelayState"].string() ? in["RelayState"].string() : ""); + string postData(in["PostData"].string() ? in["PostData"].string() : ""); // Since we're remoted, the result should either be a throw, which we pass on, // a false/0 return, which we just return as an empty structure, or a response/redirect, // which we capture in the facade and send back. doRequest( - *app, *http.get(), in["entity_id"].string(), - index.get(), in["acsLocation"].string(), bind.get(), + *app, NULL, *http.get(), in["entity_id"].string(), + index.get(), + (in["artifact"].integer() != 0), + in["acsLocation"].string(), bind.get(), in["isPassive"].integer()==1, in["forceAuthn"].integer()==1, in["authnContextClassRef"].string(), in["authnContextComparison"].string(), relayState ); + if (!ret.isstruct()) + ret.structure(); + ret.addmember("RelayState").unsafe_string(relayState.c_str()); out << ret; } @@ -498,9 +553,11 @@ namespace { pair SAML2SessionInitiator::doRequest( const Application& app, + const HTTPRequest* httpRequest, HTTPResponse& httpResponse, const char* entityID, const XMLCh* acsIndex, + bool artifactInbound, const char* acsLocation, const XMLCh* acsBinding, bool isPassive, @@ -521,7 +578,7 @@ pair SAML2SessionInitiator::doRequest( // We won't need this for ECP, but safety dictates we get the lock here. MetadataProvider* m=app.getMetadataProvider(); Locker locker(m); - + if (ECP) { encoder = m_ecp; if (!encoder) { @@ -538,11 +595,17 @@ pair SAML2SessionInitiator::doRequest( throw MetadataException("Unable to locate metadata for identity provider ($entityID)", namedparams(1, "entityID", entityID)); } else if (!entity.second) { - m_log.warn("unable to locate SAML 2.0 identity provider role for provider (%s)", entityID); + m_log.log(getParent() ? Priority::INFO : Priority::WARN, "unable to locate SAML 2.0 identity provider role for provider (%s)", entityID); if (getParent()) return make_pair(false,0L); throw MetadataException("Unable to locate SAML 2.0 identity provider role for provider ($entityID)", namedparams(1, "entityID", entityID)); } + else if (artifactInbound && !SPConfig::getConfig().getArtifactResolver()->isSupported(dynamic_cast(*entity.second))) { + m_log.warn("artifact binding selected for response, but identity provider lacks support"); + if (getParent()) + return make_pair(false,0L); + throw MetadataException("Identity provider ($entityID) lacks SAML 2.0 artifact support.", namedparams(1, "entityID", entityID)); + } // Loop over the supportable outgoing bindings. role = dynamic_cast(entity.second); @@ -609,7 +672,7 @@ pair SAML2SessionInitiator::doRequest( cref->setReference(wideclass.get()); reqContext->getAuthnContextClassRefs().push_back(cref); } - + if (reqContext->getAuthnContextClassRefs().empty() && reqContext->getAuthnContextDeclRefs().empty()) { req->setRequestedAuthnContext(NULL); } @@ -619,6 +682,24 @@ pair SAML2SessionInitiator::doRequest( } } + pair requestDelegation = getBool("requestDelegation"); + if (requestDelegation.first && requestDelegation.second && entity.first) { + // Request delegation by including the IdP as an Audience. + // Also specify the expected session lifetime as the bound on the assertion lifetime. + const PropertySet* sessionProps = app.getPropertySet("Sessions"); + pair lifetime = sessionProps ? sessionProps->getUnsignedInt("lifetime") : pair(true,28800); + if (!lifetime.first || lifetime.second == 0) + lifetime.second = 28800; + if (!req->getConditions()) + req->setConditions(ConditionsBuilder::buildConditions()); + req->getConditions()->setNotOnOrAfter(time(NULL) + lifetime.second + 300); + AudienceRestriction* audrest = AudienceRestrictionBuilder::buildAudienceRestriction(); + req->getConditions()->getConditions().push_back(audrest); + Audience* aud = AudienceBuilder::buildAudience(); + audrest->getAudiences().push_back(aud); + aud->setAudienceURI(entity.first->getEntityID()); + } + if (ECP && entityID) { auto_ptr_XMLCh wideid(entityID); Scoping* scoping = req->getScoping(); @@ -641,6 +722,11 @@ pair SAML2SessionInitiator::doRequest( auto_ptr_char dest(ep ? ep->getLocation() : NULL); + if (httpRequest) { + // If the request object is available, we're responsible for the POST data. + preservePostData(app, *httpRequest, httpResponse, relayState.c_str()); + } + long ret = sendMessage( *encoder, req.get(), relayState.c_str(), dest.get(), role, app, httpResponse, role ? role->WantAuthnRequestsSigned() : false ); diff --git a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp index 90d189c..5ff0878 100644 --- a/shibsp/handler/impl/SAMLDSSessionInitiator.cpp +++ b/shibsp/handler/impl/SAMLDSSessionInitiator.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * SAMLDSSessionInitiator.cpp - * + * * SAML Discovery Service support. */ @@ -65,7 +65,7 @@ namespace shibsp { m_returnParam = url.second; } virtual ~SAMLDSSessionInitiator() {} - + pair run(SPRequest& request, string& entityID, bool isHandler=true) const; #ifndef SHIBSP_LITE @@ -78,11 +78,11 @@ namespace shibsp { hurl += loc; auto_ptr_XMLCh widen(hurl.c_str()); ElementProxy* ep = new AnyElementImpl(m_discoNS.get(), LOCAL_NAME); - ep->setAttribute(QName(NULL,EndpointType::LOCATION_ATTRIB_NAME), widen.get()); - ep->setAttribute(QName(NULL,EndpointType::BINDING_ATTRIB_NAME), getXMLString("Binding").second); + ep->setAttribute(xmltooling::QName(NULL,EndpointType::LOCATION_ATTRIB_NAME), widen.get()); + ep->setAttribute(xmltooling::QName(NULL,EndpointType::BINDING_ATTRIB_NAME), m_discoNS.get()); pair ix = getXMLString("index"); - ep->setAttribute(QName(NULL,IndexedEndpointType::INDEX_ATTRIB_NAME), ix.first ? ix.second : xmlconstants::XML_ONE); - + ep->setAttribute(xmltooling::QName(NULL,IndexedEndpointType::INDEX_ATTRIB_NAME), ix.first ? ix.second : xmlconstants::XML_ONE); + Extensions* ext = role.getExtensions(); if (!ext) { ext = ExtensionsBuilder::buildExtensions(); @@ -131,7 +131,7 @@ pair SAMLDSSessionInitiator::run(SPRequest& request, string& entityID ex.addProperty("statusCode2", "urn:oasis:names:tc:SAML:2.0:status:NoAvailableIDP"); ex.raise(); } - + option = request.getParameter("target"); if (option) target = option; @@ -166,6 +166,8 @@ pair SAMLDSSessionInitiator::run(SPRequest& request, string& entityID target = option; } preserveRelayState(request.getApplication(), request, target); + if (!isHandler) + preservePostData(request.getApplication(), request, request, target.c_str()); const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder(); if (isHandler) { diff --git a/shibsp/handler/impl/SessionHandler.cpp b/shibsp/handler/impl/SessionHandler.cpp index 789793c..813aad9 100644 --- a/shibsp/handler/impl/SessionHandler.cpp +++ b/shibsp/handler/impl/SessionHandler.cpp @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 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 @@ -16,7 +16,7 @@ /** * SessionHandler.cpp - * + * * Handler for dumping information about an active session. */ @@ -45,7 +45,12 @@ namespace shibsp { class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter { public: - short acceptNode(const DOMNode* node) const { +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { return FILTER_REJECT; } }; @@ -102,7 +107,7 @@ pair SessionHandler::run(SPRequest& request, bool isHandler) const if (!m_acl.empty() && m_acl.count(request.getRemoteAddr()) == 0) { m_log.error("session handler request blocked from invalid address (%s)", request.getRemoteAddr().c_str()); istringstream msg("Session Handler Blocked"); - return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_UNAUTHORIZED)); + return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN)); } stringstream s; @@ -186,7 +191,7 @@ pair SessionHandler::run(SPRequest& request, bool isHandler) const if (!m_values && !attributes.empty()) s << count << " value(s)" << endl; - + s << ""; request.setContentType("text/html; charset=UTF-8"); request.setResponseHeader("Expires","01-Jan-1997 12:00:00 GMT"); diff --git a/shibsp/handler/impl/Shib1SessionInitiator.cpp b/shibsp/handler/impl/Shib1SessionInitiator.cpp index aeda3c7..c9de163 100644 --- a/shibsp/handler/impl/Shib1SessionInitiator.cpp +++ b/shibsp/handler/impl/Shib1SessionInitiator.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * Shib1SessionInitiator.cpp - * + * * Shibboleth 1.x AuthnRequest support. */ @@ -64,17 +64,20 @@ namespace shibsp { } } virtual ~Shib1SessionInitiator() {} - + void setParent(const PropertySet* parent); void receive(DDF& in, ostream& out); + pair unwrap(SPRequest& request, DDF& out) const; pair run(SPRequest& request, string& entityID, bool isHandler=true) const; private: pair doRequest( const Application& application, + const HTTPRequest* httpRequest, HTTPResponse& httpResponse, const char* entityID, const char* acsLocation, + bool artifact, string& relayState ) const; string m_appId; @@ -111,6 +114,7 @@ pair Shib1SessionInitiator::run(SPRequest& request, string& entityID, return make_pair(false,0L); string target; + string postData; const Handler* ACS=NULL; const char* option; const Application& app=request.getApplication(); @@ -149,6 +153,21 @@ pair Shib1SessionInitiator::run(SPRequest& request, string& entityID, ACS = app.getDefaultAssertionConsumerService(); } + // Validate the ACS for use with this protocol. + pair ACSbinding = ACS ? ACS->getString("Binding") : pair(false,NULL); + if (ACSbinding.first) { + pair compatibleBindings = getString("compatibleBindings"); + if (compatibleBindings.first && strstr(compatibleBindings.second, ACSbinding.second) == NULL) { + m_log.info("configured or requested ACS has non-SAML 1.x binding"); + return make_pair(false,0L); + } + else if (strcmp(ACSbinding.second, samlconstants::SAML1_PROFILE_BROWSER_POST) && + strcmp(ACSbinding.second, samlconstants::SAML1_PROFILE_BROWSER_ARTIFACT)) { + m_log.info("configured or requested ACS has non-SAML 1.x binding"); + return make_pair(false,0L); + } + } + // Compute the ACS URL. We add the ACS location to the base handlerURL. string ACSloc=request.getHandlerURL(target.c_str()); pair loc=ACS ? ACS->getString("Location") : pair(false,NULL); @@ -163,10 +182,17 @@ pair Shib1SessionInitiator::run(SPRequest& request, string& entityID, target = option; } + // Is the in-bound binding artifact? + bool artifactInbound = ACS ? XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML1_PROFILE_BROWSER_ARTIFACT) : false; + m_log.debug("attempting to initiate session using Shibboleth with provider (%s)", entityID.c_str()); - if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) - return doRequest(app, request, entityID.c_str(), ACSloc.c_str(), target); + if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { + // Out of process means the POST data via the request can be exposed directly to the private method. + // The method will handle POST preservation if necessary *before* issuing the response, but only if + // it dispatches to an IdP. + return doRequest(app, &request, request, entityID.c_str(), ACSloc.c_str(), artifactInbound, target); + } // Remote the call. DDF out,in = DDF(m_address.c_str()).structure(); @@ -174,14 +200,26 @@ pair Shib1SessionInitiator::run(SPRequest& request, string& entityID, in.addmember("application_id").string(app.getId()); in.addmember("entity_id").string(entityID.c_str()); in.addmember("acsLocation").string(ACSloc.c_str()); + if (artifactInbound) + in.addmember("artifact").integer(1); if (!target.empty()) - in.addmember("RelayState").string(target.c_str()); + in.addmember("RelayState").unsafe_string(target.c_str()); - // Remote the processing. + // Remote the processing. Our unwrap method will handle POST data if necessary. out = request.getServiceProvider().getListenerService()->send(in); return unwrap(request, out); } +pair Shib1SessionInitiator::unwrap(SPRequest& request, DDF& out) const +{ + // See if there's any response to send back. + if (!out["redirect"].isnull() || !out["response"].isnull()) { + // If so, we're responsible for handling the POST data, probably by dropping a cookie. + preservePostData(request.getApplication(), request, request, out["RelayState"].string()); + } + return RemotedHandler::unwrap(request, out); +} + void Shib1SessionInitiator::receive(DDF& in, ostream& out) { // Find application. @@ -209,15 +247,20 @@ void Shib1SessionInitiator::receive(DDF& in, ostream& out) // Since we're remoted, the result should either be a throw, which we pass on, // a false/0 return, which we just return as an empty structure, or a response/redirect, // which we capture in the facade and send back. - doRequest(*app, *http.get(), entityID, acsLocation, relayState); + doRequest(*app, NULL, *http.get(), entityID, acsLocation, (in["artifact"].integer() != 0), relayState); + if (!ret.isstruct()) + ret.structure(); + ret.addmember("RelayState").unsafe_string(relayState.c_str()); out << ret; } pair Shib1SessionInitiator::doRequest( const Application& app, + const HTTPRequest* httpRequest, HTTPResponse& httpResponse, const char* entityID, const char* acsLocation, + bool artifact, string& relayState ) const { @@ -232,11 +275,18 @@ pair Shib1SessionInitiator::doRequest( throw MetadataException("Unable to locate metadata for identity provider ($entityID)", namedparams(1, "entityID", entityID)); } else if (!entity.second) { - m_log.warn("unable to locate Shibboleth-aware identity provider role for provider (%s)", entityID); + m_log.log(getParent() ? Priority::INFO : Priority::WARN, "unable to locate Shibboleth-aware identity provider role for provider (%s)", entityID); if (getParent()) return make_pair(false,0L); throw MetadataException("Unable to locate Shibboleth-aware identity provider role for provider ($entityID)", namedparams(1, "entityID", entityID)); } + else if (artifact && !SPConfig::getConfig().getArtifactResolver()->isSupported(dynamic_cast(*entity.second))) { + m_log.warn("artifact profile selected for response, but identity provider lacks support"); + if (getParent()) + return make_pair(false,0L); + throw MetadataException("Identity provider ($entityID) lacks SAML artifact support.", namedparams(1, "entityID", entityID)); + } + const EndpointType* ep=EndpointManager( dynamic_cast(entity.second)->getSingleSignOnServices() ).getByBinding(shibspconstants::SHIB1_AUTHNREQUEST_PROFILE_URI); @@ -261,6 +311,11 @@ pair Shib1SessionInitiator::doRequest( "&time=" + timebuf + "&target=" + urlenc->encode(relayState.c_str()) + "&providerId=" + urlenc->encode(app.getRelyingParty(entity.first)->getString("entityID").second); + if (httpRequest) { + // If the request object is available, we're responsible for the POST data. + preservePostData(app, *httpRequest, httpResponse, relayState.c_str()); + } + return make_pair(true, httpResponse.sendRedirect(req.c_str())); #else return make_pair(false,0L); diff --git a/shibsp/handler/impl/StatusHandler.cpp b/shibsp/handler/impl/StatusHandler.cpp index 41e9f1f..b9e066d 100644 --- a/shibsp/handler/impl/StatusHandler.cpp +++ b/shibsp/handler/impl/StatusHandler.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * StatusHandler.cpp - * + * * Handler for exposing information about the internals of the SP. */ @@ -50,7 +50,12 @@ namespace shibsp { class SHIBSP_DLLLOCAL Blocker : public DOMNodeFilter { public: - short acceptNode(const DOMNode* node) const { +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { return FILTER_REJECT; } }; @@ -146,17 +151,6 @@ namespace shibsp { m_uri += slash; break; } - else if (*slash == ';') { - // If this is Java being stupid, skip everything up to the query string, if any. - if (!strncmp(slash, ";jsessionid=", 12)) { - if (slash = strchr(slash, '?')) - m_uri += slash; - break; - } - else { - m_uri += *slash; - } - } else if (*slash != '%') { m_uri += *slash; } @@ -215,7 +209,7 @@ namespace shibsp { { if (!m_parser) m_parser=new CGIParser(*this); - + pair bounds=m_parser->getParameters(name); return (bounds.first==bounds.second) ? NULL : bounds.first->second; } @@ -238,7 +232,7 @@ namespace shibsp { #ifndef XMLTOOLING_NO_XMLSEC std::vector& #else - std::vector& + std::vector& #endif getClientCertificates() const { return g_NoCerts; @@ -283,7 +277,7 @@ pair StatusHandler::run(SPRequest& request, bool isHandler) const if (!m_acl.empty() && m_acl.count(request.getRemoteAddr()) == 0) { m_log.error("status handler request blocked from invalid address (%s)", request.getRemoteAddr().c_str()); istringstream msg("Status Handler Blocked"); - return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_UNAUTHORIZED)); + return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN)); } } @@ -312,7 +306,7 @@ pair StatusHandler::run(SPRequest& request, bool isHandler) const msg << ""; return make_pair(true,request.sendResponse(msg)); } - + try { if (conf.isEnabled(SPConfig::OutOfProcess)) { // When out of process, we run natively and directly process the message. @@ -321,7 +315,7 @@ pair StatusHandler::run(SPRequest& request, bool isHandler) const else { // When not out of process, we remote all the message processing. DDF out,in = wrap(request); - DDFJanitor jin(in), jout(out); + DDFJanitor jin(in), jout(out); out=request.getServiceProvider().getListenerService()->send(in); return unwrap(request, out); } @@ -368,13 +362,13 @@ void StatusHandler::receive(DDF& in, ostream& out) m_log.error("couldn't find application (%s) for status request", aid ? aid : "(missing)"); throw ConfigurationException("Unable to locate application for status request, deleted?"); } - + // Wrap a response shim. DDF ret(NULL); DDFJanitor jout(ret); auto_ptr req(getRequest(in)); auto_ptr resp(getResponse(ret)); - + // Since we're remoted, the result should either be a throw, a false/0 return, // which we just return as an empty structure, or a response/redirect, // which we capture in the facade and send back. diff --git a/shibsp/handler/impl/TransformSessionInitiator.cpp b/shibsp/handler/impl/TransformSessionInitiator.cpp index 36759f3..390d3d8 100644 --- a/shibsp/handler/impl/TransformSessionInitiator.cpp +++ b/shibsp/handler/impl/TransformSessionInitiator.cpp @@ -55,7 +55,12 @@ namespace shibsp { class SHIBSP_DLLLOCAL TransformSINodeFilter : public DOMNodeFilter { public: - short acceptNode(const DOMNode* node) const { +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { return FILTER_REJECT; } }; diff --git a/shibsp/handler/impl/WAYFSessionInitiator.cpp b/shibsp/handler/impl/WAYFSessionInitiator.cpp index 19510ec..f0e075d 100644 --- a/shibsp/handler/impl/WAYFSessionInitiator.cpp +++ b/shibsp/handler/impl/WAYFSessionInitiator.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -80,6 +80,7 @@ pair WAYFSessionInitiator::run(SPRequest& request, string& entityID, return make_pair(false,0L); string target; + string postData; const char* option; const Handler* ACS=NULL; const Application& app=request.getApplication(); @@ -115,6 +116,21 @@ pair WAYFSessionInitiator::run(SPRequest& request, string& entityID, ACS = app.getDefaultAssertionConsumerService(); } + // Validate the ACS for use with this protocol. + pair ACSbinding = ACS ? ACS->getString("Binding") : pair(false,NULL); + if (ACSbinding.first) { + pair compatibleBindings = getString("compatibleBindings"); + if (compatibleBindings.first && strstr(compatibleBindings.second, ACSbinding.second) == NULL) { + m_log.info("configured or requested ACS has non-SAML 1.x binding"); + return make_pair(false,0L); + } + else if (strcmp(ACSbinding.second, samlconstants::SAML1_PROFILE_BROWSER_POST) && + strcmp(ACSbinding.second, samlconstants::SAML1_PROFILE_BROWSER_ARTIFACT)) { + m_log.info("configured or requested ACS has non-SAML 1.x binding"); + return make_pair(false,0L); + } + } + m_log.debug("sending request to WAYF (%s)", m_url); // Compute the ACS URL. We add the ACS location to the base handlerURL. @@ -130,6 +146,8 @@ pair WAYFSessionInitiator::run(SPRequest& request, string& entityID, target = option; } preserveRelayState(request.getApplication(), request, target); + if (!isHandler) + preservePostData(request.getApplication(), request, request, target.c_str()); // WAYF requires a target value. if (target.empty()) diff --git a/shibsp/impl/ChainingAccessControl.cpp b/shibsp/impl/ChainingAccessControl.cpp new file mode 100644 index 0000000..8b73906 --- /dev/null +++ b/shibsp/impl/ChainingAccessControl.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 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. + */ + +/** + * ChainingAccessControl.cpp + * + * Access control plugin that combines other plugins. + */ + +#include "internal.h" +#include "exceptions.h" +#include "AccessControl.h" +#include "SessionCache.h" +#include "SPRequest.h" + +#include +#include + +using namespace shibsp; +using namespace xmltooling; +using namespace std; + +namespace shibsp { + + class ChainingAccessControl : public AccessControl + { + public: + ChainingAccessControl(const DOMElement* e); + + ~ChainingAccessControl() { + for_each(m_ac.begin(), m_ac.end(), xmltooling::cleanup()); + } + + Lockable* lock() { + for_each(m_ac.begin(), m_ac.end(), mem_fun(&Lockable::lock)); + return this; + } + void unlock() { + for_each(m_ac.begin(), m_ac.end(), mem_fun(&Lockable::unlock)); + } + + aclresult_t authorized(const SPRequest& request, const Session* session) const; + + private: + enum operator_t { OP_AND, OP_OR } m_op; + vector m_ac; + }; + + AccessControl* SHIBSP_DLLLOCAL ChainingAccessControlFactory(const DOMElement* const & e) + { + return new ChainingAccessControl(e); + } + + static const XMLCh _AccessControl[] = UNICODE_LITERAL_13(A,c,c,e,s,s,C,o,n,t,r,o,l); + static const XMLCh _operator[] = UNICODE_LITERAL_8(o,p,e,r,a,t,o,r); + static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e); + static const XMLCh AND[] = UNICODE_LITERAL_3(A,N,D); + static const XMLCh OR[] = UNICODE_LITERAL_2(O,R); + + extern AccessControl* SHIBSP_DLLLOCAL XMLAccessControlFactory(const DOMElement* const & e); +} + +void SHIBSP_API shibsp::registerAccessControls() +{ + SPConfig& conf=SPConfig::getConfig(); + conf.AccessControlManager.registerFactory(CHAINING_ACCESS_CONTROL, ChainingAccessControlFactory); + conf.AccessControlManager.registerFactory(XML_ACCESS_CONTROL, XMLAccessControlFactory); + conf.AccessControlManager.registerFactory("edu.internet2.middleware.shibboleth.sp.provider.XMLAccessControl", XMLAccessControlFactory); +} + +ChainingAccessControl::ChainingAccessControl(const DOMElement* e) +{ + const XMLCh* op = e ? e->getAttributeNS(NULL, _operator) : NULL; + if (XMLString::equals(op, AND)) + m_op=OP_AND; + else if (XMLString::equals(op, OR)) + m_op=OP_OR; + else + throw ConfigurationException("Missing or unrecognized operator in Chaining AccessControl configuration."); + + try { + e = e ? XMLHelper::getFirstChildElement(e, _AccessControl) : NULL; + while (e) { + auto_ptr_char type(e->getAttributeNS(NULL, _type)); + if (type.get() && *type.get()) { + Category::getInstance(SHIBSP_LOGCAT".AccessControl.Chaining").info("building AccessControl provider of type (%s)...", type.get()); + m_ac.push_back(SPConfig::getConfig().AccessControlManager.newPlugin(type.get(), e)); + } + e = XMLHelper::getNextSiblingElement(e, _AccessControl); + } + } + catch (exception&) { + for_each(m_ac.begin(), m_ac.end(), xmltooling::cleanup()); + throw; + } + if (m_ac.empty()) + throw ConfigurationException("Chaining AccessControl plugin requires at least one child plugin."); +} + +AccessControl::aclresult_t ChainingAccessControl::authorized(const SPRequest& request, const Session* session) const +{ + switch (m_op) { + case OP_AND: + { + for (vector::const_iterator i=m_ac.begin(); i!=m_ac.end(); ++i) { + if ((*i)->authorized(request, session) != shib_acl_true) + return shib_acl_false; + } + return shib_acl_true; + } + + case OP_OR: + { + for (vector::const_iterator i=m_ac.begin(); i!=m_ac.end(); ++i) { + if ((*i)->authorized(request,session) == shib_acl_true) + return shib_acl_true; + } + return shib_acl_false; + } + } + request.log(SPRequest::SPWarn, "unknown operation in access control policy, denying access"); + return shib_acl_false; +} diff --git a/shibsp/impl/StorageServiceSessionCache.cpp b/shibsp/impl/StorageServiceSessionCache.cpp index cb51042..27f7610 100644 --- a/shibsp/impl/StorageServiceSessionCache.cpp +++ b/shibsp/impl/StorageServiceSessionCache.cpp @@ -847,7 +847,7 @@ void SSCache::insert(const char* key, time_t expires, const char* name, const ch } else { // New record. - obj.structure(); + obj = DDF(NULL).structure(); } if (!index || !*index) diff --git a/shibsp/impl/XMLAccessControl.cpp b/shibsp/impl/XMLAccessControl.cpp index 939ea68..5d9d352 100644 --- a/shibsp/impl/XMLAccessControl.cpp +++ b/shibsp/impl/XMLAccessControl.cpp @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 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 @@ -40,8 +40,8 @@ using namespace shibsp; using namespace xmltooling; using namespace std; -namespace { - +namespace shibsp { + class Rule : public AccessControl { public: @@ -52,12 +52,12 @@ namespace { void unlock() {} aclresult_t authorized(const SPRequest& request, const Session* session) const; - + private: string m_alias; vector m_vals; }; - + class RuleRegex : public AccessControl { public: @@ -65,18 +65,18 @@ namespace { ~RuleRegex() { delete m_re; } - + Lockable* lock() {return this;} void unlock() {} aclresult_t authorized(const SPRequest& request, const Session* session) const; - + private: string m_alias; auto_arrayptr m_exp; RegularExpression* m_re; }; - + class Operator : public AccessControl { public: @@ -87,7 +87,7 @@ namespace { void unlock() {} aclresult_t authorized(const SPRequest& request, const Session* session) const; - + private: enum operator_t { OP_NOT, OP_AND, OP_OR } m_op; vector m_operands; @@ -102,10 +102,10 @@ namespace { { public: XMLAccessControl(const DOMElement* e) - : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".AccessControl")), m_rootAuthz(NULL) { + : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".AccessControl.XML")), m_rootAuthz(NULL) { load(); // guarantees an exception or the policy is loaded } - + ~XMLAccessControl() { delete m_rootAuthz; } @@ -140,13 +140,6 @@ namespace { static const XMLCh _RuleRegex[] = UNICODE_LITERAL_9(R,u,l,e,R,e,g,e,x); } -void SHIBSP_API shibsp::registerAccessControls() -{ - SPConfig& conf=SPConfig::getConfig(); - conf.AccessControlManager.registerFactory(XML_ACCESS_CONTROL, XMLAccessControlFactory); - conf.AccessControlManager.registerFactory("edu.internet2.middleware.shibboleth.sp.provider.XMLAccessControl", XMLAccessControlFactory); -} - Rule::Rule(const DOMElement* e) { auto_ptr_char req(e->getAttributeNS(NULL,require)); @@ -157,14 +150,14 @@ Rule::Rule(const DOMElement* e) auto_arrayptr vals(toUTF8(e->hasChildNodes() ? e->getFirstChild()->getNodeValue() : NULL)); if (!vals.get()) return; - + const XMLCh* flag = e->getAttributeNS(NULL,_list); if (flag && (*flag == chLatin_f || *flag == chDigit_0)) { if (*vals.get()) m_vals.push_back(vals.get()); return; } - + #ifdef HAVE_STRTOK_R char* pos=NULL; const char* token=strtok_r(const_cast(vals.get())," ",&pos); @@ -191,7 +184,7 @@ AccessControl::aclresult_t Rule::authorized(const SPRequest& request, const Sess request.log(SPRequest::SPWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?"); return shib_acl_false; } - + if (m_alias == "valid-user") { if (session) { request.log(SPRequest::SPDebug,"AccessControl plugin accepting valid-user based on active session"); @@ -261,11 +254,11 @@ RuleRegex::RuleRegex(const DOMElement* e) : m_exp(toUTF8(e->hasChildNodes() ? e- if (!req.get() || !*req.get() || !m_exp.get() || !*m_exp.get()) throw ConfigurationException("Access control rule missing require attribute or element content."); m_alias=req.get(); - + const XMLCh* flag = e->getAttributeNS(NULL,ignoreCase); bool ignore = (flag && (*flag == chLatin_t || *flag == chDigit_1)); try { - m_re = new RegularExpression(e->getFirstChild()->getNodeValue(), (ignore ? ignoreOption : &chNull)); + m_re = new RegularExpression(e->getFirstChild()->getNodeValue(), (ignore ? ignoreOption : &chNull)); } catch (XMLException& ex) { auto_ptr_char tmp(ex.getMessage()); @@ -280,7 +273,7 @@ AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const request.log(SPRequest::SPWarn, "AccessControl plugin not given a valid session to evaluate, are you using lazy sessions?"); return shib_acl_false; } - + if (m_alias == "valid-user") { if (session) { request.log(SPRequest::SPDebug,"AccessControl plugin accepting valid-user based on active session"); @@ -335,7 +328,7 @@ AccessControl::aclresult_t RuleRegex::authorized(const SPRequest& request, const auto_ptr_char tmp(ex.getMessage()); request.log(SPRequest::SPError, string("caught exception while parsing RuleRegex regular expression: ") + tmp.get()); } - + return shib_acl_false; } @@ -349,7 +342,7 @@ Operator::Operator(const DOMElement* e) m_op=OP_OR; else throw ConfigurationException("Unrecognized operator in access control rule"); - + try { e=XMLHelper::getFirstChildElement(e); if (XMLString::equals(e->getLocalName(),_Rule)) @@ -358,10 +351,10 @@ Operator::Operator(const DOMElement* e) m_operands.push_back(new RuleRegex(e)); else m_operands.push_back(new Operator(e)); - + if (m_op==OP_NOT) return; - + e=XMLHelper::getNextSiblingElement(e); while (e) { if (XMLString::equals(e->getLocalName(),_Rule)) @@ -396,7 +389,7 @@ AccessControl::aclresult_t Operator::authorized(const SPRequest& request, const default: return shib_acl_indeterminate; } - + case OP_AND: { for (vector::const_iterator i=m_operands.begin(); i!=m_operands.end(); i++) { @@ -405,7 +398,7 @@ AccessControl::aclresult_t Operator::authorized(const SPRequest& request, const } return shib_acl_true; } - + case OP_OR: { for (vector::const_iterator i=m_operands.begin(); i!=m_operands.end(); i++) { @@ -423,14 +416,14 @@ pair XMLAccessControl::load() { // Load from source using base class. pair raw = ReloadableXMLFile::load(); - + // If we own it, wrap it. XercesJanitor docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL); // Check for AccessControl wrapper and drop a level. if (XMLString::equals(raw.second->getLocalName(),_AccessControl)) raw.second = XMLHelper::getFirstChildElement(raw.second); - + AccessControl* authz; if (XMLString::equals(raw.second->getLocalName(),_Rule)) authz=new Rule(raw.second); diff --git a/shibsp/impl/XMLRequestMapper.cpp b/shibsp/impl/XMLRequestMapper.cpp index 9c5f294..78e6edf 100644 --- a/shibsp/impl/XMLRequestMapper.cpp +++ b/shibsp/impl/XMLRequestMapper.cpp @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 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 @@ -15,7 +15,7 @@ */ /** XMLRequestMapper.cpp - * + * * XML-based RequestMapper implementation */ @@ -39,16 +39,16 @@ using namespace std; namespace shibsp { - // Blocks access when an ACL plugin fails to load. + // Blocks access when an ACL plugin fails to load. class AccessControlDummy : public AccessControl { public: Lockable* lock() { return this; } - + void unlock() {} - + aclresult_t authorized(const SPRequest& request, const Session* session) const { return shib_acl_false; } @@ -62,20 +62,25 @@ namespace shibsp { ~Override(); // Provides filter to exclude special config elements. - short acceptNode(const DOMNode* node) const { +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { return FILTER_REJECT; } const Override* locate(const HTTPRequest& request) const; AccessControl* getAC() const { return (m_acl ? m_acl : (getParent() ? dynamic_cast(getParent())->getAC() : NULL)); } - + protected: void loadACL(const DOMElement* e, Category& log); - + map m_map; vector< pair > m_regexps; vector< pair< pair,Override*> > m_queries; - + private: AccessControl* m_acl; }; @@ -93,10 +98,10 @@ namespace shibsp { void setDocument(DOMDocument* doc) { m_document = doc; } - + const Override* findOverride(const char* vhost, const HTTPRequest& request) const; - private: + private: map m_extras; DOMDocument* m_document; }; @@ -193,19 +198,19 @@ Override::Override(const DOMElement* e, Category& log, const Override* base) : m // Load the property set. load(e,NULL,this); setParent(base); - + // Load any AccessControl provider. loadACL(e,log); - + // Handle nested Paths. DOMElement* path = XMLHelper::getFirstChildElement(e,Path); for (int i=1; path; ++i, path=XMLHelper::getNextSiblingElement(path,Path)) { const XMLCh* n=path->getAttributeNS(NULL,name); - + // Skip any leading slashes. while (n && *n==chForwardSlash) n++; - + // Check for empty name. if (!n || !*n) { log.warn("skipping Path element (%d) with empty name attribute", i); @@ -220,14 +225,14 @@ Override::Override(const DOMElement* e, Category& log, const Override* base) : m for (int pos=0; pos < slash; pos++) namebuf[pos]=n[pos]; namebuf[slash]=chNull; - + // Move past the slash in the original pathname. n=n+slash+1; - + // Skip any leading slashes again. while (*n==chForwardSlash) n++; - + if (*n) { // Create a placeholder Path element for the first path segment and replant under it. DOMElement* newpath=path->getOwnerDocument()->createElementNS(shibspconstants::SHIB2SPCONFIG_NS,Path); @@ -235,7 +240,7 @@ Override::Override(const DOMElement* e, Category& log, const Override* base) : m path->setAttributeNS(NULL,name,n); path->getParentNode()->replaceChild(newpath,path); newpath->appendChild(path); - + // Repoint our locals at the new parent. path=newpath; n=path->getAttributeNS(NULL,name); @@ -247,7 +252,7 @@ Override::Override(const DOMElement* e, Category& log, const Override* base) : m } delete[] namebuf; } - + Override* o=new Override(path,log,this); pair name=o->getString("name"); char* dup=strdup(name.second); @@ -317,7 +322,7 @@ Override::Override(const DOMElement* e, Category& log, const Override* base) : m log.error("caught exception while parsing Query regular expression (%d): %s", i, tmp.get()); throw ConfigurationException("Invalid regular expression in Query element."); } - + log.debug("added mapping (%s)", ntemp.get()); } } @@ -384,7 +389,7 @@ const Override* Override::locate(const HTTPRequest& request) const break; // Once there's no match, we've consumed as much of the path as possible here. // We found a match, so reset the settings pointer. o=i->second; - + // We descended a step down the path, so we need to advance the original // parameter for the regex step later. path += strlen(token); @@ -451,7 +456,7 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : // Load the property set. load(e,NULL,this); - + // Load any AccessControl provider. loadACL(e,log); @@ -489,12 +494,12 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : log.warn("Skipping Host element (%d) with empty name attribute",i); continue; } - + Override* o=new Override(host,log,this); pair name=o->getString("name"); pair scheme=o->getString("scheme"); pair port=o->getString("port"); - + char* dup=strdup(name.second); for (char* pch=dup; *pch; pch++) *pch=tolower(*pch); @@ -523,7 +528,7 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : if (scheme.first) { string url(scheme.second); url=url + "://" + dup; - + // Is this the default port? if ((!strcmp(scheme.second,"http") && !strcmp(port.second,"80")) || (!strcmp(scheme.second,"https") && !strcmp(port.second,"443")) || @@ -538,7 +543,7 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : } m_map[url]=o; log.debug("Added mapping for %s",url.c_str()); - + // Now append the port. We use the extras vector, to avoid double freeing the object later. url=url + ':' + port.second; m_extras[url]=o; @@ -566,7 +571,7 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : } m_map[url]=o; log.debug("Added mapping for %s",url.c_str()); - + url = url + ":80"; if (m_map.count(url) || m_extras.count(url)) { log.warn("Skipping duplicate Host element (%s)",url.c_str()); @@ -574,7 +579,7 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : } m_extras[url]=o; log.debug("Added mapping for %s",url.c_str()); - + url = "https://"; url = url + dup; if (m_map.count(url) || m_extras.count(url)) { @@ -583,7 +588,7 @@ XMLRequestMapperImpl::XMLRequestMapperImpl(const DOMElement* e, Category& log) : } m_extras[url]=o; log.debug("Added mapping for %s",url.c_str()); - + url = url + ":443"; if (m_map.count(url) || m_extras.count(url)) { log.warn("Skipping duplicate Host element (%s)",url.c_str()); @@ -612,7 +617,7 @@ const Override* XMLRequestMapperImpl::findOverride(const char* vhost, const HTTP } } } - + return o ? o->locate(request) : this; } @@ -620,12 +625,12 @@ pair XMLRequestMapper::load() { // Load from source using base class. pair raw = ReloadableXMLFile::load(); - + // If we own it, wrap it. XercesJanitor docjanitor(raw.first ? raw.second->getOwnerDocument() : NULL); XMLRequestMapperImpl* impl = new XMLRequestMapperImpl(raw.second,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()); @@ -639,16 +644,6 @@ RequestMapper::Settings XMLRequestMapper::getSettings(const HTTPRequest& request { ostringstream vhost; vhost << request.getScheme() << "://" << request.getHostname() << ':' << request.getPort(); - const Override* o=m_impl->findOverride(vhost.str().c_str(), request); - - if (m_log.isDebugEnabled()) { -#ifdef _DEBUG - xmltooling::NDC ndc("getSettings"); -#endif - pair ret=o->getString("applicationId"); - m_log.debug("mapped %s%s to %s", vhost.str().c_str(), request.getRequestURI() ? request.getRequestURI() : "", ret.second); - } - return Settings(o,o->getAC()); } diff --git a/shibsp/impl/XMLServiceProvider.cpp b/shibsp/impl/XMLServiceProvider.cpp index fb16a21..5b9de5f 100644 --- a/shibsp/impl/XMLServiceProvider.cpp +++ b/shibsp/impl/XMLServiceProvider.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -22,12 +22,14 @@ #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" @@ -42,8 +44,10 @@ #endif #include #include +#include #include #include +#include #include #ifndef SHIBSP_LITE @@ -53,12 +57,11 @@ # include "attribute/resolver/AttributeResolver.h" # include "security/PKIXTrustEngine.h" # include +# include # include # include # include # include -# include -# include # include using namespace opensaml::saml2; using namespace opensaml::saml2p; @@ -70,6 +73,10 @@ 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) @@ -85,7 +92,7 @@ namespace { 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 @@ -135,6 +142,10 @@ namespace { 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; @@ -156,12 +167,18 @@ namespace { } // Provides filter to exclude special config elements. - short acceptNode(const DOMNode* node) const; - +#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 m_attributePrefix; #ifndef SHIBSP_LITE MetadataProvider* m_metadata; TrustEngine* m_trust; @@ -172,11 +189,7 @@ namespace { vector m_audiences; // RelyingParty properties -#ifdef HAVE_GOOD_STL map m_partyMap; -#else - map m_partyMap; -#endif #endif vector m_remoteUsers,m_frontLogout,m_backLogout; @@ -188,16 +201,12 @@ namespace { // maps unique indexes to consumer services map m_acsIndexMap; - + // pointer to default consumer service const Handler* m_acsDefault; // maps binding strings to supporting consumer service(s) -#ifdef HAVE_GOOD_STL typedef map > ACSBindingMap; -#else - typedef map > ACSBindingMap; -#endif ACSBindingMap m_acsBindingMap; // pointer to default session initiator @@ -222,16 +231,21 @@ namespace { public: XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* outer, Category& log); ~XMLConfigImpl(); - + RequestMapper* m_requestMapper; map m_appmap; #ifndef SHIBSP_LITE map< string,pair< PropertySet*,vector > > m_policyMap; vector< pair< string, pair > > m_transportOptions; #endif - + // Provides filter to exclude special config elements. - short acceptNode(const DOMNode* node) const; +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const; void setDocument(DOMDocument* doc) { m_document = doc; @@ -258,7 +272,7 @@ namespace { #endif { } - + void init() { load(); } @@ -406,6 +420,7 @@ namespace { 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); @@ -427,7 +442,12 @@ namespace { class SHIBSP_DLLLOCAL PolicyNodeFilter : public DOMNodeFilter { public: - short acceptNode(const DOMNode* node) const { +#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE + short +#else + FilterAction +#endif + acceptNode(const DOMNode* node) const { return FILTER_REJECT; } }; @@ -479,6 +499,18 @@ XMLApplication::XMLApplication( m_hash += (DIGITS[0x0F & *ch]); } + // Populate prefix pair. + m_attributePrefix.second = "HTTP_"; + pair 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 attributes = getString("REMOTE_USER"); @@ -502,9 +534,9 @@ XMLApplication::XMLApplication( attributes = getString("unsetHeaders"); if (attributes.first) { - string transformedprefix("HTTP_"); + string transformedprefix(m_attributePrefix.second); const char* pch; - pair prefix = getString("metadataAttributePrefix"); + prefix = getString("metadataAttributePrefix"); if (prefix.first) { pch = prefix.second; while (*pch) { @@ -530,13 +562,14 @@ XMLApplication::XMLApplication( transformed += (isalnum(*pch) ? toupper(*pch) : '_'); pch++; } - m_unsetHeaders.push_back(pair(start,string("HTTP_") + transformed)); + + m_unsetHeaders.push_back(pair(m_attributePrefix.first + start, m_attributePrefix.second + transformed)); if (prefix.first) - m_unsetHeaders.push_back(pair(string(prefix.second) + start, transformedprefix + transformed)); + m_unsetHeaders.push_back(pair(m_attributePrefix.first + prefix.second + start, transformedprefix + transformed)); start = pos ? pos+1 : NULL; } free(dup); - m_unsetHeaders.push_back(pair("Shib-Application-ID","HTTP_SHIB_APPLICATION_ID")); + m_unsetHeaders.push_back(pair(m_attributePrefix.first + "Shib-Application-ID", m_attributePrefix.second + "SHIB_APPLICATION_ID")); } } @@ -589,13 +622,9 @@ XMLApplication::XMLApplication( } 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) -#ifdef HAVE_GOOD_STL m_acsBindingMap[handler->getXMLString("Binding").second].push_back(handler); -#else - m_acsBindingMap[handler->getString("Binding").second].push_back(handler); -#endif m_acsIndexMap[handler->getUnsignedInt("index").second]=handler; - + if (!hardACS) { pair defprop=handler->getBool("isDefault"); if (defprop.first) { @@ -649,7 +678,7 @@ XMLApplication::XMLApplication( continue; } handler=conf.ArtifactResolutionServiceManager.newPlugin(bindprop.get(),make_pair(child, getId())); - + if (!hardArt) { pair defprop=handler->getBool("isDefault"); if (defprop.first) { @@ -703,7 +732,7 @@ XMLApplication::XMLApplication( catch (exception& ex) { log.error("caught exception processing handler element: %s", ex.what()); } - + child = XMLHelper::getNextSiblingElement(child); } @@ -724,9 +753,12 @@ XMLApplication::XMLApplication( #ifndef SHIBSP_LITE nlist=e->getElementsByTagNameNS(samlconstants::SAML20_NS,Audience::LOCAL_NAME); - for (XMLSize_t i=0; nlist && igetLength(); i++) - if (nlist->item(i)->getParentNode()->isSameNode(e) && nlist->item(i)->hasChildNodes()) - m_audiences.push_back(nlist->item(i)->getFirstChild()->getNodeValue()); + if (nlist && nlist->getLength()) { + log.warn("use of elements outside of a Security Policy Rule is deprecated"); + for (XMLSize_t i=0; igetLength(); 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); @@ -801,18 +833,20 @@ XMLApplication::XMLApplication( 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); } - if (unsetHeaders.empty()) { - if (m_base) - m_unsetHeaders.insert(m_unsetHeaders.end(), m_base->m_unsetHeaders.begin(), m_base->m_unsetHeaders.end()); - else - m_unsetHeaders.push_back(pair("Shib-Application-ID","HTTP_SHIB_APPLICATION_ID")); + else if (m_base && m_base->m_attrResolver) { + Locker extlock(m_base->m_attrResolver); + m_base->m_attrResolver->getAttributeIds(unsetHeaders); } - else { - string transformedprefix("HTTP_"); + if (!unsetHeaders.empty()) { + string transformedprefix(m_attributePrefix.second); const char* pch; pair prefix = getString("metadataAttributePrefix"); if (prefix.first) { @@ -829,12 +863,12 @@ XMLApplication::XMLApplication( transformed += (isalnum(*pch) ? toupper(*pch) : '_'); pch++; } - m_unsetHeaders.push_back(pair(*hdr, string("HTTP_") + transformed)); + m_unsetHeaders.push_back(pair(m_attributePrefix.first + *hdr, m_attributePrefix.second + transformed)); if (prefix.first) - m_unsetHeaders.push_back(pair(string(prefix.second) + *hdr, transformedprefix + transformed)); + m_unsetHeaders.push_back(pair(m_attributePrefix.first + prefix.second + *hdr, transformedprefix + transformed)); } - m_unsetHeaders.push_back(pair("Shib-Application-ID","HTTP_SHIB_APPLICATION_ID")); } + m_unsetHeaders.push_back(pair(m_attributePrefix.first + "Shib-Application-ID", m_attributePrefix.second + "SHIB_APPLICATION_ID")); } } @@ -896,11 +930,7 @@ void XMLApplication::cleanup() for_each(m_handlers.begin(),m_handlers.end(),xmltooling::cleanup()); m_handlers.clear(); #ifndef SHIBSP_LITE -#ifdef HAVE_GOOD_STL for_each(m_partyMap.begin(),m_partyMap.end(),cleanup_pair()); -#else - for_each(m_partyMap.begin(),m_partyMap.end(),cleanup_pair()); -#endif m_partyMap.clear(); delete m_credResolver; m_credResolver = NULL; @@ -917,7 +947,12 @@ void XMLApplication::cleanup() #endif } -short XMLApplication::acceptNode(const DOMNode* node) const +#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) || @@ -948,8 +983,7 @@ const PropertySet* XMLApplication::getRelyingParty(const EntityDescriptor* provi { if (!provider) return this; - -#ifdef HAVE_GOOD_STL + map::const_iterator i=m_partyMap.find(provider->getEntityID()); if (i!=m_partyMap.end()) return i->second; @@ -962,19 +996,6 @@ const PropertySet* XMLApplication::getRelyingParty(const EntityDescriptor* provi } group=dynamic_cast(group->getParent()); } -#else - map::const_iterator i=m_partyMap.begin(); - for (; i!=m_partyMap.end(); i++) { - if (XMLString::equals(i->first,provider->getEntityID())) - return i->second; - const EntitiesDescriptor* group=dynamic_cast(provider->getParent()); - while (group) { - if (XMLString::equals(i->first,group->getName())) - return i->second; - group=dynamic_cast(group->getParent()); - } - } -#endif return this; } @@ -982,18 +1003,10 @@ const PropertySet* XMLApplication::getRelyingParty(const XMLCh* entityID) const { if (!entityID) return this; - -#ifdef HAVE_GOOD_STL + map::const_iterator i=m_partyMap.find(entityID); if (i!=m_partyMap.end()) return i->second; -#else - map::const_iterator i=m_partyMap.begin(); - for (; i!=m_partyMap.end(); i++) { - if (XMLString::equals(i->first,entityID)) - return i->second; - } -#endif return this; } @@ -1015,7 +1028,7 @@ string XMLApplication::getNotificationURL(const char* resource, bool front, unsi 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( @@ -1071,6 +1084,49 @@ string XMLApplication::getNotificationURL(const char* resource, bool front, unsi 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; @@ -1099,12 +1155,7 @@ const Handler* XMLApplication::getAssertionConsumerServiceByIndex(unsigned short const vector& XMLApplication::getAssertionConsumerServicesByBinding(const XMLCh* binding) const { -#ifdef HAVE_GOOD_STL ACSBindingMap::const_iterator i=m_acsBindingMap.find(binding); -#else - auto_ptr_char temp(binding); - ACSBindingMap::const_iterator i=m_acsBindingMap.find(temp.get()); -#endif if (i!=m_acsBindingMap.end()) return i->second; return m_base ? m_base->getAssertionConsumerServicesByBinding(binding) : g_noHandlers; @@ -1113,6 +1164,7 @@ const vector& XMLApplication::getAssertionConsumerServicesByBind const Handler* XMLApplication::getHandler(const char* path) const { string wrap(path); + wrap = wrap.substr(0,wrap.find(';')); map::const_iterator i=m_handlerMap.find(wrap.substr(0,wrap.find('?'))); if (i!=m_handlerMap.end()) return i->second; @@ -1130,7 +1182,12 @@ void XMLApplication::getHandlers(vector& handlers) const } } -short XMLConfigImpl::acceptNode(const DOMNode* node) const +#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; @@ -1162,7 +1219,8 @@ void XMLConfigImpl::doExtensions(const DOMElement* e, const char* label, Categor auto_ptr_char path(exts->getAttributeNS(NULL,_path)); try { if (path.get()) { - XMLToolingConfig::getConfig().load_library(path.get(),(void*)exts); + 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()); } } @@ -1209,15 +1267,29 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o 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()); - XMLToolingConfig::getConfig().log_config(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); @@ -1229,7 +1301,11 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o // Set clock skew. pair skew=getUnsignedInt("clockSkew"); if (skew.first) - xmlConf.clock_skew_secs=skew.second; + xmlConf.clock_skew_secs=min(skew.second,(60*60*24*7*28)); + + pair unsafe = getString("unsafeChars"); + if (unsafe.first) + TemplateEngine::unsafe_chars = unsafe.second; // Extensions doExtensions(e, "global", log); @@ -1238,7 +1314,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o 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); @@ -1269,8 +1345,10 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o #ifndef SHIBSP_LITE if (m_outer->m_listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess)) { - m_outer->m_listener->regListener("set::RelayState", m_outer->m_listener); - m_outer->m_listener->regListener("get::RelayState", m_outer->m_listener); + m_outer->m_listener->regListener("set::RelayState", const_cast(m_outer)); + m_outer->m_listener->regListener("get::RelayState", const_cast(m_outer)); + m_outer->m_listener->regListener("set::PostData", const_cast(m_outer)); + m_outer->m_listener->regListener("get::PostData", const_cast(m_outer)); } #endif @@ -1310,7 +1388,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o else { log.warn("no ReplayCache built, missing conf:ReplayCache element?"); } - + // ArtifactMap child=XMLHelper::getFirstChildElement(e,_ArtifactMap); if (child) { @@ -1338,7 +1416,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o } } } // 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); @@ -1352,7 +1430,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o throw ConfigurationException("Can't build RequestMapper, missing conf:RequestMapper element?"); } } - + #ifndef SHIBSP_LITE // Load security policies. child = XMLHelper::getLastChildElement(e,SecurityPolicies); @@ -1366,9 +1444,9 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o auto_ptr settings(new DOMPropertySet()); settings->load(child, NULL, &filter); rules.first = settings.release(); - - // Process Rule elements. - const DOMElement* rule = XMLHelper::getFirstChildElement(child,Rule); + + // Process PolicyRule elements. + const DOMElement* rule = XMLHelper::getFirstChildElement(child,PolicyRule); while (rule) { auto_ptr_char type(rule->getAttributeNS(NULL,_type)); try { @@ -1377,9 +1455,29 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o 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); + 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); } } @@ -1407,7 +1505,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o } XMLApplication* defapp=new XMLApplication(m_outer,child); m_appmap[defapp->getId()]=defapp; - + // Load any overrides. child = XMLHelper::getFirstChildElement(child,ApplicationOverride); while (child) { @@ -1475,7 +1573,7 @@ void XMLConfig::receive(DDF& in, ostream& out) } // Repack for return to caller. - DDF ret=DDF(NULL).string(relayState.c_str()); + DDF ret=DDF(NULL).unsafe_string(relayState.c_str()); DDFJanitor jret(ret); out << ret; } @@ -1503,6 +1601,60 @@ void XMLConfig::receive(DDF& in, ostream& out) 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 @@ -1510,12 +1662,12 @@ pair XMLConfig::load() { // Load from source using base class. pair raw = ReloadableXMLFile::load(); - + // If we own it, wrap it. XercesJanitor 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()); diff --git a/shibsp/lite/CommonDomainCookie.cpp b/shibsp/lite/CommonDomainCookie.cpp index fd3bfba..cb8ffc0 100644 --- a/shibsp/lite/CommonDomainCookie.cpp +++ b/shibsp/lite/CommonDomainCookie.cpp @@ -1,6 +1,6 @@ /* * Copyright 2001-2007 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 @@ -16,8 +16,8 @@ /** * CommonDomainCookie.cpp - * - * Helper class for maintaining discovery cookie. + * + * Helper class for maintaining discovery cookie. */ #include "internal.h" @@ -55,12 +55,16 @@ CommonDomainCookie::CommonDomainCookie(const char* cookie) free(b64); // Now Base64 decode the list. - unsigned int len; + xsecsize_t len; for (vector::iterator i=templist.begin(); i!=templist.end(); ++i) { XMLByte* decoded=Base64::decode(reinterpret_cast(i->c_str()),&len); if (decoded && *decoded) { m_list.push_back(reinterpret_cast(decoded)); +#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE XMLString::release(&decoded); +#else + XMLString::release((char**)&decoded); +#endif } } } @@ -74,27 +78,31 @@ const char* CommonDomainCookie::set(const char* entityID) break; } } - + // Append it to the end. m_list.push_back(entityID); - + // Now rebuild the delimited list. - unsigned int len; + xsecsize_t len; string delimited; for (vector::const_iterator j=m_list.begin(); j!=m_list.end(); j++) { if (!delimited.empty()) delimited += ' '; - + XMLByte* b64=Base64::encode(reinterpret_cast(j->c_str()),j->length(),&len); XMLByte *pos, *pos2; for (pos=b64, pos2=b64; *pos2; pos2++) if (isgraph(*pos2)) *pos++=*pos2; *pos=0; - + delimited += reinterpret_cast(b64); +#ifdef SHIBSP_XERCESC_HAS_XMLBYTE_RELEASE XMLString::release(&b64); +#else + XMLString::release((char**)&b64); +#endif } - + m_encoded=XMLToolingConfig::getConfig().getURLEncoder()->encode(delimited.c_str()); return m_encoded.c_str(); } diff --git a/shibsp/lite/SAMLConstants.cpp b/shibsp/lite/SAMLConstants.cpp index 5c2be15..59f8e8b 100644 --- a/shibsp/lite/SAMLConstants.cpp +++ b/shibsp/lite/SAMLConstants.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,8 +16,8 @@ /** * SAMLConstants.cpp - * - * SAML XML namespace constants + * + * SAML XML namespace constants */ @@ -184,6 +184,36 @@ const XMLCh samlconstants::SAML20P_THIRDPARTY_EXT_NS[] = // urn:oasis:names:tc:S const XMLCh samlconstants::SAML20P_THIRDPARTY_EXT_PREFIX[] = UNICODE_LITERAL_6(t,h,r,p,t,y); +const XMLCh samlconstants::SAML20_ATTRIBUTE_EXT_NS[] = // urn:oasis:names:tc:SAML:attribute:ext +{ chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_o, chLatin_a, chLatin_s, chLatin_i, chLatin_s, chColon, + chLatin_n, chLatin_a, chLatin_m, chLatin_e, chLatin_s, chColon, chLatin_t, chLatin_c, chColon, + chLatin_S, chLatin_A, chLatin_M, chLatin_L, chColon, + chLatin_a, chLatin_t, chLatin_t, chLatin_r, chLatin_i, chLatin_b, chLatin_u, chLatin_t, chLatin_e, chColon, + chLatin_e, chLatin_x, chLatin_t, chNull +}; + +const XMLCh samlconstants::SAML20_ATTRIBUTE_EXT_PREFIX[] = UNICODE_LITERAL_3(e,x,t); + +const XMLCh samlconstants::SAML20MD_ENTITY_ATTRIBUTE_NS[] = // urn:oasis:names:tc:SAML:metadata:attribute +{ chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_o, chLatin_a, chLatin_s, chLatin_i, chLatin_s, chColon, + chLatin_n, chLatin_a, chLatin_m, chLatin_e, chLatin_s, chColon, chLatin_t, chLatin_c, chColon, + chLatin_S, chLatin_A, chLatin_M, chLatin_L, chColon, + chLatin_m, chLatin_e, chLatin_t, chLatin_a, chLatin_d, chLatin_a, chLatin_t, chLatin_a, chColon, + chLatin_a, chLatin_t, chLatin_t, chLatin_r, chLatin_i, chLatin_b, chLatin_u, chLatin_t, chLatin_e, chNull +}; + +const XMLCh samlconstants::SAML20MD_ENTITY_ATTRIBUTE_PREFIX[] = UNICODE_LITERAL_6(m,d,a,t,t,r); + +const XMLCh samlconstants::SAML20_DELEGATION_CONDITION_NS[] = // urn:oasis:names:tc:SAML:2.0:conditions:delegation +{ chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_o, chLatin_a, chLatin_s, chLatin_i, chLatin_s, chColon, + chLatin_n, chLatin_a, chLatin_m, chLatin_e, chLatin_s, chColon, chLatin_t, chLatin_c, chColon, + chLatin_S, chLatin_A, chLatin_M, chLatin_L, chColon, chDigit_2, chPeriod, chDigit_0, chColon, + chLatin_c, chLatin_o, chLatin_n, chLatin_d, chLatin_i, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chLatin_s, chColon, + chLatin_d, chLatin_e, chLatin_l, chLatin_e, chLatin_g, chLatin_a, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chNull +}; + +const XMLCh samlconstants::SAML20_DELEGATION_CONDITION_PREFIX[] = UNICODE_LITERAL_3(d,e,l); + const char samlconstants::SAML1_BINDING_SOAP[] = "urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding"; const char samlconstants::SAML1_PROFILE_BROWSER_ARTIFACT[] = "urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"; diff --git a/shibsp/lite/SAMLConstants.h b/shibsp/lite/SAMLConstants.h index 74e0004..f04b3f3 100644 --- a/shibsp/lite/SAMLConstants.h +++ b/shibsp/lite/SAMLConstants.h @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,8 +16,8 @@ /** * @file shibsp/lite/SAMLConstants.h - * - * SAML XML namespace constants + * + * SAML XML namespace constants */ #ifndef __shibsp_xmlconstants_h__ @@ -29,10 +29,10 @@ * SAML related constants. */ namespace samlconstants { - + /** Liberty PAOS XML Namespace ("urn:liberty:paos:2003-08") */ extern SHIBSP_API const XMLCh PAOS_NS[]; - + /** Liberty PAOS QName prefix ("paos") */ extern SHIBSP_API const XMLCh PAOS_PREFIX[]; @@ -41,16 +41,16 @@ namespace samlconstants { /** SAML 1.X Protocol XML namespace ("urn:oasis:names:tc:SAML:1.0:protocol") */ extern SHIBSP_API const XMLCh SAML1P_NS[]; - + /** SAML 1.X Assertion QName prefix ("saml") */ extern SHIBSP_API const XMLCh SAML1_PREFIX[]; /** SAML 1.X Protocol QName prefix ("samlp") */ extern SHIBSP_API const XMLCh SAML1P_PREFIX[]; - + /** SAML 2.0 Version ("2.0") */ extern SHIBSP_API const XMLCh SAML20_VERSION[]; - + /** SAML 2.0 Assertion XML namespace ("urn:oasis:names:tc:SAML:2.0:assertion") */ extern SHIBSP_API const XMLCh SAML20_NS[]; @@ -62,7 +62,7 @@ namespace samlconstants { /** SAML 2.0 AuthnContext XML namespace ("urn:oasis:names:tc:SAML:2.0:ac") */ extern SHIBSP_API const XMLCh SAML20AC_NS[]; - + /** SAML 2.0 Assertion QName prefix ("saml") */ extern SHIBSP_API const XMLCh SAML20_PREFIX[]; @@ -77,52 +77,70 @@ namespace samlconstants { /** SAML 2.0 Enhanced Client/Proxy SSO Profile XML Namespace ("urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp") */ extern SHIBSP_API const XMLCh SAML20ECP_NS[]; - + /** SAML 2.0 Enhanced Client/Proxy SSO Profile QName prefix ("ecp") */ extern SHIBSP_API const XMLCh SAML20ECP_PREFIX[]; /** SAML 2.0 DCE PAC Attribute Profile XML Namespace ("urn:oasis:names:tc:SAML:2.0:profiles:attribute:DCE") */ extern SHIBSP_API const XMLCh SAML20DCE_NS[]; - + /** SAML 2.0 DCE PAC Attribute Profile QName prefix ("DCE") */ extern SHIBSP_API const XMLCh SAML20DCE_PREFIX[]; /** SAML 2.0 X.500 Attribute Profile XML Namespace ("urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500") */ extern SHIBSP_API const XMLCh SAML20X500_NS[]; - + /** SAML 2.0 X.500 Attribute Profile QName prefix ("x500") */ extern SHIBSP_API const XMLCh SAML20X500_PREFIX[]; /** SAML 2.0 XACML Attribute Profile XML Namespace ("urn:oasis:names:tc:SAML:2.0:profiles:attribute:XACML") */ extern SHIBSP_API const XMLCh SAML20XACML_NS[]; - + /** SAML 2.0 XACML Attribute Profile QName prefix ("xacmlprof") */ extern SHIBSP_API const XMLCh SAML20XACML_PREFIX[]; /** SAML 1.x Metadata Profile XML Namespace ("urn:oasis:names:tc:SAML:profiles:v1metadata") */ extern SHIBSP_API const XMLCh SAML1MD_NS[]; - + /** SAML 1.x Metadata Profile QName prefix ("saml1md") */ extern SHIBSP_API const XMLCh SAML1MD_PREFIX[]; /** SAML 1.0 Protocol Enumeration constant ("urn:oasis:names:tc:SAML:1.0:protocol") */ extern SHIBSP_API const XMLCh SAML10_PROTOCOL_ENUM[]; - + /** SAML 1.1 Protocol Enumeration constant ("urn:oasis:names:tc:SAML:1.1:protocol") */ extern SHIBSP_API const XMLCh SAML11_PROTOCOL_ENUM[]; /** SAML Query Requester Metadata Extension XML Namespace ("urn:oasis:names:tc:SAML:metadata:ext:query") */ extern SHIBSP_API const XMLCh SAML20MD_QUERY_EXT_NS[]; - + /** SAML Query Requester Metadata Extension QName prefix ("query") */ extern SHIBSP_API const XMLCh SAML20MD_QUERY_EXT_PREFIX[]; /** SAML Third-Party Request Protocol Extension XML Namespace ("urn:oasis:names:tc:SAML:protocol:ext:third-party") */ extern SHIBSP_API const XMLCh SAML20P_THIRDPARTY_EXT_NS[]; - - /** SAML Third-Party Request Protocol Extension QName prefix ("query") */ + + /** SAML Third-Party Request Protocol Extension QName prefix ("thrpty") */ extern SHIBSP_API const XMLCh SAML20P_THIRDPARTY_EXT_PREFIX[]; + /** SAML Attribute Extension XML Namespace ("urn:oasis:names:tc:SAML:attribute:ext") */ + extern SHIBSP_API const XMLCh SAML20_ATTRIBUTE_EXT_NS[]; + + /** SAML Attribute Extension QName prefix ("ext") */ + extern SHIBSP_API const XMLCh SAML20_ATTRIBUTE_EXT_PREFIX[]; + + /** SAML Metadata Extension for Entity Attributes XML Namespace ("urn:oasis:names:tc:SAML:metadata:attribute") */ + extern SHIBSP_API const XMLCh SAML20MD_ENTITY_ATTRIBUTE_NS[]; + + /** SAML Metadata Extension for Entity Attributes QName prefix ("mdattr") */ + extern SHIBSP_API const XMLCh SAML20MD_ENTITY_ATTRIBUTE_PREFIX[]; + + /** SAML Condition for Delegation Restriction XML Namespace ("urn:oasis:names:tc:SAML:2.0:conditions:delegation") */ + extern SHIBSP_API const XMLCh SAML20_DELEGATION_CONDITION_NS[]; + + /** SAML Condition for Delegation Restriction QName prefix ("del") */ + extern SHIBSP_API const XMLCh SAML20_DELEGATION_CONDITION_PREFIX[]; + /** SAML 1.x SOAP binding ("urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding") */ extern SHIBSP_API const char SAML1_BINDING_SOAP[]; @@ -131,11 +149,11 @@ namespace samlconstants { /** SAML 1.x Browser POST profile ("urn:oasis:names:tc:SAML:1.0:profiles:browser-post") */ extern SHIBSP_API const char SAML1_PROFILE_BROWSER_POST[]; - + /** SAML 2.0 SOAP binding ("urn:oasis:names:tc:SAML:2.0:bindings:SOAP") */ extern SHIBSP_API const char SAML20_BINDING_SOAP[]; - /** SAML 2.0 SOAP binding ("urn:oasis:names:tc:SAML:2.0:bindings:PAOS") */ + /** SAML 2.0 PAOS binding ("urn:oasis:names:tc:SAML:2.0:bindings:PAOS") */ extern SHIBSP_API const char SAML20_BINDING_PAOS[]; /** SAML 2.0 URI binding ("urn:oasis:names:tc:SAML:2.0:bindings:URI") */ @@ -146,13 +164,13 @@ namespace samlconstants { /** SAML 2.0 HTTP-POST binding ("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST") */ extern SHIBSP_API const char SAML20_BINDING_HTTP_POST[]; - + /** SAML 2.0 HTTP-POST-SimpleSign binding ("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign") */ extern SHIBSP_API const char SAML20_BINDING_HTTP_POST_SIMPLESIGN[]; /** SAML 2.0 HTTP-Redirect binding ("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect") */ extern SHIBSP_API const char SAML20_BINDING_HTTP_REDIRECT[]; - + /** SAML 2.0 HTTP-Redirect DEFLATE URL encoding ("urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE") */ extern SHIBSP_API const char SAML20_BINDING_URL_ENCODING_DEFLATE[]; }; diff --git a/shibsp/metadata/DynamicMetadataProvider.cpp b/shibsp/metadata/DynamicMetadataProvider.cpp index 42545a3..9e583e4 100644 --- a/shibsp/metadata/DynamicMetadataProvider.cpp +++ b/shibsp/metadata/DynamicMetadataProvider.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2008 Internet2 + * 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. @@ -127,14 +127,16 @@ saml2md::EntityDescriptor* DynamicMetadataProvider::resolve(const saml2md::Metad Category& log=Category::getInstance(SHIBSP_LOGCAT".MetadataProvider.Dynamic"); string name; - if (criteria.entityID_ascii) + if (criteria.entityID_ascii) { name = criteria.entityID_ascii; + } else if (criteria.entityID_unicode) { auto_ptr_char temp(criteria.entityID_unicode); name = temp.get(); } - else if (criteria.artifact) - name = criteria.artifact->getSource(); + else if (criteria.artifact) { + throw saml2md::MetadataException("Unable to resolve metadata dynamically from an artifact."); + } // Establish networking properties based on calling application. const MetadataProviderCriteria* mpc = dynamic_cast(&criteria); @@ -239,9 +241,8 @@ saml2md::EntityDescriptor* DynamicMetadataProvider::resolve(const saml2md::Metad } try { - // Use an empty stream to trigger a body-less "GET" operation. - istringstream dummy; - transport->send(dummy); + // Use a NULL stream to trigger a body-less "GET" operation. + transport->send(); istream& msg = transport->receive(); DOMDocument* doc=NULL; diff --git a/shibsp/metadata/MetadataExtImpl.cpp b/shibsp/metadata/MetadataExtImpl.cpp index 6188b46..71fb17a 100644 --- a/shibsp/metadata/MetadataExtImpl.cpp +++ b/shibsp/metadata/MetadataExtImpl.cpp @@ -59,7 +59,7 @@ namespace shibsp { public: - ScopeImpl(const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const QName* schemaType) + ScopeImpl(const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const xmltooling::QName* schemaType) : AbstractXMLObject(nsURI, localName, prefix, schemaType) { init(); } @@ -99,7 +99,7 @@ namespace shibsp { XMLString::release(&m_VerifyDepth); } - KeyAuthorityImpl(const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const QName* schemaType) + KeyAuthorityImpl(const XMLCh* nsURI, const XMLCh* localName, const XMLCh* prefix, const xmltooling::QName* schemaType) : AbstractXMLObject(nsURI, localName, prefix, schemaType) { init(); } @@ -119,7 +119,7 @@ namespace shibsp { IMPL_TYPED_CHILDREN(KeyInfo,m_children.end()); public: - void setAttribute(const QName& qualifiedName, const XMLCh* value, bool ID=false) { + void setAttribute(const xmltooling::QName& qualifiedName, const XMLCh* value, bool ID=false) { if (!qualifiedName.hasNamespaceURI()) { if (XMLString::equals(qualifiedName.getLocalPart(),VERIFYDEPTH_ATTRIB_NAME)) { setVerifyDepth(value); diff --git a/shibsp/metadata/MetadataExtSchemaValidators.cpp b/shibsp/metadata/MetadataExtSchemaValidators.cpp index d175ba4..f9b4472 100644 --- a/shibsp/metadata/MetadataExtSchemaValidators.cpp +++ b/shibsp/metadata/MetadataExtSchemaValidators.cpp @@ -46,12 +46,12 @@ namespace shibsp { }; #define REGISTER_ELEMENT(cname) \ - q=QName(SHIBMD_NS,cname::LOCAL_NAME); \ + q=xmltooling::QName(SHIBMD_NS,cname::LOCAL_NAME); \ XMLObjectBuilder::registerBuilder(q,new cname##Builder()); \ SchemaValidators.registerValidator(q,new cname##SchemaValidator()) void shibsp::registerMetadataExtClasses() { - QName q; + xmltooling::QName q; REGISTER_ELEMENT(Scope); REGISTER_ELEMENT(KeyAuthority); diff --git a/shibsp/metadata/MetadataProviderCriteria.h b/shibsp/metadata/MetadataProviderCriteria.h index 4020005..286a4a0 100644 --- a/shibsp/metadata/MetadataProviderCriteria.h +++ b/shibsp/metadata/MetadataProviderCriteria.h @@ -37,6 +37,14 @@ namespace shibsp { * Constructor. * * @param app application performing the lookup + */ + MetadataProviderCriteria(const Application& app) : application(app) { + } + + /** + * Constructor. + * + * @param app application performing the lookup * @param id entityID to lookup * @param q element/type of role, if any * @param prot protocol support constant, if any diff --git a/shibsp/paths.h b/shibsp/paths.h index 4aa5d5e..6eb507d 100644 --- a/shibsp/paths.h +++ b/shibsp/paths.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2005 Internet2 + * 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. @@ -23,24 +23,31 @@ #ifndef __shibsp_paths_h__ #define __shibsp_paths_h__ -/** - * Default schema catalogs. - */ -#define SHIBSP_SCHEMAS "/usr/share/xml/xmltooling/catalog.xml:/usr/share/xml/opensaml/saml20-catalog.xml:/usr/share/xml/opensaml/saml11-catalog.xml:/usr/local/share/xml/shibboleth/catalog.xml" +/** Default schema catalogs. */ +#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. - */ +/** Default name of SP configuration file. */ #define SHIBSP_CONFIG "shibboleth2.xml" -/** - * Default name of SP console tool logging file. - */ +/** Default name of SP console tool logging file. */ #define SHIBSP_LOGGING "console.logger" -/** - * Default prefix for installation (used to resolve relative paths). - */ -#define SHIBSP_PREFIX "/usr/local" +/** Default prefix for installation (used to resolve relative paths). */ +#define SHIBSP_PREFIX "/opt/shibboleth-sp" + +/** Library directory for installation (used to resolve relative paths). */ +#define SHIBSP_LIBDIR "/opt/shibboleth-sp/lib" + +/** Log directory for installation (used to resolve relative paths). */ +#define SHIBSP_LOGDIR "/opt/shibboleth-sp/var/log" + +/** Configuration directory for installation (used to resolve relative paths). */ +#define SHIBSP_CFGDIR "/opt/shibboleth-sp/etc" + +/** Runtime state directory for installation (used to resolve relative paths). */ +#define SHIBSP_RUNDIR "/opt/shibboleth-sp/var/run" + +/** XML directory for installation (used to resolve relative paths). */ +#define SHIBSP_XMLDIR "/opt/shibboleth-sp/share/xml" #endif /* __shibsp_paths_h__ */ diff --git a/shibsp/paths.h.in b/shibsp/paths.h.in index 9f5e2b1..f35df0b 100644 --- a/shibsp/paths.h.in +++ b/shibsp/paths.h.in @@ -1,5 +1,5 @@ /* - * Copyright 2001-2005 Internet2 + * 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. @@ -23,24 +23,31 @@ #ifndef __shibsp_paths_h__ #define __shibsp_paths_h__ -/** - * Default schema catalogs. - */ +/** Default schema catalogs. */ #define SHIBSP_SCHEMAS "@-XMLTOOLINGXMLDIR-@/catalog.xml:@-OPENSAMLXMLDIR-@/saml20-catalog.xml:@-OPENSAMLXMLDIR-@/saml11-catalog.xml:@-PKGXMLDIR-@/catalog.xml" -/** - * Default name of SP configuration file. - */ +/** Default name of SP configuration file. */ #define SHIBSP_CONFIG "shibboleth2.xml" -/** - * Default name of SP console tool logging file. - */ +/** Default name of SP console tool logging file. */ #define SHIBSP_LOGGING "console.logger" -/** - * Default prefix for installation (used to resolve relative paths). - */ +/** Default prefix for installation (used to resolve relative paths). */ #define SHIBSP_PREFIX "@-PREFIX-@" +/** Library directory for installation (used to resolve relative paths). */ +#define SHIBSP_LIBDIR "@-LIBDIR-@" + +/** Log directory for installation (used to resolve relative paths). */ +#define SHIBSP_LOGDIR "@-LOGDIR-@" + +/** Configuration directory for installation (used to resolve relative paths). */ +#define SHIBSP_CFGDIR "@-SYSCONFDIR-@" + +/** Runtime state directory for installation (used to resolve relative paths). */ +#define SHIBSP_RUNDIR "@-RUNDIR-@" + +/** XML directory for installation (used to resolve relative paths). */ +#define SHIBSP_XMLDIR "@-XMLDIR-@" + #endif /* __shibsp_paths_h__ */ diff --git a/shibsp/remoting/ListenerService.h b/shibsp/remoting/ListenerService.h index 3e8c6b3..c85af8c 100644 --- a/shibsp/remoting/ListenerService.h +++ b/shibsp/remoting/ListenerService.h @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * @file shibsp/remoting/ListenerService.h - * + * * Interprocess remoting engine. */ @@ -30,7 +30,7 @@ namespace shibsp { /** * Interface to a remoted service - * + * * Classes that support remoted messages delivered by the Listener runtime * support this interface and register themselves with the runtime to receive * particular messages. @@ -45,7 +45,7 @@ namespace shibsp { /** * Remoted classes implement this method to process incoming messages. - * + * * @param in incoming DDF message * @param out stream to write outgoing DDF message to */ @@ -59,7 +59,7 @@ namespace shibsp { /** * Interface to a remoting engine. - * + * * A ListenerService supports the remoting of DDF objects, which are dynamic data trees * that other class implementations can use to remote themselves by calling an * out-of-process peer implementation with arbitrary data to carry out tasks @@ -77,53 +77,74 @@ namespace shibsp { /** * Send a remoted message and return the response. - * + * * @param in input message to send * @return response from remote service */ virtual DDF send(const DDF& in)=0; - + void receive(DDF& in, std::ostream& out); // Remoted classes register and unregister for messages using these methods. // Registration returns any existing listeners, allowing message hooking. - + /** * Register for a message. Returns existing remote service, allowing message hooking. - * + * * @param address message address to register * @param svc pointer to remote service * @return previous service registered for message, if any */ virtual Remoted* regListener(const char* address, Remoted* svc); - + /** * Unregisters service from an address, possibly restoring an original. - * + * * @param address message address to modify * @param current pointer to unregistering service * @param restore service to "restore" registration for * @return true iff the current service was still registered */ virtual bool unregListener(const char* address, Remoted* current, Remoted* restore=NULL); - + /** * Returns current service registered at an address, if any. - * + * * @param address message address to access * @return registered service, or NULL */ virtual Remoted* lookup(const char* address) const; /** + * OutOfProcess servers can implement server-side initialization that should occur + * before daemonization. + * + *

The parameter applies to implementations that can detect and remove + * the results of ungraceful shutdowns of previous executions and continue + * successfully. File-based sockets are the most common example. + * + * @param force true iff remnant network state should be forcibly cleared + * @return true iff the service initialization was successful + */ + virtual bool init(bool force) { + return true; + } + + /** * OutOfProcess servers can implement server-side transport handling by * calling the run method and supplying a flag to monitor for shutdown. - * + * * @param shutdown pointer to flag that caller will set when shutdown is required - * @return true iff ListenerService initialization was successful + * @return true iff the service execution was successful */ virtual bool run(bool* shutdown)=0; + /** + * OutOfProcess servers can implement server-side termination/cleanup. + */ + virtual void term() { + } + private: std::map m_listenerMap; }; diff --git a/shibsp/remoting/ddf.h b/shibsp/remoting/ddf.h index 041f344..b361ec0 100644 --- a/shibsp/remoting/ddf.h +++ b/shibsp/remoting/ddf.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -44,7 +44,7 @@ namespace shibsp { // constructors DDF() : m_handle(NULL) {} DDF(const char* n); - DDF(const char* n, const char* val); + DDF(const char* n, const char* val, bool safe=true); DDF(const char* n, long val); DDF(const char* n, double val); DDF(const char* n, void* val); @@ -80,7 +80,10 @@ namespace shibsp { DDF& string(const char* val) { return string(const_cast(val), true); } - DDF& string(char* val, bool copyit=true); + DDF& unsafe_string(const char* val) { + return string(const_cast(val), true, false); + } + DDF& string(char* val, bool copyit=true, bool safe=true); DDF& string(long val); DDF& string(double val); DDF& integer(long val); diff --git a/shibsp/remoting/impl/ListenerService.cpp b/shibsp/remoting/impl/ListenerService.cpp index 600c87f..f33b307 100644 --- a/shibsp/remoting/impl/ListenerService.cpp +++ b/shibsp/remoting/impl/ListenerService.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * ListenerService.cpp - * + * * Interprocess remoting engine. */ @@ -93,6 +93,6 @@ void ListenerService::receive(DDF &in, ostream& out) Remoted* dest=lookup(in.name()); if (!dest) throw ListenerException("No destination registered for incoming message addressed to ($1).",params(1,in.name())); - + dest->receive(in, out); } diff --git a/shibsp/remoting/impl/SocketListener.cpp b/shibsp/remoting/impl/SocketListener.cpp index e211b20..edac04f 100644 --- a/shibsp/remoting/impl/SocketListener.cpp +++ b/shibsp/remoting/impl/SocketListener.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * SocketListener.cpp - * + * * Berkeley Socket-based ListenerService implementation */ @@ -45,7 +45,7 @@ using namespace std; using xercesc::DOMElement; namespace shibsp { - + // Manages the pool of connections class SocketPool { @@ -55,16 +55,16 @@ namespace shibsp { ~SocketPool(); SocketListener::ShibSocket get(); void put(SocketListener::ShibSocket s); - + private: SocketListener::ShibSocket connect(); - - Category& m_log; + + Category& m_log; const SocketListener* m_listener; auto_ptr m_lock; stack m_pool; }; - + // Worker threads in server class ServerThread { public: @@ -104,7 +104,7 @@ SocketListener::ShibSocket SocketPool::connect() connected = true; break; } - + m_log.warn("cannot connect socket (%u)...%s", sock, (i > 0 ? "retrying" : "")); if (i) { @@ -158,8 +158,9 @@ void SocketPool::put(SocketListener::ShibSocket s) m_lock->unlock(); } -SocketListener::SocketListener(const DOMElement* e) : m_catchAll(false), log(&Category::getInstance(SHIBSP_LOGCAT".Listener")), - m_socketpool(NULL), m_shutdown(NULL), m_child_lock(NULL), m_child_wait(NULL), m_socket((ShibSocket)0) +SocketListener::SocketListener(const DOMElement* e) + : m_catchAll(false), log(&Category::getInstance(SHIBSP_LOGCAT".Listener")), m_socketpool(NULL), + m_shutdown(NULL), m_child_lock(NULL), m_child_wait(NULL), m_socket((ShibSocket)0) { // Are we a client? if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) { @@ -179,10 +180,10 @@ SocketListener::~SocketListener() delete m_child_lock; } -bool SocketListener::run(bool* shutdown) +bool SocketListener::init(bool force) { #ifdef _DEBUG - NDC ndc("run"); + NDC ndc("init"); #endif log->info("listener service starting"); @@ -194,28 +195,36 @@ bool SocketListener::run(bool* shutdown) m_catchAll = flag.first && flag.second; } sp->unlock(); - - // Save flag to monitor for shutdown request. - m_shutdown=shutdown; - unsigned long count = 0; if (!create(m_socket)) { log->crit("failed to create socket"); return false; } - if (!bind(m_socket,true)) { + if (!bind(m_socket, force)) { this->close(m_socket); log->crit("failed to bind to socket."); return false; } + return true; +} + +bool SocketListener::run(bool* shutdown) +{ +#ifdef _DEBUG + NDC ndc("run"); +#endif + // Save flag to monitor for shutdown request. + m_shutdown=shutdown; + unsigned long count = 0; + while (!*m_shutdown) { fd_set readfds; FD_ZERO(&readfds); FD_SET(m_socket, &readfds); struct timeval tv = { 0, 0 }; tv.tv_sec = 5; - + switch (select(m_socket + 1, &readfds, 0, 0, &tv)) { #ifdef WIN32 case SOCKET_ERROR: @@ -225,24 +234,30 @@ bool SocketListener::run(bool* shutdown) if (errno == EINTR) continue; log_error(); log->error("select() on main listener socket failed"); - return false; - + *m_shutdown = true; + break; + case 0: continue; - + default: { // Accept the connection. SocketListener::ShibSocket newsock; - if (!accept(m_socket, newsock)) + if (!accept(m_socket, newsock)) { log->crit("failed to accept incoming socket connection"); + continue; + } // We throw away the result because the children manage themselves... try { new ServerThread(newsock,this,++count); } + catch (exception& ex) { + log->crit("exception starting new server thread to service incoming request: %s", ex.what()); + } catch (...) { - log->crit("error starting new server thread to service incoming request"); + log->crit("unknown error starting new server thread to service incoming request"); if (!m_catchAll) *m_shutdown = true; } @@ -257,9 +272,13 @@ bool SocketListener::run(bool* shutdown) m_child_wait->wait(m_child_lock); m_child_lock->unlock(); + return true; +} + +void SocketListener::term() +{ this->close(m_socket); m_socket=(ShibSocket)0; - return true; } DDF SocketListener::send(const DDF& in) @@ -285,7 +304,7 @@ DDF SocketListener::send(const DDF& in) SocketListener::ShibSocket sock; while (retry >= 0) { sock = m_socketpool->get(); - + int outlen = ostr.length(); len = htonl(outlen); if (send(sock,(char*)&len,sizeof(len)) != sizeof(len) || send(sock,ostr.c_str(),outlen) != outlen) { @@ -312,7 +331,7 @@ DDF SocketListener::send(const DDF& in) throw ListenerException("Failure receiving response to remoted message ($1).", params(1,in.name())); } len = ntohl(len); - + char buf[16384]; int size_read; stringstream is; @@ -326,25 +345,25 @@ DDF SocketListener::send(const DDF& in) break; } } - + if (len) { log->error("error reading output message from socket"); this->close(sock); throw ListenerException("Failure receiving response to remoted message ($1).", params(1,in.name())); } - + m_socketpool->put(sock); // Unmarshall data. DDF out; is >> out; - + // Check for exception to unmarshall and throw, otherwise return. if (out.isstring() && out.name() && !strcmp(out.name(),"exception")) { // Reconstitute exception object. DDFJanitor jout(out); XMLToolingException* except=NULL; - try { + try { except=XMLToolingException::fromString(out.string()); log->error("remoted message returned an error: %s", except->what()); } @@ -418,7 +437,7 @@ ServerThread::~ServerThread() m_listener->m_children.erase(m_sock); m_listener->m_child_lock->unlock(); m_listener->m_child_wait->signal(); - + delete m_child; } @@ -432,7 +451,7 @@ void ServerThread::run() m_listener->m_child_wait->wait(m_listener->m_child_lock); m_listener->m_children[m_sock] = m_child; m_listener->m_child_lock->unlock(); - + int result; fd_set readfds; struct timeval tv = { 0, 0 }; @@ -494,19 +513,19 @@ int ServerThread::job() return -1; } len = ntohl(len); - + int size_read; stringstream is; while (len && (size_read = m_listener->recv(m_sock, m_buf, sizeof(m_buf))) > 0) { is.write(m_buf, size_read); len -= size_read; } - + if (len) { log.error("error reading input message from socket"); return -1; } - + // Unmarshall the message. DDF in; DDFJanitor jin(in); @@ -544,7 +563,7 @@ int ServerThread::job() DDFJanitor jout(out); sink << out; } - + // Return whatever's available. string response(sink.str()); int outlen = response.length(); @@ -557,6 +576,6 @@ int ServerThread::job() log.error("error sending output message"); return -1; } - + return 0; } diff --git a/shibsp/remoting/impl/SocketListener.h b/shibsp/remoting/impl/SocketListener.h index 0922cf4..a558650 100644 --- a/shibsp/remoting/impl/SocketListener.h +++ b/shibsp/remoting/impl/SocketListener.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -16,7 +16,7 @@ /** * SocketListener.h - * + * * Berkeley Socket-based ListenerService implementation */ @@ -41,7 +41,7 @@ namespace shibsp { class SocketPool; class ServerThread; - + /** * Berkeley Socket-based ListenerService implementation */ @@ -53,7 +53,10 @@ namespace shibsp { ~SocketListener(); DDF send(const DDF& in); + + bool init(bool force); bool run(bool* shutdown); + void term(); // Implemented by socket-specific subclasses. #ifdef WIN32 @@ -74,7 +77,7 @@ namespace shibsp { bool log_error() const; // for OS-level errors xmltooling::logging::Category* log; /// @endcond - + private: mutable SocketPool* m_socketpool; bool* m_shutdown; diff --git a/shibsp/remoting/impl/TCPListener.cpp b/shibsp/remoting/impl/TCPListener.cpp index 44828e6..05f2f90 100644 --- a/shibsp/remoting/impl/TCPListener.cpp +++ b/shibsp/remoting/impl/TCPListener.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * TCPListener.cpp - * + * * TCP-based SocketListener implementation */ @@ -31,6 +31,7 @@ # include # include # include +# include #endif #include @@ -60,21 +61,21 @@ namespace shibsp { bool connect(ShibSocket& s) const; bool close(ShibSocket& s) const; bool accept(ShibSocket& listener, ShibSocket& s) const; - + int send(ShibSocket& s, const char* buf, int len) const { return ::send(s, buf, len, 0); } - + int recv(ShibSocket& s, char* buf, int buflen) const { return ::recv(s, buf, buflen, 0); } - + private: void setup_tcp_sockaddr(struct sockaddr_in* addr) const; string m_address; unsigned short m_port; - vector m_acl; + set m_acl; }; ListenerService* SHIBSP_DLLLOCAL TCPListenerServiceFactory(const DOMElement* const & e) @@ -91,14 +92,14 @@ TCPListener::TCPListener(const DOMElement* e) : SocketListener(e), m_address("12 auto_ptr_char a(tag); m_address=a.get(); } - + tag=e->getAttributeNS(NULL,port); if (tag && *tag) { m_port=XMLString::parseInt(tag); if (m_port==0) m_port=12345; } - + tag=e->getAttributeNS(NULL,acl); if (tag && *tag) { auto_ptr_char temp(tag); @@ -107,15 +108,15 @@ TCPListener::TCPListener(const DOMElement* e) : SocketListener(e), m_address("12 int j = 0; for (unsigned int i=0; i < sockacl.length(); i++) { if (sockacl.at(i)==' ') { - m_acl.push_back(sockacl.substr(j, i-j)); + m_acl.insert(sockacl.substr(j, i-j)); j = i+1; } } - m_acl.push_back(sockacl.substr(j, sockacl.length()-j)); + m_acl.insert(sockacl.substr(j, sockacl.length()-j)); } } else - m_acl.push_back("127.0.0.1"); + m_acl.insert("127.0.0.1"); } void TCPListener::setup_tcp_sockaddr(struct sockaddr_in* addr) const @@ -204,12 +205,11 @@ bool TCPListener::accept(ShibSocket& listener, ShibSocket& s) const #endif return log_error(); char* client=inet_ntoa(addr.sin_addr); - for (vector::const_iterator i=m_acl.begin(); i!=m_acl.end(); i++) { - if (*i==client) - return true; + if (m_acl.count(client) == 0) { + close(s); + s=-1; + log->error("accept() rejected client at %s", client); + return false; } - close(s); - s=-1; - log->error("accept() rejected client at %s\n",client); - return false; + return true; } diff --git a/shibsp/remoting/impl/ddf.cpp b/shibsp/remoting/impl/ddf.cpp index 463bb8c..ae44d4f 100644 --- a/shibsp/remoting/impl/ddf.cpp +++ b/shibsp/remoting/impl/ddf.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -16,7 +16,7 @@ /** * ddf.cpp - * + * * C++ DDF abstraction for interpretive RPC */ @@ -32,6 +32,7 @@ #include #include #include +#include #include using namespace shibsp; @@ -61,22 +62,19 @@ char* ddf_strdup(const char* s) The name buffer is returned from the function. */ char* ddf_token(const char** path, char* name) { - const char* temp=NULL; - - *name='\0'; - if (*path==NULL || **path=='\0') + *name=0; + if (*path==NULL || **path==0) return name; - temp=strchr(*path,'.'); - if (temp==NULL) - { - strcpy(name,*path); + const char* temp=strchr(*path,'.'); + if (temp==NULL) { + strncpy(name,*path,MAX_NAME_LEN); + name[MAX_NAME_LEN]=0; *path=NULL; } - else if (temp>*path) - { + else if (temp>*path) { strncpy(name,*path,temp-*path); - name[temp-*path]='\0'; + name[temp-*path]=0; *path=temp+1; } else @@ -101,7 +99,8 @@ struct shibsp::ddf_body_t { DDF_FLOAT, DDF_STRUCT, DDF_LIST, - DDF_POINTER + DDF_POINTER, + DDF_STRING_UNSAFE } type; // data type of node union { @@ -126,11 +125,11 @@ DDF::DDF(const char* n) name(n); } -DDF::DDF(const char* n, const char* val) +DDF::DDF(const char* n, const char* val, bool safe) { m_handle=new(nothrow) ddf_body_t; name(n); - string(val); + string(const_cast(val), true, safe); } DDF::DDF(const char* n, long val) @@ -171,7 +170,8 @@ DDF DDF::copy() const case ddf_body_t::DDF_EMPTY: return DDF(m_handle->name); case ddf_body_t::DDF_STRING: - return DDF(m_handle->name,m_handle->value.string); + case ddf_body_t::DDF_STRING_UNSAFE: + return DDF(m_handle->name,m_handle->value.string,(m_handle->type==ddf_body_t::DDF_STRING)); case ddf_body_t::DDF_INT: return DDF(m_handle->name,m_handle->value.integer); case ddf_body_t::DDF_FLOAT: @@ -240,7 +240,7 @@ bool DDF::isempty() const bool DDF::isstring() const { - return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING) : false; + return m_handle ? (m_handle->type==ddf_body_t::DDF_STRING || m_handle->type==ddf_body_t::DDF_STRING_UNSAFE) : false; } bool DDF::isint() const @@ -282,6 +282,7 @@ long DDF::integer() const case ddf_body_t::DDF_FLOAT: return static_cast(m_handle->value.floating); case ddf_body_t::DDF_STRING: + case ddf_body_t::DDF_STRING_UNSAFE: return m_handle->value.string ? atol(m_handle->value.string) : 0; case ddf_body_t::DDF_STRUCT: case ddf_body_t::DDF_LIST: @@ -300,6 +301,7 @@ double DDF::floating() const case ddf_body_t::DDF_FLOAT: return m_handle->value.floating; case ddf_body_t::DDF_STRING: + case ddf_body_t::DDF_STRING_UNSAFE: return m_handle->value.string ? atof(m_handle->value.string) : 0; case ddf_body_t::DDF_STRUCT: case ddf_body_t::DDF_LIST: @@ -332,6 +334,7 @@ DDF& DDF::empty() if (m_handle) { switch (m_handle->type) { case ddf_body_t::DDF_STRING: + case ddf_body_t::DDF_STRING_UNSAFE: if (m_handle->value.string) free(m_handle->value.string); break; @@ -351,13 +354,13 @@ DDF& DDF::empty() return *this; } -DDF& DDF::string(char* val, bool copyit) +DDF& DDF::string(char* val, bool copyit, bool safe) { if (empty().m_handle) { m_handle->value.string = copyit ? ddf_strdup(val) : val; if (!m_handle->value.string && val && *val) return destroy(); - m_handle->type=ddf_body_t::DDF_STRING; + m_handle->type=(safe ? ddf_body_t::DDF_STRING : ddf_body_t::DDF_STRING_UNSAFE); } return *this; } @@ -614,7 +617,7 @@ DDF DDF::addmember(const char* path) { char name[MAX_NAME_LEN+1]; const char* path_ptr=path; - + if (m_handle && ddf_strlen(ddf_token(&path_ptr,name))>0) { if (!isstruct()) structure(); @@ -642,18 +645,32 @@ DDF DDF::addmember(const char* path) DDF DDF::getmember(const char* path) const { + DDF current; char name[MAX_NAME_LEN+1]; const char* path_ptr=path; - DDF current; - if (isstruct() && ddf_strlen(ddf_token(&path_ptr,name))>0) { - current.m_handle=m_handle->value.children.first; - while (current.m_handle && strcmp(current.m_handle->name,name)!=0) - current.m_handle=current.m_handle->next; - - if (current.m_handle && ddf_strlen(path_ptr)>0) - current=current.getmember(path_ptr); + ddf_token(&path_ptr, name); + if (*name == 0) + return current; + else if (*name == '[') { + unsigned long i = strtoul(name+1, NULL, 10); + if (islist() && i < m_handle->value.children.count) + current=operator[](i); + else if (i == 0) + current = *this; + } + else if (isstruct()) { + current.m_handle = m_handle->value.children.first; + while (current.m_handle && strcmp(current.m_handle->name,name) != 0) + current.m_handle = current.m_handle->next; + } + else if (islist()) { + current.m_handle = m_handle->value.children.first; + return current.getmember(path); } + + if (current.m_handle && path_ptr && *path_ptr) + current = current.getmember(path_ptr); return current; } @@ -672,7 +689,7 @@ void DDF::dump(FILE* f, int indent) const ddf_print_indent(f,indent); if (m_handle) { switch (m_handle->type) { - + case ddf_body_t::DDF_EMPTY: fprintf(f,"empty"); if (m_handle->name) @@ -680,6 +697,7 @@ void DDF::dump(FILE* f, int indent) const break; case ddf_body_t::DDF_STRING: + case ddf_body_t::DDF_STRING_UNSAFE: if (m_handle->name) fprintf(f,"char* %s = ",m_handle->name); else @@ -796,8 +814,9 @@ void serialize(ddf_body_t* p, ostream& os, bool name_attr=true) { if (p) { switch (p->type) { - + case ddf_body_t::DDF_STRING: + case ddf_body_t::DDF_STRING_UNSAFE: os << "name) { os << " name=\""; @@ -805,8 +824,14 @@ void serialize(ddf_body_t* p, ostream& os, bool name_attr=true) os << '"'; } if (p->value.string) { - os << '>'; - xml_encode(os,p->value.string); + if (p->type == ddf_body_t::DDF_STRING) { + os << '>'; + xml_encode(os,p->value.string); + } + else { + os << " unsafe=\"1\">"; + xml_encode(os,XMLToolingConfig::getConfig().getURLEncoder()->encode(p->value.string).c_str()); + } os << ""; } else @@ -941,11 +966,12 @@ static const XMLCh _number[] = UNICODE_LITERAL_6(n,u,m,b,e,r); static const XMLCh _array[] = UNICODE_LITERAL_5(a,r,r,a,y); static const XMLCh _struct[] = UNICODE_LITERAL_6(s,t,r,u,c,t); static const XMLCh _lowercase[] = UNICODE_LITERAL_9(l,o,w,e,r,c,a,s,e); +static const XMLCh _unsafe[] = UNICODE_LITERAL_6(u,n,s,a,f,e); DDF deserialize(DOMElement* root, bool lowercase) { DDF obj(NULL); - auto_ptr_char name_val(root->getAttribute(_name)); + auto_ptr_char name_val(root->getAttributeNS(NULL, _name)); if (name_val.get() && *name_val.get()) { if (lowercase) for (char* pch=const_cast(name_val.get()); *pch=tolower(*pch); pch++); @@ -961,9 +987,19 @@ DDF deserialize(DOMElement* root, bool lowercase) if (XMLString::equals(tag,_string)) { DOMNode* child=root->getFirstChild(); if (child && child->getNodeType()==DOMNode::TEXT_NODE) { - char* val = toUTF8(child->getNodeValue(), true); // use malloc - if (val) - obj.string(val, false); // don't re-copy the string + const XMLCh* unsafe = root->getAttributeNS(NULL, _unsafe); + if (unsafe && *unsafe==chDigit_1) { + // If it's unsafe, it's not UTF-8 data, so we have to convert to ASCII and decode it. + char* encoded = XMLString::transcode(child->getNodeValue()); + XMLToolingConfig::getConfig().getURLEncoder()->decode(encoded); + obj.string(encoded, true, false); // re-copy into free-able buffer, plus mark unsafe + XMLString::release(&encoded); + } + else { + char* val = toUTF8(child->getNodeValue(), true); // use malloc + if (val) + obj.string(val, false); // don't re-copy the string + } } } else if (XMLString::equals(tag,_number)) { diff --git a/shibsp/security/PKIXTrustEngine.cpp b/shibsp/security/PKIXTrustEngine.cpp index c8ed9bc..d52db86 100644 --- a/shibsp/security/PKIXTrustEngine.cpp +++ b/shibsp/security/PKIXTrustEngine.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -272,7 +272,6 @@ void MetadataPKIXIterator::populate() if (m_caching) { m_engine.m_credLock->unlock(); m_engine.m_credLock->wrlock(); - PKIXTrustEngine::credmap_t::iterator cached = m_credCache->second.find(m_current); if (m_credCache->second.count(m_current)==0) { // Transfer objects into cache. m_credCache->second[m_current] = m_ownedCreds; diff --git a/shibsp/security/SecurityPolicy.cpp b/shibsp/security/SecurityPolicy.cpp index 6e260d4..8317554 100644 --- a/shibsp/security/SecurityPolicy.cpp +++ b/shibsp/security/SecurityPolicy.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,21 +16,38 @@ /** * SecurityPolicy.cpp - * + * * SP-specific SecurityPolicy subclass. */ #include "internal.h" #include "Application.h" #include "ServiceProvider.h" +#include "metadata/MetadataProviderCriteria.h" #include "security/SecurityPolicy.h" using namespace shibsp; +using namespace opensaml::saml2; +using namespace std; -SecurityPolicy::SecurityPolicy(const Application& application, const xmltooling::QName* role, bool validate) - : opensaml::SecurityPolicy(application.getMetadataProvider(), role, application.getTrustEngine(), validate), m_application(application) { - - const std::vector& rules = - application.getServiceProvider().getPolicyRules(application.getString("policyId").second); +SecurityPolicy::SecurityPolicy(const Application& application, const xmltooling::QName* role, bool validate, const char* policyId) + : opensaml::SecurityPolicy(application.getMetadataProvider(), role, application.getTrustEngine(), validate), m_application(application) { + const vector& rules = + application.getServiceProvider().getPolicyRules(policyId ? policyId : application.getString("policyId").second); getRules().assign(rules.begin(), rules.end()); + + // Populate audiences. + if (application.getAudiences()) { + for (vector::const_iterator a = application.getAudiences()->begin(); a != application.getAudiences()->end(); ++a) + getAudiences().push_back(*a); + } +} + +opensaml::saml2md::MetadataProvider::Criteria& SecurityPolicy::getMetadataProviderCriteria() const +{ + if (!m_metadataCriteria) + m_metadataCriteria=new MetadataProviderCriteria(m_application); + else + m_metadataCriteria->reset(); + return *m_metadataCriteria; } diff --git a/shibsp/security/SecurityPolicy.h b/shibsp/security/SecurityPolicy.h index 74745ff..29a3bf5 100644 --- a/shibsp/security/SecurityPolicy.h +++ b/shibsp/security/SecurityPolicy.h @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,7 +16,7 @@ /** * @file shibsp/security/SecurityPolicy.h - * + * * SP-specific SecurityPolicy subclass. */ @@ -24,32 +24,35 @@ #define __shibsp_secpol_h__ #include -#include +#include namespace shibsp { - + class SHIBSP_API Application; /** * SP-specific SecurityPolicy subclass. */ - class SHIBSP_API SecurityPolicy : public opensaml::SecurityPolicy + class SHIBSP_API SecurityPolicy : public opensaml::saml2::SAML2AssertionPolicy { public: /** * Constructor for policy. - * + * * @param application an Application instance - * @param role identifies the role (generally IdP or SP) of the policy peer + * @param role identifies the role (generally IdP or SP) of the policy peer * @param validate true iff XML parsing should be done with validation + * @param policyId identifies policy rules to auto-attach, defaults to the application's set */ - SecurityPolicy(const Application& application, const xmltooling::QName* role=NULL, bool validate=true); + SecurityPolicy(const Application& application, const xmltooling::QName* role=NULL, bool validate=true, const char* policyId=NULL); virtual ~SecurityPolicy() {} + opensaml::saml2md::MetadataProvider::Criteria& getMetadataProviderCriteria() const; + /** * Returns the Application associated with the policy. - * + * * @return the associated Application */ const Application& getApplication() const { diff --git a/shibsp/shibsp-lite.vcproj b/shibsp/shibsp-lite.vcproj index 48dc6af..e465154 100644 --- a/shibsp/shibsp-lite.vcproj +++ b/shibsp/shibsp-lite.vcproj @@ -1,11 +1,12 @@ @@ -93,9 +96,6 @@ Name="VCAppVerifierTool" /> - @@ -147,12 +147,14 @@ /> @@ -175,9 +177,6 @@ Name="VCAppVerifierTool" /> - @@ -225,13 +224,15 @@ /> @@ -254,9 +255,6 @@ Name="VCAppVerifierTool" /> - @@ -305,14 +303,16 @@ /> @@ -335,9 +335,6 @@ Name="VCAppVerifierTool" /> - @@ -418,6 +415,10 @@ Name="impl" > + + @@ -441,6 +442,10 @@ RelativePath=".\attribute\Attribute.cpp" > + + + + @@ -683,6 +692,10 @@ RelativePath=".\attribute\SimpleAttribute.h" > + + @@ -93,9 +96,6 @@ Name="VCAppVerifierTool" /> - @@ -147,12 +147,14 @@ /> @@ -175,9 +177,6 @@ Name="VCAppVerifierTool" /> - @@ -224,13 +223,15 @@ /> @@ -253,9 +254,6 @@ Name="VCAppVerifierTool" /> - @@ -303,14 +301,16 @@ /> @@ -333,9 +333,6 @@ Name="VCAppVerifierTool" /> - @@ -444,6 +441,10 @@ Name="impl" > + + @@ -468,6 +469,18 @@ > + + + + + + @@ -483,6 +496,10 @@ RelativePath=".\attribute\StringAttributeDecoder.cpp" > + + @@ -498,10 +515,22 @@ > + + + + + + @@ -747,10 +776,6 @@ > - - @@ -858,6 +883,10 @@ > + + @@ -869,6 +898,10 @@ RelativePath=".\attribute\SimpleAttribute.h" > + + diff --git a/shibsp/util/CGIParser.cpp b/shibsp/util/CGIParser.cpp index 1011351..d1cba90 100644 --- a/shibsp/util/CGIParser.cpp +++ b/shibsp/util/CGIParser.cpp @@ -61,7 +61,9 @@ CGIParser::~CGIParser() pair CGIParser::getParameters(const char* name) const { - return kvp_map.equal_range(name); + if (name) + return kvp_map.equal_range(name); + return make_pair(kvp_map.begin(), kvp_map.end()); } /* Parsing routines modified from NCSA source. */ diff --git a/shibsp/util/CGIParser.h b/shibsp/util/CGIParser.h index b76953d..c0e3371 100644 --- a/shibsp/util/CGIParser.h +++ b/shibsp/util/CGIParser.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -55,7 +55,7 @@ namespace shibsp { /** * Returns a pair of bounded iterators around the values of a parameter. * - * @param name name of parameter + * @param name name of parameter, or NULL to return all parameters * @return a pair of multimap iterators surrounding the matching value(s) */ std::pair getParameters(const char* name) const; diff --git a/shibsp/version.h b/shibsp/version.h index 8836d4a..b08f76d 100644 --- a/shibsp/version.h +++ b/shibsp/version.h @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * 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 @@ -16,8 +16,8 @@ /** * version.h - * - * Library version macros and constants + * + * Library version macros and constants */ #ifndef __shibsp_version_h__ @@ -33,12 +33,12 @@ // V E R S I O N S P E C I F I C A T I O N /** - * MODIFY THESE NUMERIC VALUES TO COINCIDE WITH OPENSAML VERSION + * MODIFY THESE NUMERIC VALUES TO COINCIDE WITH SHIBSP LIBRARY VERSION * AND DO NOT MODIFY ANYTHING ELSE IN THIS VERSION HEADER FILE */ #define SHIBSP_VERSION_MAJOR 1 -#define SHIBSP_VERSION_MINOR 0 +#define SHIBSP_VERSION_MINOR 2 #define SHIBSP_VERSION_REVISION 1 /** DO NOT MODIFY BELOW THIS LINE */ diff --git a/util/mdquery.cpp b/util/mdquery.cpp index 33435cc..89cc34e 100644 --- a/util/mdquery.cpp +++ b/util/mdquery.cpp @@ -94,48 +94,25 @@ int main(int argc,char* argv[]) if (!entityID) { usage(); - exit(-10); + return -10; } - char* path=getenv("SHIBSP_SCHEMAS"); - if (!path) - path=SHIBSP_SCHEMAS; - char* config=getenv("SHIBSP_CONFIG"); - if (!config) - config=SHIBSP_CONFIG; - - XMLToolingConfig::getConfig().log_config(getenv("SHIBSP_LOGGING") ? getenv("SHIBSP_LOGGING") : SHIBSP_LOGGING); - - SPConfig& conf=SPConfig::getConfig(); - conf.setFeatures(SPConfig::Metadata | SPConfig::Trust | SPConfig::OutOfProcess | SPConfig::Credentials); - if (!conf.init(path)) - return -1; - if (rname) { if (!protocol) { if (prot) protocol = XMLString::transcode(prot); } if (!protocol) { - conf.term(); usage(); - exit(-10); + return -10; } } - try { - static const XMLCh _path[] = UNICODE_LITERAL_4(p,a,t,h); - static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e); - xercesc::DOMDocument* dummydoc=XMLToolingConfig::getConfig().getParser().newDocument(); - XercesJanitor docjanitor(dummydoc); - xercesc::DOMElement* dummy = dummydoc->createElementNS(NULL,_path); - auto_ptr_XMLCh src(config); - dummy->setAttributeNS(NULL,_path,src.get()); - dummy->setAttributeNS(NULL,validate,xmlconstants::XML_ONE); - conf.setServiceProvider(conf.ServiceProviderManager.newPlugin(XML_SERVICE_PROVIDER,dummy)); - conf.getServiceProvider()->init(); - } - catch (exception&) { + SPConfig& conf=SPConfig::getConfig(); + conf.setFeatures(SPConfig::Metadata | SPConfig::Trust | SPConfig::OutOfProcess | SPConfig::Credentials); + if (!conf.init()) + return -1; + if (!conf.instantiate()) { conf.term(); return -2; } diff --git a/util/mdquery.vcproj b/util/mdquery.vcproj index 5240966..6335253 100644 --- a/util/mdquery.vcproj +++ b/util/mdquery.vcproj @@ -1,10 +1,11 @@ - - - @@ -328,12 +326,14 @@ /> - diff --git a/util/resolvertest.cpp b/util/resolvertest.cpp index 43a0e49..6fe35e2 100644 --- a/util/resolvertest.cpp +++ b/util/resolvertest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2007 Internet2 + * 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. @@ -115,8 +115,6 @@ int main(int argc,char* argv[]) char* i_param=NULL; char* prot = NULL; const XMLCh* protocol = NULL; - char* path=NULL; - char* config=NULL; for (int i=1; i docjanitor(dummydoc); - xercesc::DOMElement* dummy = dummydoc->createElementNS(NULL,path); - auto_ptr_XMLCh src(config); - dummy->setAttributeNS(NULL,path,src.get()); - dummy->setAttributeNS(NULL,validate,xmlconstants::XML_ONE); - conf.setServiceProvider(conf.ServiceProviderManager.newPlugin(XML_SERVICE_PROVIDER,dummy)); - conf.getServiceProvider()->init(); - } - catch (exception&) { + SPConfig& conf=SPConfig::getConfig(); + conf.setFeatures( + SPConfig::Metadata | + SPConfig::Trust | + SPConfig::AttributeResolution | + SPConfig::Credentials | + SPConfig::OutOfProcess + ); + if (!conf.init()) + return -1; + if (!conf.instantiate()) { conf.term(); return -2; } @@ -270,21 +245,25 @@ int main(int argc,char* argv[]) protocol = samlconstants::SAML11_PROTOCOL_ENUM; v1name = a1->getAuthenticationStatements().size() ? a1->getAuthenticationStatements().front()->getSubject()->getNameIdentifier() : NULL; - // Normalize the SAML 1.x NameIdentifier... - v2name = saml2::NameIDBuilder::buildNameID(); - v2name->setName(v1name->getName()); - v2name->setFormat(v1name->getFormat()); - v2name->setNameQualifier(v1name->getNameQualifier()); + if (!v1name) + v1name = a1->getAttributeStatements().size() ? + a1->getAttributeStatements().front()->getSubject()->getNameIdentifier() : NULL; + if (v1name) { + // Normalize the SAML 1.x NameIdentifier... + v2name = saml2::NameIDBuilder::buildNameID(); + v2name->setName(v1name->getName()); + v2name->setFormat(v1name->getFormat()); + v2name->setNameQualifier(v1name->getNameQualifier()); + } } else { throw FatalProfileException("Unknown assertion type."); } - if (!issuer) { - if (v1name) - delete v2name; + auto_ptr nameidwrapper(v1name ? v2name : NULL); + + if (!issuer) throw FatalProfileException("Unable to determine issuer."); - } MetadataProvider* m=app->getMetadataProvider(); xmltooling::Locker mlocker(m); @@ -297,23 +276,20 @@ int main(int argc,char* argv[]) vector tokens(1, dynamic_cast(token.get())); ResolverTest rt(NULL, a_param); - try { - ctx = rt.resolveAttributes(*app, site.second, protocol, v1name, v2name, NULL, NULL, &tokens); - } - catch (...) { - if (v1name) - delete v2name; - throw; - } + ctx = rt.resolveAttributes(*app, site.second, protocol, v1name, v2name, NULL, NULL, &tokens); } auto_ptr wrapper(ctx); for (vector::const_iterator a = ctx->getResolvedAttributes().begin(); a != ctx->getResolvedAttributes().end(); ++a) { - cout << endl; - for (vector::const_iterator s = (*a)->getAliases().begin(); s != (*a)->getAliases().end(); ++s) - cout << "ID: " << *s << endl; - for (vector::const_iterator s = (*a)->getSerializedValues().begin(); s != (*a)->getSerializedValues().end(); ++s) - cout << "Value: " << *s << endl; + for (vector::const_iterator s = (*a)->getAliases().begin(); s != (*a)->getAliases().end(); ++s) { + cout << *s << ": "; + for (vector::const_iterator v = (*a)->getSerializedValues().begin(); v != (*a)->getSerializedValues().end(); ++v) { + if (v != (*a)->getSerializedValues().begin()) + cout << ';'; + cout << *v; + } + cout << endl; + } } cout << endl; } diff --git a/util/resolvertest.vcproj b/util/resolvertest.vcproj index 4ced967..26a719b 100644 --- a/util/resolvertest.vcproj +++ b/util/resolvertest.vcproj @@ -1,10 +1,11 @@ - - - @@ -330,12 +328,14 @@ /> -