From e6ed2b9d2c55e4e8a0474180eb65c58567114762 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Franti=C5=A1ek=20Dvo=C5=99=C3=A1k?= Date: Sun, 10 Mar 2013 22:05:04 +0100 Subject: [PATCH] Merge g_with_canl_1_0 to master. --- org.gridsite.core/CHANGES | 30 +- org.gridsite.core/VERSION | 8 +- org.gridsite.core/project/debian.control | 33 +- org.gridsite.core/project/debian.rules | 6 +- org.gridsite.core/project/version.properties | 2 +- org.gridsite.core/src/Makefile | 594 ++-- org.gridsite.core/src/canl_mod_gridsite.c | 4101 ++++++++++++++++++++++++++ org.gridsite.core/src/canl_mod_ssl-private.h | 204 ++ org.gridsite.core/src/grst_canl_x509.c | 2474 ++++++++++++++++ org.gridsite.core/src/htcp.c | 4 +- org.gridsite.core/src/htproxyput.c | 13 + org.gridsite.core/src/make-debian-files | 27 +- org.gridsite.core/src/make-gridsite-spec | 147 +- 13 files changed, 7152 insertions(+), 491 deletions(-) create mode 100644 org.gridsite.core/src/canl_mod_gridsite.c create mode 100644 org.gridsite.core/src/canl_mod_ssl-private.h create mode 100644 org.gridsite.core/src/grst_canl_x509.c mode change 100755 => 100644 org.gridsite.core/src/make-debian-files diff --git a/org.gridsite.core/CHANGES b/org.gridsite.core/CHANGES index f8c0a15..93d82e6 100644 --- a/org.gridsite.core/CHANGES +++ b/org.gridsite.core/CHANGES @@ -1,13 +1,23 @@ -- ==== GridSite version 1.7.24 ==== -* Mon Dec 03 2012 Zdenek Sustr -- Allowing exceptions for length constraints in proxy certificates (GGUS #87573) -- ==== GridSite version 1.7.24 ==== -* Tue Oct 30 2012 Zdenek Sustr -- Fixed Memory Leak -- ==== GridSite version 1.7.23 ==== -* Tue Oct 09 2012 Zdenek Sustr -- Fixed Memory Leak -- Packaging fixes for SL6 +- ==== GridSite version 2.0.3 ==== +* Wed Nov 14 2012 Zdeněk Šustr +- Segmentation fault in htproxyput fixed (occurred if run by non-root) +- ==== GridSite version 2.0.2 ==== +* Tue Nov 06 2012 Zdeněk Šustr +- Certificates made available in the GRST structure +- ==== GridSite version 2.0.1 ==== +* Fri Oct 22 2012 František Dvořák +- one more update of the packaging for Debian +* Fri Oct 19 2012 František Dvořák +- update of the packaging for Debian due to major version bump +- add DESTDIR to install target +* Thu Oct 18 2012 František Dvořák +- fix packaging for SL6 and Fedora (curl-devel -> libcurl-devel) +- big library versions cleanup, using libtool to compile and link +- ==== GridSite version 2.0.0 ==== +* Wed Oct 17 2012 Marcel Poul +- Internals rewritten to use caNl +* Wed Oct 17 2012 František Dvořák +- Unused tools removed - ==== GridSite version 1.7.22 ==== * Tue Jul 24 2012 František Dvořák - Proper obsoletes in rpm packages (versioned, not platform dependent) diff --git a/org.gridsite.core/VERSION b/org.gridsite.core/VERSION index 3097f0a..f0b87da 100644 --- a/org.gridsite.core/VERSION +++ b/org.gridsite.core/VERSION @@ -1,5 +1,5 @@ -MAJOR_VERSION=1 -MINOR_VERSION=1.7 -PATCH_VERSION=1.7.25 -DEFVERSION=0x010725 +MAJOR_VERSION=2 +MINOR_VERSION=2.0 +PATCH_VERSION=2.0.3 +DEFVERSION=0x020003 VERSION=$(PATCH_VERSION) diff --git a/org.gridsite.core/project/debian.control b/org.gridsite.core/project/debian.control index eae58f5..0a22564 100644 --- a/org.gridsite.core/project/debian.control +++ b/org.gridsite.core/project/debian.control @@ -5,13 +5,14 @@ Maintainer: EMI CESNET security Build-Depends: debhelper (>= 7.0.50~), apache2-prefork-dev, gsoap, + libcanl-c-dev, libglobus-gssapi-gsi-dev, libcurl4-openssl-dev, doxygen, - libfuse-dev, libxml2, libxml2-dev, libssl-dev, + libtool, pkg-config Standards-Version: 3.9.1 Homepage: http://gridsite.org @@ -25,7 +26,7 @@ Depends: \${shlibs:Depends}, \${misc:Depends} Description: GridSite mod_gridsite module and CGI binaries for Apache httpd GridSite Apache module and CGI binaries. -Package: libgridsite${MINOR_VERSION} +Package: libgridsite2 Section: libs Architecture: any Depends: \${shlibs:Depends}, \${misc:Depends}, libssl-dev @@ -38,7 +39,7 @@ Description: GridSite libraries and documentation Package: libgridsite-dev Section: libdevel Architecture: any -Depends: libgridsite${MINOR_VERSION} (= \${binary:Version}), +Depends: libgridsite2 (= \${binary:Version}), \${misc:Depends} Description: GridSite static libraries and headers Development files for GridSite - .a libraries and .h headers. @@ -53,27 +54,17 @@ Description: HTTP(S) read/write client and other GridSite commands onto remote servers using HTTPS. htcp is similar to scp(1), but uses HTTP/HTTPS rather than ssh as its transfer protocol. -Package: gridsite-gsexec -Section: web -Architecture: any -Depends: \${shlibs:Depends}, \${misc:Depends} -Description: gsexec binary for the Apache HTTP server - This package includes the /usr/sbin/gsexec binary which can be installed - to allow the Apache HTTP server to run CGI programs (and any programs - executed by SSI pages) as a user other than the 'apache' user. gsexec - is a drop-in replacement for suexec, with extended functionality for use - with GridSite and Grid Security credentials. - -Package: gridsite-slashgrid -Section: web -Architecture: any -Depends: \${shlibs:Depends}, \${misc:Depends}, libcurl3 (>=7.12.1), fuse-utils -Description: SlashGrid daemon - SlashGrid provides remote virtual filesystems under /grid (\"slash grid\"). - Package: gridsite-service-clients Section: web Architecture: any Depends: \${shlibs:Depends}, \${misc:Depends} Description: GridSite WS htproxyput GridSite WS delegation client, htproxyput + +Package: gridsite-dbg +Section: debug +Architecture: any +Priority: extra +Depends: libgridsite2 (= \${binary:Version}), \${misc:Depends} +Description: Debugging symbols for GridSite + This package contains debugging symbols for GridSite library and programs. diff --git a/org.gridsite.core/project/debian.rules b/org.gridsite.core/project/debian.rules index 8ecc773..67d6692 100644 --- a/org.gridsite.core/project/debian.rules +++ b/org.gridsite.core/project/debian.rules @@ -1,10 +1,10 @@ #! /usr/bin/make -f override_dh_auto_build: - \$(MAKE) -C src prefix=/usr build gridsite-delegation.cgi htproxyput slashgrid + \$(MAKE) -C src prefix=/usr build gridsite-delegation.cgi htproxyput override_dh_auto_install: - \$(MAKE) -C src prefix=../debian/tmp/usr httpd_name=apache2 RPM_BUILD_ROOT=../debian/tmp install install-ws install-slashgrid post-install-debian + \$(MAKE) -C src prefix=/usr httpd_name=apache2 DESTDIR=\$(shell pwd)/debian/tmp install install-ws post-install-debian %: - dh -Smakefile -Dsrc \$@ + dh -Smakefile -Dsrc --dbg-package=gridsite-dbg \$@ diff --git a/org.gridsite.core/project/version.properties b/org.gridsite.core/project/version.properties index 3b153b1..ab1e723 100644 --- a/org.gridsite.core/project/version.properties +++ b/org.gridsite.core/project/version.properties @@ -1,2 +1,2 @@ -module.version=1.7.25 +module.version=2.0.3 module.age=1 diff --git a/org.gridsite.core/src/Makefile b/org.gridsite.core/src/Makefile index c294750..3a05859 100644 --- a/org.gridsite.core/src/Makefile +++ b/org.gridsite.core/src/Makefile @@ -61,9 +61,11 @@ GSOAP_CFLAGS=`pkg-config gsoap --cflags` GSOAP_LIBS=`pkg-config gsoap --libs` GSOAPSSL_CFLAGS=`pkg-config gsoapssl --cflags` GSOAPSSL_LIBS=`pkg-config gsoapssl --libs` +CANL_C_CFLAGS= +CANL_C_LIBS=-lcanl_c ifndef MYCFLAGS -export MYCFLAGS=-I. -I../interface -fPIC -DLINUX=2 -D_REENTRANT -D_LARGEFILE64_SOURCE $(HTTPD_FLAGS) +export MYCFLAGS=-I. -I../interface -DPIC -fPIC -DLINUX=2 -D_REENTRANT -D_LARGEFILE64_SOURCE $(HTTPD_FLAGS) endif ifndef MYLDFLAGS @@ -72,243 +74,251 @@ endif -include Makefile.inc +# In order to use libtool versioning correcty, we must have: +# +# current = major + minor + offset +# revision = patch +# age = minor +# +# where offset is a sum of maximal released minor's of all previous major's +# + +# counted minors: - +offset=0 + +version_info:=-version-info $(shell \ + perl -e '$$,=":"; @F=split "\\.","$(PATCH_VERSION)"; print $$F[0]+$$F[1]+${offset},$$F[2],$$F[1]' ) + ifdef OPENSSL_GLOBUS_LIBS PC_FILES=gridsite-openssl.pc gridsite-globus.pc else PC_FILES=gridsite-openssl.pc endif +# caNl adoption part +# caNl library and headers have to be in standard system places. +# Print all warnings +ifeq ($(GRIDSITE_WITH_CANL),no) +MOD_GRIDSITE_FILE=mod_gridsite.c +GRST_X509_OBJS=grst_x509.lo +MOD_SSLPRIVATE_HEADER=mod_ssl-private.h +MYCANLLDFLAGS= +else +GRST_X509_OBJS=grst_canl_x509.lo +MOD_GRIDSITE_FILE=canl_mod_gridsite.c +MOD_SSLPRIVATE_HEADER=canl_mod_ssl-private.h +MYCANLLDFLAGS=$(CANL_C_LIBS) +endif + +ifeq ($(WALL),yes) +MYCFLAGS += -Wall +endif + +CC=gcc +COMPILE=libtool --mode=compile $(CC) $(CFLAGS) +LINK=libtool --mode=link $(CC) $(LDFLAGS) +INSTALL=libtool --mode=install install + +GRIDSITE_OBJS=grst_err.lo $(GRST_X509_OBJS) grst_gacl.lo grst_xacml.lo grst_http.lo grst_asn1.lo grst_htcp.lo +GRIDSITE_GLOBUS_OBJS=grst_err_globus.lo grst_x509_globus.lo grst_gacl_globus.lo grst_xacml_globus.lo grst_http_globus.lo grst_asn1_globus.lo grst_htcp_globus.lo +GRIDSITE_NOSSL_OBJS=grst_err_nossl.lo grst_gacl_nossl.lo grst_http_nossl.lo grst_xacml_nossl.lo grst_htcp_nossl.lo + # # Build # -build: apidoc build-lib \ - htcp gridsite-copy.cgi gridsite-storage.cgi mod_gridsite.so \ - urlencode findproxyfile gsexec real-gridsite-admin.cgi +build: apidoc build-lib htcp mod_gridsite.so urlencode findproxyfile \ + real-gridsite-admin.cgi gridsite-delegation.cgi -build-lib: libgridsite_globus.so.$(VERSION) libgridsite_globus.a \ - libgridsite.so.$(VERSION) libgridsite.a \ - libgridsite_nossl.so.$(VERSION) libgridsite_nossl.a $(PC_FILES) +build-lib: libgridsite_globus.la libgridsite_globus.a \ + libgridsite.la libgridsite.a \ + libgridsite_nossl.la libgridsite_nossl.a $(PC_FILES) # First, normal versions using system OpenSSL rather than Globus OpenSSL -libgridsite.so.$(VERSION): grst_err.o grst_x509.o grst_gacl.o grst_xacml.o \ - grst_http.o grst_asn1.o grst_htcp.o - gcc -shared -Wl,-soname,libgridsite.so.$(MINOR_VERSION) \ - -o libgridsite.so.$(PATCH_VERSION) \ - grst_err.o grst_x509.o grst_gacl.o grst_xacml.o grst_http.o \ - grst_asn1.o grst_htcp.o -lcrypto $(XML2_LIBS) - ln -sf libgridsite.so.$(VERSION) libgridsite.so - ln -sf libgridsite.so.$(VERSION) libgridsite.so.$(MINOR_VERSION) +libgridsite.a: libgridsite.la -libgridsite.a: grst_err.o grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o grst_htcp.o - ar src libgridsite.a grst_err.o grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o grst_htcp.o +libgridsite.la: $(GRIDSITE_OBJS) + $(LINK) -rpath $(prefix)/$(libdir) $(version_info) \ + -o $@ $+ -lcrypto ${MYCANLLDFLAGS} $(XML2_LIBS) -grst_err.o: grst_err.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) \ +grst_err.lo: grst_err.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) \ -I/usr/kerberos/include \ - -c grst_err.c + -c $< -o $@ + +grst_x509.lo: grst_x509.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) \ + -I/usr/kerberos/include -c $< -o $@ -grst_x509.o: grst_x509.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) \ - -I/usr/kerberos/include -c grst_x509.c +grst_canl_x509.lo: grst_canl_x509.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) $(CANL_C_CFLAGS) \ + -I/usr/kerberos/include -c $< -o $@ -grst_gacl.o: grst_gacl.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) \ - -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_gacl.c +grst_gacl.lo: grst_gacl.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) \ + -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@ -grst_xacml.o: grst_xacml.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) \ - -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_xacml.c +grst_xacml.lo: grst_xacml.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) \ + -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@ -grst_http.o: grst_http.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) \ - -I/usr/kerberos/include -c grst_http.c +grst_http.lo: grst_http.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) \ + -I/usr/kerberos/include -c $< -o $@ -grst_asn1.o: grst_asn1.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) \ - -I/usr/kerberos/include -c grst_asn1.c +grst_asn1.lo: grst_asn1.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) \ + -I/usr/kerberos/include -c $< -o $@ -grst_htcp.o: grst_htcp.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) \ - -I/usr/kerberos/include -c grst_htcp.c +grst_htcp.lo: grst_htcp.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) \ + -I/usr/kerberos/include -c $< -o $@ # Then build versions using Globus OpenSSL if configured ifdef OPENSSL_GLOBUS_LIBS -libgridsite_globus.so.$(VERSION): grst_err_globus.o \ - grst_x509_globus.o grst_gacl_globus.o grst_http_globus.o \ - grst_asn1_globus.o grst_xacml_globus.o grst_htcp_globus.o - gcc -shared -Wl,-soname,libgridsite_globus.so.$(MINOR_VERSION) \ - -o libgridsite_globus.so.$(PATCH_VERSION) \ - grst_err_globus.o grst_x509_globus.o grst_gacl_globus.o grst_xacml_globus.o \ - grst_http_globus.o grst_asn1_globus.o $(XML2_LIBS) $(OPENSSL_GLOBUS_LIBS) - ln -sf libgridsite_globus.so.$(VERSION) libgridsite_globus.so - -libgridsite_globus.a: grst_err_globus.o grst_x509_globus.o grst_gacl_globus.o grst_http_globus.o grst_asn1_globus.o - ar src libgridsite_globus.a \ - grst_err_globus.o grst_x509_globus.o grst_gacl_globus.o grst_http_globus.o grst_asn1_globus.o - -grst_err_globus.o: grst_err.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ - -I/usr/kerberos/include \ - -c grst_err.c \ - -o grst_err_globus.o - -grst_x509_globus.o: grst_x509.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ - -I/usr/kerberos/include -c grst_x509.c \ - -o grst_x509_globus.o - -grst_gacl_globus.o: grst_gacl.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ - -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_gacl.c \ - -o grst_gacl_globus.o - -grst_xacml_globus.o: grst_xacml.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ - -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_xacml.c \ - -o grst_xacml_globus.o - -grst_http_globus.o: grst_http.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ - -I/usr/kerberos/include -c grst_http.c \ - -o grst_http_globus.o - -grst_asn1_globus.o: grst_asn1.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ - -I/usr/kerberos/include -c grst_asn1.c \ - -o grst_asn1_globus.o - -grst_htcp_globus.o: grst_htcp.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ - -I/usr/kerberos/include -c grst_htcp.c \ - -o grst_htcp_globus.o +libgridsite_globus.a: libgridsite_globus.la + +libgridsite_globus.la: $(GRIDSITE_GLOBUS_OBJS) + $(LINK) -rpath $(prefix)/$(libdir) $(version_info) \ + -o $@ $+ $(XML2_LIBS) $(OPENSSL_GLOBUS_LIBS) + +grst_err_globus.lo: grst_err.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + -I/usr/kerberos/include -c $< -o $@ + +grst_x509_globus.lo: grst_x509.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + -I/usr/kerberos/include -c $< -o $@ + +grst_gacl_globus.lo: grst_gacl.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@ + +grst_xacml_globus.lo: grst_xacml.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@ + +grst_http_globus.lo: grst_http.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + -I/usr/kerberos/include -c $< -o $@ + +grst_asn1_globus.lo: grst_asn1.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + -I/usr/kerberos/include -c $< -o $@ + +grst_htcp_globus.lo: grst_htcp.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + -I/usr/kerberos/include -c $< -o $@ else -libgridsite_globus.so.$(VERSION): libgridsite.so.$(VERSION) - cp -f libgridsite.so.$(VERSION) libgridsite_globus.so.$(VERSION) +libgridsite_globus.a: libgridsite_globus.la -libgridsite_globus.a: libgridsite.a - cp -f libgridsite.a libgridsite_globus.a +libgridsite_globus.la: $(GRIDSITE_OBJS) + $(LINK) -rpath $(prefix)/$(libdir) $(version_info) \ + -o $@ $+ -lcrypto ${MYCANLLDFLAGS} $(XML2_LIBS) endif # then build versions without OpenSSL -libgridsite_nossl.so.$(VERSION): grst_err_nossl.o \ - grst_gacl_nossl.o grst_http_nossl.o \ - grst_xacml_nossl.o grst_htcp_nossl.o - gcc -shared -Wl,-soname,libgridsite_nossl.so.$(MINOR_VERSION) \ - -o libgridsite_nossl.so.$(PATCH_VERSION) \ - grst_err_nossl.o grst_gacl_nossl.o grst_xacml_nossl.o \ - grst_http_nossl.o grst_htcp_nossl.o $(XML2_LIBS) - ln -sf libgridsite_nossl.so.$(VERSION) libgridsite_nossl.so - -libgridsite_nossl.a: grst_err_nossl.o grst_gacl_nossl.o grst_http_nossl.o grst_htcp_nossl.o - ar src libgridsite_nossl.a \ - grst_err_nossl.o grst_gacl_nossl.o grst_http_nossl.o grst_htcp_nossl.o - -grst_err_nossl.o: grst_err.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) -DGRST_NO_OPENSSL \ - -I/usr/kerberos/include \ - -c grst_err.c \ - -o grst_err_nossl.o +libgridsite_nossl.a: libgridsite_nossl.la + +libgridsite_nossl.la: $(GRIDSITE_NOSSL_OBJS) + $(LINK) -rpath $(prefix)/$(libdir) $(version_info) \ + -o $@ $+ $(XML2_LIBS) + +grst_err_nossl.lo: grst_err.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) -DGRST_NO_OPENSSL \ + -I/usr/kerberos/include -c $< -o $@ -grst_gacl_nossl.o: grst_gacl.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) -DGRST_NO_OPENSSL \ - -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_gacl.c \ - -o grst_gacl_nossl.o +grst_gacl_nossl.lo: grst_gacl.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) -DGRST_NO_OPENSSL \ + -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@ -grst_xacml_nossl.o: grst_xacml.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) -DGRST_NO_OPENSSL \ - -I/usr/kerberos/include $(XML2_CFLAGS) -c grst_xacml.c \ - -o grst_xacml_nossl.o +grst_xacml_nossl.lo: grst_xacml.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) -DGRST_NO_OPENSSL \ + -I/usr/kerberos/include $(XML2_CFLAGS) -c $< -o $@ -grst_http_nossl.o: grst_http.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) -DGRST_NO_OPENSSL \ - -I/usr/kerberos/include -c grst_http.c \ - -o grst_http_nossl.o +grst_http_nossl.lo: grst_http.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) -DGRST_NO_OPENSSL \ + -I/usr/kerberos/include -c $< -o $@ -grst_htcp_nossl.o: grst_htcp.c ../interface/gridsite.h - gcc -g $(MYCFLAGS) -DGRST_NO_OPENSSL \ - -I/usr/kerberos/include -c grst_htcp.c \ - -o grst_htcp_nossl.o +grst_htcp_nossl.lo: grst_htcp.c ../interface/gridsite.h + $(COMPILE) $(MYCFLAGS) -DGRST_NO_OPENSSL \ + -I/usr/kerberos/include -c $< -o $@ # now the binary exectuables -gsexec: gsexec.c gsexec.h - gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ - -o gsexec gsexec.c +gsexec.lo urlencode.lo gridsite-copy.lo findproxyfile.lo showx509exts.lo: + $(COMPILE) -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ + -o $@ -c $(subst .lo,.c,$@) -urlencode: urlencode.c libgridsite.so.$(VERSION) - gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ - -o urlencode urlencode.c -L. \ - -I/usr/kerberos/include \ - -lgridsite +htcp.lo: + $(COMPILE) -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ + -o $@ -c $(subst .lo,.c,$@) $(CURL_CFLAGS) -htcp: htcp.c libgridsite.so.$(VERSION) - gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ - -o htcp htcp.c -L. \ - -I/usr/kerberos/include \ - $(CURL_CFLAGS) $(CURL_LIBS) \ - -lgridsite +slashgrid.lo: slashgrid.c + $(COMPILE) -o $@ -c $< $(MYCFLAGS) $(XML2_CFLAGS) -D_FILE_OFFSET_BITS=64 \ + -D_REENTRANT -DFUSE_USE_VERSION=22 -I/usr/kerberos/include \ + $(CURL_CFLAGS) -htcp-static: htcp.c libgridsite.a - gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ - -o htcp-static htcp.c -L. \ - -I/usr/kerberos/include \ - $(CURL_CFLAGS) $(CURL_LIBS) \ - -lgridsite -static +gsexec: gsexec.lo gsexec.h + $(LINK) $< -o $@ + +urlencode: urlencode.lo libgridsite.la + $(LINK) -o $@ $< -L. -lgridsite + +htcp: htcp.lo libgridsite.la + $(LINK) -o $@ $+ $(CURL_LIBS) -gridsite-copy.cgi: gridsite-copy.c libgridsite.so.$(VERSION) - gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ - -o gridsite-copy.cgi gridsite-copy.c -L. \ +htcp-static: htcp.lo libgridsite.a + $(LINK) -o $@ -L. $< \ -I/usr/kerberos/include \ - $(CURL_CFLAGS) $(CURL_LIBS) \ - $(MYFCGILIBS) -lgridsite + $(CURL_LIBS) -static -lgridsite -mod_gridsite.so: mod_gridsite.c mod_ssl-private.h libgridsite.so.$(VERSION) +gridsite-copy.cgi: gridsite-copy.lo libgridsite.la + $(LINK) -o $@ $< -L. $(CURL_LIBS) $(MYFCGILIBS) -lgridsite + +mod_gridsite.so: ${MOD_GRIDSITE_FILE} ${MOD_SSLPRIVATE_HEADER} \ + libgridsite.la gcc -g $(MYCFLAGS) -shared -Wl,-soname=gridsite_module \ -I/usr/kerberos/include \ - $(XML2_CFLAGS) -lcrypto -lssl \ + $(XML2_CFLAGS) -lcrypto -lssl ${MYCANLLDFLAGS} \ -DVERSION=\"$(VERSION)\" -o mod_gridsite.so \ - mod_gridsite.c $(MYLDFLAGS) -lgridsite + ${MOD_GRIDSITE_FILE} -L./.libs -lgridsite mod_gridsite_example.so: mod_gridsite_example.c gcc -g -shared -Wl,-soname=gridsite_example_module \ - -I/usr/include/httpd -I/usr/include/apr-0 \ + -fPIC $(HTTPD_FLAGS) \ -DVERSION=\"$(VERSION)\" -o mod_gridsite_example.so \ mod_gridsite_example.c -real-gridsite-admin.cgi: grst_admin_main.c grst_admin_gacl.c \ - grst_admin_file.c grst_admin.h - gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o real-gridsite-admin.cgi \ - grst_admin_main.c \ - grst_admin_gacl.c \ - grst_admin_file.c \ - -I/usr/kerberos/include \ - -DVERSION=\"$(VERSION)\" -lgridsite - -findproxyfile: findproxyfile.c libgridsite.so.$(VERSION) - gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) $(MYLDFLAGS) \ - -o findproxyfile findproxyfile.c -L. \ - -I/usr/kerberos/include -lgridsite - -showx509exts: showx509exts.c libgridsite.so.$(VERSION) - gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) $(MYLDFLAGS) \ - -o showx509exts showx509exts.c -L. \ - -I/usr/kerberos/include \ - -lgridsite \ - -lssl -lcrypto $(XML2_LIBS) -lz -lm +grst_admin_main.lo grst_admin_gacl.lo grst_admin_file.lo: + $(COMPILE) $(MYCFLAGS) -I/usr/kerberos/include \ + -DVERSION=\"$(VERSION)\" -o $@ -c $(subst .lo,.c,$@) + +real-gridsite-admin.cgi: grst_admin_main.lo grst_admin_gacl.lo \ + grst_admin_file.lo grst_admin.h + $(LINK) $(MYLDFLAGS) -o real-gridsite-admin.cgi \ + grst_admin_main.lo \ + grst_admin_gacl.lo \ + grst_admin_file.lo \ + -lgridsite + +findproxyfile: findproxyfile.lo libgridsite.la + $(LINK) $(MYLDFLAGS) \ + -o $@ $< -L. -lgridsite + +showx509exts: showx509exts.lo libgridsite.la + $(LINK) $(MYLDFLAGS) \ + -o $@ $< -L. -lgridsite -lssl -lcrypto $(XML2_LIBS) -lz -lm -slashgrid: slashgrid.c libgridsite.so.$(VERSION) - gcc -g -o slashgrid -lfuse -lpthread slashgrid.c \ - $(MYCFLAGS) $(MYLDFLAGS) $(XML2_CFLAGS) \ - -D_FILE_OFFSET_BITS=64 -D_REENTRANT -DFUSE_USE_VERSION=22 \ - -I/usr/kerberos/include $(CURL_CFLAGS) \ +slashgrid: slashgrid.lo libgridsite.la + $(LINK) $< -o $@ -lfuse $(MYLDFLAGS) \ -L. $(CURL_LIBS) -lgridsite -lpthread # This target is used by make-gridsite-spec to test for FUSE include+libs @@ -325,15 +335,15 @@ apidoc: cd ../doc ; for i in *.1 *.8 ; do ../src/roffit < $$i \ > $$i.html ; done -gaclexample: gaclexample.c libgridsite.a - gcc -g -o gaclexample gaclexample.c -I../interface -L. \ - -I/usr/kerberos/include -lgridsite \ - -lssl -lcrypto $(XML2_LIBS) -lz -lm +gaclexample.lo xacmlexample.lo: gaclexample.c xacmlexample.c + $(COMPILE) -c $(subst .lo,.c,$@) -o $@ -I../interface \ + -I/usr/kerberos/include + +gaclexample: gaclexample.lo libgridsite.la + $(LINK) -o $@ $< -L. -lgridsite -lssl -lcrypto $(XML2_LIBS) -lz -lm -xacmlexample: xacmlexample.c libgridsite.a - gcc -g -o xacmlexample xacmlexample.c -I../interface -L. \ - -I/usr/kerberos/include -lgridsite \ - -lssl -lcrypto $(XML2_LIBS) -lz -lm +xacmlexample: xacmlexample.lo libgridsite.a + $(LINK) -o $@ $< -L. -lgridsite -lssl -lcrypto $(XML2_LIBS) -lz -lm # # Delegation machinery, including SOAP delegation portType. To build this # you either need to use the gLite build environment and set REPOSITORY @@ -354,28 +364,29 @@ DelegationService.wsdl: delegation.h $(GSOAPDIR)/bin/soapcpp2 -c delegation.h gridsite-delegation.cgi: grst-delegation.c delegation.h \ - DelegationService.wsdl libgridsite.so.$(VERSION) + DelegationService.wsdl libgridsite.la gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o gridsite-delegation.cgi \ grst-delegation.c \ -I/usr/kerberos/include -I. $(GSOAP_CFLAGS) \ -I$(GRIDSITEDIR)/include \ - -DVERSION=\"$(VERSION)\" -L. \ + -DVERSION=\"$(VERSION)\" -L./.libs \ soapC.c soapServer.c \ - -L$(GRIDSITEDIR)/$(libdir) $(GSOAP_LIBS) \ - -lgridsite + $(GSOAP_LIBS) -lgridsite +# -L$(GRIDSITEDIR)/$(libdir) $(GSOAP_LIBS) \ + # -lgridsite -htproxyput: htproxyput.c delegation.h DelegationService.wsdl libgridsite.so.$(VERSION) +htproxyput: htproxyput.c delegation.h DelegationService.wsdl libgridsite.la gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o htproxyput \ htproxyput.c \ -I/usr/kerberos/include -I. \ -g -DVERSION=\"$(VERSION)\" \ $(GSOAPSSL_CFLAGS) \ -I$(GRIDSITEDIR)/include \ - -L. \ + -L./.libs \ $(STDSOAP2) \ soapC.c soapClient.c $(GSOAPSSL_LIBS) \ -lgridsite -lssl -lcrypto - + # This target is used by make-gridsite-spec to test for gSOAP include+libs gsoap-test: gsoap-test.c gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o gsoap-test \ @@ -386,146 +397,130 @@ gsoap-test: gsoap-test.c -I$(GRIDSITEDIR)/include \ $(STDSOAP2) -L$(GRIDSITEDIR)/$(libdir) \ $(GSOAPSSL_LIBS) -lz -lssl -lcrypto $(XML2_LIBS) -lm - -gridsite-storage.cgi: gridsite-storage.c libgridsite.so.$(VERSION) - gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o gridsite-storage.cgi \ - gridsite-storage.c \ + +gridsite-storage.lo: gridsite-storage.c + $(COMPILE) -o $@ -c $< $(MYCFLAGS) \ -I/usr/kerberos/include -I.\ -I$(GRIDSITEDIR)/include \ -DVERSION=\"$(VERSION)\" -L. \ + $(CURL_CFLAGS) + +gridsite-storage.cgi: gridsite-storage.lo libgridsite.la + $(LINK) $(MYLDFLAGS) -o $@ $< \ -L$(GRIDSITEDIR)/$(libdir) \ - -lgridsite $(CURL_CFLAGS) $(CURL_LIBS) + -lgridsite $(CURL_LIBS) %.pc: %.pc.in sed -e "s/@version@/$(VERSION)/" -e "s,@prefix@,$(prefix)," -e "s/@libdir@/$(libdir)/" $< > $@ clean: rm -rvf doxygen + rm -rvf .libs rm -vf DelegationSoapBinding.* soapC*.c soapH*.h soapS*.c soapStub.h ns.xsd rm -vf fuse-test.c gsoap-test.c gridsite.spec - rm -vf libgridsite*.so* *.cgi mod_gridsite*.so *.a *.o + rm -vf libgridsite*.so* *.cgi mod_gridsite*.so *.a *.o *.la *.lo rm -vf gsexec urlencode htcp htcp-static findproxyfile showx509exts slashgrid fuse-test gaclexample xacmlexample htproxyput gsoap-test rm -vf gridsite-openssl.pc gridsite-globus.pc +distclean: + rm -rfv ../dist ../gridsite-*.tar.gz $(MYRPMDIR) + # # Install # install: apidoc install-lib - mkdir -p $(prefix)/include \ - $(prefix)/$(libdir)/$(httpd_name)/modules \ - $(prefix)/bin \ - $(prefix)/sbin \ - $(prefix)/share/man/man1 \ - $(prefix)/share/man/man8 \ - $(prefix)/share/doc/gridsite-$(MINOR_VERSION) + mkdir -p $(DESTDIR)$(prefix)/include \ + $(DESTDIR)$(prefix)/$(libdir)/$(httpd_name)/modules \ + $(DESTDIR)$(prefix)/bin \ + $(DESTDIR)$(prefix)/sbin \ + $(DESTDIR)$(prefix)/share/man/man1 \ + $(DESTDIR)$(prefix)/share/man/man8 \ + $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) sed 's/^\(#define GRST_VERSION\).*$$/\1 $(DEFVERSION)/' \ - ../interface/gridsite.h > $(prefix)/include/gridsite.h - cp -f ../interface/gridsite-gacl.h $(prefix)/include - cp -f urlencode $(prefix)/bin - cp -f findproxyfile $(prefix)/bin - cp -f real-gridsite-admin.cgi $(prefix)/sbin - cp -f gridsite-copy.cgi $(prefix)/sbin - cp -f gridsite-storage.cgi $(prefix)/sbin + ../interface/gridsite.h > $(DESTDIR)$(prefix)/include/gridsite.h + cp -f ../interface/gridsite-gacl.h $(DESTDIR)$(prefix)/include + $(INSTALL) urlencode $(DESTDIR)$(prefix)/bin + $(INSTALL) findproxyfile $(DESTDIR)$(prefix)/bin + $(INSTALL) real-gridsite-admin.cgi $(DESTDIR)$(prefix)/sbin cp -f ../CHANGES ../README ../INSTALL ../LICENSE ../VERSION \ - $(prefix)/share/doc/gridsite-$(MINOR_VERSION) + $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) cp -f ../doc/index.html ../doc/*.conf ../doc/*.sh ../doc/*.spec \ mod_gridsite_example.c \ - $(prefix)/share/doc/gridsite-$(MINOR_VERSION) + $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) for i in htcp.1 htfind.1 htll.1 htls.1 htmkdir.1 htmv.1 htping.1 \ htrm.1 urlencode.1 findproxyfile.1 ; do \ - cp -f ../doc/$$i.html $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ - cp -f ../doc/$$i $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ - cp -f ../doc/$$i $(prefix)/share/man/man1 ; \ - gzip -f $(prefix)/share/man/man1/$$i ; done - for i in mod_gridsite.8 gsexec.8 ; do \ - cp -f ../doc/$$i.html $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ - cp -f ../doc/$$i $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ - cp -f ../doc/$$i $(prefix)/share/man/man8 ; \ - gzip -f $(prefix)/share/man/man8/$$i ; done - cp -f htcp $(prefix)/bin - ln -sf htcp $(prefix)/bin/htls - ln -sf htcp $(prefix)/bin/htll - ln -sf htcp $(prefix)/bin/htrm - ln -sf htcp $(prefix)/bin/htmkdir - ln -sf htcp $(prefix)/bin/htmv - ln -sf htcp $(prefix)/bin/htping - ln -sf htcp $(prefix)/bin/htfind - cp -f gsexec $(prefix)/sbin - cp -f mod_gridsite.so $(prefix)/$(libdir)/$(httpd_name)/modules + cp -f ../doc/$$i.html $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ + cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ + cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/man/man1 ; \ + gzip -f $(DESTDIR)$(prefix)/share/man/man1/$$i ; done + for i in mod_gridsite.8 ; do \ + cp -f ../doc/$$i.html $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ + cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ + cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/man/man8 ; \ + gzip -f $(DESTDIR)$(prefix)/share/man/man8/$$i ; done + $(INSTALL) htcp $(DESTDIR)$(prefix)/bin + ln -sf htcp $(DESTDIR)$(prefix)/bin/htls + ln -sf htcp $(DESTDIR)$(prefix)/bin/htll + ln -sf htcp $(DESTDIR)$(prefix)/bin/htrm + ln -sf htcp $(DESTDIR)$(prefix)/bin/htmkdir + ln -sf htcp $(DESTDIR)$(prefix)/bin/htmv + ln -sf htcp $(DESTDIR)$(prefix)/bin/htping + ln -sf htcp $(DESTDIR)$(prefix)/bin/htfind + cp -f mod_gridsite.so $(DESTDIR)$(prefix)/$(libdir)/$(httpd_name)/modules install-lib: - mkdir -p $(prefix)/$(libdir)/pkgconfig - cp -f libgridsite.a $(prefix)/$(libdir) - cp -f libgridsite.so.$(PATCH_VERSION) $(prefix)/$(libdir) - ln -sf libgridsite.so.$(PATCH_VERSION) \ - $(prefix)/$(libdir)/libgridsite.so - ln -sf libgridsite.so.$(PATCH_VERSION) \ - $(prefix)/$(libdir)/libgridsite.so.$(MAJOR_VERSION) - ln -sf libgridsite.so.$(PATCH_VERSION) \ - $(prefix)/$(libdir)/libgridsite.so.$(MINOR_VERSION) - cp -f libgridsite_globus.a $(prefix)/$(libdir) - cp -f libgridsite_globus.so.$(PATCH_VERSION) $(prefix)/$(libdir) - ln -sf libgridsite_globus.so.$(PATCH_VERSION) \ - $(prefix)/$(libdir)/libgridsite_globus.so - ln -sf libgridsite_globus.so.$(PATCH_VERSION) \ - $(prefix)/$(libdir)/libgridsite_globus.so.$(MAJOR_VERSION) - ln -sf libgridsite_globus.so.$(PATCH_VERSION) \ - $(prefix)/$(libdir)/libgridsite_globus.so.$(MINOR_VERSION) - cp -f libgridsite_nossl.a $(prefix)/$(libdir) - cp -f libgridsite_nossl.so.$(PATCH_VERSION) $(prefix)/$(libdir) - ln -sf libgridsite_nossl.so.$(PATCH_VERSION) \ - $(prefix)/$(libdir)/libgridsite_nossl.so - ln -sf libgridsite_nossl.so.$(PATCH_VERSION) \ - $(prefix)/$(libdir)/libgridsite_nossl.so.$(MAJOR_VERSION) - ln -sf libgridsite_nossl.so.$(PATCH_VERSION) \ - $(prefix)/$(libdir)/libgridsite_nossl.so.$(MINOR_VERSION) - cp -f $(PC_FILES) $(prefix)/$(libdir)/pkgconfig + mkdir -p $(DESTDIR)$(prefix)/$(libdir)/pkgconfig + $(INSTALL) libgridsite.la $(DESTDIR)$(prefix)/$(libdir) + $(INSTALL) libgridsite_globus.la $(DESTDIR)$(prefix)/$(libdir) + $(INSTALL) libgridsite_nossl.la $(DESTDIR)$(prefix)/$(libdir) + rm -f $(DESTDIR)$(prefix)/$(libdir)/*.la + cp -f $(PC_FILES) $(DESTDIR)$(prefix)/$(libdir)/pkgconfig install-slashgrid: slashgrid - -mkdir -p $(RPM_BUILD_ROOT)/etc/rc.d/init.d - -mkdir -p $(prefix)/share/doc/gridsite-$(MINOR_VERSION) - -mkdir -p $(prefix)/share/man/man8 - cp -f slashgrid $(prefix)/sbin - cp -f slashgrid.init $(RPM_BUILD_ROOT)/etc/rc.d/init.d/slashgrid - cp -f ../doc/slashgrid.8.html $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ - cp -f ../doc/slashgrid.8 $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ - cp -f ../doc/slashgrid.8 $(prefix)/share/man/man8 - gzip -f $(prefix)/share/man/man8/slashgrid.8 - mkdir -p $(RPM_BUILD_ROOT)/var/spool/slashgrid + -mkdir -p $(DESTDIR)/etc/rc.d/init.d + -mkdir -p $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) + -mkdir -p $(DESTDIR)$(prefix)/share/man/man8 + $(INSTALL) slashgrid $(DESTDIR)$(prefix)/sbin + cp -f slashgrid.init $(DESTDIR)/etc/rc.d/init.d/slashgrid + cp -f ../doc/slashgrid.8.html $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ + cp -f ../doc/slashgrid.8 $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ + cp -f ../doc/slashgrid.8 $(DESTDIR)$(prefix)/share/man/man8 + gzip -f $(DESTDIR)$(prefix)/share/man/man8/slashgrid.8 + mkdir -p $(DESTDIR)/var/spool/slashgrid install-ws: gridsite-delegation.cgi htproxyput - mkdir -p $(prefix)/include \ - $(prefix)/bin \ - $(prefix)/sbin \ - $(prefix)/share/man/man1 \ - $(prefix)/share/man/man8 \ - $(prefix)/share/doc/gridsite-$(MINOR_VERSION) - cp -f ../doc/*.wsdl $(prefix)/share/doc/gridsite-$(MINOR_VERSION) + mkdir -p $(DESTDIR)$(prefix)/include \ + $(DESTDIR)$(prefix)/bin \ + $(DESTDIR)$(prefix)/sbin \ + $(DESTDIR)$(prefix)/share/man/man1 \ + $(DESTDIR)$(prefix)/share/man/man8 \ + $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) + cp -f ../doc/*.wsdl $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) for i in htproxyput.1 htproxytime.1 htproxyrenew.1 htproxydestroy.1 \ htproxyunixtime.1 htproxyinfo.1 ; do \ - cp -f ../doc/$$i.html $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ - cp -f ../doc/$$i $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ - cp -f ../doc/$$i $(prefix)/share/man/man1 ; \ - gzip -f $(prefix)/share/man/man1/$$i ; done + cp -f ../doc/$$i.html $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ + cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ + cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/man/man1 ; \ + gzip -f $(DESTDIR)$(prefix)/share/man/man1/$$i ; done for i in gridsite-delegation.8 ; do \ - cp -f ../doc/$$i.html $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ - cp -f ../doc/$$i $(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ - cp -f ../doc/$$i $(prefix)/share/man/man8 ; \ - gzip -f $(prefix)/share/man/man8/$$i ; done - cp -f htproxyput $(prefix)/bin - ln -sf htproxyput $(prefix)/bin/htproxydestroy - ln -sf htproxyput $(prefix)/bin/htproxytime - ln -sf htproxyput $(prefix)/bin/htproxyunixtime - ln -sf htproxyput $(prefix)/bin/htproxyrenew - ln -sf htproxyput $(prefix)/bin/htproxyinfo - cp -f gridsite-delegation.cgi $(prefix)/sbin + cp -f ../doc/$$i.html $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ + cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION) ; \ + cp -f ../doc/$$i $(DESTDIR)$(prefix)/share/man/man8 ; \ + gzip -f $(DESTDIR)$(prefix)/share/man/man8/$$i ; done + cp -f htproxyput $(DESTDIR)$(prefix)/bin + ln -sf htproxyput $(DESTDIR)$(prefix)/bin/htproxydestroy + ln -sf htproxyput $(DESTDIR)$(prefix)/bin/htproxytime + ln -sf htproxyput $(DESTDIR)$(prefix)/bin/htproxyunixtime + ln -sf htproxyput $(DESTDIR)$(prefix)/bin/htproxyrenew + ln -sf htproxyput $(DESTDIR)$(prefix)/bin/htproxyinfo + cp -f gridsite-delegation.cgi $(DESTDIR)$(prefix)/sbin post-install-debian: sed -i \ -e 's,^\(ServerRoot\) .*,\1 "/etc/$(httpd_name)",' \ -e 's,/usr/lib/httpd/modules,/usr/$(libdir)/$(httpd_name)/modules,' \ - $(prefix)/share/doc/gridsite-$(MINOR_VERSION)/*.conf + $(DESTDIR)$(prefix)/share/doc/gridsite-$(MINOR_VERSION)/*.conf # # Distributions @@ -546,6 +541,7 @@ dist: cp -f Makefile grst*.c htcp.c slashgrid.c slashgrid.init \ urlencode.c findproxyfile.c gaclexample.c mod_gridsite*.c \ htproxyput.c grst_admin.h mod_ssl-private.h \ + canl_mod_gridsite.c canl_mod_ssl-private.h \ gsexec.c gsexec.h gridsite-copy.c gridsite-storage.c \ delegation.h \ roffit make-gridsite-spec make-debian-files \ @@ -568,7 +564,7 @@ htcp-bin: htcp mkdir -p ../htcp-bin-$(PATCH_VERSION)/bin \ ../htcp-bin-$(PATCH_VERSION)/man/man1 cp -f ../doc/README.htcp-bin ../htcp-bin-$(PATCH_VERSION) - cp -f htcp ../htcp-bin-$(PATCH_VERSION)/bin + $(INSTALL) htcp ../htcp-bin-$(PATCH_VERSION)/bin cp -f ../doc/htcp.1 ../doc/htrm.1 ../doc/htls.1 ../doc/htmkdir.1 \ ../doc/htll.1 ../doc/htmv.1 ../doc/htping.1 ../doc/htfind.1 \ ../htcp-bin-$(PATCH_VERSION)/man/man1 @@ -590,8 +586,7 @@ rpm-prepare: dist ./make-gridsite-spec rpm: rpm-prepare - rm -Rf $(MYRPMDIR) - mkdir -p $(MYRPMDIR)/SOURCES $(MYRPMDIR)/SPECS $(MYRPMDIR)/BUILD \ + -mkdir -p $(MYRPMDIR)/SOURCES $(MYRPMDIR)/SPECS $(MYRPMDIR)/BUILD \ $(MYRPMDIR)/SRPMS $(MYRPMDIR)/RPMS/i386 $(MYRPMDIR)/BUILDROOT cp -f ../gridsite-$(PATCH_VERSION).src.tar.gz $(MYRPMDIR)/SOURCES cp -f gridsite.spec $(MYRPMDIR)/SPECS @@ -607,14 +602,13 @@ deb-prepare: dist ./make-debian-files deb: deb-prepare - rm -Rf $(MYRPMDIR) - mkdir -p $(MYRPMDIR) + -mkdir -p $(MYRPMDIR) tar -xC $(MYRPMDIR) -f ../gridsite-$(PATCH_VERSION).src.tar.gz cp ../gridsite-$(PATCH_VERSION).src.tar.gz $(MYRPMDIR)/gridsite_$(PATCH_VERSION).orig.tar.gz cp -rf ../debian/ $(MYRPMDIR)/gridsite-$(PATCH_VERSION)/ (cd $(MYRPMDIR)/gridsite-$(PATCH_VERSION) && \ pwd && \ - dpkg-buildpackage -S -d -uc -us) + dpkg-buildpackage -S -d -nc -uc -us) wtf: pwd @@ -624,4 +618,4 @@ wtf: # ls -lR /usr/local/ # ls -lR $(GSOAPDIR) -.PHONY: build build-lib apidoc clean install install-lib install-slashgrid install-ws dist htcp-bin rpm deb wtf post-install-debian +.PHONY: build build-lib apidoc clean distclean install install-lib install-slashgrid install-ws dist htcp-bin rpm deb wtf post-install-debian diff --git a/org.gridsite.core/src/canl_mod_gridsite.c b/org.gridsite.core/src/canl_mod_gridsite.c new file mode 100644 index 0000000..8b8de55 --- /dev/null +++ b/org.gridsite.core/src/canl_mod_gridsite.c @@ -0,0 +1,4101 @@ +/* + Copyright (c) 2003-10, Andrew McNab, Shiv Kaushal, Joseph Dada, + and Yibiao Li, University of Manchester. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + o Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + o 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "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 COPYRIGHT OWNER 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. + + + This program includes code from dav_parse_range() from Apache mod_dav.c, + and associated code contributed by David O Callaghan + + Copyright 2000-2005 The Apache Software Foundation or its licensors, as + applicable. + + 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. + + This work has been partially funded by the EU Commission (contract + INFSO-RI-222667) under the EGEE-III collaboration. +*/ + +/*------------------------------------------------------------------* + * This program is part of GridSite: http://www.gridsite.org/ * + *------------------------------------------------------------------*/ + +#ifndef VERSION +#define VERSION "x.x.x" +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if AP_MODULE_MAGIC_AT_LEAST(20051115,0) +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include "canl_mod_ssl-private.h" + +#include "gridsite.h" + +#include +#include + +#ifndef IPV6_ADD_MEMBERSHIP +#ifdef IPV6_JOIN_GROUP +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif +#endif + +#ifndef UNSET +#define UNSET -1 +#endif + +#define GRST_SESSIONS_DIR "/var/www/sessions" + +module AP_MODULE_DECLARE_DATA gridsite_module; + +#define GRST_SITECAST_GROUPS 32 + +struct sitecast_group + { char *address; int port; }; + +#define GRST_SITECAST_ALIASES 32 + +struct sitecast_alias + { const char *sitecast_url; const char *scheme; int port; + const char *local_path; const char *local_hostname; }; + +/* Globals, defined by main server directives in httpd.conf + These are assigned default values in create_gridsite_srv_config() */ + +int gridhttpport = 0; /* set by create_gridsite_srv_config, used as flag */ +char *sessionsdir = NULL; +char *sitecastdnlists = NULL; +char *ocspmodes = NULL; +struct sitecast_group sitecastgroups[GRST_SITECAST_GROUPS+1]; +struct sitecast_alias sitecastaliases[GRST_SITECAST_ALIASES]; + + /* This global records whether the SSLSrvConfigRec struct will have + the extra BOOL insecure_reneg member */ +int mod_ssl_with_insecure_reneg = 0; + +struct sitecast_sockets { + fd_set fds; + int max_fd; +} sitecast_sockets; + +typedef struct +{ + int auth; + int autopasscode; + int requirepasscode; + int zoneslashes; + int envs; + int format; + int indexes; + char *indexheader; + int gridsitelink; + char *adminfile; + char *adminuri; + char *helpuri; + char *loginuri; + char *dnlists; + char *dnlistsuri; + char *adminlist; + int gsiproxylimit; + char *unzip; + char *methods; + char *editable; + char *headfile; + char *footfile; + int gridhttp; + char *aclformat; + char *aclpath; + char *execmethod; + char *delegationuri; + ap_unix_identity_t execugid; + apr_fileperms_t diskmode; +} mod_gridsite_dir_cfg; /* per-directory config choices */ + + +/* + * parse_content_range() is loosely + * based on modules/dav/main/mod_dav.c from Apache + */ + +int parse_content_range(request_rec *r, apr_off_t *range_start, + apr_off_t *range_end, apr_off_t *range_length) +{ +// this all needs verifying to be ok for large (>2GB, >4GB) files + + const char *range_c; + char *range; + char *dash; + char *slash; + + range_c = apr_table_get(r->headers_in, "content-range"); + if (range_c == NULL) return 0; + + range = apr_pstrdup(r->pool, range_c); + + if ((strncasecmp(range, "bytes ", 6) != 0) || + ((dash = ap_strchr(range, '-')) == NULL) || + ((slash = ap_strchr(range, '/')) == NULL)) + { + return 0; /* malformed header. ignore it (per S14.16 of RFC2616) */ + } + + *dash = *slash = '\0'; + + // Check for GridSite-specific Content-Range: bytes *-*/LENGTH form + + if ((range[6] == '*') && (dash[1] == '*')) + { + if (slash[1] == '*') return 0; /* invalid truncation length */ + + *range_length = apr_atoi64(&slash[1]); + *range_start = 0; + *range_end = 0; + + return 1; /* a valid (truncation) length */ + } + + *range_length = 0; + *range_start = apr_atoi64(&range[6]); + *range_end = apr_atoi64(&dash[1]); + + if ((*range_end < *range_start) || + ((slash[1] != '*') && (apr_atoi64(&slash[1]) <= *range_end))) + return 0; /* ignore invalid ranges */ + + /* we now have a valid range */ + return 1; +} + +char *html_escape(apr_pool_t *pool, char *s) +{ + int htmlspecials, i; + char *escaped, *p; + + for (htmlspecials=0,p=s; *p != '\0'; ++p) + if ((*p == '<') || (*p == '>') || (*p == '&') || (*p == '"')) + ++htmlspecials; + + escaped = apr_palloc(pool, strlen(s) + htmlspecials * 6 + 1); + + for (i=0,p=s; *p != '\0'; ++p) + { + if (*p == '<') + { + strcpy(&escaped[i], "<"); + i += 4; + } + else if (*p == '>') + { + strcpy(&escaped[i], ">"); + i += 4; + } + else if (*p == '&') + { + strcpy(&escaped[i], "&"); + i += 5; + } + else if (*p == '"') + { + strcpy(&escaped[i], """); + i += 6; + } + else + { + escaped[i] = *p; + ++i; + } + } + + escaped[i] = '\0'; + + return escaped; +} + +char *make_admin_footer(request_rec *r, mod_gridsite_dir_cfg *conf, + int isdirectory) +/* + make string holding last modified text and admin links +*/ +{ + char *out, *https, *p, *dn = NULL, *file = NULL, *permstr = NULL, + *temp, modified[99], *dir_uri, *grst_cred_auri_0 = NULL; + GRSTgaclPerm perm = GRST_PERM_NONE; + struct tm mtime_tm; + time_t mtime_time; + + https = (char *) apr_table_get(r->subprocess_env, "HTTPS"); + + dir_uri = apr_pstrdup(r->pool, r->uri); + p = rindex(dir_uri, '/'); + + if (p == NULL) return ""; + + file = apr_pstrdup(r->pool, &p[1]); + p[1] = '\0'; + /* dir_uri always gets both a leading and a trailing slash */ + + out = apr_pstrdup(r->pool, "

\n"); + + if (!isdirectory) + { + mtime_time = apr_time_sec(r->finfo.mtime); + + localtime_r(&mtime_time, &mtime_tm); + strftime(modified, sizeof(modified), + "%a %e %B %Y", &mtime_tm); + temp = apr_psprintf(r->pool,"


Last modified %s\n", modified); + out = apr_pstrcat(r->pool, out, temp, NULL); + + if ((conf->adminuri != NULL) && + (conf->adminuri[0] != '\0') && + (conf->adminfile != NULL) && + (conf->adminfile[0] != '\0') && + (strncmp(file, GRST_HIST_PREFIX, sizeof(GRST_HIST_PREFIX)-1) != 0)) + { + temp = apr_psprintf(r->pool, + ". " + "View page history\n", + conf->adminfile, file); + out = apr_pstrcat(r->pool, out, temp, NULL); + } + + out = apr_pstrcat(r->pool, out, "", NULL); + } + + out = apr_pstrcat(r->pool, out, "
", NULL); + + if (r->connection->notes != NULL) + { + grst_cred_auri_0 = (char *) + apr_table_get(r->notes, "GRST_CRED_AURI_0"); + } + + if ((grst_cred_auri_0 != NULL) && + (strncmp(grst_cred_auri_0, "dn:", 3) == 0)) + { + dn = GRSThttpUrlDecode(&grst_cred_auri_0[3]); + if (dn[0] == '\0') + { + free(dn); + dn = NULL; + } + } + + if (dn != NULL) + { + temp = apr_psprintf(r->pool, + "You are %s
\n", html_escape(r->pool,dn)); + out = apr_pstrcat(r->pool, out, temp, NULL); + + if (r->notes != NULL) + permstr = (char *) apr_table_get(r->notes, "GRST_PERM"); + + if ((permstr != NULL) && + (conf->adminuri != NULL) && + (conf->adminuri[0] != '\0') && + (conf->adminfile != NULL) && + (conf->adminfile[0] != '\0')) + { + sscanf(permstr, "%d", &perm); + + if (!isdirectory && + GRSTgaclPermHasWrite(perm) && + (strncmp(file, GRST_HIST_PREFIX, + sizeof(GRST_HIST_PREFIX) - 1) != 0)) + { + temp = apr_psprintf(r->pool, + "" + "Edit page .\n", conf->adminfile, file); + out = apr_pstrcat(r->pool, out, temp, NULL); + } + + if (GRSTgaclPermHasList(perm) || GRSTgaclPermHasWrite(perm)) + { + temp = apr_psprintf(r->pool, + "Manage directory .\n", + dir_uri, conf->adminfile); + + out = apr_pstrcat(r->pool, out, temp, NULL); + } + } + + free(dn); + } + + if ((https != NULL) && (strcasecmp(https, "on") == 0)) + temp = apr_psprintf(r->pool, + "Switch to HTTP \n", + r->server->server_hostname, r->unparsed_uri); + else temp = apr_psprintf(r->pool, + "Switch to HTTPS \n", + r->server->server_hostname, r->unparsed_uri); + + out = apr_pstrcat(r->pool, out, temp, NULL); + + if ((conf->loginuri != NULL) && (conf->loginuri[0] != '\0')) + { + temp = apr_psprintf(r->pool, + ". Login/Logout\n", + conf->loginuri, r->unparsed_uri); + out = apr_pstrcat(r->pool, out, temp, NULL); + } + + if ((conf->helpuri != NULL) && (conf->helpuri[0] != '\0')) + { + temp = apr_psprintf(r->pool, + ". Website Help\n", conf->helpuri); + out = apr_pstrcat(r->pool, out, temp, NULL); + } + + if ((!isdirectory) && + (conf->adminuri != NULL) && + (conf->adminuri[0] != '\0') && + (conf->adminfile != NULL) && + (conf->adminfile[0] != '\0')) + { + temp = apr_psprintf(r->pool, ". " + "Print View\n", conf->adminfile, file); + out = apr_pstrcat(r->pool, out, temp, NULL); + } + + if (conf->gridsitelink) + { + temp = apr_psprintf(r->pool, + ". Built with " + "GridSite %s\n", VERSION); + out = apr_pstrcat(r->pool, out, temp, NULL); + } + + out = apr_pstrcat(r->pool, out, "\n
\n", NULL); + + return out; +} + +void delegation_header(request_rec *r, mod_gridsite_dir_cfg *conf) +{ + apr_table_add(r->headers_out, + apr_pstrdup(r->pool, "Proxy-Delegation-Service"), + apr_psprintf(r->pool,"https://%s%s", r->hostname, conf->delegationuri)); + return; + +} + +int html_format(request_rec *r, mod_gridsite_dir_cfg *conf) +/* + try to do GridSite formatting of .html files (NOT .shtml etc) +*/ +{ + int fd; + char *buf, *p, *file, *s, *head_formatted, *header_formatted, + *body_formatted, *admin_formatted, *footer_formatted; + size_t length; + struct stat statbuf; + apr_file_t *fp; + + if (r->finfo.filetype == APR_NOFILE) return HTTP_NOT_FOUND; + + if (apr_file_open(&fp, r->filename, APR_READ, 0, r->pool) != 0) + return HTTP_INTERNAL_SERVER_ERROR; + + + /* Put in Delegation service header if required */ + if (conf->delegationuri) delegation_header(r, conf); + + file = rindex(r->uri, '/'); + if (file != NULL) ++file; /* file points to name without path */ + + buf = apr_palloc(r->pool, (size_t)(r->finfo.size + 1)); + length = r->finfo.size; + apr_file_read(fp, buf, &length); + buf[r->finfo.size] = '\0'; + apr_file_close(fp); + + /* **** try to find a header file in this or parent directories **** */ + + fd = -1; + + if (conf->headfile[0] == '/') /* try absolute */ + { + fd = open(conf->headfile, O_RDONLY); + } + else /* try relative */ + { + /* first make a buffer big enough to hold path names we want to try */ + s = apr_palloc(r->pool, + strlen(r->filename) + strlen(conf->headfile) + 1); + strcpy(s, r->filename); + + for (;;) + { + p = rindex(s, '/'); + if (p == NULL) break; /* failed to find one */ + p[1] = '\0'; + strcat(p, conf->headfile); + + fd = open(s, O_RDONLY); + if (fd != -1) break; /* found one */ + + *p = '\0'; + } + } + + if (fd == -1) /* not found, so set up not to output one */ + { + head_formatted = apr_pstrdup(r->pool, ""); + header_formatted = apr_pstrdup(r->pool, ""); + body_formatted = buf; + } + else /* found a header file, so set up head and body to surround it */ + { + fstat(fd, &statbuf); + header_formatted = apr_palloc(r->pool, statbuf.st_size + 1); + read(fd, header_formatted, statbuf.st_size); + header_formatted[statbuf.st_size] = '\0'; + close(fd); + + p = strstr(buf, "pool, ""); + body_formatted = buf; + } + else + { + *p = '\0'; + head_formatted = buf; + ++p; + + while ((*p != '>') && (*p != '\0')) ++p; + + if (*p == '\0') + { + body_formatted = p; + } + else + { + *p = '\0'; + ++p; + body_formatted = p; + } + } + } + + /* **** remove closing tag from body **** */ + + p = strstr(body_formatted, "footfile[0] == '/') /* try absolute */ + { + fd = open(conf->footfile, O_RDONLY); + } + else /* try relative */ + { + /* first make a buffer big enough to hold path names we want to try */ + s = apr_palloc(r->pool, + strlen(r->filename) + strlen(conf->footfile) + 1); + strcpy(s, r->filename); + + for (;;) + { + p = rindex(s, '/'); + if (p == NULL) break; /* failed to find one */ + + p[1] = '\0'; + strcat(p, conf->footfile); + + fd = open(s, O_RDONLY); + if (fd != -1) break; /* found one */ + + *p = '\0'; + } + } + + if (fd == -1) /* failed to find a footer, so set up empty default */ + { + footer_formatted = apr_pstrdup(r->pool, ""); + } + else /* found a footer, so set up to use it */ + { + fstat(fd, &statbuf); + footer_formatted = apr_palloc(r->pool, statbuf.st_size + 1); + read(fd, footer_formatted, statbuf.st_size); + footer_formatted[statbuf.st_size] = '\0'; + close(fd); + } + + /* **** can now calculate the Content-Length and output headers **** */ + + length = strlen(head_formatted) + strlen(header_formatted) + + strlen(body_formatted) + strlen(admin_formatted) + + strlen(footer_formatted); + + ap_set_content_length(r, length); + ap_set_content_type(r, "text/html"); + + /* ** output the HTTP body (HTML Head+Body) ** */ + + ap_rputs(head_formatted, r); + ap_rputs(header_formatted, r); + ap_rputs(body_formatted, r); + ap_rputs(admin_formatted, r); + ap_rputs(footer_formatted, r); + + return OK; +} + +int html_dir_list(request_rec *r, mod_gridsite_dir_cfg *conf) +/* + output HTML directory listing, with level of formatting controlled + by GridSiteHtmlFormat/conf->format +*/ +{ + int fd, n, nn; + char *p, *s, *head_formatted, *header_formatted, + *body_formatted, *admin_formatted, *footer_formatted, *temp, + modified[999], *d_namepath, *indexheaderpath, *indexheadertext, + *encoded, *escaped; + size_t length; + struct stat statbuf; + struct tm mtime_tm; + struct dirent **namelist; + + if (r->finfo.filetype == APR_NOFILE) return HTTP_NOT_FOUND; + + /* Put in Delegation service header if required */ + if (conf->delegationuri) delegation_header(r, conf); + + head_formatted = apr_psprintf(r->pool, + "Directory listing %s\n", r->uri); + + if (conf->format) + { + /* **** try to find a header file in this or parent directories **** */ + + /* first make a buffer big enough to hold path names we want to try */ + fd = -1; + s = apr_palloc(r->pool, + strlen(r->filename) + strlen(conf->headfile) + 1); + strcpy(s, r->filename); + + for (;;) + { + p = rindex(s, '/'); + if (p == NULL) break; /* failed to find one */ + p[1] = '\0'; + strcat(p, conf->headfile); + + fd = open(s, O_RDONLY); + if (fd != -1) break; /* found one */ + + *p = '\0'; + } + + if (fd == -1) /* not found, so set up to output sensible default */ + { + header_formatted = apr_pstrdup(r->pool, ""); + } + else /* found a header file, so set up head and body to surround it */ + { + fstat(fd, &statbuf); + header_formatted = apr_palloc(r->pool, statbuf.st_size + 1); + read(fd, header_formatted, statbuf.st_size); + header_formatted[statbuf.st_size] = '\0'; + close(fd); + } + } + else header_formatted = apr_pstrdup(r->pool, ""); + + body_formatted = apr_psprintf(r->pool, + "

Directory listing %s

\n", r->uri); + + if (conf->indexheader != NULL) + { + indexheaderpath = apr_psprintf(r->pool, "%s/%s", r->filename, + conf->indexheader); + fd = open(indexheaderpath, O_RDONLY); + if (fd != -1) + { + fstat(fd, &statbuf); + indexheadertext = apr_palloc(r->pool, statbuf.st_size + 1); + read(fd, indexheadertext, statbuf.st_size); + indexheadertext[statbuf.st_size] = '\0'; + close(fd); + + body_formatted = apr_pstrcat(r->pool, body_formatted, + indexheadertext, NULL); + } + } + + body_formatted = apr_pstrcat(r->pool, body_formatted, "

\n", NULL); + + if (r->unparsed_uri[1] != '\0') + body_formatted = apr_pstrcat(r->pool, body_formatted, + "\n", + NULL); + + nn = scandir(r->filename, &namelist, 0, versionsort); + for (n=0; n < nn; ++n) + { + if ((namelist[n]->d_name[0] != '.') && + ((conf->indexheader == NULL) || + (strcmp(conf->indexheader, namelist[n]->d_name) != 0))) + { + d_namepath = apr_psprintf(r->pool, "%s/%s", r->filename, + namelist[n]->d_name); + stat(d_namepath, &statbuf); + + localtime_r(&(statbuf.st_mtime), &mtime_tm); + strftime(modified, sizeof(modified), + "", + &mtime_tm); + + encoded = GRSThttpUrlEncode(namelist[n]->d_name); + escaped = html_escape(r->pool, namelist[n]->d_name); + + if (S_ISDIR(statbuf.st_mode)) + temp = apr_psprintf(r->pool, + "" + "%s\n", + encoded, statbuf.st_size, statbuf.st_mtime, + escaped, + statbuf.st_size, modified); + else temp = apr_psprintf(r->pool, + "" + "%s\n", + encoded, statbuf.st_size, statbuf.st_mtime, + escaped, + statbuf.st_size, modified); + + free(encoded); + /* escaped done with pool so no free() */ + + body_formatted = apr_pstrcat(r->pool,body_formatted,temp,NULL); + } + + free(namelist[n]); + } + + free(namelist); + + body_formatted = apr_pstrcat(r->pool, body_formatted, "
[Parent directory]
%R%e %b %y
" + "%s/%ld
" + "%s%ld
\n", NULL); + + if (conf->format) + { + /* **** set up dynamic part of footer to go at end of body **** */ + + admin_formatted = make_admin_footer(r, conf, TRUE); + + /* **** try to find a footer file in this or parent directories **** */ + + /* first make a buffer big enough to hold path names we want to try */ + fd = -1; + s = apr_palloc(r->pool, + strlen(r->filename) + strlen(conf->footfile) + 1); + strcpy(s, r->filename); + + for (;;) + { + p = rindex(s, '/'); + if (p == NULL) break; /* failed to find one */ + + p[1] = '\0'; + strcat(p, conf->footfile); + + fd = open(s, O_RDONLY); + if (fd != -1) break; /* found one */ + + *p = '\0'; + } + + if (fd == -1) /* failed to find a footer, so use standard default */ + { + footer_formatted = apr_pstrdup(r->pool, ""); + } + else /* found a footer, so set up to use it */ + { + fstat(fd, &statbuf); + footer_formatted = apr_palloc(r->pool, statbuf.st_size + 1); + read(fd, footer_formatted, statbuf.st_size); + footer_formatted[statbuf.st_size] = '\0'; + close(fd); + } + } + else + { + admin_formatted = apr_pstrdup(r->pool, ""); + footer_formatted = apr_pstrdup(r->pool, ""); + } + + /* **** can now calculate the Content-Length and output headers **** */ + + length = strlen(head_formatted) + strlen(header_formatted) + + strlen(body_formatted) + strlen(admin_formatted) + + strlen(footer_formatted); + + ap_set_content_length(r, length); + ap_set_content_type(r, "text/html"); + + /* ** output the HTTP body (HTML Head+Body) ** */ + + ap_rputs(head_formatted, r); + ap_rputs(header_formatted, r); + ap_rputs(body_formatted, r); + ap_rputs(admin_formatted, r); + ap_rputs(footer_formatted, r); + + return OK; +} + +char *make_passcode_file(request_rec *r, mod_gridsite_dir_cfg *conf, + char *path, apr_time_t expires_time) +{ + int i; + char *filetemplate, *notename_i, *grst_cred_i, *cookievalue=NULL; + apr_uint64_t gridauthcookie; + apr_file_t *fp; + + /* create random for use in GRIDHTTP_PASSCODE cookies and file name */ + + if (apr_generate_random_bytes((char *) &gridauthcookie, + sizeof(gridauthcookie)) + != APR_SUCCESS) return NULL; + + filetemplate = apr_psprintf(r->pool, "%s/passcode-%016lxXXXXXX", + ap_server_root_relative(r->pool, + sessionsdir), + gridauthcookie); + + if (apr_file_mktemp(&fp, + filetemplate, + APR_CREATE | APR_WRITE | APR_EXCL, + r->pool) + != APR_SUCCESS) return NULL; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Created passcode file %s", filetemplate); + + if (expires_time > 0) apr_file_printf(fp, "expires=%lu\n", + (time_t) apr_time_sec(expires_time)); + + apr_file_printf(fp, "domain=%s\npath=%s\n", r->hostname, path); + + for (i=0; ; ++i) + { + notename_i = apr_psprintf(r->pool, "GRST_CRED_AURI_%d", i); + if (grst_cred_i = (char *) + apr_table_get(r->connection->notes, notename_i)) + { + apr_file_printf(fp, "%s=%s\n", notename_i, grst_cred_i); + } + else break; /* GRST_CRED_AURI_i are numbered consecutively */ + + notename_i = apr_psprintf(r->pool, "GRST_CRED_VALID_%d", i); + if (grst_cred_i = (char *) + apr_table_get(r->connection->notes, notename_i)) + { + apr_file_printf(fp, "%s=%s\n", notename_i, grst_cred_i); + } + else break; /* GRST_CRED_VALID_i are numbered consecutively */ + } + + if (apr_file_close(fp) != APR_SUCCESS) + { + apr_file_remove(filetemplate, r->pool); /* try to clean up */ + return NULL; + } + + cookievalue = rindex(filetemplate, '-'); + if (cookievalue != NULL) + { + ++cookievalue; + return cookievalue; + } + else return NULL; +} + +int http_gridhttp(request_rec *r, mod_gridsite_dir_cfg *conf) +{ + char *httpurl, *cookievalue, expires_str[APR_RFC822_DATE_LEN]; + apr_time_t expires_time; + + /* passcode cookies are valid for only 5 mins! */ + expires_time = apr_time_now() + apr_time_from_sec(300); + + /* try to generate passcode and make passcode file */ + cookievalue = make_passcode_file(r, conf, r->uri, expires_time); + + if (cookievalue == NULL) return HTTP_INTERNAL_SERVER_ERROR; + + /* send redirection header back to client */ + + apr_rfc822_date(expires_str, expires_time); + + apr_table_add(r->headers_out, + apr_pstrdup(r->pool, "Set-Cookie"), + apr_psprintf(r->pool, + "GRIDHTTP_PASSCODE=%s; " + "expires=%s; " + "domain=%s; " + "path=%s", + cookievalue, expires_str, r->hostname, r->uri)); + + if (gridhttpport != DEFAULT_HTTP_PORT) + httpurl = apr_psprintf(r->pool, "http://%s:%d%s", r->hostname, + gridhttpport, ap_escape_uri(r->pool, r->uri)); + else httpurl = apr_pstrcat(r->pool, "http://", r->hostname, + ap_escape_uri(r->pool, r->uri), NULL); + + apr_table_setn(r->headers_out, apr_pstrdup(r->pool, "Location"), httpurl); + + r->status = HTTP_MOVED_TEMPORARILY; + return OK; +} + +int http_put_method(request_rec *r, mod_gridsite_dir_cfg *conf) +{ + char buf[2048], *filename, *dirname, *basename; + const char *p; + size_t block_length, length_sent; + int retcode, stat_ret; + apr_file_t *fp; + struct stat statbuf; + int has_range = 0, is_done = 0; + apr_off_t range_start, range_end, range_length, length_to_send, length = 0; + + /* *** check if directory creation: PUT /.../ *** */ + + if ((r->unparsed_uri != NULL) && + (r->unparsed_uri[0] != '\0') && + (r->unparsed_uri[strlen(r->unparsed_uri) - 1] == '/')) + { + if (apr_dir_make(r->filename, + conf->diskmode + | APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE, + r->pool) != 0) return HTTP_INTERNAL_SERVER_ERROR; + + /* we force the permissions, rather than accept any existing ones */ + + apr_file_perms_set(r->filename, conf->diskmode + | APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE); + + ap_set_content_length(r, 0); + ap_set_content_type(r, "text/html"); + return OK; + } + + /* *** otherwise assume trying to create a regular file *** */ + + stat_ret = stat(r->filename, &statbuf); + + /* find if a range is specified */ + + has_range = parse_content_range(r, &range_start, &range_end, &range_length); + + if (has_range) + { + if ((range_start == 0) && (range_end == 0)) /* truncate? */ + { + if (stat_ret != 0) return HTTP_NOT_FOUND; + + if (truncate(r->filename, range_length) != 0) + return HTTP_INTERNAL_SERVER_ERROR; + else return OK; + } + + filename = r->filename; + + if (apr_file_open(&fp, filename, APR_WRITE | APR_CREATE | APR_BUFFERED, + conf->diskmode, r->pool) != 0) return HTTP_INTERNAL_SERVER_ERROR; + } + else /* use temporary file if not a partial transfer */ + { + dirname = apr_pstrdup(r->pool, r->filename); + basename = rindex(dirname, '/'); + if (basename == NULL) return HTTP_INTERNAL_SERVER_ERROR; + + *basename = '\0'; + ++basename; + + filename = apr_psprintf(r->pool, + "%s/.grsttmp-%s-XXXXXX", dirname, basename); + + if (apr_file_mktemp(&fp, filename, + APR_CREATE | APR_WRITE | APR_BUFFERED | APR_EXCL, r->pool) + != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; +/* + p = apr_table_get(r->headers_in, "Content-Length"); + if (p != NULL) + { + length = (apr_off_t) atol(p); + if (length > 16384) + { + if (apr_file_seek(fp, APR_SET, &length) == 0) + { + block_length = 1; + apr_file_write(fp, "0", &block_length); + } + + apr_file_seek(fp, APR_SET, 0); + } + } +*/ + } + + /* we force the permissions, rather than accept any existing ones */ + + apr_file_perms_set(filename, conf->diskmode); + + if (has_range) + { + if (apr_file_seek(fp, APR_SET, &range_start) != 0) + { + retcode = HTTP_INTERNAL_SERVER_ERROR; + return retcode; + } + + length_to_send = range_end - range_start + 1; + } + + retcode = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); + if (retcode == OK) + { + if (has_range) length_sent = 0; + + if (ap_should_client_block(r)) + while ((block_length = ap_get_client_block(r, buf, sizeof(buf))) > 0) + { + if (has_range && (length_sent + block_length > length_to_send)) + { + block_length = length_to_send - length_sent; + is_done = 1; + } + + if (apr_file_write(fp, buf, &block_length) != 0) + { + retcode = HTTP_INTERNAL_SERVER_ERROR; + break; + } + + if (has_range) + { + if (is_done) break; + else length_sent += block_length; + } + } + ap_set_content_length(r, 0); + ap_set_content_type(r, "text/html"); + } + + if ((apr_file_close(fp) != 0) || (retcode == HTTP_INTERNAL_SERVER_ERROR)) + { + if (strcmp(filename, r->filename) != 0) remove(filename); + return HTTP_INTERNAL_SERVER_ERROR; + } + + if ((strcmp(filename, r->filename) != 0) && + (apr_file_rename(filename, r->filename, r->pool) != 0)) + return HTTP_FORBIDDEN; /* best guess as to the problem ... */ + + if ((retcode == OK) && (stat_ret != 0)) + { + retcode = HTTP_CREATED; + ap_custom_response(r, HTTP_CREATED, ""); + } + + return retcode; +} + +int http_delete_method(request_rec *r, mod_gridsite_dir_cfg *conf) +{ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Try remove(%s)", r->filename); + + if (remove(r->filename) != 0) return HTTP_FORBIDDEN; + + ap_set_content_length(r, 0); + ap_set_content_type(r, "text/html"); + + return OK; +} + +int http_move_method(request_rec *r, mod_gridsite_dir_cfg *conf) +{ + char *destination_translated = NULL; + + if (r->notes != NULL) destination_translated = + (char *) apr_table_get(r->notes, "GRST_DESTINATION_TRANSLATED"); + + if (destination_translated == NULL) return HTTP_BAD_REQUEST; + + if (strcmp(r->filename, destination_translated) == 0) + return HTTP_FORBIDDEN; + + if (apr_file_rename(r->filename, destination_translated, r->pool) != 0) + return HTTP_FORBIDDEN; + + ap_set_content_length(r, 0); + ap_set_content_type(r, "text/html"); + + return OK; +} + +static int mod_gridsite_dir_handler(request_rec *r, mod_gridsite_dir_cfg *conf) +/* + handler switch for directories +*/ +{ + /* *** is this a write method? only possible if GridSiteAuth on *** */ + + if (conf->auth) + { + if ((r->method_number == M_PUT) && + (conf->methods != NULL) && + (strstr(conf->methods, " PUT " ) != NULL)) + return http_put_method(r, conf); + + if ((r->method_number == M_DELETE) && + (conf->methods != NULL) && + (strstr(conf->methods, " DELETE ") != NULL)) + return http_delete_method(r, conf); + } + + /* *** directory listing? *** */ + if ((r->method_number == M_GET) && (conf->indexes)) + return html_dir_list(r, conf); /* directory listing */ + + return DECLINED; /* *** nothing to see here, move along *** */ +} + +static int mod_gridsite_nondir_handler(request_rec *r, mod_gridsite_dir_cfg *conf) +/* + one big handler switch for everything other than directories, since we + might be responding to MIME * / * for local PUT, MOVE, COPY and DELETE, + and GET inside ghost directories. +*/ +{ + char *upgradeheader, *upgradespaced, *p; + const char *https_env; + + /* *** is this a write method or GridHTTP HTTPS->HTTP redirection? + only possible if GridSiteAuth on *** */ + + if (conf->auth) + { + if ((conf->gridhttp) && + ((r->method_number == M_GET) || + ((r->method_number == M_PUT) && + (strstr(conf->methods, " PUT ") != NULL))) && + ((upgradeheader = (char *) apr_table_get(r->headers_in, + "Upgrade")) != NULL) && + ((https_env=apr_table_get(r->subprocess_env,"HTTPS")) != NULL) && + (strcasecmp(https_env, "on") == 0)) + { + upgradespaced = apr_psprintf(r->pool, " %s ", upgradeheader); + + for (p=upgradespaced; *p != '\0'; ++p) + if ((*p == ',') || (*p == '\t')) *p = ' '; + +// TODO: what if we're pointing at a CGI or some dynamic content??? + + if (strstr(upgradespaced, " GridHTTP/1.0 ") != NULL) + return http_gridhttp(r, conf); + } + + if ((r->method_number == M_PUT) && + (conf->methods != NULL) && + (strstr(conf->methods, " PUT " ) != NULL)) + return http_put_method(r, conf); + + if ((r->method_number == M_DELETE) && + (conf->methods != NULL) && + (strstr(conf->methods, " DELETE ") != NULL)) + return http_delete_method(r, conf); + + if ((r->method_number == M_MOVE) && + (conf->methods != NULL) && + (strstr(conf->methods, " MOVE ") != NULL)) + return http_move_method(r, conf); + } + + /* *** check if a special ghost admin CGI *** */ + + if (conf->adminfile && conf->adminuri && + (strlen(r->filename) > strlen(conf->adminfile) + 1) && + (strcmp(&(r->filename[strlen(r->filename) - strlen(conf->adminfile)]), + conf->adminfile) == 0) && + (r->filename[strlen(r->filename)-strlen(conf->adminfile)-1] == '/') && + ((r->method_number == M_POST) || + (r->method_number == M_GET))) + { + ap_internal_redirect(conf->adminuri, r); + return OK; + } + + /* *** finally look for .html files that we should format *** */ + + if ((conf->format) && /* conf->format set by GridSiteHtmlFormat on */ + (strlen(r->filename) > 5) && + (strcmp(&(r->filename[strlen(r->filename)-5]), ".html") == 0) && + (r->method_number == M_GET)) return html_format(r, conf); + + return DECLINED; /* *** nothing to see here, move along *** */ +} + +static void recurse4dirlist(char *dirname, time_t *dirs_time, + char *fulluri, int fullurilen, + char *encfulluri, int enclen, + request_rec *r, char **body, + int recurse_level) +/* try to find DN Lists in dir[] and its subdirs that match the fulluri[] + prefix. add blobs of HTML to body as they are found. */ +{ + char *unencname, modified[99], *oneline, *d_namepath, + *mildencoded; + DIR *oneDIR; + struct dirent *onedirent; + struct tm mtime_tm; + struct stat statbuf; + + if ((stat(dirname, &statbuf) != 0) || + (!S_ISDIR(statbuf.st_mode)) || + ((oneDIR = opendir(dirname)) == NULL)) return; + + if (statbuf.st_mtime > *dirs_time) *dirs_time = statbuf.st_mtime; + + while ((onedirent = readdir(oneDIR)) != NULL) + { + if (onedirent->d_name[0] == '.') continue; + + d_namepath = apr_psprintf(r->pool, "%s/%s", dirname, onedirent->d_name); + + if (stat(d_namepath, &statbuf) != 0) continue; + + if (S_ISDIR(statbuf.st_mode)) + { + if (recurse_level < GRST_RECURS_LIMIT) + recurse4dirlist(d_namepath, dirs_time, fulluri, + fullurilen, encfulluri, enclen, + r, body, recurse_level + 1); + } + else if ((strncmp(onedirent->d_name, encfulluri, enclen) == 0) && + (onedirent->d_name[strlen(onedirent->d_name) - 1] != '~')) + { + unencname = GRSThttpUrlDecode(onedirent->d_name); + + if (strncmp(unencname, fulluri, fullurilen) == 0) + { + if (statbuf.st_mtime > *dirs_time) + *dirs_time = statbuf.st_mtime; + + localtime_r(&(statbuf.st_mtime), &mtime_tm); + strftime(modified, sizeof(modified), + "%R%e %b %y", + &mtime_tm); + + mildencoded = GRSThttpUrlMildencode(&unencname[fullurilen]); + + oneline = apr_psprintf(r->pool, + "" + "%s" + "%ld%s\n", + mildencoded, statbuf.st_size, + statbuf.st_mtime, + html_escape(r->pool, unencname), + statbuf.st_size, modified); + + free(mildencoded); + + *body = apr_pstrcat(r->pool, *body, oneline, NULL); + } + + free(unencname); /* libgridsite doesnt use pools */ + } + } + + closedir(oneDIR); +} + +static int mod_gridsite_dnlistsuri_dir_handler(request_rec *r, + mod_gridsite_dir_cfg *conf) +/* + virtual DN-list file lister: make all DN lists on the dn-lists + path of this server appear to be in the dn-lists directory itself + (ie where they appear in the DN lists path doesnt matter, as long + as their name matches) +*/ +{ + int enclen, fullurilen, fd; + char *fulluri, *encfulluri, *dn_list_ptr, *dirname, + *body, *oneline, *p, *s, + *head_formatted, *header_formatted, *footer_formatted, + *permstr = NULL; + struct stat statbuf; + size_t length; + time_t dirs_time = 0; + GRSTgaclPerm perm = GRST_PERM_NONE; + + if (r->notes != NULL) + permstr = (char *) apr_table_get(r->notes, "GRST_PERM"); + + if (permstr != NULL) sscanf(permstr, "%d", &perm); + + fulluri = apr_psprintf(r->pool, "https://%s%s", + r->hostname, conf->dnlistsuri); + fullurilen = strlen(fulluri); + + encfulluri = GRSThttpUrlEncode(fulluri); + enclen = strlen(encfulluri); + + if (conf->dnlists != NULL) p = conf->dnlists; + else p = getenv("GRST_DN_LISTS"); + + if (p == NULL) p = GRST_DN_LISTS; + dn_list_ptr = apr_pstrdup(r->pool, p); + + head_formatted = apr_psprintf(r->pool, + "Directory listing %s\n", r->uri); + + if (conf->format) + { + /* **** try to find a header file in this or parent directories **** */ + + fd = -1; + + if (conf->headfile[0] == '/') /* try absolute */ + { + fd = open(conf->headfile, O_RDONLY); + } + else /* try relative */ + { + /* first make a buffer big enough to hold path names we want to try */ + s = malloc(strlen(r->filename) + strlen(conf->headfile) + 1); + strcpy(s, r->filename); + + for (;;) + { + p = rindex(s, '/'); + if (p == NULL) break; /* failed to find one */ + p[1] = '\0'; + strcat(p, conf->headfile); + + fd = open(s, O_RDONLY); + if (fd != -1) break; /* found one */ + + *p = '\0'; + } + + free(s); + } + + if (fd == -1) /* not found, so set up to output sensible default */ + { + header_formatted = apr_pstrdup(r->pool, ""); + } + else /* found a header file, so set up head and body to surround it */ + { + fstat(fd, &statbuf); + header_formatted = apr_palloc(r->pool, statbuf.st_size + 1); + read(fd, header_formatted, statbuf.st_size); + header_formatted[statbuf.st_size] = '\0'; + close(fd); + } + } + else header_formatted = apr_pstrdup(r->pool, ""); + + body = apr_psprintf(r->pool, + "

Directory listing %s

\n", r->uri); + + if ((r->uri)[1] != '\0') + body = apr_pstrcat(r->pool, body, + "\n", + NULL); + + while ((dirname = strsep(&dn_list_ptr, ":")) != NULL) + recurse4dirlist(dirname, &dirs_time, fulluri, fullurilen, + encfulluri, enclen, r, &body, 0); + + p = (char *) apr_table_get(r->subprocess_env, "HTTPS"); + if ((p != NULL) && (strcmp(p, "on") == 0)) + { + oneline = apr_psprintf(r->pool, + "\n" + "" + "\n", + r->uri, conf->adminfile); + + body = apr_pstrcat(r->pool, body, oneline, NULL); + } + + body = apr_pstrcat(r->pool, body, "
[Parent directory]
\n", NULL); + + free(encfulluri); /* libgridsite doesnt use pools */ + + if (conf->format) + { + /* **** try to find a footer file in this or parent directories **** */ + + fd = -1; + + if (conf->headfile[0] == '/') /* try absolute */ + { + fd = open(conf->headfile, O_RDONLY); + } + else /* try relative */ + { + /* first make a buffer big enough to hold path names we want to try */ + s = malloc(strlen(r->filename) + strlen(conf->footfile)); + strcpy(s, r->filename); + + for (;;) + { + p = rindex(s, '/'); + if (p == NULL) break; /* failed to find one */ + + p[1] = '\0'; + strcat(p, conf->footfile); + + fd = open(s, O_RDONLY); + if (fd != -1) break; /* found one */ + + *p = '\0'; + } + + free(s); + } + + if (fd == -1) /* failed to find a footer, so use standard default */ + { + footer_formatted = apr_pstrdup(r->pool, ""); + } + else /* found a footer, so set up to use it */ + { + fstat(fd, &statbuf); + footer_formatted = apr_palloc(r->pool, statbuf.st_size + 1); + read(fd, footer_formatted, statbuf.st_size); + footer_formatted[statbuf.st_size] = '\0'; + close(fd); + } + } + else footer_formatted = apr_pstrdup(r->pool, ""); + + /* **** can now calculate the Content-Length and output headers **** */ + + length = strlen(head_formatted) + strlen(header_formatted) + + strlen(body) + strlen(footer_formatted); + + ap_set_content_length(r, length); + r->mtime = apr_time_from_sec(dirs_time); + ap_set_last_modified(r); + ap_set_content_type(r, "text/html"); + + /* ** output the HTTP body (HTML Head+Body) ** */ + ap_rputs(head_formatted, r); + ap_rputs(header_formatted, r); + ap_rputs(body, r); + ap_rputs(footer_formatted, r); + + return OK; +} + +static char *recurse4file(char *dir, char *file, apr_pool_t *pool, + int recurse_level) +/* try to find file[] in dir[]. try subdirs if not found. + return full path to first found version or NULL on failure */ +{ + char *fullfilename, *fulldirname; + struct stat statbuf; + DIR *dirDIR; + struct dirent *file_ent; + + /* try to find in current directory */ + + fullfilename = apr_psprintf(pool, "%s/%s", dir, file); + + if (stat(fullfilename, &statbuf) == 0) return fullfilename; + + /* maybe search in subdirectories */ + + if (recurse_level >= GRST_RECURS_LIMIT) return NULL; + + dirDIR = opendir(dir); + + if (dirDIR == NULL) return NULL; + + while ((file_ent = readdir(dirDIR)) != NULL) + { + if (file_ent->d_name[0] == '.') continue; + + fulldirname = apr_psprintf(pool, "%s/%s", dir, file_ent->d_name); + if ((stat(fulldirname, &statbuf) == 0) && + S_ISDIR(statbuf.st_mode) && + ((fullfilename = recurse4file(fulldirname, file, + pool, recurse_level + 1)) != NULL)) + { + closedir(dirDIR); + return fullfilename; + } + } + + closedir(dirDIR); + + return NULL; +} + +static int mod_gridsite_dnlistsuri_handler(request_rec *r, + mod_gridsite_dir_cfg *conf) +/* + virtual DN-list file generator +*/ +{ + int fd; + char *fulluri, *encfulluri, *dn_list_ptr, *filename, *dirname, *p, + *buf; + struct stat statbuf; + + /* *** check if a special ghost admin CGI *** */ + + if (conf->adminfile && conf->adminuri && + (strlen(r->filename) > strlen(conf->adminfile) + 1) && + (strcmp(&(r->filename[strlen(r->filename) - strlen(conf->adminfile)]), + conf->adminfile) == 0) && + (r->filename[strlen(r->filename)-strlen(conf->adminfile)-1] == '/') && + ((r->method_number == M_POST) || + (r->method_number == M_GET))) + { + ap_internal_redirect(conf->adminuri, r); + return OK; + } + + if (r->uri[strlen(r->uri) - 1] == '/') + { + apr_table_setn(r->headers_out, apr_pstrdup(r->pool, "Location"), + apr_pstrdup(r->pool, conf->dnlistsuri)); + + r->status = HTTP_MOVED_TEMPORARILY; + return OK; + } + + fulluri = apr_psprintf(r->pool, "https://%s%s", + r->hostname, r->uri); + + encfulluri = GRSThttpUrlEncode(fulluri); + + if (conf->dnlists != NULL) p = conf->dnlists; + else p = getenv("GRST_DN_LISTS"); + + if (p == NULL) p = GRST_DN_LISTS; + dn_list_ptr = apr_pstrdup(r->pool, p); + + while ((dirname = strsep(&dn_list_ptr, ":")) != NULL) + { + filename = recurse4file(dirname, encfulluri, r->pool, 0); + + if (filename == NULL) continue; + + fd = open(filename, O_RDONLY); + + if (fd == -1) continue; + + fstat(fd, &statbuf); + ap_set_content_length(r, (apr_off_t) statbuf.st_size); + r->mtime = apr_time_from_sec(statbuf.st_mtime); + ap_set_content_type(r, "text/plain"); + ap_set_last_modified(r); + + buf = apr_palloc(r->pool, statbuf.st_size + 1); + read(fd, buf, statbuf.st_size); + buf[statbuf.st_size] = '\0'; + + ap_rputs(buf, r); + + close(fd); + + return OK; + } + + return HTTP_NOT_FOUND; +} + +static void *create_gridsite_srv_config(apr_pool_t *p, server_rec *s) +{ + int i; + + /* only run once (in base server) */ + if (!(s->is_virtual) && (gridhttpport == 0)) + { + gridhttpport = GRST_HTTP_PORT; + + sessionsdir = apr_pstrdup(p, GRST_SESSIONS_DIR); + /* GridSiteSessionsDir dir-path */ + + sitecastdnlists = NULL; + + sitecastgroups[0].port = GRST_HTCP_PORT; + /* GridSiteCastUniPort udp-port */ + + for (i=1; i <= GRST_SITECAST_GROUPS; ++i) + { + sitecastgroups[i].port = 0; /* GridSiteCastGroup mcast-list */ + } + + for (i=0; i <= GRST_SITECAST_ALIASES; ++i) + { + sitecastaliases[i].sitecast_url = NULL; + sitecastaliases[i].port = 0; + sitecastaliases[i].scheme = NULL; + sitecastaliases[i].local_path = NULL; + sitecastaliases[i].local_hostname = NULL; + } /* GridSiteCastAlias url path */ + } + + return NULL; +} + +static void *create_gridsite_dir_config(apr_pool_t *p, char *path) +{ + mod_gridsite_dir_cfg *conf = apr_palloc(p, sizeof(*conf)); + + if (path == NULL) /* set up document root defaults */ + { + conf->auth = 0; /* GridSiteAuth on/off */ + conf->autopasscode = 1; /* GridSiteAutoPasscode on/off */ + conf->requirepasscode = 0; /* GridSiteRequirePasscode on/off */ + conf->zoneslashes = 1; /* GridSiteZoneSlashes number */ + conf->envs = 1; /* GridSiteEnvs on/off */ + conf->format = 0; /* GridSiteHtmlFormat on/off */ + conf->indexes = 0; /* GridSiteIndexes on/off */ + conf->indexheader = NULL; /* GridSiteIndexHeader File-value */ + conf->gridsitelink = 1; /* GridSiteLink on/off */ + conf->adminfile = apr_pstrdup(p, GRST_ADMIN_FILE); + /* GridSiteAdminFile File-value */ + conf->adminuri = NULL; /* GridSiteAdminURI URI-value */ + conf->helpuri = NULL; /* GridSiteHelpURI URI-value */ + conf->loginuri = NULL; /* GridSiteLoginURI URI-value */ + conf->dnlists = NULL; /* GridSiteDNlists Search-path */ + conf->dnlistsuri = NULL; /* GridSiteDNlistsURI URI-value */ + conf->adminlist = NULL; /* GridSiteAdminList URI-value */ + conf->gsiproxylimit = 1000; /* GridSiteGSIProxyLimit number */ + conf->unzip = NULL; /* GridSiteUnzip file-path */ + + conf->methods = apr_pstrdup(p, " GET "); + /* GridSiteMethods methods */ + + conf->editable = apr_pstrdup(p, " txt shtml html htm css js php jsp "); + /* GridSiteEditable types */ + + conf->headfile = apr_pstrdup(p, GRST_HEADFILE); + conf->footfile = apr_pstrdup(p, GRST_FOOTFILE); + /* GridSiteHeadFile and GridSiteFootFile file name */ + + conf->gridhttp = 0; /* GridSiteGridHTTP on/off */ + conf->aclformat = apr_pstrdup(p, "GACL"); + /* GridSiteACLFormat gacl/xacml */ + conf->aclpath = NULL; /* GridSiteACLPath acl-path */ + conf->delegationuri = NULL; /* GridSiteDelegationURI URI-value */ + conf->execmethod = NULL; + /* GridSiteExecMethod nosetuid/suexec/X509DN/directory */ + + conf->execugid.uid = 0; /* GridSiteUserGroup User Group */ + conf->execugid.gid = 0; /* ditto */ + conf->execugid.userdir = 0; /* ditto */ + + conf->diskmode = APR_UREAD | APR_UWRITE; + /* GridSiteDiskMode group-mode world-mode + GroupNone | GroupRead | GroupWrite WorldNone | WorldRead */ + } + else + { + conf->auth = UNSET; /* GridSiteAuth on/off */ + conf->autopasscode = UNSET; /* GridSiteAutoPasscode on/off */ + conf->requirepasscode = UNSET; /* GridSiteRequirePasscode on/off */ + conf->zoneslashes = UNSET; /* GridSiteZoneSlashes number */ + conf->envs = UNSET; /* GridSiteEnvs on/off */ + conf->format = UNSET; /* GridSiteHtmlFormat on/off */ + conf->indexes = UNSET; /* GridSiteIndexes on/off */ + conf->indexheader = NULL; /* GridSiteIndexHeader File-value */ + conf->gridsitelink = UNSET; /* GridSiteLink on/off */ + conf->adminfile = NULL; /* GridSiteAdminFile File-value */ + conf->adminuri = NULL; /* GridSiteAdminURI URI-value */ + conf->helpuri = NULL; /* GridSiteHelpURI URI-value */ + conf->loginuri = NULL; /* GridSiteLoginURI URI-value */ + conf->dnlists = NULL; /* GridSiteDNlists Search-path */ + conf->dnlistsuri = NULL; /* GridSiteDNlistsURI URI-value */ + conf->adminlist = NULL; /* GridSiteAdminList URI-value */ + conf->gsiproxylimit = UNSET; /* GridSiteGSIProxyLimit number */ + conf->unzip = NULL; /* GridSiteUnzip file-path */ + conf->methods = NULL; /* GridSiteMethods methods */ + conf->editable = NULL; /* GridSiteEditable types */ + conf->headfile = NULL; /* GridSiteHeadFile file name */ + conf->footfile = NULL; /* GridSiteFootFile file name */ + conf->gridhttp = UNSET; /* GridSiteGridHTTP on/off */ + conf->aclformat = NULL; /* GridSiteACLFormat gacl/xacml */ + conf->aclpath = NULL; /* GridSiteACLPath acl-path */ + conf->delegationuri = NULL; /* GridSiteDelegationURI URI-value */ + conf->execmethod = NULL; /* GridSiteExecMethod */ + conf->execugid.uid = UNSET; /* GridSiteUserGroup User Group */ + conf->execugid.gid = UNSET; /* ditto */ + conf->execugid.userdir = UNSET; /* ditto */ + conf->diskmode = UNSET; /* GridSiteDiskMode group world */ + } + + return conf; +} + +static void *merge_gridsite_dir_config(apr_pool_t *p, void *vserver, + void *vdirect) +/* merge directory with server-wide directory configs */ +{ + mod_gridsite_dir_cfg *conf, *server, *direct; + + server = (mod_gridsite_dir_cfg *) vserver; + direct = (mod_gridsite_dir_cfg *) vdirect; + conf = apr_palloc(p, sizeof(*conf)); + + if (direct->auth != UNSET) conf->auth = direct->auth; + else conf->auth = server->auth; + + if (direct->autopasscode != UNSET) conf->autopasscode = direct->autopasscode; + else conf->autopasscode = server->autopasscode; + + if (direct->requirepasscode != UNSET) conf->requirepasscode = direct->requirepasscode; + else conf->requirepasscode = server->requirepasscode; + + if (direct->zoneslashes != UNSET) conf->zoneslashes = direct->zoneslashes; + else conf->zoneslashes = server->zoneslashes; + + if (direct->envs != UNSET) conf->envs = direct->envs; + else conf->envs = server->envs; + + if (direct->format != UNSET) conf->format = direct->format; + else conf->format = server->format; + + if (direct->indexes != UNSET) conf->indexes = direct->indexes; + else conf->indexes = server->indexes; + + if (direct->gridsitelink != UNSET) conf->gridsitelink=direct->gridsitelink; + else conf->gridsitelink=server->gridsitelink; + + if (direct->indexheader != NULL) conf->indexheader = direct->indexheader; + else conf->indexheader = server->indexheader; + + if (direct->adminfile != NULL) conf->adminfile = direct->adminfile; + else conf->adminfile = server->adminfile; + + if (direct->adminuri != NULL) conf->adminuri = direct->adminuri; + else conf->adminuri = server->adminuri; + + if (direct->helpuri != NULL) conf->helpuri = direct->helpuri; + else conf->helpuri = server->helpuri; + + if (direct->loginuri != NULL) conf->loginuri = direct->loginuri; + else conf->loginuri = server->loginuri; + + if (direct->dnlists != NULL) conf->dnlists = direct->dnlists; + else conf->dnlists = server->dnlists; + + if (direct->dnlistsuri != NULL) conf->dnlistsuri = direct->dnlistsuri; + else conf->dnlistsuri = server->dnlistsuri; + + if (direct->adminlist != NULL) conf->adminlist = direct->adminlist; + else conf->adminlist = server->adminlist; + + if (direct->gsiproxylimit != UNSET) + conf->gsiproxylimit = direct->gsiproxylimit; + else conf->gsiproxylimit = server->gsiproxylimit; + + if (direct->unzip != NULL) conf->unzip = direct->unzip; + else conf->unzip = server->unzip; + + if (direct->methods != NULL) conf->methods = direct->methods; + else conf->methods = server->methods; + + if (direct->editable != NULL) conf->editable = direct->editable; + else conf->editable = server->editable; + + if (direct->headfile != NULL) conf->headfile = direct->headfile; + else conf->headfile = server->headfile; + + if (direct->footfile != NULL) conf->footfile = direct->footfile; + else conf->footfile = server->footfile; + + if (direct->gridhttp != UNSET) conf->gridhttp = direct->gridhttp; + else conf->gridhttp = server->gridhttp; + + if (direct->aclformat != NULL) conf->aclformat = direct->aclformat; + else conf->aclformat = server->aclformat; + + if (direct->aclpath != NULL) conf->aclpath = direct->aclpath; + else conf->aclpath = server->aclpath; + + if (direct->delegationuri != NULL) conf->delegationuri = direct->delegationuri; + else conf->delegationuri = server->delegationuri; + + if (direct->execmethod != NULL) conf->execmethod = direct->execmethod; + else conf->execmethod = server->execmethod; + + if (direct->execugid.uid != UNSET) + { conf->execugid.uid = direct->execugid.uid; + conf->execugid.gid = direct->execugid.gid; + conf->execugid.userdir = direct->execugid.userdir; } + else + { conf->execugid.uid = server->execugid.uid; + conf->execugid.gid = server->execugid.gid; + conf->execugid.userdir = server->execugid.userdir; } + + if (direct->diskmode != UNSET) conf->diskmode = direct->diskmode; + else conf->diskmode = server->diskmode; + + return conf; +} + +static const char *mod_gridsite_take1_cmds(cmd_parms *a, void *cfg, + const char *parm) +{ + int n, i; + char *p; + + if (strcasecmp(a->cmd->name, "GridSiteSessionsDir") == 0) + { + if (a->server->is_virtual) + return "GridSiteSessionsDir cannot be used inside a virtual server"; + + sessionsdir = apr_pstrdup(a->pool, parm); + } +/* GridSiteOnetimesDir is deprecated in favour of GridSiteSessionsDir */ + else if (strcasecmp(a->cmd->name, "GridSiteOnetimesDir") == 0) + { + if (a->server->is_virtual) + return "GridSiteOnetimesDir cannot be used inside a virtual server"; + + sessionsdir = apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteZoneSlashes") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->zoneslashes = atoi(parm); + + if (((mod_gridsite_dir_cfg *) cfg)->zoneslashes < 1) + return "GridSiteZoneSlashes must be greater than 0"; + } + else if (strcasecmp(a->cmd->name, "GridSiteGridHTTPport") == 0) + { + gridhttpport = atoi(parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteCastDNlists") == 0) + { + if (a->server->is_virtual) + return "GridSiteDNlists cannot be used inside a virtual server"; + + sitecastdnlists = apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteCastUniPort") == 0) + { + if (a->server->is_virtual) + return "GridSiteCastUniPort cannot be used inside a virtual server"; + + if (sscanf(parm, "%d", &(sitecastgroups[0].port)) != 1) + return "Failed parsing GridSiteCastUniPort numeric value"; + } + else if (strcasecmp(a->cmd->name, "GridSiteCastGroup") == 0) + { + if (a->server->is_virtual) + return "GridSiteCastGroup cannot be used inside a virtual server"; + + for (i=1; i <= GRST_SITECAST_GROUPS; ++i) + { + if (sitecastgroups[i].port == 0) /* a free slot */ + { + sitecastgroups[i].port = GRST_HTCP_PORT; + + if (sscanf(parm, "%s:%d", + &(sitecastgroups[i].address), + &(sitecastgroups[i].port)) < 1) + return "Failed parsing GridSiteCastGroup"; + + break; + } + } + + if (i > GRST_SITECAST_GROUPS) + return "Maximum GridSiteCastGroup groups reached"; + } + else if (strcasecmp(a->cmd->name, "GridSiteAdminFile") == 0) + { + if (index(parm, '/') != NULL) + return "/ not permitted in GridSiteAdminFile"; + + ((mod_gridsite_dir_cfg *) cfg)->adminfile = + apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteAdminURI") == 0) + { + if (*parm != '/') return "GridSiteAdminURI must begin with /"; + + ((mod_gridsite_dir_cfg *) cfg)->adminuri = + apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteHelpURI") == 0) + { + if (*parm != '/') return "GridSiteHelpURI must begin with /"; + + ((mod_gridsite_dir_cfg *) cfg)->helpuri = + apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteDNlists") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->dnlists = + apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteDNlistsURI") == 0) + { + if (*parm != '/') return "GridSiteDNlistsURI must begin with /"; + + if ((*parm != '\0') && (parm[strlen(parm) - 1] == '/')) + ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri = + apr_pstrdup(a->pool, parm); + else + ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri = + apr_pstrcat(a->pool, parm, "/", NULL); + } + else if (strcasecmp(a->cmd->name, "GridSiteAdminList") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->adminlist = + apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteGSIProxyLimit") == 0) + { + n = -1; + + if ((sscanf(parm, "%d", &n) == 1) && (n >= 0)) { + if (n == 0) + n = 1000; /* thousand is an African for "unlimited" */ + ((mod_gridsite_dir_cfg *) cfg)->gsiproxylimit = n; + } + else return "GridSiteGSIProxyLimit must be a number >= 0"; + } + else if (strcasecmp(a->cmd->name, "GridSiteUnzip") == 0) + { + if (*parm != '/') return "GridSiteUnzip must begin with /"; + + ((mod_gridsite_dir_cfg *) cfg)->unzip = + apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteMethods") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->methods = + apr_psprintf(a->pool, " %s ", parm); + + for (p = ((mod_gridsite_dir_cfg *) cfg)->methods; + *p != '\0'; + ++p) if (*p == '\t') *p = ' '; + } + else if (strcasecmp(a->cmd->name, "GridSiteOCSP") == 0) + { + ocspmodes = apr_psprintf(a->pool, " %s ", parm); + + for (p = ocspmodes; *p != '\0'; ++p) + if (*p == '\t') *p = ' '; + else *p = tolower(*p); + } + else if (strcasecmp(a->cmd->name, "GridSiteEditable") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->editable = + apr_psprintf(a->pool, " %s ", parm); + + for (p = ((mod_gridsite_dir_cfg *) cfg)->editable; + *p != '\0'; + ++p) if (*p == '\t') *p = ' '; + } + else if (strcasecmp(a->cmd->name, "GridSiteHeadFile") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->headfile = + apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteFootFile") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->footfile = + apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteIndexHeader") == 0) + { + if (index(parm, '/') != NULL) + return "/ not permitted in GridSiteIndexHeader"; + + ((mod_gridsite_dir_cfg *) cfg)->indexheader = + apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteACLFormat") == 0) + { + if ((strcasecmp(parm,"GACL") != 0) && + (strcasecmp(parm,"XACML") != 0)) + return "GridsiteACLFormat must be either GACL or XACML"; + + ((mod_gridsite_dir_cfg *) cfg)->aclformat = apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteACLPath") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->aclpath = apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteDelegationURI") == 0) + { + if (*parm != '/') return "GridSiteDelegationURI must begin with /"; + + if (*parm != '\0') + ((mod_gridsite_dir_cfg *) cfg)->delegationuri = + apr_pstrdup(a->pool, parm); + + } + else if (strcasecmp(a->cmd->name, "GridSiteExecMethod") == 0) + { + if (strcasecmp(parm, "nosetuid") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->execmethod = NULL; + return NULL; + } + + if ((strcasecmp(parm, "suexec") != 0) && + (strcasecmp(parm, "X509DN") != 0) && + (strcasecmp(parm, "directory") != 0)) + return "GridsiteExecMethod must be nosetuid, suexec, X509DN or directory"; + + ((mod_gridsite_dir_cfg *) cfg)->execmethod = apr_pstrdup(a->pool, parm); + } + + return NULL; +} + +static const char *mod_gridsite_take2_cmds(cmd_parms *a, void *cfg, + const char *parm1, const char *parm2) +{ + int i; + char *p, *q, buf[APRMAXHOSTLEN + 1] = "localhost"; + + if (strcasecmp(a->cmd->name, "GridSiteUserGroup") == 0) + { + if (!(unixd_config.suexec_enabled)) + return "Using GridSiteUserGroup will " + "require rebuilding Apache with suexec support!"; + + /* NB ap_uname2id/ap_gname2id are NOT thread safe - but OK + as long as not used in .htaccess, just at server start time */ + + ((mod_gridsite_dir_cfg *) cfg)->execugid.uid = ap_uname2id(parm1); + ((mod_gridsite_dir_cfg *) cfg)->execugid.gid = ap_gname2id(parm2); + ((mod_gridsite_dir_cfg *) cfg)->execugid.userdir = 0; + } + else if (strcasecmp(a->cmd->name, "GridSiteDiskMode") == 0) + { + if ((strcasecmp(parm1, "GroupNone" ) != 0) && + (strcasecmp(parm1, "GroupRead" ) != 0) && + (strcasecmp(parm1, "GroupWrite") != 0)) + return "First parameter of GridSiteDiskMode must be " + "GroupNone, GroupRead or GroupWrite!"; + + if ((strcasecmp(parm2, "WorldNone" ) != 0) && + (strcasecmp(parm2, "WorldRead" ) != 0)) + return "Second parameter of GridSiteDiskMode must be " + "WorldNone or WorldRead!"; + + ((mod_gridsite_dir_cfg *) cfg)->diskmode = + APR_UREAD | APR_UWRITE + | ( APR_GREAD * (strcasecmp(parm1, "GroupRead") == 0)) + | ((APR_GREAD | APR_GWRITE) * (strcasecmp(parm1, "GroupWrite") == 0)) + | ((APR_GREAD | APR_WREAD) * (strcasecmp(parm2, "WorldRead") == 0)); + } + else if (strcasecmp(a->cmd->name, "GridSiteCastAlias") == 0) + { + if ((parm1[strlen(parm1)-1] != '/') || (parm2[strlen(parm2)-1] != '/')) + return "GridSiteCastAlias URL and path must end with /"; + + for (i=0; i < GRST_SITECAST_ALIASES; ++i) /* look for free slot */ + { + if (sitecastaliases[i].sitecast_url == NULL) + { + sitecastaliases[i].scheme = apr_pstrdup(a->pool, parm1); + + if (((p = index(sitecastaliases[i].scheme, ':')) == NULL) + || (p[1] != '/') || (p[2] != '/')) + return "GridSiteCastAlias URL must begin with scheme (http/https/gsiftp/...) and ://"; + + *p = '\0'; + ++p; + while (*p == '/') ++p; + + if ((q = index(p, '/')) == NULL) + return "GridSiteCastAlias URL must be of form scheme://domain:port/dirs"; + + *q = '\0'; + + p = index(p, ':'); + if (p == NULL) + { + return "GridSiteCastAlias URL must include the port number"; + } + + if (sscanf(p, ":%d", &(sitecastaliases[i].port)) != 1) + return "Unable to parse numeric port number in GridSiteCastAlias"; + + sitecastaliases[i].sitecast_url = apr_pstrdup(a->pool, parm1); + sitecastaliases[i].local_path = apr_pstrdup(a->pool, parm2); + + if (a->server->server_hostname == NULL) + { + apr_gethostname(buf, APRMAXHOSTLEN + 1, a->pool); + sitecastaliases[i].local_hostname = apr_pstrdup(a->pool, buf); + } + else sitecastaliases[i].local_hostname = apr_pstrdup(a->pool, + a->server->server_hostname); + + break; + } + } + } + + return NULL; +} + +static const char *mod_gridsite_flag_cmds(cmd_parms *a, void *cfg, + int flag) +{ + if (strcasecmp(a->cmd->name, "GridSiteAuth") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->auth = flag; + } + else if (strcasecmp(a->cmd->name, "GridSiteAutoPasscode") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->autopasscode = flag; + } + else if (strcasecmp(a->cmd->name, "GridSiteRequirePasscode") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->requirepasscode = flag; + } + else if (strcasecmp(a->cmd->name, "GridSiteEnvs") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->envs = flag; + } + else if (strcasecmp(a->cmd->name, "GridSiteHtmlFormat") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->format = flag; + } + else if (strcasecmp(a->cmd->name, "GridSiteIndexes") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->indexes = flag; + } + else if (strcasecmp(a->cmd->name, "GridSiteLink") == 0) + { + ((mod_gridsite_dir_cfg *) cfg)->gridsitelink = flag; + } + else if (strcasecmp(a->cmd->name, "GridSiteGridHTTP") == 0) + { +// TODO: return error if try this on non-HTTPS virtual server + + ((mod_gridsite_dir_cfg *) cfg)->gridhttp = flag; + } + + return NULL; +} + +static const command_rec mod_gridsite_cmds[] = +{ +// TODO: need to check and document valid contexts for each command! + + AP_INIT_FLAG("GridSiteAuth", mod_gridsite_flag_cmds, + NULL, OR_FILEINFO, "on or off"), + AP_INIT_FLAG("GridSiteAutoPasscode", mod_gridsite_flag_cmds, + NULL, OR_FILEINFO, "on or off"), + AP_INIT_FLAG("GridSiteRequirePasscode", mod_gridsite_flag_cmds, + NULL, OR_FILEINFO, "on or off"), + AP_INIT_FLAG("GridSiteEnvs", mod_gridsite_flag_cmds, + NULL, OR_FILEINFO, "on or off"), + AP_INIT_FLAG("GridSiteHtmlFormat", mod_gridsite_flag_cmds, + NULL, OR_FILEINFO, "on or off"), + AP_INIT_FLAG("GridSiteIndexes", mod_gridsite_flag_cmds, + NULL, OR_FILEINFO, "on or off"), + AP_INIT_FLAG("GridSiteLink", mod_gridsite_flag_cmds, + NULL, OR_FILEINFO, "on or off"), + + AP_INIT_TAKE1("GridSiteAdminFile", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "Ghost per-directory admin CGI"), + AP_INIT_TAKE1("GridSiteAdminURI", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "URI of real gridsite-admin.cgi"), + AP_INIT_TAKE1("GridSiteHelpURI", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "URI of Website Help pages"), + AP_INIT_TAKE1("GridSiteLoginURI", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "URI prefix of login/logout page"), + AP_INIT_TAKE1("GridSiteDNlists", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "DN Lists directories search path"), + AP_INIT_TAKE1("GridSiteDNlistsURI", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "URI of published DN lists"), + AP_INIT_TAKE1("GridSiteAdminList", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "URI of admin DN List"), + AP_INIT_TAKE1("GridSiteGSIProxyLimit", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "Max level of GSI proxy validity"), + AP_INIT_TAKE1("GridSiteUnzip", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "Absolute path to unzip command"), + + AP_INIT_RAW_ARGS("GridSiteMethods", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "permitted HTTP methods"), + AP_INIT_RAW_ARGS("GridSiteOCSP", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "Set OCSP lookups"), + AP_INIT_RAW_ARGS("GridSiteEditable", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "editable file extensions"), + AP_INIT_TAKE1("GridSiteHeadFile", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "filename of HTML header"), + AP_INIT_TAKE1("GridSiteFootFile", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "filename of HTML footer"), + AP_INIT_TAKE1("GridSiteIndexHeader", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "filename of directory header"), + + AP_INIT_FLAG("GridSiteGridHTTP", mod_gridsite_flag_cmds, + NULL, OR_FILEINFO, "on or off"), + AP_INIT_TAKE1("GridSiteGridHTTPport", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "GridHTTP port"), + AP_INIT_TAKE1("GridSiteSessionsDir", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "directory with GridHTTP passcodes and SSL session creds"), +/* GridSiteOnetimesDir is deprecated in favour of GridSiteSessionsDir */ + AP_INIT_TAKE1("GridSiteOnetimesDir", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "directory with GridHTTP passcodes"), + AP_INIT_TAKE1("GridSiteZoneSlashes", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "number of slashes in passcode cookie paths"), + + AP_INIT_TAKE1("GridSiteCastDNlists", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "DN Lists directories search path for SiteCast"), + AP_INIT_TAKE1("GridSiteCastUniPort", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "UDP port for unicast/replies"), + AP_INIT_TAKE1("GridSiteCastGroup", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "multicast group[:port] to listen for HTCP on"), + AP_INIT_TAKE2("GridSiteCastAlias", mod_gridsite_take2_cmds, + NULL, RSRC_CONF, "URL and local path mapping"), + + AP_INIT_TAKE1("GridSiteACLFormat", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "format to save access control lists in"), + AP_INIT_TAKE1("GridSiteACLPath", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "explicit location of access control file"), + + AP_INIT_TAKE1("GridSiteDelegationURI", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "URI of the delegation service CGI"), + + AP_INIT_TAKE1("GridSiteExecMethod", mod_gridsite_take1_cmds, + NULL, OR_FILEINFO, "execution strategy used by gsexec"), + + AP_INIT_TAKE2("GridSiteUserGroup", mod_gridsite_take2_cmds, + NULL, OR_FILEINFO, + "user and group of gsexec processes in suexec mode"), + + AP_INIT_TAKE2("GridSiteDiskMode", mod_gridsite_take2_cmds, + NULL, OR_FILEINFO, + "group and world file modes for new files/directories"), + + {NULL} +}; + +/* Blank unset these HTTP headers, to prevent injection attacks. + This is run before mod_shib's check_user_id hook, which may + legitimately create such headers. */ + +static int mod_gridsite_check_user_id(request_rec *r) +{ + apr_table_unset(r->headers_in, "User-Distinguished-Name"); +#if 0 + apr_table_unset(r->headers_in, "User-Distinguished-Name-2"); +#endif + apr_table_unset(r->headers_in, "Nist-LoA"); + apr_table_unset(r->headers_in, "LoA"); + apr_table_unset(r->headers_in, "VOMS-Attribute"); + + return DECLINED; /* ie carry on processing request */ +} + +static int mod_gridsite_first_fixups(request_rec *r) +{ + mod_gridsite_dir_cfg *conf; + + if (r->finfo.filetype != APR_DIR) return DECLINED; + + conf = (mod_gridsite_dir_cfg *) + ap_get_module_config(r->per_dir_config, &gridsite_module); + + /* we handle DN Lists as regular files, even if they also match + directory names */ + + if ((conf != NULL) && + (conf->dnlistsuri != NULL) && + (strncmp(r->uri, conf->dnlistsuri, strlen(conf->dnlistsuri)) == 0) && + (strcmp(r->uri, conf->dnlistsuri) != 0)) + { + r->finfo.filetype = APR_REG; + } + + return DECLINED; +} + + +int GRST_get_session_id(SSL *ssl, char *session_id, size_t len) +{ + int i; + SSL_SESSION *session; + + if (((session = SSL_get_session(ssl)) == NULL) || + (session->session_id_length == 0)) return GRST_RET_FAILED; + + if (2 * session->session_id_length + 1 > len) return GRST_RET_FAILED; + + for (i=0; i < (int) session->session_id_length; ++i) + sprintf(&(session_id[i*2]), "%02X", (unsigned char) session->session_id[i]); + + session_id[i*2] = '\0'; + + return GRST_RET_OK; +} + +int GRST_load_ssl_creds(SSL *ssl, conn_rec *conn) +{ + char session_id[(SSL_MAX_SSL_SESSION_ID_LENGTH+1)*2+1], *sessionfile = NULL, + line[512], *p; + apr_file_t *fp = NULL; + int i; + + if (GRST_get_session_id(ssl, session_id, sizeof(session_id)) != GRST_RET_OK) + return GRST_RET_FAILED; + + sessionfile = apr_psprintf(conn->pool, "%s/sslcreds-%s", + ap_server_root_relative(conn->pool, sessionsdir), + session_id); + + if (apr_file_open(&fp, sessionfile, APR_READ, 0, conn->pool) != APR_SUCCESS) + return GRST_RET_FAILED; + + while (apr_file_gets(line, sizeof(line), fp) == APR_SUCCESS) + { + if (sscanf(line, "GRST_CRED_AURI_%d=", &i) == 1) + { + if ((p = index(line, '\n')) != NULL) *p = '\0'; + p = index(line, '='); + + apr_table_setn(conn->notes, + apr_psprintf(conn->pool, "GRST_CRED_AURI_%d", i), + apr_pstrdup(conn->pool, &p[1])); + } + else if (sscanf(line, "GRST_CRED_VALID_%d=", &i) == 1) + { + if ((p = index(line, '\n')) != NULL) *p = '\0'; + p = index(line, '='); + + apr_table_setn(conn->notes, + apr_psprintf(conn->pool, "GRST_CRED_VALID_%d", i), + apr_pstrdup(conn->pool, &p[1])); + } + else if (sscanf(line, "GRST_OCSP_URL_%d=", &i) == 1) + { + if ((p = index(line, '\n')) != NULL) *p = '\0'; + p = index(line, '='); + + apr_table_setn(conn->notes, + apr_psprintf(conn->pool, "GRST_OCSP_URL_%d", i), + apr_pstrdup(conn->pool, &p[1])); + } + } + + apr_file_close(fp); + + /* connection notes created by GRST_save_ssl_creds() are now reloaded */ + apr_table_set(conn->notes, "GRST_save_ssl_creds", "yes"); + + return GRST_RET_OK; +} + +/* + Save result of AURIs and validity info from chain into connection notes, + and write out in an SSL session creds file. +*/ + +void GRST_save_ssl_creds(conn_rec *conn, GRSTx509Chain *grst_chain) +{ + int i, lowest_voms_delegation = 65535; + char *tempfile = NULL, *encoded, + *sessionfile, session_id[(SSL_MAX_SSL_SESSION_ID_LENGTH+1)*2]; + apr_file_t *fp = NULL; + SSL *ssl; + SSLConnRec *sslconn; + GRSTx509Cert *grst_cert = NULL; + + /* check if already done */ + + if ((grst_chain != NULL) && (conn->notes != NULL) && + (apr_table_get(conn->notes, "GRST_save_ssl_creds") != NULL)) return; + + /* we at least need to say we've been run - even if creds not save-able*/ + + apr_table_set(conn->notes, "GRST_save_ssl_creds", "yes"); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, conn->base_server, + "set GRST_save_ssl_creds"); + + sslconn = (SSLConnRec *)ap_get_module_config(conn->conn_config,&ssl_module); + + if ((sslconn != NULL) && + ((ssl = sslconn->ssl) != NULL) && + (GRST_get_session_id(ssl,session_id,sizeof(session_id)) == GRST_RET_OK)) + { + sessionfile = apr_psprintf(conn->pool, "%s/sslcreds-%s", + ap_server_root_relative(conn->pool, sessionsdir), + session_id); + + tempfile = apr_pstrcat(conn->pool, + ap_server_root_relative(conn->pool, sessionsdir), + "/tmp-XXXXXX", NULL); + + if ((tempfile != NULL) && (tempfile[0] != '\0')) + apr_file_mktemp(&fp, tempfile, + APR_CREATE | APR_WRITE | APR_EXCL, conn->pool); + } + + i=0; + + for (grst_cert = grst_chain->firstcert; + grst_cert != NULL; grst_cert = grst_cert->next) + { + if (grst_cert->errors) continue; + + if (grst_cert->type == GRST_CERT_TYPE_VOMS) + { + /* want to record the delegation level + of the last proxy with VOMS attributes */ + + lowest_voms_delegation = grst_cert->delegation; + } + else if ((grst_cert->type == GRST_CERT_TYPE_EEC) || + (grst_cert->type == GRST_CERT_TYPE_PROXY)) + { + encoded = GRSThttpUrlMildencode(grst_cert->dn); + + apr_table_setn(conn->notes, + apr_psprintf(conn->pool, "GRST_CRED_AURI_%d", i), + apr_pstrcat(conn->pool, "dn:", encoded, NULL)); + + if (fp != NULL) apr_file_printf(fp, "GRST_CRED_AURI_%d=dn:%s\n", + i, encoded); + + apr_table_setn(conn->notes, + apr_psprintf(conn->pool, "GRST_CRED_VALID_%d", i), + apr_psprintf(conn->pool, + "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d", + grst_cert->notbefore, + grst_cert->notafter, + grst_cert->delegation, 0)); + + if (fp != NULL) apr_file_printf(fp, + "GRST_CRED_VALID_%d=notbefore=%ld notafter=%ld delegation=%d nist-loa=%d\n", + i, grst_cert->notbefore, + grst_cert->notafter, + grst_cert->delegation, 0); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, conn->base_server, + "store GRST_CRED_AURI_%d=dn:%s", i, encoded); + + free(encoded); + + ++i; + } + } + + for (grst_cert = grst_chain->firstcert; + grst_cert != NULL; grst_cert = grst_cert->next) + { + if (grst_cert->errors) continue; + + if ((grst_cert->type == GRST_CERT_TYPE_VOMS) && + (grst_cert->delegation == lowest_voms_delegation)) + { + /* only export attributes from the last proxy to contain them */ + + encoded = GRSThttpUrlMildencode(grst_cert->value); + + apr_table_setn(conn->notes, + apr_psprintf(conn->pool, "GRST_CRED_AURI_%d", i), + apr_pstrcat(conn->pool, "fqan:", encoded, NULL)); + + if (fp != NULL) apr_file_printf(fp, "GRST_CRED_AURI_%d=fqan:%s\n", + i, encoded); + + apr_table_setn(conn->notes, + apr_psprintf(conn->pool, "GRST_CRED_VALID_%d", i), + apr_psprintf(conn->pool, + "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d", + grst_cert->notbefore, + grst_cert->notafter, + grst_cert->delegation, 0)); + + if (fp != NULL) apr_file_printf(fp, + "GRST_CRED_VALID_%d=notbefore=%ld notafter=%ld delegation=%d nist-loa=%d\n", + i, grst_cert->notbefore, + grst_cert->notafter, + grst_cert->delegation, 0); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, conn->base_server, + "store GRST_CRED_AURI_%d=fqan:%s", i, encoded); + + free(encoded); + + ++i; + } + } + + /* this needs to be merged into grst_x509? */ +#if 0 + if (ocspmodes != NULL) + { + int j; + const char *ex_sn; + char s[80]; + X509 *cert; + X509_EXTENSION *ex; + + for (j=sk_X509_num(certstack)-1; j >= 0; --j) + { + cert = sk_X509_value(certstack, j); + + for (i=0; i < X509_get_ext_count(cert); ++i) + { + ex = X509_get_ext(cert, i); + + OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 0); + + if (strcmp(s, "authorityInfoAccess") == 0) /* OCSP */ + { + apr_table_setn(conn->notes, "GRST_OCSP_URL", + (const char *) X509_EXTENSION_get_data(ex)); + + /* strategy is to remove what has been checked, + for this connnection */ + apr_table_set(conn->notes, "GRST_OCSP_UNCHECKED", + ocspmodes); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, conn->base_server, + "store GRST_OCSP_URL_%d=%s", i, X509_EXTENSION_get_data(ex)); + + if (fp != NULL) apr_file_printf(fp, "GRST_OCSP_URL_%d=%s\n", + i, X509_EXTENSION_get_data(ex)); + } + } + } + } +#endif + /* end of bit that needs to go into grst_x509 */ + + if (fp != NULL) + { + apr_file_close(fp); + apr_file_rename(tempfile, sessionfile, conn->pool); + } +} + +static char *get_aclpath_component(request_rec *r, int n) +/* + Get the nth component of REQUEST_URI, or component 0 + which is the server name. + +*/ +{ + int ii, i, nn; + + if (n == 0) return r->server->server_hostname; + + if (r->uri == NULL) return NULL; /* some kind of internal error? */ + + i = 1; /* start of first component */ + nn = 1; + + for (ii=1; r->uri[ii] != '\0'; ++ii) /* look for this component */ + { + if (r->uri[ii] == '/') /* end of a component */ + { + if (nn == n) break; + + ++nn; + i = ii + 1; + } + else if ((r->uri[ii] == '.') && (r->uri[ii+1] == '.')) + { + return NULL; /* can this happen? dont allow anyway */ + } + } + + if (nn != n) return NULL; /* no component for this number */ + + return apr_psprintf(r->pool, "%.*s", ii - i, &(r->uri[i])); +} + +static char *make_aclpath(request_rec *r, char *format) +{ + int i, n; + char *formatted, *p; + + formatted = apr_pstrdup(r->pool, format); + + while (1) + { + for (i=0; (formatted[i] != '\0') && (formatted[i] != '%'); ++i) ; + + if (formatted[i] == '\0') break; + + if ((formatted[i] == '%') && (formatted[i+1] == '%')) + { + ++i; + continue; + } + + if (sscanf(&formatted[i+1], "%d", &n) != 1) + { + return NULL; /* not %% or %0,%1,... */ + } + + formatted[i] = '\0'; + + for (i++; isdigit(formatted[i]); ++i) ; + + if ((p = get_aclpath_component(r, n)) == NULL) return NULL; + + formatted = apr_pstrcat(r->pool, formatted, p, &formatted[i],NULL); + i += strlen(p); + } + + return ap_server_root_relative(r->pool, formatted); +} + +static int mod_gridsite_perm_handler(request_rec *r) +/* + Do authentication/authorization here rather than in the normal module + auth functions since the results of mod_ssl are available. + + We also publish environment variables here if requested by GridSiteEnv. +*/ +{ + int retcode = DECLINED, i, j, n, file_is_acl = 0, cc_delegation, + destination_is_acl = 0, ishttps = 0, nist_loa, delegation, + from_cookie = 0; + char *p, *q, envname1[30], envname2[30], + *grst_cred_auri_0 = NULL, *dir_path, + *remotehost, *grst_cred_auri_i, *cookies, *file, + *cookiefile, oneline[1025], *decoded, + *destination = NULL, *destination_uri = NULL, *querytmp, + *destination_prefix = NULL, *destination_translated = NULL, + *aclpath = NULL, *grst_cred_valid_0 = NULL, *grst_cred_valid_i, + *gridauthpasscode = NULL; + const char *content_type; + time_t notbefore, notafter; + apr_table_t *env; + apr_finfo_t cookiefile_info; + apr_file_t *fp; + request_rec *destreq; + GRSTgaclCred *cred = NULL, *cred_0 = NULL; + GRSTgaclUser *user = NULL; + GRSTgaclPerm perm = GRST_PERM_NONE, destination_perm = GRST_PERM_NONE; + GRSTgaclAcl *acl = NULL; + mod_gridsite_dir_cfg *cfg; + SSLConnRec *sslconn; + + cfg = (mod_gridsite_dir_cfg *) + ap_get_module_config(r->per_dir_config, &gridsite_module); + + if (cfg == NULL) return DECLINED; + + if ((cfg->auth == 0) && (cfg->envs == 0)) + return DECLINED; /* if not turned on, look invisible */ + + env = r->subprocess_env; + + p = (char *) apr_table_get(env, "HTTPS"); + if ((p != NULL) && (strcmp(p, "on") == 0)) ishttps = 1; + + delegation = ((mod_gridsite_dir_cfg *) cfg)->gsiproxylimit + 1; + + /* reload per-connection (SSL) cred variables? (TO CONNECTION) */ + + sslconn = (SSLConnRec *) ap_get_module_config(r->connection->conn_config, + &ssl_module); + if ((user == NULL) && + (sslconn != NULL) && + (sslconn->ssl != NULL) && + (sslconn->ssl->session != NULL) && + (r->connection->notes != NULL) && + (apr_table_get(r->connection->notes, "GRST_save_ssl_creds") == NULL)) + { + if (GRST_load_ssl_creds(sslconn->ssl, r->connection) == GRST_RET_OK) + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Restored SSL session data from session cache file"); + } + + /* look for GRIDHTTP_PASSCODE in QUERY_STRING ie after ? */ + + if ((r->parsed_uri.query != NULL) && (r->parsed_uri.query[0] != '\0')) + { + querytmp = apr_pstrcat(r->pool,"&",r->parsed_uri.query,"&",NULL); + + gridauthpasscode = strstr(querytmp, "&GRIDHTTP_PASSCODE="); + if (gridauthpasscode != NULL) + { + gridauthpasscode = &gridauthpasscode[19]; + + for (p = gridauthpasscode; (*p != '\0') && (*p != '&'); ++p) + if (!isalnum(*p)) *p = '\0'; + } + } + + /* then look for GRIDHTTP_PASSCODE cookie */ + + if ((gridauthpasscode == NULL) && + ((q = (char *) apr_table_get(r->headers_in, "Cookie")) != NULL)) + { + cookies = apr_pstrcat(r->pool, " ", q, NULL); + gridauthpasscode = strstr(cookies, " GRIDHTTP_PASSCODE="); + + if (gridauthpasscode != NULL) + { + gridauthpasscode = &gridauthpasscode[19]; + + for (p = gridauthpasscode; + (*p != '\0') && (*p != ';'); ++p) + if (!isalnum(*p)) *p = '\0'; + + if (gridauthpasscode[0] != '\0') from_cookie = 1; + } + } + + /* try to load user structure from passcode file */ + + if ((user == NULL) && + (gridauthpasscode != NULL) && + (gridauthpasscode[0] != '\0')) + { + cookiefile = apr_psprintf(r->pool, "%s/passcode-%s", + ap_server_root_relative(r->pool, + sessionsdir), + gridauthpasscode); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Opening GridHTTP passcode file %s", cookiefile); + + if ((apr_stat(&cookiefile_info, cookiefile, + APR_FINFO_TYPE, r->pool) == APR_SUCCESS) && + (cookiefile_info.filetype == APR_REG) && + (apr_file_open(&fp, cookiefile, APR_READ, 0, r->pool) + == APR_SUCCESS)) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Reading GridHTTP passcode file %s", cookiefile); + + i = -1; + cred = NULL; + + while (apr_file_gets(oneline, + sizeof(oneline), fp) == APR_SUCCESS) + { + p = index(oneline, '\n'); + if (p != NULL) *p = '\0'; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "%s: %s", cookiefile, oneline); + + if ((strncmp(oneline, "expires=", 8) == 0) && + (apr_time_from_sec(atoll(&oneline[8])) < + apr_time_now())) + { + if (user != NULL) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + r->server, "Bad expires"); + GRSTgaclUserFree(user); + user = NULL; + } + break; + } + else if ((strncmp(oneline, "domain=", 7) == 0) && + (strcmp(&oneline[7], r->hostname) != 0)) + { + if (user != NULL) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + r->server, "Bad domain/host"); + GRSTgaclUserFree(user); + user = NULL; + } + break; + } + else if (strncmp(oneline, "path=", 5) == 0) + { + /* count number of slashes in Request URI */ + + for (n=0,p=r->uri; *p != '\0'; ++p) + if (*p == '/') ++n; + + /* if too few slashes or path mismatch, then stop */ + + if ((n < ((mod_gridsite_dir_cfg *) cfg)->zoneslashes) || + (strncmp(&oneline[5], r->uri, strlen(&oneline[5])) != 0)) + { + if (user != NULL) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + r->server, "Bad path"); + GRSTgaclUserFree(user); + user = NULL; + } + + break; + } + } + else if ((sscanf(oneline,"GRST_CRED_AURI_%d=",&j) == 1) + && (j == i+1) + && ((p = index(oneline, '=')) != NULL)) + { + cred = GRSTgaclCredCreate(&p[1], NULL); + + if (cred != NULL) ++i; + + if (user == NULL) user = GRSTgaclUserNew(cred); + else GRSTgaclUserAddCred(user, cred); + } + else if ((sscanf(oneline,"GRST_CRED_VALID_%d=",&j) == 1) + && (j == i) + && ((p = index(oneline, '=')) != NULL) + && (sscanf(&p[1], + "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d", + ¬before, ¬after, &delegation, + &nist_loa) == 4)) + { + if ((i == 0) && + (delegation > ((mod_gridsite_dir_cfg *) cfg)->gsiproxylimit)) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, + r->server, "Bad delegation"); + if (user != NULL) GRSTgaclUserFree(user); + user = NULL; + break; + } + + GRSTgaclCredSetNotBefore( cred, notbefore); + GRSTgaclCredSetNotAfter( cred, notafter); + GRSTgaclCredSetDelegation(cred, delegation); + + if (delegation == 0) GRSTgaclCredSetNistLoa(cred, 3); + else GRSTgaclCredSetNistLoa(cred, 2); + } + } + + apr_file_close(fp); + + /* delete passcode file if used over HTTP not HTTPS */ + if (!ishttps) remove(cookiefile); + + /* if successful and we got passcode from a cookie, then + we put cookie value into environment variables, so + can be used for double-submit cookie CSRF protection */ + + if ((user != NULL) && + from_cookie && + ((mod_gridsite_dir_cfg *) cfg)->envs) + apr_table_setn(env, "GRST_PASSCODE_COOKIE", + gridauthpasscode); + } + } + + /* + if not succeeded from passcode file, try from connection notes + if a GSI Proxy or have GridSiteAutoPasscode on (the default) + If GridSiteAutoPasscode off and GridSiteRequirePasscode on + then interactive websites must use a login script to make passcode + and file instead. + */ + + if ((user == NULL) && + (r->connection->notes != NULL) && + ((grst_cred_auri_0 = (char *) + apr_table_get(r->connection->notes, "GRST_CRED_AURI_0")) != NULL) && + (strncmp(grst_cred_auri_0, "dn:", 3) == 0) && + ((grst_cred_valid_0 = (char *) + apr_table_get(r->connection->notes, "GRST_CRED_VALID_0")) != NULL) && + (sscanf(grst_cred_valid_0, + "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d", + ¬before, ¬after, &delegation, &nist_loa) == 4) && + (delegation <= ((mod_gridsite_dir_cfg *) cfg)->gsiproxylimit) && + ((delegation > 0) || + ((mod_gridsite_dir_cfg *) cfg)->autopasscode || + !(((mod_gridsite_dir_cfg *) cfg)->requirepasscode))) + { + cred_0 = GRSTgaclCredCreate(grst_cred_auri_0, NULL); + if (cred_0 != NULL) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Using identity %s from SSL/TLS", grst_cred_auri_0); + + GRSTgaclCredSetNotBefore( cred_0, notbefore); + GRSTgaclCredSetNotAfter( cred_0, notafter); + GRSTgaclCredSetDelegation(cred_0, delegation); + + if (delegation == 0) GRSTgaclCredSetNistLoa(cred_0, 3); + else GRSTgaclCredSetNistLoa(cred_0, 2); + + user = GRSTgaclUserNew(cred_0); + + /* check for VOMS etc in GRST_CRED_AURI_i too */ + + for (i=1; ; ++i) + { + snprintf(envname1, sizeof(envname1), "GRST_CRED_AURI_%d", i); + snprintf(envname2, sizeof(envname2), "GRST_CRED_VALID_%d", i); + + if ((grst_cred_auri_i = (char *) + apr_table_get(r->connection->notes,envname1)) && + (grst_cred_valid_i = (char *) + apr_table_get(r->connection->notes,envname2))) + { + cred = GRSTgaclCredCreate(grst_cred_auri_i, NULL); + if (cred != NULL) + { + notbefore = 0; + notafter = 0; + delegation = 0; + nist_loa = 0; + + sscanf(grst_cred_valid_i, + "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d", + ¬before, ¬after, &delegation, &nist_loa); + + GRSTgaclCredSetNotBefore( cred, notbefore); + GRSTgaclCredSetNotAfter( cred, notafter); + GRSTgaclCredSetDelegation(cred, delegation); + GRSTgaclCredSetDelegation(cred, nist_loa); + + GRSTgaclUserAddCred(user, cred); + } + } + else break; /* GRST_CRED_AURI_i are numbered consecutively */ + } + } + + /* if user from SSL ok and not a GSI Proxy and have + GridSiteAutoPasscode on we create passcode and file + automatically, and return cookie to client. + (if GridSiteAutoPasscode off then the site must use + a login script to make passcode and file instead.) */ + + if (((mod_gridsite_dir_cfg *) cfg)->autopasscode && + (user != NULL) && + (GRSTgaclCredGetDelegation(cred_0) == 0)) + { + n = 0; /* number of slashes seen */ + + for (i=0; r->uri[i] != '\0'; ++i) + { + if (n >= ((mod_gridsite_dir_cfg *) cfg)->zoneslashes) break; + + if (r->uri[i] == '/') ++n; + } + + if ((n >= ((mod_gridsite_dir_cfg *) cfg)->zoneslashes) + && (i > 0)) + { + p = apr_pstrdup(r->pool, r->uri); + p[i] = '\0'; + + /* try to generate passcode and make passcode file */ + gridauthpasscode = make_passcode_file(r, cfg, p, 0); + + if (gridauthpasscode != NULL) + { + apr_table_add(r->headers_out, + apr_pstrdup(r->pool, "Set-Cookie"), + apr_psprintf(r->pool, + "GRIDHTTP_PASSCODE=%s; " + "domain=%s; " + "path=%s; " + "secure", gridauthpasscode, r->hostname, p)); + } + } + } + } + + /* + GridSite passcode files don't include groups, IP or DNS so we add + them last so they're not written to passcode files by GridSite. + + (site-supplied login scripts might create passcode files with + optional or additional AURIs. for example, valid roles selected by + the user on the login page.) + + */ + + /* first add groups from DN lists - ie non-optional attributes */ + + if ((user != NULL) && ((mod_gridsite_dir_cfg *) cfg)->dnlists) + GRSTgaclUserLoadDNlists(user, ((mod_gridsite_dir_cfg *) cfg)->dnlists); + + /* then add DNS credential */ + + remotehost = (char *) ap_get_remote_host(r->connection, + r->per_dir_config, REMOTE_DOUBLE_REV, NULL); + if ((remotehost != NULL) && (*remotehost != '\0')) + { + cred = GRSTgaclCredCreate("dns:", remotehost); + GRSTgaclCredSetNotAfter(cred, GRST_MAX_TIME_T); + + if (user == NULL) user = GRSTgaclUserNew(cred); + else GRSTgaclUserAddCred(user, cred); + } + + /* finally add IP credential */ + + if (r->connection->remote_ip) + { + cred = GRSTgaclCredCreate("ip:", r->connection->remote_ip); + GRSTgaclCredSetNotAfter(cred, GRST_MAX_TIME_T); + + if (user == NULL) user = GRSTgaclUserNew(cred); + else GRSTgaclUserAddCred(user, cred); + } + + /* write contents of user to per-request environment variables */ + + if (((mod_gridsite_dir_cfg *) cfg)->envs && (user != NULL)) + { + cred = user->firstcred; + + /* old-style Compact Credentials have the same delegation level + for all credentials. eg Using EEC delegation=0; using 1st GSI + Proxy then delegation=1 for X509USER _and_ GSIPROXY credentials. + So we remember the delegation level of any X509USER here */ + if (cred != NULL) cc_delegation = cred->delegation; + + for (i=0; (cred != NULL) && (cred->auri != NULL); ++i) + { + if (strncmp(cred->auri, "dn:", 3) == 0) + { + decoded = GRSThttpUrlDecode(&(cred->auri[3])); + apr_table_setn(env, + apr_psprintf(r->pool, "GRST_CRED_%d", i), + apr_psprintf(r->pool, + "%s %ld %ld %d %s", + (i==0) ? "X509USER" : "GSIPROXY", + cred->notbefore, + cred->notafter, + cc_delegation, + decoded)); + free(decoded); + } + else if (strncmp(cred->auri, "fqan:", 5) == 0) + { + decoded = GRSThttpUrlDecode(&(cred->auri[5])); + apr_table_setn(env, + apr_psprintf(r->pool, "GRST_CRED_%d", i), + apr_psprintf(r->pool, + "VOMS %ld %ld 0 %s", + notbefore, notafter, + decoded)); + free(decoded); + } + + apr_table_setn(env, + apr_psprintf(r->pool, "GRST_CRED_AURI_%d", i), + apr_pstrdup(r->pool, cred->auri)); + + apr_table_setn(env, + apr_psprintf(r->pool, "GRST_CRED_VALID_%d", i), + apr_psprintf(r->pool, + "notbefore=%ld notafter=%ld delegation=%d nist-loa=%d", + cred->notbefore, + cred->notafter, + cred->delegation, + cred->nist_loa)); + + cred = cred->next; + } + } + + /* check for Destination: header and evaluate if present */ + + if ((destination = (char *) apr_table_get(r->headers_in, + "Destination")) != NULL) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Destination header found, value=%s", destination); + + destination_prefix = apr_psprintf(r->pool, "https://%s:%d/", + r->server->server_hostname, (int) r->server->port); + + if (strncmp(destination_prefix, destination, + strlen(destination_prefix)) == 0) + destination_uri = &destination[strlen(destination_prefix)-1]; + else if ((int) r->server->port == 443) + { + destination_prefix = apr_psprintf(r->pool, "https://%s/", + r->server->server_hostname); + + if (strncmp(destination_prefix, destination, + strlen(destination_prefix)) == 0) + destination_uri = &destination[strlen(destination_prefix)-1]; + } + + if (destination_uri != NULL) + { + destreq = ap_sub_req_method_uri("GET", destination_uri, r, NULL); + + if ((destreq != NULL) && (destreq->filename != NULL) + && (destreq->path_info != NULL)) + { + destination_translated = apr_pstrcat(r->pool, + destreq->filename, destreq->path_info, NULL); + + apr_table_setn(r->notes, "GRST_DESTINATION_TRANSLATED", + destination_translated); + + if (((mod_gridsite_dir_cfg *) cfg)->envs) + apr_table_setn(env, "GRST_DESTINATION_TRANSLATED", + destination_translated); + + p = rindex(destination_translated, '/'); + if ((p != NULL) && (strcmp(&p[1], GRST_ACL_FILE) == 0)) + destination_is_acl = 1; + } + } + } + + if ((((mod_gridsite_dir_cfg *) cfg)->adminlist != NULL) && (user != NULL)) + { + cred = user->firstcred; + + while ((cred != NULL) && (cred->auri != NULL)) + { + if (strcmp(((mod_gridsite_dir_cfg *) cfg)->adminlist, + cred->auri) == 0) + { + perm = GRST_PERM_ALL; + if (destination_translated != NULL) + destination_perm = GRST_PERM_ALL; + break; + } + + cred = cred->next; + } + } + + if (perm != GRST_PERM_ALL) /* cannot improve on perfection... */ + { + if (((mod_gridsite_dir_cfg *) cfg)->aclpath != NULL) + { + aclpath = make_aclpath(r,((mod_gridsite_dir_cfg *) cfg)->aclpath); + + if (aclpath != NULL) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Examine ACL file %s (from ACL path %s)", + aclpath, ((mod_gridsite_dir_cfg *) cfg)->aclpath); + + acl = GRSTgaclAclLoadFile(aclpath); + } + else ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Failed to make ACL file from ACL path %s, URI %s)", + ((mod_gridsite_dir_cfg *) cfg)->aclpath, r->uri); + } + else if ((((mod_gridsite_dir_cfg *) cfg)->dnlistsuri == NULL) || + (strncmp(r->uri, + ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri, + strlen(((mod_gridsite_dir_cfg *) cfg)->dnlistsuri)) != 0) || + (strlen(r->uri) <= strlen(((mod_gridsite_dir_cfg *) cfg)->dnlistsuri))) + { + acl = GRSTgaclAclLoadforFile(r->filename); + } + + if (acl != NULL) perm = GRSTgaclAclTestUser(acl, user); + GRSTgaclAclFree(acl); + + if (destination_translated != NULL) + { + acl = GRSTgaclAclLoadforFile(destination_translated); + if (acl != NULL) destination_perm = GRSTgaclAclTestUser(acl, user); + GRSTgaclAclFree(acl); + + apr_table_setn(r->notes, "GRST_DESTINATION_PERM", + apr_psprintf(r->pool, "%d", destination_perm)); + + if (((mod_gridsite_dir_cfg *) cfg)->envs) + apr_table_setn(env, "GRST_DESTINATION_PERM", + apr_psprintf(r->pool, "%d", destination_perm)); + } + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "After GACL/Onetime evaluation, GRST_PERM=%d", perm); + + /* add permission and first AURI to request notes */ + + apr_table_setn(r->notes, "GRST_PERM", apr_psprintf(r->pool, "%d", perm)); + + cred = NULL; + if (user) + cred = user->firstcred; + if ((cred != NULL) && (strncmp(cred->auri, "dn:", 3) == 0)) + { + apr_table_setn(r->notes, "GRST_CRED_AURI_0", + apr_psprintf(r->pool, "%s", cred->auri)); + } + + + if (((mod_gridsite_dir_cfg *) cfg)->envs) + { + /* copy any credentials from (SSL) connection to environment */ + + for (i=0; ; ++i) + { + snprintf(envname1, sizeof(envname1), "GRST_CRED_AURI_%d", i); + snprintf(envname2, sizeof(envname2), "GRST_CRED_VALID_%d", i); + + if ((grst_cred_auri_i = (char *) + apr_table_get(r->connection->notes,envname1)) && + (grst_cred_valid_i = (char *) + apr_table_get(r->connection->notes,envname2))) + { + apr_table_setn(env, + apr_psprintf(r->pool, "GRST_CONN_AURI_%d", i), + apr_pstrdup(r->pool, grst_cred_auri_i)); + + apr_table_setn(env, + apr_psprintf(r->pool, "GRST_CONN_VALID_%d", i), + apr_pstrdup(r->pool, grst_cred_valid_i)); + } + else break; + } + + apr_table_setn(env, "GRST_PERM", apr_psprintf(r->pool, "%d", perm)); + + if (((mod_gridsite_dir_cfg *) cfg)->requirepasscode == 0) + apr_table_set(env, "GRST_REQUIRE_PASSCODE", "off"); + else apr_table_set(env, "GRST_REQUIRE_PASSCODE", "on"); + + if (((dir_path = apr_pstrdup(r->pool, r->filename)) != NULL) && + ((p = rindex(dir_path, '/')) != NULL)) + { + *p = '\0'; + apr_table_setn(env, "GRST_DIR_PATH", dir_path); + } + + if (((mod_gridsite_dir_cfg *) cfg)->helpuri != NULL) + apr_table_setn(env, "GRST_HELP_URI", + ((mod_gridsite_dir_cfg *) cfg)->helpuri); + + if (((mod_gridsite_dir_cfg *) cfg)->loginuri != NULL) + apr_table_setn(env, "GRST_LOGIN_URI", + ((mod_gridsite_dir_cfg *) cfg)->loginuri); + + if (((mod_gridsite_dir_cfg *) cfg)->adminfile != NULL) + apr_table_setn(env, "GRST_ADMIN_FILE", + ((mod_gridsite_dir_cfg *) cfg)->adminfile); + + if (((mod_gridsite_dir_cfg *) cfg)->editable != NULL) + apr_table_setn(env, "GRST_EDITABLE", + ((mod_gridsite_dir_cfg *) cfg)->editable); + + if (((mod_gridsite_dir_cfg *) cfg)->headfile != NULL) + apr_table_setn(env, "GRST_HEAD_FILE", + ((mod_gridsite_dir_cfg *) cfg)->headfile); + + if (((mod_gridsite_dir_cfg *) cfg)->footfile != NULL) + apr_table_setn(env, "GRST_FOOT_FILE", + ((mod_gridsite_dir_cfg *) cfg)->footfile); + + if (((mod_gridsite_dir_cfg *) cfg)->dnlists != NULL) + apr_table_setn(env, "GRST_DN_LISTS", + ((mod_gridsite_dir_cfg *) cfg)->dnlists); + + if (((mod_gridsite_dir_cfg *) cfg)->dnlistsuri != NULL) + apr_table_setn(env, "GRST_DN_LISTS_URI", + ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri); + + if (((mod_gridsite_dir_cfg *) cfg)->adminlist != NULL) + apr_table_setn(env, "GRST_ADMIN_LIST", + ((mod_gridsite_dir_cfg *) cfg)->adminlist); + + apr_table_setn(env, "GRST_GSIPROXY_LIMIT", + apr_psprintf(r->pool, "%d", + ((mod_gridsite_dir_cfg *)cfg)->gsiproxylimit)); + + if (((mod_gridsite_dir_cfg *) cfg)->unzip != NULL) + apr_table_setn(env, "GRST_UNZIP", + ((mod_gridsite_dir_cfg *) cfg)->unzip); + + if (!(((mod_gridsite_dir_cfg *) cfg)->gridsitelink)) + apr_table_setn(env, "GRST_NO_LINK", "1"); + + if (((mod_gridsite_dir_cfg *) cfg)->aclformat != NULL) + apr_table_setn(env, "GRST_ACL_FORMAT", + ((mod_gridsite_dir_cfg *) cfg)->aclformat); + + if (((mod_gridsite_dir_cfg *) cfg)->aclpath != NULL) + apr_table_setn(env, "GRST_ACL_PATH", + ((mod_gridsite_dir_cfg *) cfg)->aclpath); + + if (((mod_gridsite_dir_cfg *) cfg)->delegationuri != NULL) + apr_table_setn(env, "GRST_DELEGATION_URI", + ((mod_gridsite_dir_cfg *) cfg)->delegationuri); + + + if (((mod_gridsite_dir_cfg *) cfg)->execmethod != NULL) + { + apr_table_setn(env, "GRST_EXEC_METHOD", + ((mod_gridsite_dir_cfg *) cfg)->execmethod); + + if ((strcasecmp(((mod_gridsite_dir_cfg *) cfg)->execmethod, + "directory") == 0) && (r->filename != NULL)) + { + if ((r->content_type != NULL) && + (strcmp(r->content_type, DIR_MAGIC_TYPE) == 0)) + apr_table_setn(env, "GRST_EXEC_DIRECTORY", r->filename); + else + { + file = apr_pstrdup(r->pool, r->filename); + p = rindex(file, '/'); + if (p != NULL) + { + *p = '\0'; + apr_table_setn(env, "GRST_EXEC_DIRECTORY", file); + } + } + } + } + + apr_table_setn(env, "GRST_DISK_MODE", + apr_psprintf(r->pool, "0x%04x", + ((mod_gridsite_dir_cfg *)cfg)->diskmode)); + } + + if (((mod_gridsite_dir_cfg *) cfg)->auth) + { + /* *** Check HTTP method to decide which perm bits to check *** */ + + if ((r->filename != NULL) && + ((p = rindex(r->filename, '/')) != NULL) && + (strcmp(&p[1], GRST_ACL_FILE) == 0)) file_is_acl = 1; + + content_type = r->content_type; + if ((content_type != NULL) && + (strcmp(content_type, DIR_MAGIC_TYPE) == 0) && + (((mod_gridsite_dir_cfg *) cfg)->dnlistsuri != NULL) && + (strncmp(r->uri, + ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri, + strlen(((mod_gridsite_dir_cfg *) cfg)->dnlistsuri)) == 0) && + (strlen(r->uri) > strlen(((mod_gridsite_dir_cfg *) cfg)->dnlistsuri))) + content_type = "text/html"; + + if ( GRSTgaclPermHasNone(perm) || + + /* first two M_GET conditions make the subtle distinction + between .../ that maps to .../index.html (governed by + Read perm) or to dir list (governed by List perm); + third M_GET condition deals with typeless CGI requests */ + + ((r->method_number == M_GET) && + !GRSTgaclPermHasRead(perm) && + (content_type != NULL) && + (strcmp(content_type, DIR_MAGIC_TYPE) != 0)) || + + ((r->method_number == M_GET) && + !GRSTgaclPermHasList(perm) && + (content_type != NULL) && + (strcmp(content_type, DIR_MAGIC_TYPE) == 0)) || + + ((r->method_number == M_GET) && + !GRSTgaclPermHasRead(perm) && + (content_type == NULL)) || + + ((r->method_number == M_POST) && !GRSTgaclPermHasRead(perm) ) || + + (((r->method_number == M_PUT) || + (r->method_number == M_DELETE)) && + !GRSTgaclPermHasWrite(perm) && !file_is_acl) || + + ((r->method_number == M_MOVE) && + ((!GRSTgaclPermHasWrite(perm) && !file_is_acl) || + (!GRSTgaclPermHasAdmin(perm) && file_is_acl) || + (!GRSTgaclPermHasWrite(destination_perm) + && !destination_is_acl) || + (!GRSTgaclPermHasAdmin(destination_perm) + && destination_is_acl)) ) || + + (((r->method_number == M_PUT) || + (r->method_number == M_DELETE)) && + !GRSTgaclPermHasAdmin(perm) && file_is_acl) || + + /* for WebDAV/Subversion */ + + (((r->method_number == M_PROPFIND) || + (r->method_number == M_REPORT)) && + !GRSTgaclPermHasRead(perm)) || + + (((r->method_number == M_CHECKOUT) || + (r->method_number == M_MERGE) || + (r->method_number == M_MKACTIVITY) || + (r->method_number == M_MKCOL) || + (r->method_number == M_LOCK) || + (r->method_number == M_UNLOCK)) && + !GRSTgaclPermHasWrite(perm)) + + ) retcode = HTTP_FORBIDDEN; + } + + if (user != NULL) GRSTgaclUserFree(user); + + return retcode; +} + +int GRST_callback_SSLVerify_wrapper(int ok, X509_STORE_CTX *ctx) +{ + SSL *ssl = (SSL *) X509_STORE_CTX_get_app_data(ctx); + conn_rec *conn = (conn_rec *) SSL_get_app_data(ssl); + int errnum = X509_STORE_CTX_get_error(ctx); + int errdepth = X509_STORE_CTX_get_error_depth(ctx); + int returned_ok; + STACK_OF(X509) *certstack; + GRSTx509Chain *grst_chain; + + /* Call caNl callback directly */ + returned_ok = canl_direct_pv_clb(NULL, ctx, ok); + + /* in case ssl_callback_SSLVerify changed it */ + errnum = X509_STORE_CTX_get_error(ctx); + + if ((errdepth == 0) && (errnum == X509_V_OK)) + /* + * We've now got the last certificate - the identity being used for + * this connection. At this point we check the whole chain for valid + * CAs or, failing that, GSI-proxy validity using GRSTx509CheckChain. + */ + { + certstack = (STACK_OF(X509) *) X509_STORE_CTX_get_chain(ctx); + + errnum = GRSTx509ChainLoad(&grst_chain, certstack, NULL, + "/etc/grid-security/certificates", + "/etc/grid-security/vomsdir"); + + if (returned_ok) + /* Put result of GRSTx509ChainLoadCheck into connection notes */ + GRST_save_ssl_creds(conn, grst_chain); + if (grst_chain) + GRSTx509ChainFree(grst_chain); + } + + return returned_ok; +} + +void sitecast_handle_NOP_request(server_rec *main_server, + GRSThtcpMessage *htcp_mesg, int s, + struct sockaddr *client_addr_ptr, + socklen_t client_addr_len) +{ + int outbuf_len; + char *outbuf; + char host[INET6_ADDRSTRLEN]; + char serv[8]; + + if (GRSThtcpNOPresponseMake(&outbuf, &outbuf_len, + htcp_mesg->trans_id) == GRST_RET_OK) + { + getnameinfo(client_addr_ptr, client_addr_len, + host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast sends NOP response to %s:%s", + host, serv); + + sendto(s, outbuf, outbuf_len, 0, + client_addr_ptr, client_addr_len); + + free(outbuf); + } +} + +void sitecast_handle_TST_GET(server_rec *main_server, + GRSThtcpMessage *htcp_mesg, int s, + struct sockaddr *client_addr_ptr, + socklen_t client_addr_len) +{ + int outbuf_len, ialias; + char *filename, *outbuf, *location; + struct stat statbuf; + char host[INET6_ADDRSTRLEN]; + char serv[8]; + + getnameinfo(client_addr_ptr, client_addr_len, + host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast responder received TST GET with uri %s", + htcp_mesg->uri->text, GRSThtcpCountstrLen(htcp_mesg->uri)); + + /* find if any GridSiteCastAlias lines match */ + + for (ialias=0; ialias < GRST_SITECAST_ALIASES ; ++ialias) + { + if (sitecastaliases[ialias].sitecast_url == NULL) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast responder does not handle %*s requested by %s:%s", + GRSThtcpCountstrLen(htcp_mesg->uri), + htcp_mesg->uri->text, + host, serv); + + return; /* no match */ + } + + if ((strlen(sitecastaliases[ialias].sitecast_url) + <= GRSThtcpCountstrLen(htcp_mesg->uri)) && + (strncmp(sitecastaliases[ialias].sitecast_url, + htcp_mesg->uri->text, + strlen(sitecastaliases[ialias].sitecast_url))==0)) break; + } + + if (ialias == GRST_SITECAST_ALIASES) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast responder does not handle %*s requested by %s:%s", + GRSThtcpCountstrLen(htcp_mesg->uri), + htcp_mesg->uri->text, + host, serv); + + return; /* no match */ + } + + /* convert URL to filename, using alias mapping */ + + asprintf(&filename, "%s%*s", + sitecastaliases[ialias].local_path, + GRSThtcpCountstrLen(htcp_mesg->uri) + - strlen(sitecastaliases[ialias].sitecast_url), + &(htcp_mesg->uri->text[strlen(sitecastaliases[ialias].sitecast_url)]) ); + + if (stat(filename, &statbuf) == 0) /* found file */ + { + asprintf(&location, "Location: %s://%s:%d/%s\r\n", + sitecastaliases[ialias].scheme, + sitecastaliases[ialias].local_hostname, + sitecastaliases[ialias].port, + &(htcp_mesg->uri->text[strlen(sitecastaliases[ialias].sitecast_url)]) ); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast finds %*s at %s, redirects with %s", + GRSThtcpCountstrLen(htcp_mesg->uri), + htcp_mesg->uri->text, filename, location); + + if (GRSThtcpTSTresponseMake(&outbuf, &outbuf_len, + htcp_mesg->trans_id, + location, "", "") == GRST_RET_OK) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast sends TST response to %s:%s", + host, serv); + + sendto(s, outbuf, outbuf_len, 0, + client_addr_ptr, client_addr_len); + + free(outbuf); + } + + free(location); + } + else ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast does not find %*s (would be at %s)", + GRSThtcpCountstrLen(htcp_mesg->uri), + htcp_mesg->uri->text, filename); + + + free(filename); +} + +void sitecast_handle_request(server_rec *main_server, + char *reqbuf, int reqbuf_len, + int s, + struct sockaddr *client_addr_ptr, + socklen_t client_addr_len) +{ + GRSThtcpMessage htcp_mesg; + char host[INET6_ADDRSTRLEN]; + char serv[8]; + + getnameinfo(client_addr_ptr, client_addr_len, + host, sizeof(host), + serv, sizeof(serv), NI_NUMERICHOST); + if (GRSThtcpMessageParse(&htcp_mesg,reqbuf,reqbuf_len) != GRST_RET_OK) + { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "SiteCast responder rejects format of UDP message from %s:%s", + host, serv); + return; + } + + if (htcp_mesg.rr != 0) /* ignore HTCP responses: we just do requests */ + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast responder ignores HTCP response from %s:%s", + host, serv); + return; + } + + if (htcp_mesg.opcode == GRSThtcpNOPop) + { + sitecast_handle_NOP_request(main_server, &htcp_mesg, s, + client_addr_ptr, client_addr_len); + return; + } + + if (htcp_mesg.opcode == GRSThtcpTSTop) + { + if (((GRSThtcpCountstrLen(htcp_mesg.method) == 3) && + (strncmp(htcp_mesg.method->text, "GET", 3) == 0)) || + ((GRSThtcpCountstrLen(htcp_mesg.method) == 4) && + (strncmp(htcp_mesg.method->text, "HEAD", 4) == 0))) + { + sitecast_handle_TST_GET(main_server, &htcp_mesg, s, + client_addr_ptr, client_addr_len); + return; + } + + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "SiteCast responder rejects method %*s in TST message from %s:%s", + GRSThtcpCountstrLen(htcp_mesg.method), htcp_mesg.method->text, + host, serv); + return; + } + + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "SiteCast does not implement HTCP op-code %d in message from %s:%s", + htcp_mesg.opcode, + host, serv); +} + +static int +bind_sitecast_sockets(server_rec *main_server, const char *node, + unsigned int port, int is_unicast) +{ + int s, open, ret; + struct addrinfo *ai; + struct addrinfo hints; + struct ipv6_mreq mreq6; + struct ip_mreq mreq; + char serv[8]; + struct addrinfo *a; + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; + if (!is_unicast) + hints.ai_flags |= AI_NUMERICHOST; + + snprintf(serv, sizeof(serv), "%u", port); + + ret = getaddrinfo(node, serv, &hints, &ai); + if (ret) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "%s UDP Responder fails to look up %s", + (is_unicast) ? "Unicast" : "Multicast", node); + return -1; + } + + open = 0; + for (a = ai; a != NULL; a = a->ai_next) { + s = socket(a->ai_family, a->ai_socktype, a->ai_protocol); + if (s < 0) + continue; + + ret = bind(s, a->ai_addr, a->ai_addrlen); + if (ret < 0) { + close (s); + continue; + } + + if (!is_unicast) { + switch (a->ai_family) { + case AF_INET: + bzero(&mreq, sizeof(mreq)); + mreq.imr_multiaddr = ((struct sockaddr_in *)(a->ai_addr))->sin_addr; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)); + break; + case AF_INET6: + mreq6.ipv6mr_multiaddr = + ((struct sockaddr_in6 *)a->ai_addr)->sin6_addr; + mreq6.ipv6mr_interface = + ((struct sockaddr_in6 *)a->ai_addr)->sin6_scope_id; + ret = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, + &mreq6, sizeof(mreq6)); + break; + default: + continue; + } + if (ret < 0) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "SiteCast UDP Responder fails on setting multicast (%s)", + strerror(errno)); + continue; + } + } + + FD_SET(s, &sitecast_sockets.fds); + if (s > sitecast_sockets.max_fd) + sitecast_sockets.max_fd = s; + open = 1; + } + freeaddrinfo(ai); + + if (!open) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "mod_gridsite: sitecast responder fails on unicast"); + return -1; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast UDP %s responder on %s:%s", + (is_unicast) ? "unicast" : "multicast", + node, serv); + return 0; +} + +void sitecast_responder(server_rec *main_server) +{ +#define GRST_SITECAST_MAXBUF 8192 + char reqbuf[GRST_SITECAST_MAXBUF]; + int reqbuf_len, ret, retval, i; + struct sockaddr client_addr; + socklen_t client_addr_len; + fd_set readsckts; + int s; + char host[INET6_ADDRSTRLEN]; + char serv[8]; + + strcpy((char *) main_server->process->argv[0], "GridSiteCast UDP responder"); + + FD_ZERO(&sitecast_sockets.fds); + sitecast_sockets.max_fd = -1; + + /* initialise unicast/replies socket first */ + ret = bind_sitecast_sockets(main_server, main_server->server_hostname, sitecastgroups[0].port, 1); + if (ret) + return; + + /* initialise multicast listener sockets next */ + + for (i=1; (i <= GRST_SITECAST_GROUPS) && + (sitecastgroups[i].port != 0); ++i) + { + ret = bind_sitecast_sockets(main_server, sitecastgroups[i].address, sitecastgroups[i].port, 0); + if (ret) + continue; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast UDP Responder listening on %s:%d", + sitecastgroups[i].address, + sitecastgroups[i].port); + } + + for (i=0; (i < GRST_SITECAST_ALIASES) && + (sitecastaliases[i].sitecast_url != NULL) ; ++i) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast alias for %s (%s,%d) to %s (%s)", + sitecastaliases[i].sitecast_url, + sitecastaliases[i].scheme, + sitecastaliases[i].port, + sitecastaliases[i].local_path, + sitecastaliases[i].local_hostname); + } + + while (1) /* **** main listening loop **** */ + { + /* set up bitmasks for select */ + + readsckts = sitecast_sockets.fds; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast UDP Responder waiting for requests"); + + if ((retval = select(sitecast_sockets.max_fd + 1, &readsckts, NULL, NULL, NULL)) < 1) + continue; /* < 1 on timeout or error */ + + for (s = 0; s <= sitecast_sockets.max_fd; s++) { + if (FD_ISSET(s, &readsckts)) + break; + } + if (s > sitecast_sockets.max_fd) + continue; + + client_addr_len = sizeof(client_addr); + reqbuf_len = recvfrom(s, reqbuf, GRST_SITECAST_MAXBUF, 0, + &client_addr, &client_addr_len); + if (reqbuf_len < 0) + continue; + + getnameinfo(&client_addr, client_addr_len, + host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast receives UDP message from %s:%s", + host, serv); + + sitecast_handle_request(main_server, reqbuf, reqbuf_len, s, + &client_addr, client_addr_len); + } /* **** end of main listening loop **** */ +} + +static int mod_gridsite_server_post_config(apr_pool_t *pPool, + apr_pool_t *pLog, apr_pool_t *pTemp, server_rec *main_server) +{ + SSL_CTX *ctx; + SSLSrvConfigRec *sc; + int i = 0; + server_rec *this_server; + apr_proc_t *procnew = NULL; + apr_status_t status; + char *path; + const char *userdata_key = "sitecast_init"; + const char *insecure_reneg = "SSLInsecureRenegotiation"; + canl_ctx c_ctx = NULL; + + c_ctx = canl_create_ctx(); + if (!c_ctx){ + ap_log_error(APLOG_MARK, APLOG_CRIT, status, main_server, + "mod_gridsite: Failed to create caNl context."); + return HTTP_INTERNAL_SERVER_ERROR; + } + + apr_pool_userdata_get((void **) &procnew, userdata_key, + main_server->process->pool); + + /* we only fork responder if one not already forked and we have at + least one GridSiteCastAlias defined. This means it is possible + to run a responder with no groups - listening on unicast only! */ + + if ((procnew == NULL) && + (sitecastaliases[0].sitecast_url != NULL)) + { + /* UDP multicast responder required but not yet started */ + + procnew = apr_pcalloc(main_server->process->pool, sizeof(*procnew)); + apr_pool_userdata_set((const void *) procnew, userdata_key, + apr_pool_cleanup_null, main_server->process->pool); + + status = apr_proc_fork(procnew, pPool); + + if (status < 0) + { + ap_log_error(APLOG_MARK, APLOG_CRIT, status, main_server, + "mod_gridsite: Failed to spawn SiteCast responder process"); + return HTTP_INTERNAL_SERVER_ERROR; + } + else if (status == APR_INCHILD) + { + ap_log_error(APLOG_MARK, APLOG_NOTICE, status, main_server, + "mod_gridsite: Spawning SiteCast responder process"); + sitecast_responder(main_server); + exit(-1); + } + + apr_pool_note_subprocess(main_server->process->pool, + procnew, APR_KILL_AFTER_TIMEOUT); + } + + /* continue with normal HTTP/HTTPS servers */ + + ap_add_version_component(pPool, + apr_psprintf(pPool, "mod_gridsite/%s", VERSION)); + + /* look for a SSLInsecureRenegotiation flag - if it exists then the mod_ssl + internal variable 'SSLSrvConfigRec' is different */ + while ( ssl_module.cmds[i].name && !mod_ssl_with_insecure_reneg) + { + mod_ssl_with_insecure_reneg = (strncmp( ssl_module.cmds[i].name, + insecure_reneg, sizeof(insecure_reneg) ) == 0); + i++; + } + + ap_log_error(APLOG_MARK, APLOG_NOTICE, status, main_server, + "mod_gridsite: mod_ssl_with_insecure_reneg = %d", mod_ssl_with_insecure_reneg); + + for (this_server = main_server; + this_server != NULL; + this_server = this_server->next) + { + /* we do some GridSite OpenSSL magic for HTTPS servers */ + + sc = ap_get_module_config(this_server->module_config, &ssl_module); + + if ((sc != NULL) && + (sc->enabled) && + (SSLSrvConfigRec_server(sc) != NULL) && + (SSLSrvConfigRec_server(sc)->ssl_ctx != NULL)) + { + ctx = SSLSrvConfigRec_server(sc)->ssl_ctx; + + /* Use default caNl callbacks to verify certificates*/ + canl_ssl_ctx_set_clb(c_ctx, ctx, ctx->verify_mode, + GRST_callback_SSLVerify_wrapper); + + if (main_server->loglevel >= APLOG_DEBUG) + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "Set mod_ssl verify callbacks to GridSite wrappers: %s", + canl_get_error_message(c_ctx)); + } + } + + /* create sessions directory if necessary */ + + path = ap_server_root_relative(pPool, sessionsdir); + apr_dir_make_recursive(path, APR_UREAD | APR_UWRITE | APR_UEXECUTE, pPool); + chown(path, unixd_config.user_id, unixd_config.group_id); + + canl_free_ctx(c_ctx); + return OK; +} + +static server_rec *mod_gridsite_log_func_server; +static int mod_gridsite_log_func(char *file, int line, int level, + char *fmt, ...) +{ + char *mesg; + va_list ap; + + va_start(ap, fmt); + vasprintf(&mesg, fmt, ap); + va_end(ap); + + ap_log_error(file, line, level, + 0, mod_gridsite_log_func_server, "%s", mesg); + + free(mesg); + return 0; +} + +static void mod_gridsite_child_init(apr_pool_t *pPool, server_rec *pServer) +{ + apr_time_t cutoff_time; + apr_dir_t *dir; + char *filename; + apr_finfo_t finfo; + SSLSrvConfigRec *sc = ap_get_module_config(pServer->module_config, + &ssl_module); + GRSTgaclInit(); + mod_gridsite_log_func_server = pServer; + GRSTerrorLogFunc = mod_gridsite_log_func; + + /* expire old ssl creds files */ + + if (sc != NULL) + { + cutoff_time = apr_time_now() + - apr_time_from_sec(sc->session_cache_timeout); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, pServer, + "Cutoff time for ssl creds cache: %ld", + (long) apr_time_sec(cutoff_time)); + + if (apr_dir_open(&dir, + ap_server_root_relative(pPool, sessionsdir), pPool) == APR_SUCCESS) + { + while (apr_dir_read(&finfo, + APR_FINFO_CTIME | APR_FINFO_NAME, dir) == APR_SUCCESS) + { + if ((finfo.ctime < cutoff_time) && + (strncmp(finfo.name, "sslcreds-", 9) == 0)) + { + filename = apr_pstrcat(pPool, + ap_server_root_relative(pPool, sessionsdir), + "/", finfo.name, NULL); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, pServer, + "Remove %s from ssl creds cache", filename); + + apr_file_remove(filename, pPool); + } + } + + apr_dir_close(dir); + } + } +} + +static int mod_gridsite_handler(request_rec *r) +{ + mod_gridsite_dir_cfg *conf; + + conf = (mod_gridsite_dir_cfg *) + ap_get_module_config(r->per_dir_config, &gridsite_module); + + if (conf->dnlistsuri != NULL) + { + if (strcmp(r->uri, conf->dnlistsuri) == 0) + return mod_gridsite_dnlistsuri_dir_handler(r, conf); + + if (strncmp(r->uri, conf->dnlistsuri, strlen(conf->dnlistsuri)) == 0) + return mod_gridsite_dnlistsuri_handler(r, conf); + + if ((strncmp(r->uri, conf->dnlistsuri, strlen(r->uri)) == 0) + && (strlen(r->uri) == strlen(conf->dnlistsuri) - 1)) + { + apr_table_setn(r->headers_out, apr_pstrdup(r->pool, "Location"), + apr_pstrcat(r->pool, r->uri, "/", NULL)); + + r->status = HTTP_MOVED_TEMPORARILY; + return OK; + } + } + + if (strcmp(r->handler, DIR_MAGIC_TYPE) == 0) + return mod_gridsite_dir_handler(r, conf); + + return mod_gridsite_nondir_handler(r, conf); +} + +static ap_unix_identity_t *mod_gridsite_get_suexec_id_doer(const request_rec *r) +{ + mod_gridsite_dir_cfg *conf; + + conf = (mod_gridsite_dir_cfg *) + ap_get_module_config(r->per_dir_config, &gridsite_module); + + if ((conf->execugid.uid != UNSET) && + (conf->execmethod != NULL)) + { + + /* also push GRST_EXEC_DIRECTORY into request environment here too */ + + return &(conf->execugid); + } + + return NULL; +} + +static void register_hooks(apr_pool_t *p) +{ + /* config and handler stuff */ + static const char * const aszPre[] = { "mod_ssl.c", NULL }; + + ap_hook_post_config(mod_gridsite_server_post_config, NULL, NULL, + APR_HOOK_LAST); + ap_hook_child_init(mod_gridsite_child_init, aszPre, NULL, APR_HOOK_MIDDLE); + + ap_hook_check_user_id(mod_gridsite_check_user_id, NULL, NULL, + APR_HOOK_REALLY_FIRST); + + ap_hook_fixups(mod_gridsite_first_fixups,NULL,NULL,APR_HOOK_FIRST); + + ap_hook_fixups(mod_gridsite_perm_handler,NULL,NULL,APR_HOOK_REALLY_LAST); + + ap_hook_handler(mod_gridsite_handler, NULL, NULL, APR_HOOK_FIRST); + + ap_hook_get_suexec_identity(mod_gridsite_get_suexec_id_doer, + NULL, NULL, APR_HOOK_MIDDLE); +} + +module AP_MODULE_DECLARE_DATA gridsite_module = +{ + STANDARD20_MODULE_STUFF, + create_gridsite_dir_config, /* dir config creater */ + merge_gridsite_dir_config, /* dir merger */ + create_gridsite_srv_config, /* create server config */ + NULL, /* merge server config */ + mod_gridsite_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ +}; diff --git a/org.gridsite.core/src/canl_mod_ssl-private.h b/org.gridsite.core/src/canl_mod_ssl-private.h new file mode 100644 index 0000000..d6e47f6 --- /dev/null +++ b/org.gridsite.core/src/canl_mod_ssl-private.h @@ -0,0 +1,204 @@ +/* + Copyright (c) 2003-8, Andrew McNab, University of Manchester + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + o Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + o 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "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 COPYRIGHT OWNER 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. +*/ + +/* + + Portions of this code are derived from Apache mod_ssl, and are covered + by the Apache Software License: + + * Copyright 2001-2004 The Apache Software Foundation + * + * 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. + */ + +/* + This work has been partially funded by the EU Commission (contract + INFSO-RI-222667) under the EGEE-III collaboration. +*/ + +/*------------------------------------------------------------------* + * This program is part of GridSite: http://www.gridsite.org/ * + *------------------------------------------------------------------*/ + + +/* + * After 2.0.49, Apache mod_ssl has most of the mod_ssl structures defined + * in ssl_private.h, which is not installed along with httpd-devel (eg in + * the FC2 RPM.) This include file provides SIMPLIFIED structures for use + * by mod_gridsite: for example, pointers to unused structures are replaced + * by void * and some of the structures are truncated when only the early + * members are used. + * + * CLEARLY, THIS WILL BREAK IF THERE ARE MAJOR CHANGES TO ssl_private.h!!! + */ + +#include + +#ifndef BOOL +#define BOOL unsigned int +#endif + +typedef enum { + SSL_SHUTDOWN_TYPE_UNSET, + SSL_SHUTDOWN_TYPE_STANDARD, + SSL_SHUTDOWN_TYPE_UNCLEAN, + SSL_SHUTDOWN_TYPE_ACCURATE +} ssl_shutdown_type_e; + +typedef enum { + SSL_ENABLED_UNSET = -1, + SSL_ENABLED_FALSE = 0, + SSL_ENABLED_TRUE = 1, + SSL_ENABLED_OPTIONAL = 3 +} ssl_enabled_t; + +#if AP_MODULE_MAGIC_AT_LEAST(20051115,0) +typedef enum { + SSL_CVERIFY_UNSET = -1, + SSL_CVERIFY_NONE = 0, + SSL_CVERIFY_OPTIONAL = 1, + SSL_CVERIFY_REQUIRE = 2, + SSL_CVERIFY_OPTIONAL_NO_CA = 3 +} ssl_verify_t; + +#endif + +typedef struct { + SSL *ssl; + const char *client_dn; + X509 *client_cert; + ssl_shutdown_type_e shutdown_type; + const char *verify_info; + const char *verify_error; + int verify_depth; + int is_proxy; + int disabled; + int non_ssl_request; +} SSLConnRec; + +#if AP_MODULE_MAGIC_AT_LEAST(20051115,0) +typedef struct { + const char *ca_cert_path; + const char *ca_cert_file; + + const char *cipher_suite; + + int verify_depth; + ssl_verify_t verify_mode; +} modssl_auth_ctx_t; +#endif + +typedef struct { + void *sc; /* pointer back to server config */ + SSL_CTX *ssl_ctx; +#if AP_MODULE_MAGIC_AT_LEAST(20051115,0) + void *pks; + void *pkp; + + int protocol; + + int pphrase_dialog_type; + const char *pphrase_dialog_path; + + const char *cert_chain; + + const char *crl_path; + const char *crl_file; + X509_STORE *crl; + + modssl_auth_ctx_t auth; +#endif +} modssl_ctx_t; + +/* original SSLSrvConfigRec */ +typedef struct { + void *mc; + BOOL enabled; + BOOL proxy_enabled; + const char *vhost_id; + int vhost_id_len; + int session_cache_timeout; +#if AP_MODULE_MAGIC_AT_LEAST(20051115,0) + BOOL cipher_server_pref; +#endif + modssl_ctx_t *server; + modssl_ctx_t *proxy; +} SSLSrvConfigRec; + +/* SSLSrvConfigRec after mod_ssl patch for CVE-2009-3555 */ +typedef struct { + void *mc; + unsigned int enabled; + unsigned int proxy_enabled; + const char *vhost_id; + int vhost_id_len; + int session_cache_timeout; +#if AP_MODULE_MAGIC_AT_LEAST(20051115,0) + BOOL cipher_server_pref; +#endif + /* this is the member that was added */ + int insecure_reneg; + modssl_ctx_t *server; + modssl_ctx_t *proxy; +} SSLSrvConfigRec2; + +/* The server and proxy members of SSLSrvConfigRec must only be accessed + using these macros: */ +#define SSLSrvConfigRec_server(sc) (mod_ssl_with_insecure_reneg ? (((SSLSrvConfigRec2 *) sc)->server) : (((SSLSrvConfigRec *) sc)->server)) +#define SSLSrvConfigRec_proxy(sc) (mod_ssl_with_insecure_reneg ? (((SSLSrvConfigRec2 *) sc)->proxy) : (((SSLSrvConfigRec *) sc)->proxy)) + +#if AP_MODULE_MAGIC_AT_LEAST(20051115,0) +typedef struct { + BOOL bSSLRequired; + apr_array_header_t *aRequirement; + int nOptions; + int nOptionsAdd; + int nOptionsDel; + const char *szCipherSuite; + ssl_verify_t nVerifyClient; + int nVerifyDepth; + const char *szCACertificatePath; + const char *szCACertificateFile; + const char *szUserName; +} SSLDirConfigRec; +#endif + +extern module AP_MODULE_DECLARE_DATA ssl_module; diff --git a/org.gridsite.core/src/grst_canl_x509.c b/org.gridsite.core/src/grst_canl_x509.c new file mode 100644 index 0000000..4d2b238 --- /dev/null +++ b/org.gridsite.core/src/grst_canl_x509.c @@ -0,0 +1,2474 @@ +/* + Copyright (c) 2002-10, Andrew McNab, University of Manchester + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + o Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + o 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "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 COPYRIGHT OWNER 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. + + --------------------------------------------------------------- + For more information about GridSite: http://www.gridsite.org/ + --------------------------------------------------------------- +*/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef GRST_NO_OPENSSL +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include + +#include "gridsite.h" + +#define GRST_KEYSIZE 512 +#define GRST_PROXYCACHE "/../proxycache/" +#define GRST_MAX_CHAIN_LEN 9 +#define GRST_BACKDATE_SECONDS 300 + +int +GRSTasn1FindField(const char *oid, char *coords, + char *asn1string, + struct GRSTasn1TagList taglist[], int lasttag, + int *result); + +/// Compare X509 Distinguished Name strings +int GRSTx509NameCmp(char *a, char *b) +/// +/// This function attempts to do with string representations what +/// would ideally be done with OIDs/values. In particular, we equate +/// "/Email=" == "/emailAddress=" to deal with this important change +/// between OpenSSL 0.9.6 and 0.9.7. +/// Other than that, it is currently the same as ordinary strcasecmp(3) +/// (for consistency with EDG/LCG/EGEE gridmapdir case insensitivity.) +{ + int ret; + char *aa, *bb, *p; + + if ((a == NULL) || (b == NULL)) return 1; /* NULL never matches */ + + aa = strdup(a); + while ((p = strstr(aa, "/emailAddress=")) != NULL) + { + memmove(&p[6], &p[13], strlen(&p[13]) + 1); + p[1] = 'E'; + } + + bb = strdup(b); + while ((p = strstr(bb, "/emailAddress=")) != NULL) + { + memmove(&p[6], &p[13], strlen(&p[13]) + 1); + p[1] = 'E'; + } + + ret = strcasecmp(aa, bb); + + free(aa); + free(bb); + + return ret; +} + + +/// Check critical extensions +/*TODO MBD*/ +int GRSTx509KnownCriticalExts(X509 *cert) +/// +/// Returning GRST_RET_OK if all of extensions are known to us or +/// OpenSSL; GRST_REF_FAILED otherwise. +/// +/// Since this function relies on functionality (X509_supported_extension) +/// introduced in 0.9.7, then we do nothing and report an error +/// (GRST_RET_FAILED) if one of the associated defines +/// (X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) is absent. +{ + int i; + char s[80]; + X509_EXTENSION *ex; + +#ifdef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION + for (i = 0; i < X509_get_ext_count(cert); ++i) + { + ex = X509_get_ext(cert, i); + + if (X509_EXTENSION_get_critical(ex) && + !X509_supported_extension(ex)) + { + OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 1); + + if ((strcmp(s, GRST_PROXYCERTINFO_OID) != 0) && + (strcmp(s, GRST_PROXYCERTINFO_OLD_OID) != 0)) + return GRST_RET_FAILED; + } + } + + return GRST_RET_OK; +#else + return GRST_RET_FAILED; +#endif +} + +/// Check if certificate can be used as a CA to sign standard X509 certs +int GRSTx509IsCA(X509 *cert) +/// +/// Return GRST_RET_OK if true; GRST_RET_FAILED if not. +{ + int purpose_id; + + purpose_id = X509_PURPOSE_get_by_sname("sslclient"); + + /* final argument to X509_check_purpose() is whether to check for CAness */ + + if (X509_check_purpose(cert, purpose_id + X509_PURPOSE_MIN, 1)) + return GRST_RET_OK; + else return GRST_RET_FAILED; +} + +int GRSTx509ChainFree(GRSTx509Chain *chain) +{ + GRSTx509Cert *grst_cert, *next_grst_cert; + + if (chain == NULL) return GRST_RET_OK; + + next_grst_cert = chain->firstcert; + + while (next_grst_cert != NULL) + { + grst_cert = next_grst_cert; + + if (grst_cert->issuer != NULL) free(grst_cert->issuer); + if (grst_cert->dn != NULL) free(grst_cert->dn); + if (grst_cert->value != NULL) free(grst_cert->value); + if (grst_cert->ocsp != NULL) free(grst_cert->ocsp); + + next_grst_cert = grst_cert->next; + free(grst_cert); + } + + free(chain); + + return GRST_RET_OK; +} + +/// Check a specific signature against a specific (VOMS) cert +static int GRSTx509VerifySig(time_t *time1_time, time_t *time2_time, + unsigned char *txt, int txt_len, + unsigned char *sig, int sig_len, + X509 *cert, EVP_MD *md_type) +/// +/// Returns GRST_RET_OK if signature is ok, other values if not. +{ + int ret; + EVP_PKEY *prvkey; + EVP_MD_CTX ctx; + time_t voms_service_time1, voms_service_time2; + + prvkey = X509_extract_key(cert); + if (prvkey == NULL) return GRST_RET_FAILED; + + OpenSSL_add_all_digests(); +#if OPENSSL_VERSION_NUMBER >= 0x0090701fL + EVP_MD_CTX_init(&ctx); + EVP_VerifyInit_ex(&ctx, md_type, NULL); +#else + EVP_VerifyInit(&ctx, md_type); +#endif + + EVP_VerifyUpdate(&ctx, txt, txt_len); + + ret = EVP_VerifyFinal(&ctx, sig, sig_len, prvkey); + +#if OPENSSL_VERSION_NUMBER >= 0x0090701fL + EVP_MD_CTX_cleanup(&ctx); +#endif + EVP_PKEY_free(prvkey); + + if (ret != 1) return GRST_RET_FAILED; + + voms_service_time1 = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0); + if (voms_service_time1 > *time1_time) + *time1_time = voms_service_time1; + + voms_service_time2 = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0); + if (voms_service_time2 < *time1_time) + *time2_time = voms_service_time2; + + return GRST_RET_OK ; /* verified */ +} + +/// Check the signature of the VOMS attributes +static int GRSTx509VerifyVomsSig(time_t *time1_time, time_t *time2_time, + unsigned char *asn1string, + struct GRSTasn1TagList taglist[], + int lasttag, + char *vomsdir, int acnumber) +/// +/// Returns GRST_RET_OK if signature is ok, other values if not. +{ +#define GRST_ASN1_COORDS_VOMS_DN "-1-1-%d-1-3-1-1-1-%%d-1-%%d" +#define GRST_ASN1_COORDS_VOMS_INFO "-1-1-%d-1" +#define GRST_ASN1_COORDS_VOMS_HASH "-1-1-%d-2-1" +#define GRST_ASN1_COORDS_VOMS_SIG "-1-1-%d-3" + int ihash, isig, iinfo; + char *certpath, *certpath2, acvomsdn[200], dn_coords[200], + info_coords[200], sig_coords[200], hash_coords[200]; + unsigned char *p; + DIR *vomsDIR, *vomsDIR2; + struct dirent *vomsdirent, *vomsdirent2; + X509 *cert; + FILE *fp; + EVP_MD *md_type = NULL; + struct stat statbuf; + time_t voms_service_time1 = GRST_MAX_TIME_T, voms_service_time2 = 0, + tmp_time1, tmp_time2; + ASN1_OBJECT *hash_obj = NULL; + + if ((vomsdir == NULL) || (vomsdir[0] == '\0')) return GRST_RET_FAILED; + + snprintf(dn_coords, sizeof(dn_coords), + GRST_ASN1_COORDS_VOMS_DN, acnumber); + + if (GRSTasn1GetX509Name(acvomsdn, sizeof(acvomsdn), dn_coords, + asn1string, taglist, lasttag) != GRST_RET_OK) return GRST_RET_FAILED; + + snprintf(info_coords, sizeof(info_coords), + GRST_ASN1_COORDS_VOMS_INFO, acnumber); + iinfo = GRSTasn1SearchTaglist(taglist, lasttag, info_coords); + + snprintf(hash_coords, sizeof(hash_coords), + GRST_ASN1_COORDS_VOMS_HASH, acnumber); + ihash = GRSTasn1SearchTaglist(taglist, lasttag, hash_coords); + + snprintf(sig_coords, sizeof(sig_coords), + GRST_ASN1_COORDS_VOMS_SIG, acnumber); + isig = GRSTasn1SearchTaglist(taglist, lasttag, sig_coords); + + if ((iinfo < 0) || (ihash < 0) || (isig < 0)) return GRST_RET_FAILED; + + /* determine hash algorithm's type */ + + p = &asn1string[taglist[ihash].start]; + + d2i_ASN1_OBJECT(&hash_obj, &p, + taglist[ihash].length+taglist[ihash].headerlength); + + md_type = EVP_get_digestbyname(OBJ_nid2sn(OBJ_obj2nid(hash_obj))); + + if (md_type == NULL) return GRST_RET_FAILED; + + + vomsDIR = opendir(vomsdir); + if (vomsDIR == NULL) return GRST_RET_FAILED; + + while ((vomsdirent = readdir(vomsDIR)) != NULL) + { + if (vomsdirent->d_name[0] == '.') continue; + + asprintf(&certpath, "%s/%s", vomsdir, vomsdirent->d_name); + stat(certpath, &statbuf); + + if (S_ISDIR(statbuf.st_mode)) + { + vomsDIR2 = opendir(certpath); + GRSTerrorLog(GRST_LOG_DEBUG, + "Descend VOMS subdirectory %s", certpath); + free(certpath); + + if (vomsDIR2 == NULL) continue; + + while ((vomsdirent2 = readdir(vomsDIR2)) != NULL) + { + if (vomsdirent2->d_name[0] == '.') continue; + + asprintf(&certpath2, "%s/%s/%s", + vomsdir, vomsdirent->d_name, vomsdirent2->d_name); + + fp = fopen(certpath2, "r"); + GRSTerrorLog(GRST_LOG_DEBUG, + "Examine VOMS cert %s", certpath2); + free(certpath2); + if (fp == NULL) continue; + + cert = PEM_read_X509(fp, NULL, NULL, NULL); + fclose(fp); + if (cert == NULL) continue; + + tmp_time1 = 0; + tmp_time2 = GRST_MAX_TIME_T; + + if (GRSTx509VerifySig(&tmp_time1, &tmp_time2, + &asn1string[taglist[iinfo].start], + taglist[iinfo].length+taglist[iinfo].headerlength, + &asn1string[taglist[isig].start+ + taglist[isig].headerlength+1], + taglist[isig].length - 1, + cert, md_type) == GRST_RET_OK) + { + GRSTerrorLog(GRST_LOG_DEBUG, "Matched VOMS cert file %s", vomsdirent2->d_name); + + /* Store more permissive time ranges for now */ + + if (tmp_time1 < voms_service_time1) + voms_service_time1 = tmp_time1; + + if (tmp_time2 > voms_service_time2) + voms_service_time2 = tmp_time2; + } + + X509_free(cert); + } + + closedir(vomsDIR2); + } + else + { + fp = fopen(certpath, "r"); + GRSTerrorLog(GRST_LOG_DEBUG, "Examine VOMS cert %s", certpath); + free(certpath); + if (fp == NULL) continue; + + cert = PEM_read_X509(fp, NULL, NULL, NULL); + fclose(fp); + if (cert == NULL) continue; + + tmp_time1 = 0; + tmp_time2 = GRST_MAX_TIME_T; + + if (GRSTx509VerifySig(&tmp_time1, &tmp_time2, + &asn1string[taglist[iinfo].start], + taglist[iinfo].length+taglist[iinfo].headerlength, + &asn1string[taglist[isig].start+ + taglist[isig].headerlength+1], + taglist[isig].length - 1, + cert, md_type) == GRST_RET_OK) + { + GRSTerrorLog(GRST_LOG_DEBUG, "Matched VOMS cert file %s", vomsdirent->d_name); + + /* Store more permissive time ranges for now */ + + if (tmp_time1 < voms_service_time1) + voms_service_time1 = tmp_time1; + + if (tmp_time2 > voms_service_time2) + voms_service_time2 = tmp_time2; + } + + X509_free(cert); + } + } + + closedir(vomsDIR); + + if ((voms_service_time1 == GRST_MAX_TIME_T) || (voms_service_time2 == 0)) + return GRST_RET_FAILED; + + /* now we tighten up the VOMS AC time range using the most permissive + pair of VOMS certificate ranges (in case of multiple, possibly + overlapping, matching certificates in the VOMS certs store) */ + + if (voms_service_time1 > *time1_time) *time1_time = voms_service_time1; + + if (voms_service_time2 < *time2_time) *time2_time = voms_service_time2; + + return GRST_RET_OK; +} + +/// Check the signature of the VOMS attributes using the LSC file cert +static int GRSTx509VerifyVomsSigCert(time_t *time1_time, time_t *time2_time, + unsigned char *asn1string, + struct GRSTasn1TagList taglist[], + int lasttag, + char *vomsdir, int acnumber, + int ivomscert, + char *capath, + char *acvomsdn, + char *voname) +/// +/// Returns GRST_RET_OK if signature is ok, other values if not. +{ +#define GRST_ASN1_COORDS_VOMS_DN "-1-1-%d-1-3-1-1-1-%%d-1-%%d" +#define GRST_ASN1_COORDS_VOMS_INFO "-1-1-%d-1" +#define GRST_ASN1_COORDS_VOMS_SIG "-1-1-%d-3" + int ret, isig, iinfo, chain_errors = GRST_RET_OK, + cadn_len, vomsdn_len, lsc_found = 0, ihash; + char *lscpath, *p, *cacertpath, + *vodir, *vomscert_cadn, *vomscert_vomsdn, + *lsc_cadn, *lsc_vomsdn; + unsigned char *q; + unsigned long issuerhash = 0; + DIR *voDIR; + struct dirent *vodirent; + X509 *cacert = NULL, *vomscert = NULL; + FILE *fp; + struct stat statbuf; + time_t tmp_time; + ASN1_OBJECT *hash_obj = NULL; + char coords[200]; + EVP_MD *md_type = NULL; + time_t voms_service_time1, voms_service_time2; + + if ((vomsdir == NULL) || (vomsdir[0] == '\0')) return GRST_RET_FAILED; + + q = &asn1string[taglist[ivomscert].start + 12]; + + vomscert = d2i_X509(NULL, (const unsigned char **) &q, + taglist[ivomscert].length - 8); + + if (vomscert == NULL) + { + GRSTerrorLog(GRST_LOG_DEBUG, "Failed to read included VOMS cert in GRSTx509VerifyVomsSigCert()"); + return GRST_RET_FAILED; + } + + GRSTerrorLog(GRST_LOG_DEBUG, "Found included VOMS cert in GRSTx509VerifyVomsSigCert()"); + + snprintf(coords, sizeof(coords), GRST_ASN1_COORDS_VOMS_INFO, acnumber); + iinfo = GRSTasn1SearchTaglist(taglist, lasttag, coords); + + snprintf(coords, sizeof(coords), GRST_ASN1_COORDS_VOMS_HASH, acnumber); + ihash = GRSTasn1SearchTaglist(taglist, lasttag, coords); + + snprintf(coords, sizeof(coords), GRST_ASN1_COORDS_VOMS_SIG, acnumber); + isig = GRSTasn1SearchTaglist(taglist, lasttag, coords); + + if ((iinfo < 0) || (ihash < 0) || (isig < 0)) return GRST_RET_FAILED; + + q = &asn1string[taglist[ihash].start]; + d2i_ASN1_OBJECT(&hash_obj, &q, + taglist[ihash].length+taglist[ihash].headerlength); + + md_type = EVP_get_digestbyname(OBJ_nid2sn(OBJ_obj2nid(hash_obj))); + if (md_type == NULL) return GRST_RET_FAILED; + + /* check issuer CA certificate */ + + issuerhash = X509_NAME_hash(X509_get_issuer_name(vomscert)); + asprintf(&cacertpath, "%s/%.8x.0", capath, issuerhash); + + /* check voms cert DN matches DN from AC */ + + vomscert_vomsdn = X509_NAME_oneline(X509_get_subject_name(vomscert),NULL,0); + + if (GRSTx509NameCmp(vomscert_vomsdn, acvomsdn) != 0) + { + free(vomscert_vomsdn); + + GRSTerrorLog(GRST_LOG_DEBUG, "Included VOMS cert DN does not match AC issuer DN!"); + return GRST_RET_FAILED; + } + + free(vomscert_vomsdn); + + GRSTerrorLog(GRST_LOG_DEBUG, "Look for CA root file %s", cacertpath); + + fp = fopen(cacertpath, "r"); + free(cacertpath); + + if (fp == NULL) return GRST_RET_FAILED; + else + { + cacert = PEM_read_X509(fp, NULL, NULL, NULL); + fclose(fp); + if (cacert != NULL) + { + GRSTerrorLog(GRST_LOG_DEBUG, " Loaded CA root cert from file"); + } + else + { + GRSTerrorLog(GRST_LOG_DEBUG, " Failed to load CA root cert file"); + return GRST_RET_FAILED; + } + } + + /* check times CA cert times, and reject if necessary */ + + tmp_time = GRSTasn1TimeToTimeT( + ASN1_STRING_data(X509_get_notBefore(cacert)), 0); + if (tmp_time > *time1_time) chain_errors |= GRST_CERT_BAD_TIME; + + tmp_time = GRSTasn1TimeToTimeT( + ASN1_STRING_data(X509_get_notAfter(cacert)), 0); + if (tmp_time < *time2_time) chain_errors |= GRST_CERT_BAD_TIME; + + /* check times VOMS cert times, and tighten if necessary */ + + tmp_time = GRSTasn1TimeToTimeT( + ASN1_STRING_data(X509_get_notBefore(vomscert)), 0); + if (tmp_time > *time1_time) chain_errors |= GRST_CERT_BAD_TIME; + + tmp_time = GRSTasn1TimeToTimeT( + ASN1_STRING_data(X509_get_notAfter(vomscert)), 0); + if (tmp_time < *time2_time) chain_errors |= GRST_CERT_BAD_TIME; + + ret = X509_check_issued(cacert, vomscert); + GRSTerrorLog(GRST_LOG_DEBUG, "X509_check_issued returns %d", ret); + + vomscert_cadn = X509_NAME_oneline(X509_get_issuer_name(vomscert),NULL,0); + + + if (ret != X509_V_OK) return (chain_errors | GRST_CERT_BAD_SIG); + + asprintf(&vodir, "%s/%s", vomsdir, voname); + + voDIR = opendir(vodir); + if (voDIR == NULL) return GRST_RET_FAILED; + + cadn_len = strlen(vomscert_cadn); + vomsdn_len = strlen(acvomsdn); + + lsc_cadn = malloc(cadn_len + 2); + lsc_vomsdn = malloc(vomsdn_len + 2); + + while (((vodirent = readdir(voDIR)) != NULL) && !lsc_found) + { + if (vodirent->d_name[0] == '.') continue; + + if (strlen(vodirent->d_name) < 5) continue; + + if (strcmp(&(vodirent->d_name[strlen(vodirent->d_name)-4]), ".lsc") != 0) continue; + + asprintf(&lscpath, "%s/%s", vodir, vodirent->d_name); + stat(lscpath, &statbuf); + + GRSTerrorLog(GRST_LOG_DEBUG, "Check LSC file %s for %s,%s", + lscpath, acvomsdn, vomscert_cadn); + + if ((fp = fopen(lscpath, "r")) != NULL) + { + lsc_cadn[0] = '\0'; + lsc_vomsdn[0] = '\0'; + + if ((fgets(lsc_vomsdn, vomsdn_len + 2, fp) != NULL) + && (fgets(lsc_cadn, cadn_len + 2, fp) != NULL)) + { + if ((p = index(lsc_cadn, '\n')) != NULL) *p = '\0'; + + if ((p = index(lsc_vomsdn, '\n')) != NULL) *p = '\0'; + + if ((GRSTx509NameCmp(lsc_cadn, vomscert_cadn) == 0) && + (GRSTx509NameCmp(lsc_vomsdn, acvomsdn) == 0)) + { + GRSTerrorLog(GRST_LOG_DEBUG, "Matched LSC file %s", lscpath); + lsc_found = 1; + } + } + + fclose(fp); + } + + free(lscpath); + } + + closedir(voDIR); + free(vodir); + free(vomscert_cadn); + free(lsc_cadn); + free(lsc_vomsdn); + + if (!lsc_found) { + chain_errors |= GRST_CERT_BAD_SIG; + goto end; + } + + ret = GRSTx509VerifySig(&voms_service_time1, &voms_service_time2, + &asn1string[taglist[iinfo].start], + taglist[iinfo].length+taglist[iinfo].headerlength, + &asn1string[taglist[isig].start+ + taglist[isig].headerlength+1], + taglist[isig].length - 1, + vomscert, md_type); + if (ret != GRST_RET_OK) { + chain_errors |= GRST_CERT_BAD_SIG; + goto end; + } + + if (voms_service_time1 > *time1_time) *time1_time = voms_service_time1; + if (voms_service_time2 < *time2_time) *time2_time = voms_service_time2; + +end: + if (cacert) + X509_free(cacert); + if (vomscert) + X509_free(vomscert); + + return (chain_errors ? GRST_RET_FAILED : GRST_RET_OK); +} + +/// Get the VOMS attributes in the given extension +static int GRSTx509ChainVomsAdd(GRSTx509Cert **grst_cert, + time_t time1_time, time_t time2_time, + int delegation, + X509_EXTENSION *ex, + GRSTx509Cert *user_cert, char *vomsdir, char *capath) +/// +/// Add any VOMS credentials found into the chain. Always returns GRST_RET_OK +/// - even for invalid credentials, which are flagged in errors field +{ +#define MAXTAG 500 +#define GRST_ASN1_COORDS_FQAN "-1-1-%d-1-7-1-2-1-2-%d" +#define GRST_ASN1_COORDS_ISSUER_DN "-1-1-%d-1-2-1-1-1-1-%%d-1-%%d" +#define GRST_ASN1_COORDS_ISSUER_SERIAL "-1-1-%d-1-2-1-2" +#define GRST_ASN1_COORDS_VOMS_DN "-1-1-%d-1-3-1-1-1-%%d-1-%%d" +#define GRST_ASN1_COORDS_TIME1 "-1-1-%d-1-6-1" +#define GRST_ASN1_COORDS_TIME2 "-1-1-%d-1-6-2" +#define GRST_ASN1_COORDS_VOMSCERT "-1-1-%d-1-8-%%d-%%d" + + ASN1_OCTET_STRING *asn1data; + char *asn1string, acissuerdn[200], acvomsdn[200], + dn_coords[200], fqan_coords[200], time1_coords[200], + time2_coords[200], vomscert_coords[200], *voname = NULL, + serial_coords[200]; + long asn1length; + int lasttag=-1, itag, i, j, acnumber = 1, chain_errors = 0, + ivomscert, tmp_chain_errors, ret; + char *acissuerserial = NULL; + struct GRSTasn1TagList taglist[MAXTAG+1]; + time_t actime1 = 0, actime2 = 0, time_now, + tmp_time1, tmp_time2; + ASN1_INTEGER acissuerserialASN1; + + asn1data = X509_EXTENSION_get_data(ex); + asn1string = ASN1_STRING_data(asn1data); + asn1length = ASN1_STRING_length(asn1data); + + GRSTasn1ParseDump(NULL, asn1string, asn1length, taglist, MAXTAG, &lasttag); + + for (acnumber = 1; ; ++acnumber) /* go through ACs one by one */ + { + chain_errors = 0; + + snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_ISSUER_DN, acnumber); + if (GRSTasn1GetX509Name(acissuerdn, sizeof(acissuerdn), dn_coords, + asn1string, taglist, lasttag) != GRST_RET_OK) + break; + + snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_VOMS_DN, acnumber); + if (GRSTasn1GetX509Name(acvomsdn, sizeof(acvomsdn), dn_coords, + asn1string, taglist, lasttag) != GRST_RET_OK) + break; + + if ((GRSTx509NameCmp(user_cert->dn, acissuerdn) != 0) && /* old */ + (GRSTx509NameCmp(user_cert->issuer, acissuerdn) != 0)) /* new */ + chain_errors |= GRST_CERT_BAD_CHAIN; + + /* check serial numbers */ + + snprintf(serial_coords, sizeof(serial_coords), + GRST_ASN1_COORDS_ISSUER_SERIAL, acnumber); + + itag = GRSTasn1SearchTaglist(taglist, lasttag, serial_coords); + + if (itag > -1) + { + acissuerserialASN1.length = taglist[itag].length; + acissuerserialASN1.type = V_ASN1_INTEGER; + acissuerserialASN1.data = &asn1string[taglist[itag].start+taglist[itag].headerlength]; + + acissuerserial = i2s_ASN1_INTEGER(NULL, &acissuerserialASN1); +/* + p = &asn1string[taglist[itag].start+taglist[itag].headerlength]; + + if (taglist[itag].length == 2) + acissuerserial = p[1] + p[0] * 0x100; + else if (taglist[itag].length == 3) + acissuerserial = p[2] + p[1] * 0x100 + p[0] * 0x10000; + else if (taglist[itag].length == 4) + acissuerserial = p[3] + p[2] * 0x100 + p[1] * 0x10000 + + p[0] * 0x1000000; +*/ + } + + if (strcmp(acissuerserial, user_cert->serial) != 0) + chain_errors |= GRST_CERT_BAD_CHAIN; + + /* get times */ + + snprintf(time1_coords, sizeof(time1_coords), GRST_ASN1_COORDS_TIME1, acnumber); + itag = GRSTasn1SearchTaglist(taglist, lasttag, time1_coords); + + if (itag > -1) actime1 = GRSTasn1TimeToTimeT( + &asn1string[taglist[itag].start+ + taglist[itag].headerlength], + taglist[itag].length); + else actime1 = 0; + + snprintf(time2_coords, sizeof(time2_coords), GRST_ASN1_COORDS_TIME2, acnumber); + itag = GRSTasn1SearchTaglist(taglist, lasttag, time2_coords); + + if (itag > -1) actime2 = GRSTasn1TimeToTimeT( + &asn1string[taglist[itag].start+ + taglist[itag].headerlength], + taglist[itag].length); + else actime2 = 0; + + if (actime1 > time1_time) time1_time = actime1; + if (actime2 < time2_time) time2_time = actime2; + + time(&time_now); + if ((time1_time > time_now + 300) || (time2_time < time_now)) + chain_errors |= GRST_CERT_BAD_TIME; + + /* get first FQAN and use to get VO name */ + + snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, 1); + itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords); + + if ((itag > -1) && + (asn1string[taglist[itag].start+taglist[itag].headerlength] == '/')) + { + for (j=1; + (asn1string[taglist[itag].start+taglist[itag].headerlength+j] != '/') && + (j < taglist[itag].length); + ++j) ; + + asprintf(&voname, "%.*s", j-1, + &(asn1string[taglist[itag].start+taglist[itag].headerlength+1])); + } + + snprintf(vomscert_coords, sizeof(vomscert_coords), + GRST_ASN1_COORDS_VOMSCERT, acnumber); + ret = GRSTasn1FindField(GRST_VOMS_PK_CERT_LIST_OID, vomscert_coords, asn1string, + taglist, lasttag, &ivomscert); + + /* try using internal VOMS issuer cert */ + tmp_chain_errors = GRST_CERT_BAD_SIG; + tmp_time1 = time1_time; + tmp_time2 = time2_time; + if ((ivomscert > -1) && + (voname != NULL) && + (GRSTx509VerifyVomsSigCert(&tmp_time1, &tmp_time2, + asn1string, taglist, lasttag, vomsdir, acnumber, + ivomscert, capath, acvomsdn, + voname) == GRST_RET_OK)) + { + tmp_chain_errors = 0; + time1_time = tmp_time1; + time2_time = tmp_time2; + } + + if (voname != NULL) + { + free(voname); + voname = NULL; + } + + if ((tmp_chain_errors != 0) && + (GRSTx509VerifyVomsSig(&time1_time, &time2_time, + asn1string, taglist, lasttag, vomsdir, acnumber) + != GRST_RET_OK)) + chain_errors |= GRST_CERT_BAD_SIG; + + for (i=1; ; ++i) /* now go through FQANs */ + { + snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, i); + itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords); + + if (itag > -1) + { + (*grst_cert)->next = malloc(sizeof(GRSTx509Cert)); + *grst_cert = (*grst_cert)->next; + bzero(*grst_cert, sizeof(GRSTx509Cert)); + + (*grst_cert)->notbefore = time1_time; + (*grst_cert)->notafter = time2_time; + asprintf(&((*grst_cert)->value), "%.*s", + taglist[itag].length, + &asn1string[taglist[itag].start+ + taglist[itag].headerlength]); + + (*grst_cert)->errors = chain_errors; /* ie may be invalid */ + (*grst_cert)->type = GRST_CERT_TYPE_VOMS; + (*grst_cert)->issuer = strdup(acvomsdn); + (*grst_cert)->dn = strdup(user_cert->dn); + (*grst_cert)->delegation = delegation; + } + else break; + } + } + + return GRST_RET_OK; +} +int GRSTx509ChainLoad(GRSTx509Chain **chain, + STACK_OF(X509) *certstack, X509 *lastcert, + char *capath, char *vomsdir) +{ + X509 *cert; /* Points to the current cert in the loop */ + X509 *cacert = NULL; /* The CA root cert */ + int depth = 0; /* Depth of cert chain */ + int chain_errors = 0; /* records previous errors */ + int first_non_ca; /* number of the EEC issued to user by CA */ + size_t len,len2; /* Lengths of issuer and cert DN */ + int IsCA; /* Holds whether cert is allowed to sign */ + int prevIsCA; /* Holds whether previous cert in chain is + allowed to sign */ + int prevIsLimited; /* previous cert was proxy and limited */ + int i,j,ret; /* Iteration/temp variables */ + char *proxy_part_DN; /* Pointer to end part of current-cert-in-chain + maybe eg "/CN=proxy" */ + char s[80], *p; + char *cacertpath; + unsigned long subjecthash = 0; /* hash of the name of first cert */ + unsigned long issuerhash = 0; /* hash of issuer name of first cert */ + FILE *fp; + X509_EXTENSION *ex; + time_t now; + GRSTx509Cert *grst_cert, *new_grst_cert, *user_cert = NULL; + + GRSTerrorLog(GRST_LOG_DEBUG, "GRSTx509ChainLoadCheck() starts"); + + time(&now); + + first_non_ca = 0; /* set to something predictable if things fail */ + + /* Set necessary preliminary values */ + IsCA = TRUE; /* =prevIsCA - start from a CA */ + prevIsLimited = 0; + + /* Get the client cert chain */ + if (certstack != NULL) + depth = sk_X509_num(certstack); /* How deep is that chain? */ + + if ((depth == 0) && (lastcert == NULL)) + { + *chain = NULL; + return GRST_RET_FAILED; + } + + cert = sk_X509_value(certstack, depth - 1); + subjecthash = X509_NAME_hash(X509_get_subject_name(cert)); + issuerhash = X509_NAME_hash(X509_get_issuer_name(cert)); + asprintf(&cacertpath, "%s/%.8x.0", capath, issuerhash); + + GRSTerrorLog(GRST_LOG_DEBUG, "Look for CA root file %s", cacertpath); + + fp = fopen(cacertpath, "r"); + free(cacertpath); + + if (fp == NULL) chain_errors |= GRST_CERT_BAD_CHAIN; + else + { + cacert = PEM_read_X509(fp, NULL, NULL, NULL); + fclose(fp); + if (cacert != NULL) + GRSTerrorLog(GRST_LOG_DEBUG, " Loaded CA root cert from file"); + else + GRSTerrorLog(GRST_LOG_DEBUG, " Failed to load CA root cert file"); + } + + *chain = malloc(sizeof(GRSTx509Chain)); + bzero(*chain, sizeof(GRSTx509Chain)); + + /* Check the client chain */ + for (i = depth - ((subjecthash == issuerhash) ? 1 : 0); + i >= ((lastcert == NULL) ? 0 : -1); + --i) + /* loop through client-presented chain starting at CA end */ + { + GRSTerrorLog(GRST_LOG_DEBUG, "Process cert at depth %d in chain", i); + + prevIsCA=IsCA; + + new_grst_cert = malloc(sizeof(GRSTx509Cert)); + bzero(new_grst_cert, sizeof(GRSTx509Cert)); + new_grst_cert->errors = chain_errors; + + if ((*chain)->firstcert == NULL) + { + GRSTerrorLog(GRST_LOG_DEBUG, "Initialise chain"); + (*chain)->firstcert = new_grst_cert; + } + else grst_cert->next = new_grst_cert; + + grst_cert = new_grst_cert; + + /* Choose X509 certificate and point to it with 'cert' */ + if (i < 0) cert = lastcert; + else if (i == depth) + cert = cacert; /* the self-signed CA from the store*/ + else if ((i == depth - 1) && (subjecthash == issuerhash)) + cert = cacert; /* ie claims to be a copy of a self-signed CA */ + else cert = sk_X509_value(certstack, i); + + if (cert != NULL) + { + if ((i == depth - 1) && (subjecthash != issuerhash)) + { + /* if first cert does not claim to be a self-signed copy + of a CA root cert in the store, we check the signature */ + + if (cacert == NULL) + { + chain_errors |= GRST_CERT_BAD_CHAIN; + ret = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; + } + else + { + ret = X509_check_issued(cacert, cert); + + GRSTerrorLog(GRST_LOG_DEBUG, + "Cert sig check %d returns %d", i, ret); + + if (ret != X509_V_OK) + new_grst_cert->errors |= GRST_CERT_BAD_SIG; + } + } + else if ((i == depth - 2) && (subjecthash == issuerhash)) + { + /* first cert claimed to be a self-signed copy of a CA root + cert in the store, we check the signature of the second + cert, using OUR copy of the CA cert DIRECT from the store */ + + if (cacert == NULL) + { + chain_errors |= GRST_CERT_BAD_CHAIN; + ret = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; + } + else + { + ret = X509_check_issued(cacert, cert); + + GRSTerrorLog(GRST_LOG_DEBUG, + "Cert sig check %d returns %d", i, ret); + + if (ret != X509_V_OK) + new_grst_cert->errors |= GRST_CERT_BAD_SIG; + } + } + else if (i < depth - 1) + { + /* otherwise a normal part of the chain: note that if the + first cert claims to be a self-signed copy of a CA root + cert in the store, we never use it for sig checking */ + + ret = X509_check_issued(sk_X509_value(certstack, i + 1), cert); + + GRSTerrorLog(GRST_LOG_DEBUG, + "Cert sig check %d returns %d", i, ret); + + if ((ret != X509_V_OK) && + (ret != X509_V_ERR_KEYUSAGE_NO_CERTSIGN)) + new_grst_cert->errors |= GRST_CERT_BAD_SIG; + + /* NO_CERTSIGN can still be ok due to Proxy Certificates */ + } + + p = i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(cert)); + strncpy(new_grst_cert->serial, p, GRST_X509_SERIAL_DIGITS); + new_grst_cert->serial[GRST_X509_SERIAL_DIGITS] = '\0'; + free(p); + + new_grst_cert->notbefore = GRSTasn1TimeToTimeT( + ASN1_STRING_data(X509_get_notBefore(cert)), 0); + new_grst_cert->notafter = GRSTasn1TimeToTimeT( + ASN1_STRING_data(X509_get_notAfter(cert)), 0); + + /* we check times and record if invalid */ + + if (now < new_grst_cert->notbefore) + new_grst_cert->errors |= GRST_CERT_BAD_TIME; + + if (now > new_grst_cert->notafter) + new_grst_cert->errors |= GRST_CERT_BAD_TIME; + + new_grst_cert->dn = X509_NAME_oneline(X509_get_subject_name(cert),NULL,0); + new_grst_cert->issuer = X509_NAME_oneline(X509_get_issuer_name(cert),NULL,0); + len = strlen(new_grst_cert->dn); + len2 = strlen(new_grst_cert->issuer); + + /* always treat a first cert from the CA files as a + CA: this is really for lousy CAs that dont create + proper v3 root certificates */ + + if (i == depth) IsCA = TRUE; + else IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK); + + /* If any forebear certificate is not allowed to sign we must + assume all decendents are proxies and cannot sign either */ + if (prevIsCA) + { + if (IsCA) + { + new_grst_cert->type = GRST_CERT_TYPE_CA; + } + else + { + new_grst_cert->type = GRST_CERT_TYPE_EEC; + first_non_ca = i; + user_cert = new_grst_cert; + new_grst_cert->delegation + = (lastcert == NULL) ? i : i + 1; + } + } + else + { + new_grst_cert->type = GRST_CERT_TYPE_PROXY; + + IsCA = FALSE; + /* Force proxy check next iteration. Important because I can + sign any CA I create! */ + + new_grst_cert->delegation = (lastcert == NULL) ? i : i + 1; + } + + if (!prevIsCA) + { + /* issuer didn't have CA status, so this is (at best) a proxy: + check for bad proxy extension*/ + + if (prevIsLimited) /* we reject proxies of limited proxies! */ + { + new_grst_cert->errors |= GRST_CERT_BAD_CHAIN; + chain_errors |= GRST_CERT_BAD_CHAIN; + } + + /* User not allowed to sign shortened DN */ + if (len2 > len) + { + new_grst_cert->errors |= GRST_CERT_BAD_CHAIN; + chain_errors |= GRST_CERT_BAD_CHAIN; + } + + /* Proxy subject must begin with issuer. */ + if (strncmp(new_grst_cert->dn, new_grst_cert->issuer, len2) != 0) + { + new_grst_cert->errors |= GRST_CERT_BAD_CHAIN; + chain_errors |= GRST_CERT_BAD_CHAIN; + } + + /* Set pointer to end of base DN in cert_DN */ + proxy_part_DN = &(new_grst_cert->dn[len2]); + + /* First attempt at support for Old and New style GSI + proxies: /CN=anything is ok for now */ + if (strncmp(proxy_part_DN, "/CN=", 4) != 0) + { + new_grst_cert->errors |= GRST_CERT_BAD_CHAIN; + chain_errors |= GRST_CERT_BAD_CHAIN; + } + + if (strncmp(proxy_part_DN, "/CN=limited proxy", 17) == 0) + prevIsLimited = 1; /* ready for next cert ... */ + + for (j=0; j < X509_get_ext_count(cert); ++j) + { + ex = X509_get_ext(cert, j); + OBJ_obj2txt(s,sizeof(s),X509_EXTENSION_get_object(ex),1); + + if (strcmp(s, GRST_VOMS_OID) == 0) /* a VOMS extension */ + { + GRSTx509ChainVomsAdd(&grst_cert, + new_grst_cert->notbefore, + new_grst_cert->notafter, + (lastcert == NULL) ? i : i+1, + ex, + user_cert, + vomsdir, + capath); + } + } + } + } + + + } /* end of for loop */ + + if (cacert != NULL) X509_free(cacert); + + return GRST_RET_OK; +} + +/// Check certificate chain for GSI proxy acceptability. +int GRSTx509ChainLoadCheck(GRSTx509Chain **chain, + STACK_OF(X509) *certstack, X509 *lastcert, + char *capath, char *vomsdir) +/// +/// Returns GRST_RET_OK if valid; caNl errors otherwise. +/// +/// The GridSite version handles old and new style Globus proxies, and +/// proxies derived from user certificates issued with "X509v3 Basic +/// Constraints: CA:FALSE" (eg UK e-Science CA) +/// +/// TODO: we do not yet check ProxyCertInfo and ProxyCertPolicy extensions +/// (although via GRSTx509KnownCriticalExts() we can accept them.) +{ + canl_ctx cctx = NULL; + int err = 0; + + cctx = canl_create_ctx(); + if (cctx == NULL) + return GRST_RET_FAILED; + + /* Use caNl to check the certificate*/ + err = canl_verify_chain(cctx, NULL, certstack, capath); + + /* Then fetch info about chain into grst structures */ + GRSTx509ChainLoad(chain, certstack, lastcert, capath, vomsdir); + + canl_free_ctx(cctx); + + return err; +} + +/* Check certificate chain for GSI proxy acceptability. */ +int GRSTx509CheckChain(int *first_non_ca, X509_STORE_CTX *store_ctx) +/* Returns X509_V_OK/GRST_RET_OK if valid; caNl errors otherwise. + We do not check chain links between certs here: this is done by + GRST_check_issued/X509_check_issued in mod_ssl's ssl_engine_init.c */ +{ + canl_err_code ret = 0; /* Canl error code */ + + canl_ctx cctx = NULL; + + /* Look for the store context */ + if (!store_ctx) + return X509_V_ERR_INVALID_CA; /* TODO really not clever*/ + + cctx = canl_create_ctx(); + if (cctx == NULL) + return GRST_RET_FAILED; + + /* Verify chain using caNl, without openssl part */ + ret = canl_verify_chain_wo_ossl(cctx, NULL, store_ctx); + + canl_free_ctx(cctx); + + return ret; /* this is also GRST_RET_OK, of course - by choice */ +} + +/// Example VerifyCallback routine +int GRSTx509VerifyCallback (int ok, X509_STORE_CTX *ctx) +{ + int errnum = X509_STORE_CTX_get_error(ctx); + int errdepth = X509_STORE_CTX_get_error_depth(ctx); + int first_non_ca; + +#ifndef X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION +#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34 +#endif + + if (errnum == X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) + { + if (GRSTx509KnownCriticalExts(X509_STORE_CTX_get_current_cert(ctx)) + == GRST_RET_OK) + { + ok = TRUE; + errnum = X509_V_OK; + X509_STORE_CTX_set_error(ctx, errnum); + } + } + else if ((errdepth == 0) && + (errnum == X509_V_OK) && + (GRSTx509CheckChain(&first_non_ca, ctx) != X509_V_OK)) ok = FALSE; + + + return ok; + +// check this + +// if (ok) return GRST_RET_OK; +// else return GRST_RET_FAILED; +} + +/// Get the VOMS attributes in the given extension +int GRSTx509ParseVomsExt(int *lastcred, int maxcreds, size_t credlen, + char *creds, time_t time1_time, time_t time2_time, + X509_EXTENSION *ex, + char *ucuserdn, char *ucissuerdn, char *ucserial, + char *vomsdir) +/// +/// Puts any VOMS credentials found into the Compact Creds string array +/// starting at *creds. Always returns GRST_RET_OK - even for invalid +/// credentials, which are just ignored. +{ +#define MAXTAG 500 +#define GRST_ASN1_COORDS_FQAN "-1-1-%d-1-7-1-2-1-2-%d" +#define GRST_ASN1_COORDS_ISSUER_DN "-1-1-%d-1-2-1-1-1-1-%%d-1-%%d" +#define GRST_ASN1_COORDS_TIME1 "-1-1-%d-1-6-1" +#define GRST_ASN1_COORDS_TIME2 "-1-1-%d-1-6-2" + ASN1_OCTET_STRING *asn1data; + char *asn1string, acissuerdn[200], + dn_coords[200], fqan_coords[200], time1_coords[200], + time2_coords[200], serial_coords[200]; + long asn1length; + int lasttag=-1, itag, i, acnumber = 1; + char *acissuerserial = NULL; + struct GRSTasn1TagList taglist[MAXTAG+1]; + time_t actime1, actime2, time_now; + ASN1_INTEGER acissuerserialASN1; + + asn1data = X509_EXTENSION_get_data(ex); + asn1string = ASN1_STRING_data(asn1data); + asn1length = ASN1_STRING_length(asn1data); + + GRSTasn1ParseDump(NULL, asn1string, asn1length, taglist, MAXTAG, &lasttag); + + for (acnumber = 1; ; ++acnumber) /* go through ACs one by one */ + { + /* check names */ + + snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_ISSUER_DN, acnumber); + if (GRSTasn1GetX509Name(acissuerdn, sizeof(acissuerdn), dn_coords, + asn1string, taglist, lasttag) != GRST_RET_OK) break; + + if ((GRSTx509NameCmp(ucuserdn, acissuerdn) != 0) && /* old */ + (GRSTx509NameCmp(ucissuerdn, acissuerdn) != 0)) /* new */ + continue; + + /* check serial numbers */ + + snprintf(serial_coords, sizeof(serial_coords), + GRST_ASN1_COORDS_ISSUER_SERIAL, acnumber); + + itag = GRSTasn1SearchTaglist(taglist, lasttag, serial_coords); + + if (itag > -1) + { + acissuerserialASN1.length = taglist[itag].length; + acissuerserialASN1.type = V_ASN1_INTEGER; + acissuerserialASN1.data = &asn1string[taglist[itag].start+taglist[itag].headerlength]; + + acissuerserial = i2s_ASN1_INTEGER(NULL, &acissuerserialASN1); +/* + p = &asn1string[taglist[itag].start+taglist[itag].headerlength]; + + if (taglist[itag].length == 2) + acissuerserial = p[1] + p[0] * 0x100; + else if (taglist[itag].length == 3) + acissuerserial = p[2] + p[1] * 0x100 + p[0] * 0x10000; + else if (taglist[itag].length == 4) + acissuerserial = p[3] + p[2] * 0x100 + p[1] * 0x10000 + + p[0] * 0x1000000; +*/ + } + + if (strcmp(acissuerserial, ucserial) != 0) continue; + + if (GRSTx509VerifyVomsSig(&time1_time, &time2_time, + asn1string, taglist, lasttag, vomsdir, acnumber) + != GRST_RET_OK) continue; + + snprintf(time1_coords, sizeof(time1_coords), GRST_ASN1_COORDS_TIME1, acnumber); + itag = GRSTasn1SearchTaglist(taglist, lasttag, time1_coords); + actime1 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+ + taglist[itag].headerlength], + taglist[itag].length); + if (actime1 > time1_time) time1_time = actime1; + + snprintf(time2_coords, sizeof(time2_coords), GRST_ASN1_COORDS_TIME2, acnumber); + itag = GRSTasn1SearchTaglist(taglist, lasttag, time2_coords); + actime2 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+ + taglist[itag].headerlength], + taglist[itag].length); + if (actime2 < time2_time) time2_time = actime2; + + time(&time_now); + if ((time1_time > time_now + 300) || (time2_time < time_now)) + continue; /* expiration isnt invalidity ...? */ + + for (i=1; ; ++i) + { + snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, i); + itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords); + + if (itag > -1) + { + if (*lastcred < maxcreds - 1) + { + ++(*lastcred); + snprintf(&creds[*lastcred * (credlen + 1)], credlen+1, + "VOMS %010lu %010lu 0 %.*s", + time1_time, time2_time, + taglist[itag].length, + &asn1string[taglist[itag].start+ + taglist[itag].headerlength]); + } + } + else break; + } + } + + return GRST_RET_OK; +} + +/// Get the VOMS attributes in the extensions to the given cert stack +int GRSTx509GetVomsCreds(int *lastcred, int maxcreds, size_t credlen, + char *creds, X509 *usercert, STACK_OF(X509) *certstack, + char *vomsdir) +/// +/// Puts any VOMS credentials found into the Compact Creds string array +/// starting at *creds. Always returns GRST_RET_OK. +{ + int i, j; + char s[80], *ucserial; + unsigned char *ucuser, *ucissuer; + X509_EXTENSION *ex; + X509 *cert; + time_t time1_time = 0, time2_time = 0, uctime1_time, uctime2_time; + + uctime1_time = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(usercert)),0); + uctime2_time = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(usercert)),0); + ucuser = + X509_NAME_oneline(X509_get_subject_name(usercert), NULL, 0); + ucissuer = + X509_NAME_oneline(X509_get_issuer_name(usercert), NULL, 0); + ucserial = i2s_ASN1_INTEGER(NULL, X509_get_serialNumber(usercert)); + + for (j=sk_X509_num(certstack)-1; j >= 0; --j) + { + cert = sk_X509_value(certstack, j); + + time1_time = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0); + uctime1_time = (time1_time > uctime1_time) ? time1_time:uctime1_time; + + time2_time = + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0); + uctime2_time = (time2_time < uctime2_time) ? time2_time:uctime2_time; + + for (i=0; i < X509_get_ext_count(cert); ++i) + { + ex = X509_get_ext(cert, i); + OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 1); + + if (strcmp(s, GRST_VOMS_OID) == 0) /* a VOMS extension */ + { + GRSTx509ParseVomsExt(lastcred, maxcreds, credlen, creds, + uctime1_time, uctime2_time, + ex, ucuser, ucissuer, ucserial, + vomsdir); + } + } + } + + return GRST_RET_OK; +} + +/// Turn a Compact Cred line into a GRSTgaclCred object +GRSTgaclCred *GRSTx509CompactToCred(char *grst_cred) +/// +/// Returns pointer to created GRSTgaclCred or NULL or failure. +{ + int delegation; + char *p, *encoded; + time_t now, notbefore, notafter; + GRSTgaclCred *cred = NULL; + + time(&now); + + if (grst_cred == NULL) return NULL; /* just in case */ + + if (strncmp(grst_cred, "X509USER ", 9) == 0) + { + if ((sscanf(grst_cred, "X509USER %lu %lu %d", + ¬before, ¬after, &delegation) == 3) + && (now >= notbefore) + && (now <= notafter) + && (p = index(grst_cred, ' ')) + && (p = index(++p, ' ')) + && (p = index(++p, ' ')) + && (p = index(++p, ' '))) + { + encoded = GRSThttpUrlMildencode(&p[1]); + cred = GRSTgaclCredCreate("dn:", encoded); + free(encoded); + GRSTgaclCredSetDelegation(cred, delegation); + } + + return cred; + } + + if (strncmp(grst_cred, "VOMS ", 5) == 0) + { + if ((sscanf(grst_cred, "VOMS %lu %lu %d", + ¬before, ¬after, &delegation) == 3) + && (now >= notbefore) + && (now <= notafter) + && (p = index(grst_cred, ' ')) + && (p = index(++p, ' ')) + && (p = index(++p, ' ')) + && (p = index(++p, ' '))) + { + /* include /VO/group/subgroup/Role=role/Capability=cap */ + + if (p[1] != '/') return NULL; /* must begin with / */ + + encoded = GRSThttpUrlMildencode(&p[1]); + cred = GRSTgaclCredCreate("fqan:", encoded); + free(encoded); + GRSTgaclCredSetDelegation(cred, delegation); + } + + return cred; + } + + return NULL; /* dont recognise this credential type */ +} + +/// Get the credentials in an X509 cert/GSI proxy, including any VOMS +int GRSTx509CompactCreds(int *lastcred, int maxcreds, size_t credlen, + char *creds, STACK_OF(X509) *certstack, char *vomsdir, + X509 *peercert) +/// +/// Credentials are placed in Compact Creds string array at *creds. +/// +/// Function returns GRST_RET_OK on success, or GRST_RET_FAILED if +/// some inconsistency found in certificate. +{ + int i, delegation = 0; + char credtemp[credlen+1]; + X509 *cert, *usercert = NULL, *gsiproxycert = NULL; + + *lastcred = -1; + + for (i = sk_X509_num(certstack) - 1; i >= 0; --i) + { + cert = sk_X509_value(certstack, i); + + if (usercert != NULL) + { /* found a (GSI proxy) cert after the user cert */ + gsiproxycert = cert; + ++delegation; + } + + if ((usercert == NULL) && + (i < sk_X509_num(certstack) - 1) && + (GRSTx509IsCA(cert) != GRST_RET_OK)) usercert = cert; + /* found the 1st non-CA cert */ + } + + if (peercert != NULL) + { + if (usercert != NULL) /* found a (GSI proxy) cert after user cert */ + { + gsiproxycert = peercert; + ++delegation; + } + + if ((usercert == NULL) && + (GRSTx509IsCA(peercert) != GRST_RET_OK)) usercert = peercert; + /* found the 1st non-CA cert */ + } + + if ((usercert == NULL) /* if no usercert ("EEC"), we're not interested */ + || + (snprintf(credtemp, credlen+1, "X509USER %010lu %010lu %d %s", + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(usercert)),0), + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(usercert)),0), + delegation, + X509_NAME_oneline(X509_get_subject_name(usercert), NULL, 0)) >= credlen+1) + || + (*lastcred >= maxcreds-1)) + { + *lastcred = -1; /* just in case the caller looks at it */ + return GRST_RET_FAILED; /* tell caller that things didn't work out */ + } + + ++(*lastcred); + strcpy(&creds[*lastcred * (credlen + 1)], credtemp); + + if ((gsiproxycert != NULL) + && + (snprintf(credtemp, credlen+1, "GSIPROXY %010lu %010lu %d %s", + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(gsiproxycert)),0), + GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(gsiproxycert)),0), + delegation, + X509_NAME_oneline(X509_get_subject_name(gsiproxycert), NULL, 0)) < credlen+1) + && + (*lastcred < maxcreds-1)) + { + ++(*lastcred); + strcpy(&creds[*lastcred * (credlen + 1)], credtemp); + + GRSTx509GetVomsCreds(lastcred, maxcreds, credlen, creds, + usercert, certstack, vomsdir); + + } + + return GRST_RET_OK; +} + +/// Find proxy file name of the current user +char *GRSTx509FindProxyFileName(void) +/// +/// Return a string with the proxy file name or NULL if not present. +/// This function does not check if the proxy has expired. +{ + char *p; + + p = getenv("X509_USER_PROXY"); + + if (p != NULL) return strdup(p); + + p = malloc(sizeof("/tmp/x509up_uXYYYXXXYYY")); + + sprintf(p, "/tmp/x509up_u%d", getuid()); + + return p; +} + +static void mpcerror(FILE *debugfp, char *msg) +{ + if (debugfp != NULL) + { + fputs(msg, debugfp); + ERR_print_errors_fp(debugfp); + } +} + +/// Make a GSI Proxy chain from a request, certificate and private key +int GRSTx509MakeProxyCert(char **proxychain, FILE *debugfp, + char *reqtxt, char *cert, char *key, int minutes) +/// +/// The proxy chain is returned in *proxychain. If debugfp is non-NULL, +/// errors are output to that file pointer. The proxy will expired in +/// the given number of minutes starting from the current time. +{ + char *ptr = NULL, *certchain = NULL; + static unsigned char pci_str[] = { 0x30, 0x0c, 0x30, 0x0a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x15, 0x01, 0 }, + kyu_str[] = { 0x03, 0x02, 0x03, + X509v3_KU_DIGITAL_SIGNATURE | + X509v3_KU_KEY_ENCIPHERMENT | + X509v3_KU_KEY_AGREEMENT, + 0 }; + int i = 0, ncerts = 0, any_rfc_proxies = 0; + long ptrlen = 0; + EVP_PKEY *pkey = NULL, *signer_pkey = NULL; + X509 *certs[GRST_MAX_CHAIN_LEN]; + X509_REQ *req = NULL; + ASN1_OBJECT *pci_obj = NULL, *kyu_obj = NULL; + ASN1_OCTET_STRING *pci_oct = NULL, *kyu_oct = NULL; + FILE *fp = NULL; + BIO *reqmem = NULL, *certmem = NULL; + canl_ctx ctx = NULL; + int retval = 1, ret = 0; + canl_cred proxy_cert = NULL, signer = NULL; + + ctx = canl_create_ctx(); + if (ctx == NULL) { + fprintf(debugfp, "GRSTx509MakeProxyCert(): Failed to create" + " caNl library context\n"); + return 1; + } + + /* read in the request */ + reqmem = BIO_new(BIO_s_mem()); + BIO_puts(reqmem, reqtxt); + + if (!(req = PEM_read_bio_X509_REQ(reqmem, NULL, NULL, NULL))) { + fprintf(debugfp, "GRSTx509MakeProxyCert(): error reading" + " request from BIO memory\n"); + goto end; + } + BIO_free(reqmem); + reqmem = NULL; + + /* load req into canl cred. context */ + ret = canl_cred_new(ctx, &proxy_cert); + if (ret) { + fprintf(debugfp, "GRSTx509MakeProxyCert(): caNl cred. context" + " cannot be created: %s\n", canl_get_error_message(ctx)); + goto end; + } + ret = canl_cred_load_req(ctx, proxy_cert, req); + if (ret) { + fprintf(stderr, "GRSTx509MakeProxyCert(): Failed to load certificate " + "request container: %s\n", canl_get_error_message(ctx)); + goto end; + } + + /*TODO MP Should proxy sognature verification be in caN???*/ + /* verify signature on the request */ + if (!(pkey = X509_REQ_get_pubkey(req))) { + mpcerror(debugfp, "GRSTx509MakeProxyCert(): error getting public" + " key from request\n"); + } + + if (X509_REQ_verify(req, pkey) != 1) { + mpcerror(debugfp, "GRSTx509MakeProxyCert(): error verifying signature" + " on certificate\n"); + goto end; + } + EVP_PKEY_free(pkey); + pkey = NULL; + X509_REQ_free(req); + req = NULL; + + /* read in the signing certificate */ + if (!(fp = fopen(cert, "r"))) { + mpcerror(debugfp, "GRSTx509MakeProxyCert(): error opening" + " signing certificate file\n"); + goto end; + } + /* load req signer cert into caNl cred. context*/ + ret = canl_cred_new(ctx, &signer); + if (ret) { + fprintf(debugfp, "GRSTx509MakeProxyCert(): caNl cred. context" + " cannot be created: %s\n", canl_get_error_message(ctx)); + goto end; + } + + for (ncerts = 1; ncerts < GRST_MAX_CHAIN_LEN; ++ncerts) + if ((certs[ncerts] = PEM_read_X509(fp, NULL, NULL, NULL)) == NULL) + break; + /* zeroth cert will be new proxy cert */ + if (ncerts == 1) { + mpcerror(debugfp, "GRSTx509MakeProxyCert(): error reading" + " signing certificate file\n"); + goto end; + } + fclose(fp); + fp = NULL; + + /* read in the signer certificate*/ + ret = canl_cred_load_cert_file(ctx, signer, cert); + if (ret){ + fprintf(stderr, "[DELEGATION] Cannot load signer's certificate" + ": %s\n", canl_get_error_message(ctx)); + goto end; + } + + + /* read in the signer private key */ + if (!(fp = fopen(key, "r"))) { + mpcerror(debugfp, "GRSTx509MakeProxyCert(): error reading" + " signing private key file\n"); + goto end; + } + + if (!(signer_pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL))) { + mpcerror(debugfp, "GRSTx509MakeProxyCert(): error reading " + "signing private key in file\n"); + goto end; + } + fclose(fp); + fp = NULL; + canl_cred_load_priv_key(ctx, signer, signer_pkey); + EVP_PKEY_free(signer_pkey); + signer_pkey = NULL; + + /*TODO MP Compare with VOMS in caNl (orig gridsite proxyver = 2L)*/ + /* set version number for the certificate (X509v3) and the serial number + We now use 2 = v3 for the GSI proxy, rather than the old Globus + behaviour of 3 = v4. See Savannah Bug #53721 */ + + /* TODO MP what about serial numbers? Should caNl provide API function for + * setting custom serial number?*/ + /* ASN1_INTEGER_set(X509_get_serialNumber(certs[0]), (long) time(NULL));*/ + + /* TODO MP use some default timeout?*/ + if (minutes >= 0) { + ret = canl_cred_set_lifetime(ctx, proxy_cert, 60 * minutes); + if (ret) + fprintf(debugfp, "[DELEGATION] Failed set new cert lifetime" + ": %s\n", canl_get_error_message(ctx)); + } + + /* go through chain making sure this proxy is not longer lived */ + + pci_obj = OBJ_txt2obj(GRST_PROXYCERTINFO_OID, 0); + + /* TODO MP is this necessary? caNl test if new proxy timeout + * is longer than signer cert proxy timeout */ + for (i=1; i < ncerts; ++i) + if (X509_get_ext_by_OBJ(certs[i], pci_obj, -1) > 0) + any_rfc_proxies = 1; + + /* if any earlier proxies are RFC 3820, then new proxy must be + an RFC 3820 proxy too with the required extensions */ + if (any_rfc_proxies) { + X509_EXTENSION *pci_ex = NULL, *kyu_ex = NULL; + /* key usage */ + kyu_obj = OBJ_txt2obj(GRST_KEYUSAGE_OID, 0); + kyu_ex = X509_EXTENSION_new(); + + X509_EXTENSION_set_object(kyu_ex, kyu_obj); + ASN1_OBJECT_free(kyu_obj); + X509_EXTENSION_set_critical(kyu_ex, 1); + + kyu_oct = ASN1_OCTET_STRING_new(); + ASN1_OCTET_STRING_set(kyu_oct, kyu_str, strlen(kyu_str)); + X509_EXTENSION_set_data(kyu_ex, kyu_oct); + ASN1_OCTET_STRING_free(kyu_oct); + + canl_cred_set_extension(ctx, proxy_cert, kyu_ex); + X509_EXTENSION_free(kyu_ex); + + /* proxy certificate info */ + pci_ex = X509_EXTENSION_new(); + X509_EXTENSION_set_object(pci_ex, pci_obj); + X509_EXTENSION_set_critical(pci_ex, 1); + + pci_oct = ASN1_OCTET_STRING_new(); + ASN1_OCTET_STRING_set(pci_oct, pci_str, strlen(pci_str)); + X509_EXTENSION_set_data(pci_ex, pci_oct); + ASN1_OCTET_STRING_free(pci_oct); + + canl_cred_set_extension(ctx, proxy_cert, kyu_ex); + X509_EXTENSION_free(pci_ex); + } + ASN1_OBJECT_free(pci_obj); + pci_obj = NULL; + + /* Sign the proxy */ + ret = canl_cred_sign_proxy(ctx, signer, proxy_cert); + if (ret){ + fprintf(stderr, "[DELEGATION] Cannot sign new proxy" + ": %s\n", canl_get_error_message(ctx)); + goto end; + } + + ret = canl_cred_save_cert(ctx, proxy_cert, &certs[0]); + if (ret) { + fprintf(stderr, "GRSTx509MakeProxyCert(): Cannot save new cert file" + ": %s\n", canl_get_error_message(ctx)); + goto end; + } + canl_free_ctx(ctx); + ctx = NULL; + + /* store the completed certificate chain */ + certchain = strdup(""); + for (i=0; i < ncerts; ++i) { + certmem = BIO_new(BIO_s_mem()); + + if (PEM_write_bio_X509(certmem, certs[i]) != 1) { + mpcerror(debugfp, "GRSTx509MakeProxyCert(): error writing" + " certificate to memory BIO\n"); + goto end; + } + ptrlen = BIO_get_mem_data(certmem, &ptr); + certchain = realloc(certchain, strlen(certchain) + ptrlen + 1); + strncat(certchain, ptr, ptrlen); + + BIO_free(certmem); + certmem = NULL; + X509_free(certs[i]); + certs[i] = NULL; + } + ncerts = 0; + *proxychain = certchain; + retval = GRST_RET_OK; + +end: + if (reqmem) + BIO_free(reqmem); + if (pkey) + EVP_PKEY_free(pkey); + if (signer_pkey) + EVP_PKEY_free(signer_pkey); + if (req) + X509_REQ_free(req); + if (pci_obj) + ASN1_OBJECT_free(pci_obj); + if (fp) { + fclose(fp); + fp = NULL; + } + if (certmem) + BIO_free(certmem); + if (ctx) + canl_free_ctx(ctx); + for (i=0; i < ncerts; ++i) { + if (certs[i]) + X509_free(certs[i]); + } + if (proxy_cert) + canl_cred_free(ctx, proxy_cert); + if (signer) + canl_cred_free(ctx, signer); + return retval; +} + +/// Find a proxy file in the proxy cache +char *GRSTx509CachedProxyFind(char *proxydir, char *delegation_id, + char *user_dn) +/// +/// Returns the full path and file name of proxy file associated +/// with given delegation ID and user DN. +/// +/// Return a pointer to a malloc'd string with the full path of the +/// proxy file corresponding to the given delegation_id, or NULL +/// if not found. +{ + char *user_dn_enc, *proxyfile; + struct stat statbuf; + + user_dn_enc = GRSThttpUrlEncode(user_dn); + + asprintf(&proxyfile, "%s/%s/%s/userproxy.pem", + proxydir, user_dn_enc, delegation_id); + + free(user_dn_enc); + + if ((stat(proxyfile, &statbuf) != 0) || !S_ISREG(statbuf.st_mode)) + { + free(proxyfile); + return NULL; + } + + return proxyfile; +} + +/// Find a temporary proxy private key file in the proxy cache +char *GRSTx509CachedProxyKeyFind(char *proxydir, char *delegation_id, + char *user_dn) +/// +/// Returns the full path and file name of the private key file associated +/// with given delegation ID and user DN. +/// +/// Return a pointer to a malloc'd string with the full path of the +/// private proxy key corresponding to the given delegation_id, or NULL +/// if not found. +{ + char *user_dn_enc, *prvkeyfile; + struct stat statbuf; + + user_dn_enc = GRSThttpUrlEncode(user_dn); + + asprintf(&prvkeyfile, "%s/cache/%s/%s/userkey.pem", + proxydir, user_dn_enc, delegation_id); + + free(user_dn_enc); + + if ((stat(prvkeyfile, &statbuf) != 0) || !S_ISREG(statbuf.st_mode)) + { + free(prvkeyfile); + return NULL; + } + + return prvkeyfile; +} + +static void mkdir_printf(mode_t mode, char *fmt, ...) +{ + int ret; + char *path; + va_list ap; + + va_start(ap, fmt); + vasprintf(&path, fmt, ap); + va_end(ap); + + ret = mkdir(path, mode); + + free(path); +} + +/// Create a X.509 request for a GSI proxy and its private key +int GRSTx509CreateProxyRequest(char **reqtxt, char **keytxt, char *ocspurl) +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. Request string +/// and private key are PEM encoded strings +{ + char *ptr = 0; + size_t ptrlen = 0; + EVP_PKEY *pkey = NULL; + X509_REQ *req = NULL; + BIO *reqmem = NULL, *keymem = NULL; + canl_cred proxy_bob = NULL; + int retval = 0; + canl_ctx c_ctx = NULL; + int ret = 0; + + /*Make new canl_ctx, TODO MP how to initialize it only once?*/ + c_ctx = canl_create_ctx(); + if (c_ctx == NULL) { + return 10; /* TODO MP we can use caNl error codes now */ + } + + ret = canl_cred_new(c_ctx, &proxy_bob); + if (ret){ + retval = 11; + goto end; + } + + /*use caNl to generate a X509 request*/ + ret = canl_cred_new_req(c_ctx, proxy_bob, GRST_KEYSIZE); + if (ret) { + retval = 12; + goto end; + } + + ret = canl_cred_save_req(c_ctx, proxy_bob, &req); + if (ret) { + retval = 13; + goto end; + } + + /*Convert request into a string*/ + reqmem = BIO_new(BIO_s_mem()); + PEM_write_bio_X509_REQ(reqmem, req); + ptrlen = BIO_get_mem_data(reqmem, &ptr); + + *reqtxt = malloc(ptrlen + 1); + memcpy(*reqtxt, ptr, ptrlen); + (*reqtxt)[ptrlen] = '\0'; + + /* Put keypair in a PEM string */ + ret = canl_cred_save_priv_key(c_ctx, proxy_bob, &pkey); + if (ret){ + retval = 15; + goto end; + } + keymem = BIO_new(BIO_s_mem()); + if (!PEM_write_bio_PrivateKey(keymem, pkey, NULL, NULL, 0, NULL, NULL)) + { + BIO_free(keymem); + return 3; + } + + ptrlen = BIO_get_mem_data(keymem, &ptr); + *keytxt = malloc(ptrlen + 1); + memcpy(*keytxt, ptr, ptrlen); + (*keytxt)[ptrlen] = '\0'; + +end: + if (proxy_bob) + canl_cred_free(c_ctx, proxy_bob); + if (pkey) + EVP_PKEY_free(pkey); + if (reqmem) + BIO_free(reqmem); + if (keymem) + BIO_free(keymem); + if (req) + X509_REQ_free(req); + if (c_ctx) + canl_free_ctx(c_ctx); + + return retval; +} + +/// Make and store a X.509 request for a GSI proxy +int GRSTx509MakeProxyRequest(char **reqtxt, char *proxydir, + char *delegation_id, char *user_dn) +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. Request string +/// is PEM encoded, and the key is stored in the temporary cache under +/// proxydir +{ + char *prvkeyfile = NULL, *ptr = NULL, *user_dn_enc = NULL; + size_t ptrlen = 0; + FILE *fp = NULL; + EVP_PKEY *pkey = NULL; + BIO *reqmem = NULL; + canl_ctx c_ctx = NULL; + canl_cred proxy_bob = NULL; + X509_REQ *req = NULL; + int retval = GRST_RET_OK; + int ret = 0; + + if (strcmp(user_dn, "cache") == 0) + return GRST_RET_FAILED; + + user_dn_enc = GRSThttpUrlEncode(user_dn); + + /* create directories if necessary */ + + mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, + "%s/cache", proxydir); + mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, + "%s/cache/%s", proxydir, user_dn_enc); + mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, + "%s/cache/%s/%s", proxydir, user_dn_enc, delegation_id); + + /* make the new proxy private key */ + + asprintf(&prvkeyfile, "%s/cache/%s/%s/userkey.pem", + proxydir, user_dn_enc, delegation_id); + + if (prvkeyfile == NULL){ + free(user_dn_enc); + return GRST_RET_FAILED; + } + + /*Make new canl_ctx, TODO MP how to initialize it only once?*/ + c_ctx = canl_create_ctx(); + if (c_ctx == NULL) { + free(prvkeyfile); + return 10; /* TODO MP we can use caNl error codes now */ + } + + ret = canl_cred_new(c_ctx, &proxy_bob); + if (ret){ + retval = 11; + goto end; + } + + /*use caNl to generate X509 request*/ + ret = canl_cred_new_req(c_ctx, proxy_bob, GRST_KEYSIZE); + if (ret) { + retval = 12; + goto end; + } + + ret = canl_cred_save_req(c_ctx, proxy_bob, &req); + if (ret) { + retval = 13; + goto end; + } + + if ((fp = fopen(prvkeyfile, "w")) == NULL) + { + retval = 14; + goto end; + } + + /*Save private key in cache*/ + chmod(prvkeyfile, S_IRUSR | S_IWUSR); + free(prvkeyfile); + prvkeyfile = NULL; + free(user_dn_enc); + user_dn_enc = NULL; + + ret = canl_cred_save_priv_key(c_ctx, proxy_bob, &pkey); + if (ret) { + retval = 15; + goto end; + } + + if (!PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL)) { + retval = 3; + goto end; + } + + if (fclose(fp) != 0){ + retval = 4; + goto end; + } + + /* TODO MP ask. "Dummy" vs "proxy" in sslutils.c from Voms + ent=X509_NAME_ENTRY_create_by_NID(NULL, OBJ_txt2nid("organizationName"), + MBSTRING_ASC, "Dummy", -1); + TODO MD5 vs SHA1 defaults. + */ + + /*Convert request into string*/ + reqmem = BIO_new(BIO_s_mem()); + PEM_write_bio_X509_REQ(reqmem, req); + ptrlen = BIO_get_mem_data(reqmem, &ptr); + + *reqtxt = malloc(ptrlen + 1); + memcpy(*reqtxt, ptr, ptrlen); + (*reqtxt)[ptrlen] = '\0'; +end: + if (proxy_bob) + canl_cred_free(c_ctx, proxy_bob); + if (reqmem) + BIO_free(reqmem); + if (req) + X509_REQ_free(req); + if (prvkeyfile) + free(prvkeyfile); + if (user_dn_enc) + free(user_dn_enc); + if (c_ctx) + canl_free_ctx(c_ctx); + + return retval; +} + +/// Destroy stored GSI proxy files +int GRSTx509ProxyDestroy(char *proxydir, char *delegation_id, char *user_dn) +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. +/// (Including GRST_RET_NO_SUCH_FILE if the private key or cert chain +/// were not found.) +{ + int ret = GRST_RET_OK; + char *filename, *user_dn_enc; + + if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED; + + user_dn_enc = GRSThttpUrlEncode(user_dn); + + /* proxy file */ + + asprintf(&filename, "%s/%s/%s/userproxy.pem", + proxydir, user_dn_enc, delegation_id); + + if (filename == NULL) + { + free(user_dn_enc); + return GRST_RET_FAILED; + } + + if (unlink(filename) != 0) ret = GRST_RET_NO_SUCH_FILE; + free(filename); + + /* voms file */ + + asprintf(&filename, "%s/%s/%s/voms.attributes", + proxydir, user_dn_enc, delegation_id); + + if (filename == NULL) + { + free(user_dn_enc); + return GRST_RET_FAILED; + } + + unlink(filename); + free(filename); + + return ret; +} + +/// Get start and finish validity times of stored GSI proxy file +int GRSTx509ProxyGetTimes(char *proxydir, char *delegation_id, char *user_dn, + time_t *start, time_t *finish) +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. +/// (Including GRST_RET_NO_SUCH_FILE if the cert chain was not found.) +{ + char *docroot, *filename, *user_dn_enc; + FILE *fp; + X509 *cert; + + if (strcmp(user_dn, "cache") == 0) return GRST_RET_FAILED; + + user_dn_enc = GRSThttpUrlEncode(user_dn); + + asprintf(&filename, "%s/%s/%s/userproxy.pem", + proxydir, user_dn_enc, delegation_id); + + free(user_dn_enc); + + if (filename == NULL) return GRST_RET_FAILED; + + fp = fopen(filename, "r"); + free(filename); + + if (fp == NULL) return GRST_RET_NO_SUCH_FILE; + + cert = PEM_read_X509(fp, NULL, NULL, NULL); /* first cert is X.509 PC */ + + fclose(fp); + + *start = GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notBefore(cert)),0); + *finish = GRSTasn1TimeToTimeT(ASN1_STRING_data(X509_get_notAfter(cert)),0); + + X509_free(cert); + + return GRST_RET_OK; +} + +/// Create a stack of X509 certificate from a PEM-encoded string +int GRSTx509StringToChain(STACK_OF(X509) **certstack, char *certstring) +/// +/// Creates a dynamically allocated stack of X509 certificate objects +/// by walking through the PEM-encoded X509 certificates. +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. +{ + STACK_OF(X509_INFO) *sk=NULL; + BIO *certbio; + X509_INFO *xi; + + *certstack = sk_X509_new_null(); + if (*certstack == NULL) return GRST_RET_FAILED; + + certbio = BIO_new_mem_buf(certstring, -1); + + if (!(sk=PEM_X509_INFO_read_bio(certbio, NULL, NULL, NULL))) + { + BIO_free(certbio); + sk_X509_INFO_free(sk); + sk_X509_free(*certstack); + return GRST_RET_FAILED; + } + + while (sk_X509_INFO_num(sk)) + { + xi=sk_X509_INFO_shift(sk); + if (xi->x509 != NULL) + { + sk_X509_push(*certstack, xi->x509); + xi->x509=NULL; + } + X509_INFO_free(xi); + } + + if (!sk_X509_num(*certstack)) + { + BIO_free(certbio); + sk_X509_INFO_free(sk); + sk_X509_free(*certstack); + return GRST_RET_FAILED; + } + + BIO_free(certbio); + sk_X509_INFO_free(sk); + + return GRST_RET_OK; +} + +/// Returns a Delegation ID based on hash of GRST_CRED_0, ... +char *GRSTx509MakeDelegationID(void) +/// +/// Returns a malloc'd string with Delegation ID made by SHA1-hashing the +/// values of the compact credentials exported by mod_gridsite +{ + unsigned char hash_delegation_id[EVP_MAX_MD_SIZE]; + int i, delegation_id_len; + char cred_name[14], *cred_value, *delegation_id; + const EVP_MD *m; + EVP_MD_CTX ctx; + + OpenSSL_add_all_digests(); + + m = EVP_sha1(); + if (m == NULL) return NULL; + + EVP_DigestInit(&ctx, m); + + for (i=0; i <= 999; ++i) + { + snprintf(cred_name, sizeof(cred_name), "GRST_CRED_%d", i); + if ((cred_value = getenv(cred_name)) == NULL) break; + + EVP_DigestUpdate(&ctx, cred_value, strlen(cred_value)); + } + + EVP_DigestFinal(&ctx, hash_delegation_id, &delegation_id_len); + + delegation_id = malloc(17); + + for (i=0; i <=7; ++i) + sprintf(&delegation_id[i*2], "%02x", hash_delegation_id[i]); + + delegation_id[16] = '\0'; + + return delegation_id; +} + +#if 0 +/// Return the short file name for the given delegation_id and user_dn +char *GRSTx509MakeProxyFileName(char *delegation_id, + STACK_OF(X509) *certstack) +/// +/// Returns a malloc'd string with the short file name (no paths) that +/// derived from the hashed delegation_id and user_dn +/// +/// File name is SHA1_HASH(DelegationID)+"-"+SHA1_HASH(DN) where DN +/// is DER encoded version of user_dn with any trailing CN=proxy removed +/// Hashes are the most significant 8 bytes, in lowercase hexadecimal. +{ + int i, depth, prevIsCA = 1, IsCA, hash_name_len, delegation_id_len, + der_name_len; + unsigned char *der_name, *buf, hash_name[EVP_MAX_MD_SIZE], + hash_delegation_id[EVP_MAX_MD_SIZE], + filename[34]; + X509_NAME *subject_name; + X509 *cert; + const EVP_MD *m; + EVP_MD_CTX ctx; + + depth = sk_X509_num(certstack); + + for (i=depth-1; i >= 0; --i) + /* loop through the proxy chain starting at CA end */ + { + if (cert = sk_X509_value(certstack, i)) + { + IsCA = (GRSTx509IsCA(cert) == GRST_RET_OK); + + if (prevIsCA && !IsCA) /* the full certificate of the user */ + { + break; + } + } + } + + if (i < 0) return NULL; /* not found: something wrong with the chain */ + + if ((subject_name = X509_get_subject_name(cert)) == NULL) return NULL; + + der_name_len = i2d_X509_NAME(X509_get_subject_name(cert), NULL); + if (der_name_len == 0) return NULL; + + buf = OPENSSL_malloc(der_name_len); + der_name = buf; + + + if (!i2d_X509_NAME(X509_get_subject_name(cert), &der_name)) + { + OPENSSL_free(der_name); + return NULL; + } + + OpenSSL_add_all_digests(); + + m = EVP_sha1(); + if (m == NULL) + { + OPENSSL_free(der_name); + return NULL; + } + + + EVP_DigestInit(&ctx, m); + EVP_DigestUpdate(&ctx, delegation_id, strlen(delegation_id)); + EVP_DigestFinal(&ctx, hash_delegation_id, &delegation_id_len); + + /* lots of nasty hard coded numbers: + "8bytes/16chars delegation ID" + "-" + "8bytes/16chars DN" */ + + for (i=0; i <=7; ++i) + sprintf(&filename[i*2], "%02x", hash_delegation_id[i]); + + filename[16] = '-'; + + EVP_DigestInit(&ctx, m); + EVP_DigestUpdate(&ctx, buf, der_name_len); + EVP_DigestFinal(&ctx, hash_name, &hash_name_len); + + for (i=0; i <=7; ++i) + sprintf(&filename[17 + i*2], "%02x", hash_name[i]); + + return strdup(filename); +} +#endif + +/// Store a GSI proxy chain in the proxy cache, along with the private key +int GRSTx509CacheProxy(char *proxydir, char *delegation_id, + char *user_dn, char *proxychain) +/// +/// Returns GRST_RET_OK on success, non-zero otherwise. The existing +/// private key with the same delegation ID and user DN is moved out of +/// the temporary cache. +{ + int ret = 0; + int retval = GRST_RET_FAILED; + char *user_dn_enc, *prvkeyfile, *proxyfile; + STACK_OF(X509) *certstack; + canl_ctx c_ctx = NULL; + canl_cred proxy_bob = NULL; + + if (strcmp(user_dn, "cache") == 0) + return GRST_RET_FAILED; + + + /* get the X509 stack */ + if (GRSTx509StringToChain(&certstack, proxychain) != GRST_RET_OK){ + return GRST_RET_FAILED;/* TODO MP we can use caNl error codes now */ + } + + /*Make new canl_ctx, TODO MP how to initialize it only once?*/ + c_ctx = canl_create_ctx(); + if (c_ctx == NULL) + goto end; + + ret = canl_cred_new(c_ctx, &proxy_bob); + if (ret){ + goto end; /* TODO MP we can use caNl error codes now */ + } + + /*Use caNl to load certstack into proxy_bob*/ + ret = canl_cred_load_chain(c_ctx, proxy_bob, certstack); + if (ret){ + goto end; + } + + /* create directories if necessary, and set proxy filename */ + user_dn_enc = GRSThttpUrlEncode(user_dn); + + mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, + "%s/%s", proxydir, user_dn_enc); + mkdir_printf(S_IRUSR | S_IWUSR | S_IXUSR, + "%s/%s/%s", proxydir, user_dn_enc, delegation_id); + + asprintf(&proxyfile, "%s/%s/%s/userproxy.pem", + proxydir, user_dn_enc, delegation_id); + free(user_dn_enc); + user_dn_enc = NULL; + + /* find the existing private key file */ + prvkeyfile = GRSTx509CachedProxyKeyFind(proxydir, delegation_id, user_dn); + if (prvkeyfile == NULL) + goto end; + + /* insert proxy private key into canl structure, + read from private key file */ + ret = canl_cred_load_priv_key_file(c_ctx, proxy_bob, prvkeyfile, NULL, NULL); + if (ret) + goto end; + unlink(prvkeyfile); + free(prvkeyfile); + prvkeyfile = NULL; + + ret = canl_cred_save_proxyfile(c_ctx, proxy_bob, proxyfile); + if (ret) + goto end; + + retval = GRST_RET_OK; + +end: + if (proxyfile) + free(proxyfile); + if (proxy_bob) + canl_cred_free(c_ctx, proxy_bob); + if (certstack) + sk_X509_free(certstack); + if (prvkeyfile) + free(prvkeyfile); + if (user_dn_enc) + free(user_dn_enc); + if (c_ctx) + canl_free_ctx(c_ctx); + + return retval; +} diff --git a/org.gridsite.core/src/htcp.c b/org.gridsite.core/src/htcp.c index cbd9eb0..6d60720 100644 --- a/org.gridsite.core/src/htcp.c +++ b/org.gridsite.core/src/htcp.c @@ -895,7 +895,7 @@ int do_finds(char *sources[], struct sockaddr from; socklen_t fromlen; #define MAXBUF 8192 - char *request, response[MAXBUF], *p; + char *request, response[MAXBUF]; GRSThtcpMessage msg; struct timeval start_timeval, wait_timeval; struct grst_sitecast_group sitecast_groups[HTCP_SITECAST_GROUPS]; @@ -1014,7 +1014,7 @@ int translate_sitecast_url(char **source_ptr, struct sockaddr from; socklen_t fromlen; #define MAXBUF 8192 - char *request, response[MAXBUF], *p; + char *request, response[MAXBUF]; GRSThtcpMessage msg; struct timeval start_timeval, wait_timeval; struct grst_sitecast_group sitecast_groups[HTCP_SITECAST_GROUPS]; diff --git a/org.gridsite.core/src/htproxyput.c b/org.gridsite.core/src/htproxyput.c index 031a502..64a9bdb 100644 --- a/org.gridsite.core/src/htproxyput.c +++ b/org.gridsite.core/src/htproxyput.c @@ -265,12 +265,25 @@ int main(int argc, char *argv[]) fd = mkstemp(keycert); ofp = fdopen(fd, "w"); + if (!ofp) + { + printf(stderr, "Cannot open tmp file for the key\n"); + return 1; + } ifp = fopen(key, "r"); + { + printf(stderr, "Cannot open the file with the key\n"); + return 1; + } while ((c = fgetc(ifp)) != EOF) fputc(c, ofp); fclose(ifp); ifp = fopen(cert, "r"); + { + printf(stderr, "Cannot open the file with the cert\n"); + return 1; + } while ((c = fgetc(ifp)) != EOF) fputc(c, ofp); fclose(ifp); diff --git a/org.gridsite.core/src/make-debian-files b/org.gridsite.core/src/make-debian-files old mode 100755 new mode 100644 index e2f13bf..61c3016 --- a/org.gridsite.core/src/make-debian-files +++ b/org.gridsite.core/src/make-debian-files @@ -34,9 +34,7 @@ echo "3.0 (quilt)" > source/format cat > gridsite.install < gridsite.manpages < gridsite-gsexec.install < gridsite-gsexec.manpages < gridsite-service-clients.install < gridsite-slashgrid.dirs < gridsite-slashgrid.install < gridsite-slashgrid.manpages < libgridsite${MINOR_VERSION}.docs < libgridsite2.docs < libgridsite${MINOR_VERSION}.install < libgridsite2.install <fuse-test.c -#include -int main() { struct fuse_context ctx; -return fuse_main(0, (char **) 0, (struct fuse_operations *) 0); } -EOF -make fuse-test -if [ $? = 0 ] ; then have_fuse=1 ; fi - if [ x"$project" = x"glite" ]; then globusdep=',vdt_globus_sdk' else @@ -18,11 +8,11 @@ fi if [ x"$project" = x"emi1" ]; then shared_name="shared" - shared_provide_name="libs" + shared_provide="libs" shared_obsoletes="gridsite-libs < $PATCH_VERSION" else shared_name="libs" - shared_provide_name="shared" + shared_provide="shared" shared_obsoletes="gridsite-shared <= $PATCH_VERSION" fi @@ -38,9 +28,6 @@ make gridsite-delegation.cgi if [ $? = 0 ] ; then have_gsoap=1 ; fi rm -f a.out -if [ "$have_fuse" = "1" ]; then - fusedep=',fuse-devel' -fi if [ "$have_gsoap" = "1" ]; then gsoapdep=',gsoap-devel' fi @@ -62,8 +49,15 @@ Source: %{name}-%{version}.src.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root URL: http://www.gridsite.org/ Vendor: GridPP -Buildrequires: libxml2-devel,curl-devel,httpd-devel,openssl-devel,pkgconfig -Buildrequires: doxygen${gsoapdep}${fusedep}${globusdep} +BuildRequires: libxml2-devel%{?_isa},httpd-devel%{?_isa},openssl-devel%{?_isa},pkgconfig +BuildRequires: doxygen${gsoapdep}${globusdep} +BuildRequires: canl-c-devel%{?_isa} +%if %{?fedora}%{!?fedora:0} >= 10 || %{?rhel}%{!?rhel:0} >= 6 +BuildRequires: libcurl-devel%{?_isa} +%else +BuildRequires: curl-devel%{?_isa} +%endif +BuildRequires: libtool Packager: Andrew McNab Provides: gridsite-apache = %{version}-%{release} Obsoletes: gridsite-apache < %{version}-%{release} @@ -84,7 +78,7 @@ See http://www.gridsite.org/ for details. Group: Development/Libraries Summary: GridSite shared library and core documentation Obsoletes: $shared_obsoletes -Provides: gridsite-$shared_provide_name = %{version}-%{release} +Provides: gridsite-$shared_provide = %{version}-%{release} %description $shared_name GridSite shared library and core documentation @@ -114,37 +108,6 @@ HTTP/HTTPS rather than ssh as its transfer protocol. See http://www.gridsite.org/ for details. -%package gsexec -Group: Applications/Internet -Summary: gsexec binary for the Apache HTTP server - -%description gsexec -This package includes the /usr/sbin/gsexec binary which can be installed -to allow the Apache HTTP server to run CGI programs (and any programs -executed by SSI pages) as a user other than the 'apache' user. gsexec -is a drop-in replacement for suexec, with extended functionality for use -with GridSite and Grid Security credentials. - -See http://www.gridsite.org/ for details. - -EOF - -if [ x"$project" = x"emi1" ]; then -cat <>gridsite.spec -%package apache -Group: System Environment/Daemons -Summary: GridSite mod_gridsite module for Apache httpd - -%description apache -GridSite Apache module and CGI binaries. This is helper package to fix upgrade -from upstream gridsite 1.7.19. - -See http://www.gridsite.org/ for details. - -EOF -fi - -cat <>gridsite.spec %prep %setup -q @@ -155,13 +118,6 @@ make prefix=%{_prefix} EOF -if [ $have_fuse ] ; then -cat <>gridsite.spec -make prefix=%{_prefix} slashgrid - -EOF -fi - if [ $have_gsoap ] ; then cat <>gridsite.spec make prefix=%{_prefix} gridsite-delegation.cgi htproxyput @@ -176,23 +132,15 @@ rm -rf \$RPM_BUILD_ROOT cd src -make install prefix=\$RPM_BUILD_ROOT/%{_prefix} libdir=%{_lib} +make install prefix=%{_prefix} libdir=%{_lib} DESTDIR=\$RPM_BUILD_ROOT +rm -f \$RPM_BUILD_ROOT/%{_libdir}/*.a EOF -if [ $have_fuse ] ; then - -cat <>gridsite.spec - -mkdir -p \$RPM_BUILD_ROOT/etc/rc.d/init.d -make install-slashgrid prefix=\$RPM_BUILD_ROOT/%{_prefix} -EOF -fi - if [ $have_gsoap ] ; then cat <>gridsite.spec -make install-ws prefix=\$RPM_BUILD_ROOT/%{_prefix} +make install-ws prefix=%{_prefix} libdir=%{_lib} DESTDIR=\$RPM_BUILD_ROOT EOF fi @@ -214,8 +162,6 @@ fi %attr(-, root, root) %{_prefix}/share/man/man8/mod_gridsite.8.gz %attr(-, root, root) %{_prefix}/%{_lib}/httpd/modules/mod_gridsite.so %attr(-, root, root) %{_prefix}/sbin/real-gridsite-admin.cgi -%attr(-, root, root) %{_prefix}/sbin/gridsite-copy.cgi -%attr(-, root, root) %{_prefix}/sbin/gridsite-storage.cgi EOF if [ $have_gsoap ] ; then @@ -230,23 +176,17 @@ fi cat <>gridsite.spec %files $shared_name -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so.%{version} -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so.${MINOR_VERSION:-1.x} -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so.${MAJOR_VERSION:-1} -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so.%{version} -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so.${MINOR_VERSION:-1.x} -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so.${MAJOR_VERSION:-1} -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so.%{version} -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so.${MINOR_VERSION:-1.x} -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so.${MAJOR_VERSION:-1} %attr(-, root, root) %{_prefix}/share/doc/gridsite-${MINOR_VERSION:-1.x} +%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so.2 +%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so.2.* +%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so.2 +%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so.2.* +%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so.2 +%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so.2.* %files devel %attr(-, root, root) %{_prefix}/include/gridsite.h %attr(-, root, root) %{_prefix}/include/gridsite-gacl.h -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.a -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.a -%attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.a %attr(-, root, root) %{_prefix}/%{_lib}/libgridsite.so %attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_globus.so %attr(-, root, root) %{_prefix}/%{_lib}/libgridsite_nossl.so @@ -273,39 +213,8 @@ cat <>gridsite.spec %attr(-, root, root) %{_prefix}/share/man/man1/htfind.1.gz %attr(-, root, root) %{_prefix}/share/man/man1/urlencode.1.gz %attr(-, root, root) %{_prefix}/share/man/man1/findproxyfile.1.gz - -%files gsexec -%attr(4510, root, apache) %{_prefix}/sbin/gsexec -%attr(-, root, root) %{_prefix}/share/man/man8/gsexec.8.gz EOF -if [ $have_fuse ] ; then - -cat <>gridsite.spec -%package slashgrid -Group: Applications/Internet -Summary: slashgrid daemon -Requires: curl >= 7.12.1,fuse - -%description slashgrid -SlashGrid daemon - -%post slashgrid -mkdir -p /grid - -%preun slashgrid -/sbin/service slashgrid stop ; : - -%files slashgrid -%attr(0744, root, root) %{_prefix}/sbin/slashgrid -%attr(0744, root, root) /etc/rc.d/init.d/slashgrid -%attr(0700, root, root) /var/spool/slashgrid -%attr(-, root, root) %{_prefix}/share/man/man8/slashgrid.8.gz - -EOF - -fi - if [ $have_gsoap ] ; then cat <>gridsite.spec @@ -341,21 +250,9 @@ See http://www.gridsite.org/ for details. EOF -if [ x"$project" = x"emi1" ]; then -cat <>gridsite.spec -%files apache -%attr(-, root, root) %{_prefix}/share/man/man8/mod_gridsite.8.gz -%attr(-, root, root) %{_prefix}/%{_lib}/httpd/modules/mod_gridsite.so -%attr(-, root, root) %{_prefix}/sbin/real-gridsite-admin.cgi -%attr(-, root, root) %{_prefix}/sbin/gridsite-copy.cgi -%attr(-, root, root) %{_prefix}/sbin/gridsite-storage.cgi - -EOF -fi - LC_TIME=C cat >> gridsite.spec <