Consolidated pull
cover Consolidated pull
The following changes since commit e44ed8c18e395b9c055aefee113b90708e8a8a2f:
build-appliance-image: Update to jethro head revision (2015-11-03 14:02:57 +0000)
are available in the git repository at:
git://git.openembedded.org/openembedded-core-contrib rbt/jethro-next
http://cgit.openembedded.org/cgit.cgi/openembedded-core-contrib/log/?h=rbt/jethro-next
Alejandro del Castillo (1):
opkg: add cache filename length fixes
Armin Kuster (1):
libxslt: CVE-2015-7995
Bhuvanchandra DV (1):
linux-firmware: rtl8192cx: Add latest available firmware
Jussi Kukkonen (1):
pulseaudio: Fix HDMI profile selection
Li Zhou (1):
rpcbind: Security Advisory - rpcbind - CVE-2015-7236
Mark Hatle (1):
binutils: Fix octeon3 disassembly patch
Maxin B. John (1):
libsndfile: fix CVE-2014-9756
Mike Crowe (1):
allarch: Force TARGET_*FLAGS variable values
Ng, Mei Yeen (2):
init-install-efi: fix script for eMMC installation
init-install-efi: fix script for gummiboot loader
Ross Burton (3):
libarchive: rename patch to reflect CVE
readline: rename patch to contain CVE reference
unzip: rename patch to reflect CVE fix
Wenzong Fan (2):
subversion: fix CVE-2015-3184
subversion: fix CVE-2015-3187
meta/classes/allarch.bbclass | 4 +
.../initrdscripts/files/init-install-efi.sh | 9 +-
...ne63-003.patch => readline-cve-2014-2524.patch} | 0
meta/recipes-core/readline/readline_6.3.bb | 2 +-
.../binutils/binutils/binutils-octeon3.patch | 2 +-
...ng_util-New-file-with-bin_to_hex-function.patch | 122 ++
.../opkg/0002-md5-Add-md5_to_string-function.patch | 110 +
...0003-sha256-Add-sha256_to_string-function.patch | 110 +
...4-opkg_download-Use-short-cache-file-name.patch | 85 +
meta/recipes-devtools/opkg/opkg_0.3.0.bb | 4 +
.../subversion-CVE-2015-3184.patch | 2094 ++++++++++++++++++++
.../subversion-CVE-2015-3187.patch | 346 ++++
.../subversion/subversion_1.8.13.bb | 2 +
...option.patch => libarchive-CVE-2015-2304.patch} | 0
.../libarchive/libarchive_3.1.2.bb | 2 +-
.../rpcbind/rpcbind/cve-2015-7236.patch | 83 +
meta/recipes-extended/rpcbind/rpcbind_0.2.3.bb | 1 +
...nzip-6.0_overflow3.diff => cve-2014-9636.patch} | 0
meta/recipes-extended/unzip/unzip_6.0.bb | 2 +-
.../linux-firmware/linux-firmware_git.bb | 4 +-
.../files/libsndfile-fix-CVE-2014-9756.patch | 24 +
.../libsndfile/libsndfile1_1.0.25.bb | 1 +
.../0001-card-add-pa_card_profile.ports.patch | 245 +++
...oth-fail-if-user-requested-profile-doesn-.patch | 60 +
...-move-profile-selection-after-pa_card_new.patch | 363 ++++
...vailability-for-some-unavailable-profiles.patch | 75 +
.../pulseaudio/pulseaudio_6.0.bb | 4 +
.../libxslt/libxslt/CVE-2015-7995.patch | 33 +
meta/recipes-support/libxslt/libxslt_1.1.28.bb | 3 +-
29 files changed, 3779 insertions(+), 11 deletions(-)
rename meta/recipes-core/readline/readline-6.3/{readline63-003.patch => readline-cve-2014-2524.patch} (100%)
create mode 100644 meta/recipes-devtools/opkg/opkg/0001-string_util-New-file-with-bin_to_hex-function.patch
create mode 100644 meta/recipes-devtools/opkg/opkg/0002-md5-Add-md5_to_string-function.patch
create mode 100644 meta/recipes-devtools/opkg/opkg/0003-sha256-Add-sha256_to_string-function.patch
create mode 100644 meta/recipes-devtools/opkg/opkg/0004-opkg_download-Use-short-cache-file-name.patch
create mode 100644 meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3184.patch
create mode 100644 meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3187.patch
rename meta/recipes-extended/libarchive/libarchive/{0001-Add-ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS-option.patch => libarchive-CVE-2015-2304.patch} (100%)
create mode 100644 meta/recipes-extended/rpcbind/rpcbind/cve-2015-7236.patch
rename meta/recipes-extended/unzip/unzip/{unzip-6.0_overflow3.diff => cve-2014-9636.patch} (100%)
create mode 100644 meta/recipes-multimedia/libsndfile/files/libsndfile-fix-CVE-2014-9756.patch
create mode 100644 meta/recipes-multimedia/pulseaudio/pulseaudio/0001-card-add-pa_card_profile.ports.patch
create mode 100644 meta/recipes-multimedia/pulseaudio/pulseaudio/0002-alsa-bluetooth-fail-if-user-requested-profile-doesn-.patch
create mode 100644 meta/recipes-multimedia/pulseaudio/pulseaudio/0003-card-move-profile-selection-after-pa_card_new.patch
create mode 100644 meta/recipes-multimedia/pulseaudio/pulseaudio/0004-alsa-set-availability-for-some-unavailable-profiles.patch
create mode 100644 meta/recipes-support/libxslt/libxslt/CVE-2015-7995.patchSignature · 1 line
1.7.9.5
PATCH 1/15 opkg: add cache filename length fixes
From: Alejandro del Castillo <[email protected]>
(From OE-Core master rev: 8e53500a7c05204fc63759f456639545a022e82b)
--- ...ng_util-New-file-with-bin_to_hex-function.patch | 122 ++++++++++++++++++++ .../opkg/0002-md5-Add-md5_to_string-function.patch | 110 ++++++++++++++++++ ...0003-sha256-Add-sha256_to_string-function.patch | 110 ++++++++++++++++++ ...4-opkg_download-Use-short-cache-file-name.patch | 85 ++++++++++++++ meta/recipes-devtools/opkg/opkg_0.3.0.bb | 4 + 5 files changed, 431 insertions(+) create mode 100644 meta/recipes-devtools/opkg/opkg/0001-string_util-New-file-with-bin_to_hex-function.patch create mode 100644 meta/recipes-devtools/opkg/opkg/0002-md5-Add-md5_to_string-function.patch create mode 100644 meta/recipes-devtools/opkg/opkg/0003-sha256-Add-sha256_to_string-function.patch create mode 100644 meta/recipes-devtools/opkg/opkg/0004-opkg_download-Use-short-cache-file-name.patch
meta/recipes-devtools/opkg/opkg/0001-string_util-New-file-with-bin_to_hex-function.patch new file +122 -0
@@ -0,0 +1,122 @@ +From 646b80024567a6245c598be3374653fa1fa09a12 Mon Sep 17 00:00:00 2001 +From: Paul Barker <[email protected]> +Date: Sat, 7 Nov 2015 10:23:49 +0000 +Subject: [PATCH 1/4] string_util: New file with bin_to_hex function + +This function does very simple conversion from binary data to a hex string. + +Signed-off-by: Paul Barker <[email protected]> +Signed-off-by: Alejandro del Castillo <[email protected]> + +Upstream-Status: Accepted +--- + libopkg/Makefile.am | 4 ++-- + libopkg/string_util.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + libopkg/string_util.h | 24 ++++++++++++++++++++++++ + 3 files changed, 68 insertions(+), 2 deletions(-) + create mode 100644 libopkg/string_util.c + create mode 100644 libopkg/string_util.h + +diff --git a/libopkg/Makefile.am b/libopkg/Makefile.am +index ee3fbee..3e62c24 100644 +--- a/libopkg/Makefile.am ++++ b/libopkg/Makefile.am +@@ -13,7 +13,7 @@ opkg_headers = active_list.h cksum_list.h conffile.h conffile_list.h \ + pkg_depends.h pkg_dest.h pkg_dest_list.h pkg_extract.h pkg_hash.h \ + pkg_parse.h pkg_src.h pkg_src_list.h pkg_vec.h release.h \ + release_parse.h sha256.h sprintf_alloc.h str_list.h void_list.h \ +- xregex.h xsystem.h xfuncs.h opkg_verify.h ++ xregex.h xsystem.h xfuncs.h opkg_verify.h string_util.h + + opkg_sources = opkg_cmd.c opkg_configure.c opkg_download.c \ + opkg_install.c opkg_remove.c opkg_conf.c release.c \ +@@ -23,7 +23,7 @@ opkg_sources = opkg_cmd.c opkg_configure.c opkg_download.c \ + pkg_src.c pkg_src_list.c str_list.c void_list.c active_list.c \ + file_util.c opkg_message.c md5.c parse_util.c cksum_list.c \ + sprintf_alloc.c xregex.c xsystem.c xfuncs.c opkg_archive.c \ +- opkg_verify.c ++ opkg_verify.c string_util.c + + if HAVE_CURL + opkg_sources += opkg_download_curl.c +diff --git a/libopkg/string_util.c b/libopkg/string_util.c +new file mode 100644 +index 0000000..822cab6 +--- /dev/null ++++ b/libopkg/string_util.c +@@ -0,0 +1,42 @@ ++/* vi: set expandtab sw=4 sts=4: */ ++/* string_util.c - convenience routines for common string operations ++ ++ Copyright (C) 2015 Paul Barker ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License as ++ published by the Free Software Foundation; either version 2, or (at ++ your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++*/ ++ ++#include "config.h" ++ ++#include "string_util.h" ++#include "xfuncs.h" ++ ++char *bin_to_hex(const void *bin_data, size_t len) ++{ ++ const unsigned char *src = (const unsigned char *)bin_data; ++ char *buf = xmalloc(2 * len + 1); ++ int i; ++ ++ static const unsigned char bin2hex[16] = { ++ '0', '1', '2', '3', ++ '4', '5', '6', '7', ++ '8', '9', 'a', 'b', ++ 'c', 'd', 'e', 'f' ++ }; ++ ++ for (i = 0; i < len; i++) { ++ buf[i * 2] = bin2hex[src[i] >> 4]; ++ buf[i * 2 + 1] = bin2hex[src[i] & 0xf]; ++ } ++ ++ buf[len * 2] = '\0'; ++ return buf; ++} +diff --git a/libopkg/string_util.h b/libopkg/string_util.h +new file mode 100644 +index 0000000..a920e2a +--- /dev/null ++++ b/libopkg/string_util.h +@@ -0,0 +1,24 @@ ++/* vi: set expandtab sw=4 sts=4: */ ++/* string_util.h - convenience routines for common file operations ++ ++ Copyright (C) 2015 Paul Barker ++ ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License as ++ published by the Free Software Foundation; either version 2, or (at ++ your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ General Public License for more details. ++*/ ++ ++#ifndef STRING_UTIL_H ++#define STRING_UTIL_H ++ ++#include <stddef.h> ++ ++char *bin_to_hex(const void *bin_data, size_t len); ++ ++#endif /* STRING_UTIL_H */ +-- +1.9.1 +
meta/recipes-devtools/opkg/opkg/0002-md5-Add-md5_to_string-function.patch new file +110 -0
110 lines not on this page · raw message
meta/recipes-devtools/opkg/opkg/0003-sha256-Add-sha256_to_string-function.patch new file +110 -0
110 lines not on this page · raw message
meta/recipes-devtools/opkg/opkg/0004-opkg_download-Use-short-cache-file-name.patch new file +85 -0
85 lines not on this page · raw message
meta/recipes-devtools/opkg/opkg_0.3.0.bb +4 -0
@@ -17,6 +17,10 @@ SRC_URI = "http://downloads.yoctoproject.org/releases/${BPN}/${BPN}-${PV}.tar.gz file://0001-opkg_archive-add-support-for-empty-compressed-files.patch \ file://0001-libopkg-include-stdio.h-for-getting-FILE-defined.patch \ file://0001-opkg_conf-create-opkg.lock-in-run-instead-of-var-run.patch \ + file://0001-string_util-New-file-with-bin_to_hex-function.patch \ + file://0002-md5-Add-md5_to_string-function.patch \ + file://0003-sha256-Add-sha256_to_string-function.patch \ + file://0004-opkg_download-Use-short-cache-file-name.patch \ " SRC_URI[md5sum] = "3412cdc71d78b98facc84b19331ec64e"
Signature · 1 line
1.7.9.5
PATCH 2/15 binutils: Fix octeon3 disassembly patch
In reply to Robert YangFrom: Mark Hatle <[email protected]>
The structure has apparently changed, and there was a missing setting. This corrects a segfault when disassembling code.
(From OE-Core master rev: 2e8f1ffe3a8d7740b0ac68eefbba3fe28f7ba6d4)
--- .../binutils/binutils/binutils-octeon3.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
meta/recipes-devtools/binutils/binutils/binutils-octeon3.patch +1 -1
@@ -229,7 +229,7 @@ Index: git/opcodes/mips-dis.c + { "octeon3", 1, bfd_mach_mips_octeon3, CPU_OCTEON3, + ISA_MIPS64R2 | INSN_OCTEON3, ASE_VIRT | ASE_VIRT64, + mips_cp0_names_numeric, -+ NULL, 0, mips_hwr_names_numeric }, ++ NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric }, + { "xlr", 1, bfd_mach_mips_xlr, CPU_XLR, ISA_MIPS64 | INSN_XLR, 0,
Signature · 1 line
1.7.9.5
PATCH 3/15 libarchive: rename patch to reflect CVE
In reply to Robert YangFrom: Ross Burton <[email protected]>
This patch is a CVE fix, so rename it to help CVE detection tools identify it as such.
(From OE-Core master rev: 3fd05ce1f709cbbd8fdeb1dbfdffbd39922eca6e)
---
...option.patch => libarchive-CVE-2015-2304.patch} | 0
.../libarchive/libarchive_3.1.2.bb | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
rename meta/recipes-extended/libarchive/libarchive/{0001-Add-ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS-option.patch => libarchive-CVE-2015-2304.patch} (100%)meta/recipes-extended/libarchive/libarchive/libarchive-CVE-2015-2304.patch renamed from meta/recipes-extended/libarchive/libarchive/0001-Add-ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS-option.patch +0 -0
meta/recipes-extended/libarchive/libarchive_3.1.2.bb +1 -1
@@ -32,7 +32,7 @@ PACKAGECONFIG[nettle] = "--with-nettle,--without-nettle,nettle," SRC_URI = "http://libarchive.org/downloads/libarchive-${PV}.tar.gz \ file://libarchive-CVE-2013-0211.patch \ file://pkgconfig.patch \ - file://0001-Add-ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS-option.patch \ + file://libarchive-CVE-2015-2304.patch \ file://mkdir.patch \ "
Signature · 1 line
1.7.9.5
PATCH 4/15 readline: rename patch to contain CVE reference
In reply to Robert YangFrom: Ross Burton <[email protected]>
To help automated scanning of CVEs, put the CVE ID in the filename.
(From OE-Core master rev: 211bce4f23230c7898cccdb73b582420f830f977)
---
...ne63-003.patch => readline-cve-2014-2524.patch} | 0
meta/recipes-core/readline/readline_6.3.bb | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
rename meta/recipes-core/readline/readline-6.3/{readline63-003.patch => readline-cve-2014-2524.patch} (100%)meta/recipes-core/readline/readline-6.3/readline-cve-2014-2524.patch renamed from meta/recipes-core/readline/readline-6.3/readline63-003.patch +0 -0
meta/recipes-core/readline/readline_6.3.bb +1 -1
@@ -1,6 +1,6 @@ require readline.inc -SRC_URI += "file://readline63-003.patch;striplevel=0 \ +SRC_URI += "file://readline-cve-2014-2524.patch;striplevel=0 \ file://readline-dispatch-multikey.patch" SRC_URI[archive.md5sum] = "33c8fb279e981274f485fd91da77e94a"
Signature · 1 line
1.7.9.5
PATCH 5/15 unzip: rename patch to reflect CVE fix
In reply to Robert YangFrom: Ross Burton <[email protected]>
(From OE-Core rev: e3d2974348bd830ec2fcf84ea08cbf38abbc0327)
(master rev: 78e05984b1ac48b1f25547ccd9740611cd5890a9)
---
...nzip-6.0_overflow3.diff => cve-2014-9636.patch} | 0
meta/recipes-extended/unzip/unzip_6.0.bb | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
rename meta/recipes-extended/unzip/unzip/{unzip-6.0_overflow3.diff => cve-2014-9636.patch} (100%)meta/recipes-extended/unzip/unzip/cve-2014-9636.patch renamed from meta/recipes-extended/unzip/unzip/unzip-6.0_overflow3.diff +0 -0
meta/recipes-extended/unzip/unzip_6.0.bb +1 -1
@@ -10,7 +10,7 @@ SRC_URI = "ftp://ftp.info-zip.org/pub/infozip/src/unzip60.tgz \ file://avoid-strip.patch \ file://define-ldflags.patch \ file://06-unzip60-alt-iconv-utf8_CVE-2015-1315.patch \ - file://unzip-6.0_overflow3.diff \ + file://cve-2014-9636.patch \ file://09-cve-2014-8139-crc-overflow.patch \ file://10-cve-2014-8140-test-compr-eb.patch \ file://11-cve-2014-8141-getzip64data.patch \
Signature · 1 line
1.7.9.5
PATCH 6/15 libxslt: CVE-2015-7995
In reply to Robert YangFrom: Armin Kuster <[email protected]>
This is a is being give a High rating so please consider it for all 1.1.28 versions.
A type confusion error within the libxslt "xsltStylePreCompute()" function in preproc.c can lead to a DoS. Confirmed in version 1.1.28, other versions may also be affected.
(From OE-Core master rev: 0f89bbab6588a1171259801fa879516740030acb)
--- .../libxslt/libxslt/CVE-2015-7995.patch | 33 ++++++++++++++++++++ meta/recipes-support/libxslt/libxslt_1.1.28.bb | 3 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 meta/recipes-support/libxslt/libxslt/CVE-2015-7995.patch
meta/recipes-support/libxslt/libxslt/CVE-2015-7995.patch new file +33 -0
@@ -0,0 +1,33 @@ +From 7ca19df892ca22d9314e95d59ce2abdeff46b617 Mon Sep 17 00:00:00 2001 +From: Daniel Veillard <[email protected]> +Date: Thu, 29 Oct 2015 19:33:23 +0800 +Subject: Fix for type confusion in preprocessing attributes + +CVE-2015-7995 http://www.openwall.com/lists/oss-security/2015/10/27/10 +We need to check that the parent node is an element before dereferencing +its namespace + +Upstream-Status: Backport + +https://git.gnome.org/browse/libxslt/commit/?id=7ca19df892ca22d9314e95d59ce2abdeff46b617 + +Signed-off-by: Armin Kuster <[email protected]> + +--- + libxslt/preproc.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +Index: libxslt-1.1.28/libxslt/preproc.c +=================================================================== +--- libxslt-1.1.28.orig/libxslt/preproc.c ++++ libxslt-1.1.28/libxslt/preproc.c +@@ -2245,7 +2245,8 @@ xsltStylePreCompute(xsltStylesheetPtr st + } else if (IS_XSLT_NAME(inst, "attribute")) { + xmlNodePtr parent = inst->parent; + +- if ((parent == NULL) || (parent->ns == NULL) || ++ if ((parent == NULL) || ++ (parent->type != XML_ELEMENT_NODE) || (parent->ns == NULL) || + ((parent->ns != inst->ns) && + (!xmlStrEqual(parent->ns->href, inst->ns->href))) || + (!xmlStrEqual(parent->name, BAD_CAST "attribute-set"))) {
meta/recipes-support/libxslt/libxslt_1.1.28.bb +2 -1
@@ -10,7 +10,8 @@ DEPENDS = "libxml2" SRC_URI = "ftp://xmlsoft.org/libxslt//libxslt-${PV}.tar.gz \ file://pkgconfig_fix.patch \ - file://pkgconfig.patch" + file://pkgconfig.patch \ + file://CVE-2015-7995.patch" SRC_URI[md5sum] = "9667bf6f9310b957254fdcf6596600b7" SRC_URI[sha256sum] = "5fc7151a57b89c03d7b825df5a0fae0a8d5f05674c0e7cf2937ecec4d54a028c"
Signature · 1 line
1.7.9.5
PATCH 7/15 libsndfile: fix CVE-2014-9756
In reply to Robert YangFrom: "Maxin B. John" <[email protected]>
Fix divide by zero bug (CVE-2014-9756)
(From OE-Core master rev: f47cf07ab9d00ed7eddc8e867138481f7bd2bb7d)
--- .../files/libsndfile-fix-CVE-2014-9756.patch | 24 ++++++++++++++++++++ .../libsndfile/libsndfile1_1.0.25.bb | 1 + 2 files changed, 25 insertions(+) create mode 100644 meta/recipes-multimedia/libsndfile/files/libsndfile-fix-CVE-2014-9756.patch
meta/recipes-multimedia/libsndfile/files/libsndfile-fix-CVE-2014-9756.patch new file +24 -0
@@ -0,0 +1,24 @@ +src/file_io.c : Prevent potential divide-by-zero. + +Closes: https://github.com/erikd/libsndfile/issues/92 + +Upstream-Status: Backport + +Fixes CVE-2014-9756 + +Signed-off-by: Erik de Castro Lopo <[email protected]> +Signed-off-by: Maxin B. John <[email protected]> +--- +diff -Naur libsndfile-1.0.25-orig/src/file_io.c libsndfile-1.0.25/src/file_io.c +--- libsndfile-1.0.25-orig/src/file_io.c 2011-01-19 12:12:28.000000000 +0200 ++++ libsndfile-1.0.25/src/file_io.c 2015-11-04 15:02:04.337395618 +0200 +@@ -358,6 +358,9 @@ + { sf_count_t total = 0 ; + ssize_t count ; + ++ if (bytes == 0 || items == 0) ++ return 0 ; ++ + if (psf->virtual_io) + return psf->vio.write (ptr, bytes*items, psf->vio_user_data) / bytes ; +
meta/recipes-multimedia/libsndfile/libsndfile1_1.0.25.bb +1 -0
@@ -9,6 +9,7 @@ PR = "r2" SRC_URI = "http://www.mega-nerd.com/libsndfile/files/libsndfile-${PV}.tar.gz \ file://0001-src-sd2.c-Fix-segfault-in-SD2-RSRC-parser.patch \ file://0001-src-sd2.c-Fix-two-potential-buffer-read-overflows.patch \ + file://libsndfile-fix-CVE-2014-9756.patch \ " SRC_URI[md5sum] = "e2b7bb637e01022c7d20f95f9c3990a2"
Signature · 1 line
1.7.9.5
PATCH 8/15 allarch: Force TARGET_*FLAGS variable values
In reply to Robert YangFrom: Mike Crowe <[email protected]>
TARGET_CPPFLAGS, TARGET_CFLAGS, TARGET_CPPFLAGS and TARGET_LDFLAGS may differ between MACHINEs. Since they are exported they affect task hashes even if unused which leads to multiple variants of allarch packages existing in sstate and bouncing in the sysroot when switching between MACHINEs.
allarch packages shouldn't be using these variables anyway, so let's ensure they have a fixed value in order to avoid this problem.
(Compare with 05a70ac30b37cab0952f1b9df501993a9dec70da and 14f4d016fef9d660da1e7e91aec4a0e807de59ab.)
--- meta/classes/allarch.bbclass | 4 ++++ 1 file changed, 4 insertions(+)
meta/classes/allarch.bbclass +4 -0
@@ -27,6 +27,10 @@ python () { d.setVar("PACKAGE_EXTRA_ARCHS", "") d.setVar("SDK_ARCH", "none") d.setVar("SDK_CC_ARCH", "none") + d.setVar("TARGET_CPPFLAGS", "none") + d.setVar("TARGET_CFLAGS", "none") + d.setVar("TARGET_CXXFLAGS", "none") + d.setVar("TARGET_LDFLAGS", "none") # Avoid this being unnecessarily different due to nuances of # the target machine that aren't important for "all" arch
Signature · 1 line
1.7.9.5
PATCH 9/15 pulseaudio: Fix HDMI profile selection
In reply to Robert YangFrom: Jussi Kukkonen <[email protected]>
On systems with two cards, the correct output profile does not get selected automatically even in the simple case where there is one available profile. This scenario is typical at least with HDMI audio (which is on a separate card).
Fixes [YOCTO #8448]
--- .../0001-card-add-pa_card_profile.ports.patch | 245 +++++++++++++ ...oth-fail-if-user-requested-profile-doesn-.patch | 60 ++++ ...-move-profile-selection-after-pa_card_new.patch | 363 ++++++++++++++++++++ ...vailability-for-some-unavailable-profiles.patch | 75 ++++ .../pulseaudio/pulseaudio_6.0.bb | 4 + 5 files changed, 747 insertions(+) create mode 100644 meta/recipes-multimedia/pulseaudio/pulseaudio/0001-card-add-pa_card_profile.ports.patch create mode 100644 meta/recipes-multimedia/pulseaudio/pulseaudio/0002-alsa-bluetooth-fail-if-user-requested-profile-doesn-.patch create mode 100644 meta/recipes-multimedia/pulseaudio/pulseaudio/0003-card-move-profile-selection-after-pa_card_new.patch create mode 100644 meta/recipes-multimedia/pulseaudio/pulseaudio/0004-alsa-set-availability-for-some-unavailable-profiles.patch
meta/recipes-multimedia/pulseaudio/pulseaudio/0001-card-add-pa_card_profile.ports.patch new file +245 -0
@@ -0,0 +1,245 @@ +From 6f814b40a01d03f93b36184c19339033949de472 Mon Sep 17 00:00:00 2001 +From: Tanu Kaskinen <[email protected]> +Date: Fri, 23 Oct 2015 12:23:13 +0300 +Subject: [PATCH 1/4] card: add pa_card_profile.ports + +Having ports accessible from pa_card_profile allows checking whether all ports +of a profile are unavailable, and therefore helps with managing the profile +availability (implemented in a later patch). + +http://bugzilla.yoctoproject.org/show_bug.cgi?id=8448 + +Upstream-Status: Submitted [http://lists.freedesktop.org/archives/pulseaudio-discuss/2015-October/024614.html] +Signed-off-by: Jussi Kukkonen <[email protected]> +--- + src/modules/alsa/alsa-mixer.c | 4 +++- + src/modules/alsa/alsa-ucm.c | 1 + + src/modules/bluetooth/module-bluez4-device.c | 6 ++++++ + src/modules/bluetooth/module-bluez5-device.c | 6 ++++++ + src/pulsecore/card.c | 16 ++++++++++++++++ + src/pulsecore/card.h | 18 ++++++++++++------ + src/pulsecore/device-port.c | 7 ++++++- + 7 files changed, 50 insertions(+), 8 deletions(-) + +diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c +index 47cbd14..c5b82b0 100644 +--- a/src/modules/alsa/alsa-mixer.c ++++ b/src/modules/alsa/alsa-mixer.c +@@ -4654,8 +4654,10 @@ static pa_device_port* device_port_alsa_init(pa_hashmap *ports, /* card ports */ + path->port = p; + } + +- if (cp) ++ if (cp) { + pa_hashmap_put(p->profiles, cp->name, cp); ++ pa_card_profile_add_port(cp, p); ++ } + + if (extra) { + pa_hashmap_put(extra, p->name, p); +diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c +index aa2d601..c8199d6 100644 +--- a/src/modules/alsa/alsa-ucm.c ++++ b/src/modules/alsa/alsa-ucm.c +@@ -761,6 +761,7 @@ static void ucm_add_port_combination( + if (cp) { + pa_log_debug("Adding profile %s to port %s.", cp->name, port->name); + pa_hashmap_put(port->profiles, cp->name, cp); ++ pa_card_profile_add_port(cp, port); + } + + if (hash) { +diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c +index db69d34..b40c6a0 100644 +--- a/src/modules/bluetooth/module-bluez4-device.c ++++ b/src/modules/bluetooth/module-bluez4-device.c +@@ -2183,6 +2183,7 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid + p->max_sink_channels = 2; + p->max_source_channels = 0; + pa_hashmap_put(output_port->profiles, p->name, p); ++ pa_card_profile_add_port(p, output_port); + + d = PA_CARD_PROFILE_DATA(p); + *d = PA_BLUEZ4_PROFILE_A2DP; +@@ -2194,6 +2195,7 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid + p->max_sink_channels = 0; + p->max_source_channels = 2; + pa_hashmap_put(input_port->profiles, p->name, p); ++ pa_card_profile_add_port(p, input_port); + + d = PA_CARD_PROFILE_DATA(p); + *d = PA_BLUEZ4_PROFILE_A2DP_SOURCE; +@@ -2206,6 +2208,8 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid + p->max_source_channels = 1; + pa_hashmap_put(input_port->profiles, p->name, p); + pa_hashmap_put(output_port->profiles, p->name, p); ++ pa_card_profile_add_port(p, input_port); ++ pa_card_profile_add_port(p, output_port); + + d = PA_CARD_PROFILE_DATA(p); + *d = PA_BLUEZ4_PROFILE_HSP; +@@ -2218,6 +2222,8 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid + p->max_source_channels = 1; + pa_hashmap_put(input_port->profiles, p->name, p); + pa_hashmap_put(output_port->profiles, p->name, p); ++ pa_card_profile_add_port(p, input_port); ++ pa_card_profile_add_port(p, output_port); + + d = PA_CARD_PROFILE_DATA(p); + *d = PA_BLUEZ4_PROFILE_HFGW; +diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c +index 7238e6f..3321785 100644 +--- a/src/modules/bluetooth/module-bluez5-device.c ++++ b/src/modules/bluetooth/module-bluez5-device.c +@@ -1790,6 +1790,7 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid + cp->max_sink_channels = 2; + cp->max_source_channels = 0; + pa_hashmap_put(output_port->profiles, cp->name, cp); ++ pa_card_profile_add_port(cp, output_port); + + p = PA_CARD_PROFILE_DATA(cp); + *p = PA_BLUETOOTH_PROFILE_A2DP_SINK; +@@ -1801,6 +1802,7 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid + cp->max_sink_channels = 0; + cp->max_source_channels = 2; + pa_hashmap_put(input_port->profiles, cp->name, cp); ++ pa_card_profile_add_port(cp, input_port); + + p = PA_CARD_PROFILE_DATA(cp); + *p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE; +@@ -1813,6 +1815,8 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid + cp->max_source_channels = 1; + pa_hashmap_put(input_port->profiles, cp->name, cp); + pa_hashmap_put(output_port->profiles, cp->name, cp); ++ pa_card_profile_add_port(cp, input_port); ++ pa_card_profile_add_port(cp, output_port); + + p = PA_CARD_PROFILE_DATA(cp); + *p = PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT; +@@ -1825,6 +1829,8 @@ static pa_card_profile *create_card_profile(struct userdata *u, const char *uuid + cp->max_source_channels = 1; + pa_hashmap_put(input_port->profiles, cp->name, cp); + pa_hashmap_put(output_port->profiles, cp->name, cp); ++ pa_card_profile_add_port(cp, input_port); ++ pa_card_profile_add_port(cp, output_port); + + p = PA_CARD_PROFILE_DATA(cp); + *p = PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY; +diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c +index 6f9391e..cc4c784 100644 +--- a/src/pulsecore/card.c ++++ b/src/pulsecore/card.c +@@ -50,6 +50,7 @@ pa_card_profile *pa_card_profile_new(const char *name, const char *description, + c->n_sinks = c->n_sources = 0; + c->max_sink_channels = c->max_source_channels = 0; + c->available = PA_AVAILABLE_UNKNOWN; ++ c->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + + return c; + } +@@ -57,11 +58,25 @@ pa_card_profile *pa_card_profile_new(const char *name, const char *description, + void pa_card_profile_free(pa_card_profile *c) { + pa_assert(c); + ++ if (c->ports) { ++ pa_device_port *port; ++ void *state; ++ PA_HASHMAP_FOREACH(port, c->ports, state) ++ pa_hashmap_remove (port->profiles, c->name); ++ pa_hashmap_free(c->ports); ++ } ++ + pa_xfree(c->name); + pa_xfree(c->description); + pa_xfree(c); + } + ++void pa_card_profile_add_port(pa_card_profile *profile, pa_device_port *port) { ++ pa_assert(profile); ++ ++ pa_hashmap_put(profile->ports, port->name, port); ++} ++ + void pa_card_profile_set_available(pa_card_profile *c, pa_available_t available) { + pa_core *core; + +@@ -198,6 +213,7 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) { + + c->userdata = NULL; + c->set_profile = NULL; ++ c->active_profile = NULL; + + pa_device_init_description(c->proplist, c); + pa_device_init_icon(c->proplist, true); +diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h +index 3e2c004..1c33958 100644 +--- a/src/pulsecore/card.h ++++ b/src/pulsecore/card.h +@@ -22,19 +22,21 @@ + + typedef struct pa_card pa_card; + +-#include <pulse/proplist.h> +-#include <pulsecore/core.h> +-#include <pulsecore/module.h> +-#include <pulsecore/idxset.h> +- + /* This enum replaces pa_port_available_t (defined in pulse/def.h) for +- * internal use, so make sure both enum types stay in sync. */ ++ * internal use, so make sure both enum types stay in sync. This is defined ++ * before the #includes, because device-port.h depends on this enum. */ + typedef enum pa_available { + PA_AVAILABLE_UNKNOWN = 0, + PA_AVAILABLE_NO = 1, + PA_AVAILABLE_YES = 2, + } pa_available_t; + ++#include <pulse/proplist.h> ++#include <pulsecore/core.h> ++#include <pulsecore/device-port.h> ++#include <pulsecore/module.h> ++#include <pulsecore/idxset.h> ++ + typedef struct pa_card_profile { + pa_card *card; + char *name; +@@ -43,6 +45,8 @@ typedef struct pa_card_profile { + unsigned priority; + pa_available_t available; /* PA_AVAILABLE_UNKNOWN, PA_AVAILABLE_NO or PA_AVAILABLE_YES */ + ++ pa_hashmap *ports; /* port name -> pa_device_port */ ++ + /* We probably want to have different properties later on here */ + unsigned n_sinks; + unsigned n_sources; +@@ -100,6 +104,8 @@ typedef struct pa_card_new_data { + pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra); + void pa_card_profile_free(pa_card_profile *c); + ++void pa_card_profile_add_port(pa_card_profile *profile, pa_device_port *port); ++ + /* The profile's available status has changed */ + void pa_card_profile_set_available(pa_card_profile *c, pa_available_t available); + +diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c +index cfe2a80..f16ecef 100644 +--- a/src/pulsecore/device-port.c ++++ b/src/pulsecore/device-port.c +@@ -95,8 +95,13 @@ static void device_port_free(pa_object *o) { + if (p->proplist) + pa_proplist_free(p->proplist); + +- if (p->profiles) ++ if (p->profiles) { ++ pa_card_profile *profile; ++ void *state; ++ PA_HASHMAP_FOREACH(profile, p->profiles, state) ++ pa_hashmap_remove (profile->ports, p->name); + pa_hashmap_free(p->profiles); ++ } + + pa_xfree(p->name); + pa_xfree(p->description); +-- +2.1.4 +
meta/recipes-multimedia/pulseaudio/pulseaudio/0002-alsa-bluetooth-fail-if-user-requested-profile-doesn-.patch new file +60 -0
60 lines not on this page · raw message
meta/recipes-multimedia/pulseaudio/pulseaudio/0003-card-move-profile-selection-after-pa_card_new.patch new file +363 -0
363 lines not on this page · raw message
meta/recipes-multimedia/pulseaudio/pulseaudio/0004-alsa-set-availability-for-some-unavailable-profiles.patch new file +75 -0
75 lines not on this page · raw message
meta/recipes-multimedia/pulseaudio/pulseaudio_6.0.bb +4 -0
@@ -6,6 +6,10 @@ SRC_URI = "http://freedesktop.org/software/pulseaudio/releases/${BP}.tar.xz \ file://0001-conf-parser-add-support-for-.d-directories.patch \ file://fix-git-version-gen.patch \ file://volatiles.04_pulse \ + file://0001-card-add-pa_card_profile.ports.patch \ + file://0002-alsa-bluetooth-fail-if-user-requested-profile-doesn-.patch \ + file://0003-card-move-profile-selection-after-pa_card_new.patch \ + file://0004-alsa-set-availability-for-some-unavailable-profiles.patch \ " SRC_URI[md5sum] = "b691e83b7434c678dffacfa3a027750e" SRC_URI[sha256sum] = "b50640e0b80b1607600accfad2e45aabb79d379bf6354c9671efa2065477f6f6"
Signature · 1 line
1.7.9.5
PATCH 10/15 init-install-efi: fix script for eMMC installation
In reply to Robert YangFrom: "Ng, Mei Yeen" <[email protected]>
Running the install option from bootloader to install image to eMMC will fail with error:
Formatting /dev/mmcblk01 to vfat...
mkfs.fat 3.0.28 (2015-05-16)/dev/mmcblk01: No such file or directory
This issue impacts both grub and gummiboot install option to eMMC device. The installation failure is due to the following:
[1] Unable to partition eMMC as the partition prefix 'p' is not appended The condition checking failed with the additional /dev/ appended with the target device name.
[2] The partition uuid for boot, root and swap partition is not captured for eMMC
This fix updated the condition checking and changed the variables to reference the boot, root and swap partitions for UUID.
[YOCTO #8710]
(master rev: a7d081c3db776c8b0734942df6bf96f811f15bd3)
--- .../initrdscripts/files/init-install-efi.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
meta/recipes-core/initrdscripts/files/init-install-efi.sh +4 -4
@@ -134,7 +134,7 @@ swap_start=$((rootfs_end)) # 2) they are detected asynchronously (need rootwait) rootwait="" part_prefix="" -if [ ! "${device#mmcblk}" = "${device}" ]; then +if [ ! "${device#/dev/mmcblk}" = "${device}" ]; then part_prefix="p" rootwait="rootwait" fi
@@ -184,8 +184,8 @@ mount -o rw,loop,noatime,nodiratime /run/media/$1/$2 /src_root echo "Copying rootfs files..." cp -a /src_root/* /tgt_root if [ -d /tgt_root/etc/ ] ; then - boot_uuid=$(blkid -o value -s UUID ${device}1) - swap_part_uuid=$(blkid -o value -s PARTUUID ${device}3) + boot_uuid=$(blkid -o value -s UUID ${bootfs}) + swap_part_uuid=$(blkid -o value -s PARTUUID ${swap}) echo "/dev/disk/by-partuuid/$swap_part_uuid swap swap defaults 0 0" >> /tgt_root/etc/fstab echo "UUID=$boot_uuid /boot vfat defaults 1 2" >> /tgt_root/etc/fstab # We dont want udev to mount our root device while we're booting...
@@ -206,7 +206,7 @@ mkdir -p $EFIDIR cp /run/media/$1/EFI/BOOT/*.efi $EFIDIR if [ -f /run/media/$1/EFI/BOOT/grub.cfg ]; then - root_part_uuid=$(blkid -o value -s PARTUUID ${device}2) + root_part_uuid=$(blkid -o value -s PARTUUID ${rootfs}) GRUBCFG="$EFIDIR/grub.cfg" cp /run/media/$1/EFI/BOOT/grub.cfg $GRUBCFG # Update grub config for the installed image
Signature · 1 line
1.7.9.5
PATCH 11/15 init-install-efi: fix script for gummiboot loader
In reply to Robert YangFrom: "Ng, Mei Yeen" <[email protected]>
After running gummiboot loader install option, the installed target storage device boot parameter for root=PARTUUID is empty causing boot failure. This issue is only observed with gummiboot and not with GRUB loader.
This fix assign the rootuuid of the rootfs partition for gummiboot loader.
--- .../initrdscripts/files/init-install-efi.sh | 1 + 1 file changed, 1 insertion(+)
meta/recipes-core/initrdscripts/files/init-install-efi.sh +1 -0
@@ -223,6 +223,7 @@ if [ -f /run/media/$1/EFI/BOOT/grub.cfg ]; then fi if [ -d /run/media/$1/loader ]; then + rootuuid=$(blkid -o value -s PARTUUID ${rootfs}) GUMMIBOOT_CFGS="/boot/loader/entries/*.conf" # copy config files for gummiboot cp -dr /run/media/$1/loader /boot
Signature · 1 line
1.7.9.5
PATCH 12/15 linux-firmware: rtl8192cx: Add latest available firmware
In reply to Robert YangFrom: Bhuvanchandra DV <[email protected]>
Add latest available firmware binaries for RTL8192CX chipsets. These new firmwares have been released in 2012, have been used by the mainline kernel as preferred firmware since 3.13 and even backported to stable branches.
(master rev: 2dc67b53d1b7c056bbbff2f90ad16ed214b57609)
--- .../linux-firmware/linux-firmware_git.bb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
meta/recipes-kernel/linux-firmware/linux-firmware_git.bb +2 -2
@@ -260,10 +260,10 @@ FILES_${PN}-rtl-license = " \ /lib/firmware/LICENCE.rtlwifi_firmware.txt \ " FILES_${PN}-rtl8192cu = " \ - /lib/firmware/rtlwifi/rtl8192cufw.bin \ + /lib/firmware/rtlwifi/rtl8192cufw*.bin \ " FILES_${PN}-rtl8192ce = " \ - /lib/firmware/rtlwifi/rtl8192cfw.bin \ + /lib/firmware/rtlwifi/rtl8192cfw*.bin \ " FILES_${PN}-rtl8192su = " \ /lib/firmware/rtlwifi/rtl8712u.bin \
Signature · 1 line
1.7.9.5
PATCH 13/15 subversion: fix CVE-2015-3184
In reply to Robert YangFrom: Wenzong Fan <[email protected]>
mod_authz_svn in Apache Subversion 1.7.x before 1.7.21 and 1.8.x before 1.8.14, when using Apache httpd 2.4.x, does not properly restrict anonymous access, which allows remote anonymous users to read hidden files via the path name.
Patch is from:
http://subversion.apache.org/security/CVE-2015-3184-advisory.txt
(From OE-Core master rev: 29eb921ed074d86fa8d5b205a313eb3177473a63)
--- .../subversion-CVE-2015-3184.patch | 2094 ++++++++++++++++++++ .../subversion/subversion_1.8.13.bb | 1 + 2 files changed, 2095 insertions(+) create mode 100644 meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3184.patch
meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3184.patch new file +2094 -0
@@ -0,0 +1,2094 @@ +Fix CVE-2015-3184 + +Patch is from: +http://subversion.apache.org/security/CVE-2015-3184-advisory.txt + +Upstream-Status: Backport + +Signed-off-by: Wenzong Fan <[email protected]> + +Index: Makefile.in +=================================================================== +--- a/Makefile.in (revision 1691883) ++++ b/Makefile.in (working copy) +@@ -357,6 +357,7 @@ TEST_SHLIB_VAR_SWIG_RB=\ + fi; + + APXS = @APXS@ ++HTTPD_VERSION = @HTTPD_VERSION@ + + PYTHON = @PYTHON@ + PERL = @PERL@ +@@ -509,6 +510,9 @@ check: bin @TRANSFORM_LIBTOOL_SCRIPTS@ $(TEST_DEPS + if test "$(HTTP_LIBRARY)" != ""; then \ + flags="--http-library $(HTTP_LIBRARY) $$flags"; \ + fi; \ ++ if test "$(HTTPD_VERSION)" != ""; then \ ++ flags="--httpd-version $(HTTPD_VERSION) $$flags"; \ ++ fi; \ + if test "$(SERVER_MINOR_VERSION)" != ""; then \ + flags="--server-minor-version $(SERVER_MINOR_VERSION) $$flags"; \ + fi; \ +Index: build/ac-macros/apache.m4 +=================================================================== +--- a/build/ac-macros/apache.m4 (revision 1691883) ++++ b/build/ac-macros/apache.m4 (working copy) +@@ -160,6 +160,20 @@ if test -n "$APXS" && test "$APXS" != "no"; then + BUILD_APACHE_RULE=apache-mod + INSTALL_APACHE_RULE=install-mods-shared + INSTALL_APACHE_MODS=true ++ HTTPD="`$APXS -q sbindir`/`$APXS -q PROGNAME`" ++ if ! test -e $HTTPD ; then ++ HTTPD="`$APXS -q bindir`/`$APXS -q PROGNAME`" ++ fi ++ HTTPD_VERSION=["`$HTTPD -v | $SED -e 's@^.*/\([0-9.]*\)\(.*$\)@\1@ ; 1q'`"] ++ AC_ARG_ENABLE(broken-httpd-auth, ++ AS_HELP_STRING([--enable-broken-httpd-auth], ++ [Allow building against httpd 2.4 with broken auth]), ++ [broken_httpd_auth=$enableval],[broken_httpd_auth=no]) ++ if test "$enable_broken_httpd_auth" = "yes"; then ++ AC_MSG_NOTICE([Building with broken httpd auth]) ++ AC_DEFINE(SVN_ALLOW_BROKEN_HTTPD_AUTH, 1, ++ [Defined to allow building against httpd 2.4 with broken auth]) ++ fi + + case $host in + *-*-cygwin*) +@@ -178,6 +192,7 @@ AC_SUBST(APACHE_LDFLAGS) + AC_SUBST(APACHE_INCLUDES) + AC_SUBST(APACHE_LIBEXECDIR) + AC_SUBST(INSTALL_APACHE_MODS) ++AC_SUBST(HTTPD_VERSION) + + # there aren't any flags that interest us ... + #if test -n "$APXS" && test "$APXS" != "no"; then +Index: build/run_tests.py +=================================================================== +--- a/build/run_tests.py (revision 1691883) ++++ b/build/run_tests.py (working copy) +@@ -29,6 +29,7 @@ + [--fs-type=<fs-type>] [--fsfs-packing] [--fsfs-sharding=<n>] + [--list] [--milestone-filter=<regex>] [--mode-filter=<type>] + [--server-minor-version=<version>] [--http-proxy=<host>:<port>] ++ [--httpd-version=<version>] + [--config-file=<file>] [--ssl-cert=<file>] + <abs_srcdir> <abs_builddir> + <prog ...> +@@ -125,7 +126,7 @@ class TestHarness: + fsfs_sharding=None, fsfs_packing=None, + list_tests=None, svn_bin=None, mode_filter=None, + milestone_filter=None, set_log_level=None, ssl_cert=None, +- http_proxy=None): ++ http_proxy=None, httpd_version=None): + '''Construct a TestHarness instance. + + ABS_SRCDIR and ABS_BUILDDIR are the source and build directories. +@@ -178,6 +179,7 @@ class TestHarness: + self.log = None + self.ssl_cert = ssl_cert + self.http_proxy = http_proxy ++ self.httpd_version = httpd_version + if not sys.stdout.isatty() or sys.platform == 'win32': + TextColors.disable() + +@@ -481,6 +483,8 @@ class TestHarness: + svntest.main.options.ssl_cert = self.ssl_cert + if self.http_proxy is not None: + svntest.main.options.http_proxy = self.http_proxy ++ if self.httpd_version is not None: ++ svntest.main.options.httpd_version = self.httpd_version + + svntest.main.options.srcdir = self.srcdir + +@@ -645,7 +649,7 @@ def main(): + 'enable-sasl', 'parallel', 'config-file=', + 'log-to-stdout', 'list', 'milestone-filter=', + 'mode-filter=', 'set-log-level=', 'ssl-cert=', +- 'http-proxy=']) ++ 'http-proxy=', 'httpd-version=']) + except getopt.GetoptError: + args = [] + +@@ -656,9 +660,9 @@ def main(): + base_url, fs_type, verbose, cleanup, enable_sasl, http_library, \ + server_minor_version, fsfs_sharding, fsfs_packing, parallel, \ + config_file, log_to_stdout, list_tests, mode_filter, milestone_filter, \ +- set_log_level, ssl_cert, http_proxy = \ ++ set_log_level, ssl_cert, http_proxy, httpd_version = \ + None, None, None, None, None, None, None, None, None, None, None, \ +- None, None, None, None, None, None, None ++ None, None, None, None, None, None, None, None + for opt, val in opts: + if opt in ['-u', '--url']: + base_url = val +@@ -696,6 +700,8 @@ def main(): + ssl_cert = val + elif opt in ['--http-proxy']: + http_proxy = val ++ elif opt in ['--httpd-version']: ++ httpd_version = val + else: + raise getopt.GetoptError + +@@ -712,7 +718,7 @@ def main(): + fsfs_sharding, fsfs_packing, list_tests, + mode_filter=mode_filter, milestone_filter=milestone_filter, + set_log_level=set_log_level, ssl_cert=ssl_cert, +- http_proxy=http_proxy) ++ http_proxy=http_proxy, httpd_version=httpd_version) + + failed = th.run(args[2:]) + if failed: +Index: subversion/mod_authz_svn/mod_authz_svn.c +=================================================================== +--- a/subversion/mod_authz_svn/mod_authz_svn.c (revision 1691883) ++++ b/subversion/mod_authz_svn/mod_authz_svn.c (working copy) +@@ -48,6 +48,23 @@ + #include "svn_dirent_uri.h" + #include "private/svn_fspath.h" + ++/* The apache headers define these and they conflict with our definitions. */ ++#ifdef PACKAGE_BUGREPORT ++#undef PACKAGE_BUGREPORT ++#endif ++#ifdef PACKAGE_NAME ++#undef PACKAGE_NAME ++#endif ++#ifdef PACKAGE_STRING ++#undef PACKAGE_STRING ++#endif ++#ifdef PACKAGE_TARNAME ++#undef PACKAGE_TARNAME ++#endif ++#ifdef PACKAGE_VERSION ++#undef PACKAGE_VERSION ++#endif ++#include "svn_private_config.h" + + #ifdef APLOG_USE_MODULE + APLOG_USE_MODULE(authz_svn); +@@ -67,6 +84,30 @@ typedef struct authz_svn_config_rec { + const char *force_username_case; + } authz_svn_config_rec; + ++#if AP_MODULE_MAGIC_AT_LEAST(20060110,0) /* version where ++ ap_some_auth_required breaks */ ++# if AP_MODULE_MAGIC_AT_LEAST(20120211,47) /* first version with ++ force_authn hook and ++ ap_some_authn_required() which ++ allows us to work without ++ ap_some_auth_required() */ ++# define USE_FORCE_AUTHN 1 ++# define IN_SOME_AUTHN_NOTE "authz_svn-in-some-authn" ++# define FORCE_AUTHN_NOTE "authz_svn-force-authn" ++# else ++ /* ap_some_auth_required() is busted and no viable alternative exists */ ++# ifndef SVN_ALLOW_BROKEN_HTTPD_AUTH ++# error This version of httpd has a security hole with mod_authz_svn ++# else ++ /* user wants to build anyway */ ++# define USE_FORCE_AUTHN 0 ++# endif ++# endif ++#else ++ /* old enough that ap_some_auth_required() still works */ ++# define USE_FORCE_AUTHN 0 ++#endif ++ + /* + * Configuration + */ +@@ -819,9 +860,51 @@ access_checker(request_rec *r) + &authz_svn_module); + const char *repos_path = NULL; + const char *dest_repos_path = NULL; +- int status; ++ int status, authn_required; + ++#if USE_FORCE_AUTHN ++ /* Use the force_authn() hook available in 2.4.x to work securely ++ * given that ap_some_auth_required() is no longer functional for our ++ * purposes in 2.4.x. ++ */ ++ int authn_configured; ++ + /* We are not configured to run */ ++ if (!conf->anonymous || apr_table_get(r->notes, IN_SOME_AUTHN_NOTE) ++ || (! (conf->access_file || conf->repo_relative_access_file))) ++ return DECLINED; ++ ++ /* Authentication is configured */ ++ authn_configured = ap_auth_type(r) != NULL; ++ if (authn_configured) ++ { ++ /* If the user is trying to authenticate, let him. It doesn't ++ * make much sense to grant anonymous access but deny authenticated ++ * users access, even though you can do that with '$anon' in the ++ * access file. ++ */ ++ if (apr_table_get(r->headers_in, ++ (PROXYREQ_PROXY == r->proxyreq) ++ ? "Proxy-Authorization" : "Authorization")) ++ { ++ /* Set the note to force authn regardless of what access_checker_ex ++ hook requires */ ++ apr_table_setn(r->notes, FORCE_AUTHN_NOTE, (const char*)1); ++ ++ /* provide the proper return so the access_checker hook doesn't ++ * prevent the code from continuing on to the other auth hooks */ ++ if (ap_satisfies(r) != SATISFY_ANY) ++ return OK; ++ else ++ return HTTP_FORBIDDEN; ++ } ++ } ++ ++#else ++ /* Support for older versions of httpd that have a working ++ * ap_some_auth_required() */ ++ ++ /* We are not configured to run */ + if (!conf->anonymous + || (! (conf->access_file || conf->repo_relative_access_file))) + return DECLINED; +@@ -834,9 +917,10 @@ access_checker(request_rec *r) + if (ap_satisfies(r) != SATISFY_ANY) + return DECLINED; + +- /* If the user is trying to authenticate, let him. If anonymous +- * access is allowed, so is authenticated access, by definition +- * of the meaning of '*' in the access file. ++ /* If the user is trying to authenticate, let him. It doesn't ++ * make much sense to grant anonymous access but deny authenticated ++ * users access, even though you can do that with '$anon' in the ++ * access file. + */ + if (apr_table_get(r->headers_in, + (PROXYREQ_PROXY == r->proxyreq) +@@ -848,6 +932,7 @@ access_checker(request_rec *r) + return HTTP_FORBIDDEN; + } + } ++#endif + + /* If anon access is allowed, return OK */ + status = req_check_access(r, conf, &repos_path, &dest_repos_path); +@@ -856,7 +941,26 @@ access_checker(request_rec *r) + if (!conf->authoritative) + return DECLINED; + ++#if USE_FORCE_AUTHN ++ if (authn_configured) { ++ /* We have to check to see if authn is required because if so we must ++ * return UNAUTHORIZED (401) rather than FORBIDDEN (403) since returning ++ * the 403 leaks information about what paths may exist to ++ * unauthenticated users. We must set a note here in order ++ * to use ap_some_authn_rquired() without triggering an infinite ++ * loop since the call will trigger this function to be called again. */ ++ apr_table_setn(r->notes, IN_SOME_AUTHN_NOTE, (const char*)1); ++ authn_required = ap_some_authn_required(r); ++ apr_table_unset(r->notes, IN_SOME_AUTHN_NOTE); ++ if (authn_required) ++ { ++ ap_note_auth_failure(r); ++ return HTTP_UNAUTHORIZED; ++ } ++ } ++#else + if (!ap_some_auth_required(r)) ++#endif + log_access_verdict(APLOG_MARK, r, 0, repos_path, dest_repos_path); + + return HTTP_FORBIDDEN; +@@ -937,6 +1041,17 @@ auth_checker(request_rec *r) + return OK; + } + ++#if USE_FORCE_AUTHN ++static int ++force_authn(request_rec *r) ++{ ++ if (apr_table_get(r->notes, FORCE_AUTHN_NOTE)) ++ return OK; ++ ++ return DECLINED; ++} ++#endif ++ + /* + * Module flesh + */ +@@ -953,6 +1068,9 @@ register_hooks(apr_pool_t *p) + * give SSLOptions +FakeBasicAuth a chance to work. */ + ap_hook_check_user_id(check_user_id, mod_ssl, NULL, APR_HOOK_FIRST); + ap_hook_auth_checker(auth_checker, NULL, NULL, APR_HOOK_FIRST); ++#if USE_FORCE_AUTHN ++ ap_hook_force_authn(force_authn, NULL, NULL, APR_HOOK_FIRST); ++#endif + ap_register_provider(p, + AUTHZ_SVN__SUBREQ_BYPASS_PROV_GRP, + AUTHZ_SVN__SUBREQ_BYPASS_PROV_NAME, +Index: subversion/tests/cmdline/README +=================================================================== +--- a/subversion/tests/cmdline/README (revision 1691883) ++++ b/subversion/tests/cmdline/README (working copy) +@@ -83,6 +83,133 @@ paths adjusted appropriately: + Require valid-user + </Location> + ++ <Location /authz-test-work/anon> ++ DAV svn ++ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp ++ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz ++ SVNListParentPath On ++ # This may seem unnecessary but granting access to everyone here is necessary ++ # to exercise a bug with httpd 2.3.x+. The "Require all granted" syntax is ++ # new to 2.3.x+ which we can detect with the mod_authz_core.c module ++ # signature. Use the "Allow from all" syntax with older versions for symmetry. ++ <IfModule mod_authz_core.c> ++ Require all granted ++ </IfModule> ++ <IfModule !mod_authz_core.c> ++ Allow from all ++ </IfMOdule> ++ </Location> ++ <Location /authz-test-work/mixed> ++ DAV svn ++ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp ++ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile /usr/local/apache2/conf/users ++ Require valid-user ++ Satisfy Any ++ </Location> ++ <Location /authz-test-work/mixed-noauthwhenanon> ++ DAV svn ++ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp ++ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile /usr/local/apache2/conf/users ++ Require valid-user ++ AuthzSVNNoAuthWhenAnonymousAllowed On ++ </Location> ++ <Location /authz-test-work/authn> ++ DAV svn ++ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp ++ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile /usr/local/apache2/conf/users ++ Require valid-user ++ </Location> ++ <Location /authz-test-work/authn-anonoff> ++ DAV svn ++ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp ++ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile /usr/local/apache2/conf/users ++ Require valid-user ++ AuthzSVNAnonymous Off ++ </Location> ++ <Location /authz-test-work/authn-lcuser> ++ DAV svn ++ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp ++ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile /usr/local/apache2/conf/users ++ Require valid-user ++ AuthzForceUsernameCase Lower ++ </Location> ++ <Location /authz-test-work/authn-lcuser> ++ DAV svn ++ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp ++ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile /usr/local/apache2/conf/users ++ Require valid-user ++ AuthzForceUsernameCase Lower ++ </Location> ++ <Location /authz-test-work/authn-group> ++ DAV svn ++ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp ++ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile /usr/local/apache2/conf/users ++ AuthGroupFile /usr/local/apache2/conf/groups ++ Require group random ++ AuthzSVNAuthoritative Off ++ </Location> ++ <IfModule mod_authz_core.c> ++ <Location /authz-test-work/sallrany> ++ DAV svn ++ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp ++ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile /usr/local/apache2/conf/users ++ AuthzSendForbiddenOnFailure On ++ Satisfy All ++ <RequireAny> ++ Require valid-user ++ Require expr req('ALLOW') == '1' ++ </RequireAny> ++ </Location> ++ <Location /authz-test-work/sallrall> ++ DAV svn ++ SVNParentPath /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/local_tmp ++ AuthzSVNAccessFile /home/yourusernamehere/projects/svn/subversion/tests/cmdline/svn-test-work/authz ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile /usr/local/apache2/conf/users ++ AuthzSendForbiddenOnFailure On ++ Satisfy All ++ <RequireAll> ++ Require valid-user ++ Require expr req('ALLOW') == '1' ++ </RequireAll> ++ </Location> ++ </IfModule> ++ ++ + RedirectMatch permanent ^/svn-test-work/repositories/REDIRECT-PERM-(.*)$ /svn-test-work/repositories/$1 + RedirectMatch ^/svn-test-work/repositories/REDIRECT-TEMP-(.*)$ /svn-test-work/repositories/$1 + +@@ -101,8 +228,17 @@ just drop the following 2-line snippet into the + ---------------------------- + jrandom:xCGl35kV9oWCY + jconstant:xCGl35kV9oWCY ++JRANDOM:xCGl35kV9oWCY ++JCONSTANT:xCGl35kV9oWCY + ---------------------------- + ++and these lines into the ++/usr/local/apache/conf/groups file: ++---------------------------- ++random: jrandom ++constant: jconstant ++---------------------------- ++ + Now, (re)start Apache and run the tests over mod_dav_svn. + + You can run a test script over DAV: +@@ -138,6 +274,8 @@ Note [1]: It would be quite too much to expect tho + ---------------------------- + jrandom:$apr1$3p1.....$FQW6RceW5QhJ2blWDQgKn0 + jconstant:$apr1$jp1.....$Usrqji1c9H6AbOxOGAzzb0 ++ JRANDOM:$apr1$3p1.....$FQW6RceW5QhJ2blWDQgKn0 ++ JCONSTANT:$apr1$jp1.....$Usrqji1c9H6AbOxOGAzzb0 + ---------------------------- + + +Index: subversion/tests/cmdline/davautocheck.sh +=================================================================== +--- a/subversion/tests/cmdline/davautocheck.sh (revision 1691883) ++++ b/subversion/tests/cmdline/davautocheck.sh (working copy) +@@ -289,8 +289,6 @@ LOAD_MOD_AUTHN_CORE="$(get_loadmodule_config mod_a + || fail "Authn_Core module not found." + LOAD_MOD_AUTHZ_CORE="$(get_loadmodule_config mod_authz_core)" \ + || fail "Authz_Core module not found." +-LOAD_MOD_AUTHZ_HOST="$(get_loadmodule_config mod_authz_host)" \ +- || fail "Authz_Host module not found." + LOAD_MOD_UNIXD=$(get_loadmodule_config mod_unixd) \ + || fail "UnixD module not found" + } +@@ -298,6 +296,10 @@ LOAD_MOD_AUTHN_FILE="$(get_loadmodule_config mod_a + || fail "Authn_File module not found." + LOAD_MOD_AUTHZ_USER="$(get_loadmodule_config mod_authz_user)" \ + || fail "Authz_User module not found." ++LOAD_MOD_AUTHZ_GROUPFILE="$(get_loadmodule_config mod_authz_groupfile)" \ ++ || fail "Authz_GroupFile module not found." ++LOAD_MOD_AUTHZ_HOST="$(get_loadmodule_config mod_authz_host)" \ ++ || fail "Authz_Host module not found." + } + if [ ${APACHE_MPM:+set} ]; then + LOAD_MOD_MPM=$(get_loadmodule_config mod_mpm_$APACHE_MPM) \ +@@ -328,6 +330,7 @@ HTTPD_ERROR_LOG="$HTTPD_ROOT/error_log" + HTTPD_MIME_TYPES="$HTTPD_ROOT/mime.types" + BASE_URL="http://localhost:$HTTPD_PORT" + HTTPD_USERS="$HTTPD_ROOT/users" ++HTTPD_GROUPS="$HTTPD_ROOT/groups" + + mkdir "$HTTPD_ROOT" \ + || fail "couldn't create temporary directory '$HTTPD_ROOT'" +@@ -388,6 +391,14 @@ fi + say "Adding users for lock authentication" + $HTPASSWD -bc $HTTPD_USERS jrandom rayjandom + $HTPASSWD -b $HTTPD_USERS jconstant rayjandom ++$HTPASSWD -b $HTTPD_USERS JRANDOM rayjandom ++$HTPASSWD -b $HTTPD_USERS JCONSTANT rayjandom ++ ++say "Adding groups for mod_authz_svn tests" ++cat > "$HTTPD_GROUPS" <<__EOF__ ++random: jrandom ++constant: jconstant ++__EOF__ + + touch $HTTPD_MIME_TYPES + +@@ -405,7 +416,9 @@ $LOAD_MOD_AUTHN_CORE + $LOAD_MOD_AUTHN_FILE + $LOAD_MOD_AUTHZ_CORE + $LOAD_MOD_AUTHZ_USER ++$LOAD_MOD_AUTHZ_GROUPFILE + $LOAD_MOD_AUTHZ_HOST ++$LOAD_MOD_ACCESS_COMPAT + LoadModule authz_svn_module "$MOD_AUTHZ_SVN" + + __EOF__ +@@ -497,6 +510,161 @@ CustomLog "$HTTPD_ROOT/ops" "%t %u %{SVN + SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL} + ${SVN_PATH_AUTHZ_LINE} + </Location> ++<Location /authz-test-work/anon> ++ DAV svn ++ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp" ++ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz" ++ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL} ++ SVNCacheRevProps ${CACHE_REVPROPS_SETTING} ++ SVNListParentPath On ++ # This may seem unnecessary but granting access to everyone here is necessary ++ # to exercise a bug with httpd 2.3.x+. The "Require all granted" syntax is ++ # new to 2.3.x+ which we can detect with the mod_authz_core.c module ++ # signature. Use the "Allow from all" syntax with older versions for symmetry. ++ <IfModule mod_authz_core.c> ++ Require all granted ++ </IfModule> ++ <IfModule !mod_authz_core.c> ++ Allow from all ++ </IfMOdule> ++ ${SVN_PATH_AUTHZ_LINE} ++</Location> ++<Location /authz-test-work/mixed> ++ DAV svn ++ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp" ++ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz" ++ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL} ++ SVNCacheRevProps ${CACHE_REVPROPS_SETTING} ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile $HTTPD_USERS ++ Require valid-user ++ Satisfy Any ++ ${SVN_PATH_AUTHZ_LINE} ++</Location> ++<Location /authz-test-work/mixed-noauthwhenanon> ++ DAV svn ++ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp" ++ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz" ++ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL} ++ SVNCacheRevProps ${CACHE_REVPROPS_SETTING} ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile $HTTPD_USERS ++ Require valid-user ++ AuthzSVNNoAuthWhenAnonymousAllowed On ++ SVNPathAuthz On ++</Location> ++<Location /authz-test-work/authn> ++ DAV svn ++ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp" ++ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz" ++ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL} ++ SVNCacheRevProps ${CACHE_REVPROPS_SETTING} ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile $HTTPD_USERS ++ Require valid-user ++ ${SVN_PATH_AUTHZ_LINE} ++</Location> ++<Location /authz-test-work/authn-anonoff> ++ DAV svn ++ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp" ++ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz" ++ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL} ++ SVNCacheRevProps ${CACHE_REVPROPS_SETTING} ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile $HTTPD_USERS ++ Require valid-user ++ AuthzSVNAnonymous Off ++ SVNPathAuthz On ++</Location> ++<Location /authz-test-work/authn-lcuser> ++ DAV svn ++ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp" ++ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz" ++ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL} ++ SVNCacheRevProps ${CACHE_REVPROPS_SETTING} ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile $HTTPD_USERS ++ Require valid-user ++ AuthzForceUsernameCase Lower ++ ${SVN_PATH_AUTHZ_LINE} ++</Location> ++<Location /authz-test-work/authn-lcuser> ++ DAV svn ++ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp" ++ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz" ++ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL} ++ SVNCacheRevProps ${CACHE_REVPROPS_SETTING} ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile $HTTPD_USERS ++ Require valid-user ++ AuthzForceUsernameCase Lower ++ ${SVN_PATH_AUTHZ_LINE} ++</Location> ++<Location /authz-test-work/authn-group> ++ DAV svn ++ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp" ++ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz" ++ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL} ++ SVNCacheRevProps ${CACHE_REVPROPS_SETTING} ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile $HTTPD_USERS ++ AuthGroupFile $HTTPD_GROUPS ++ Require group random ++ AuthzSVNAuthoritative Off ++ SVNPathAuthz On ++</Location> ++<IfModule mod_authz_core.c> ++ <Location /authz-test-work/sallrany> ++ DAV svn ++ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp" ++ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz" ++ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL} ++ SVNCacheRevProps ${CACHE_REVPROPS_SETTING} ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile $HTTPD_USERS ++ AuthzSendForbiddenOnFailure On ++ Satisfy All ++ <RequireAny> ++ Require valid-user ++ Require expr req('ALLOW') == '1' ++ </RequireAny> ++ ${SVN_PATH_AUTHZ_LINE} ++ </Location> ++ <Location /authz-test-work/sallrall> ++ DAV svn ++ SVNParentPath "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/local_tmp" ++ AuthzSVNAccessFile "$ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/authz" ++ SVNAdvertiseV2Protocol ${ADVERTISE_V2_PROTOCOL} ++ SVNCacheRevProps ${CACHE_REVPROPS_SETTING} ++ SVNListParentPath On ++ AuthType Basic ++ AuthName "Subversion Repository" ++ AuthUserFile $HTTPD_USERS ++ AuthzSendForbiddenOnFailure On ++ Satisfy All ++ <RequireAll> ++ Require valid-user ++ Require expr req('ALLOW') == '1' ++ </RequireAll> ++ ${SVN_PATH_AUTHZ_LINE} ++ </Location> ++</IfModule> + RedirectMatch permanent ^/svn-test-work/repositories/REDIRECT-PERM-(.*)\$ /svn-test-work/repositories/\$1 + RedirectMatch ^/svn-test-work/repositories/REDIRECT-TEMP-(.*)\$ /svn-test-work/repositories/\$1 + __EOF__ +Index: subversion/tests/cmdline/mod_authz_svn_tests.py +=================================================================== +--- a/subversion/tests/cmdline/mod_authz_svn_tests.py (nonexistent) ++++ b/subversion/tests/cmdline/mod_authz_svn_tests.py (working copy) +@@ -0,0 +1,1073 @@ ++#!/usr/bin/env python ++# ++# mod_authz_svn_tests.py: testing mod_authz_svn ++# ++# Subversion is a tool for revision control. ++# See http://subversion.apache.org for more information. ++# ++# ==================================================================== ++# Licensed to the Apache Software Foundation (ASF) under one ++# or more contributor license agreements. See the NOTICE file ++# distributed with this work for additional information ++# regarding copyright ownership. The ASF licenses this file ++# to you 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. ++###################################################################### ++ ++# General modules ++import os, re, logging ++ ++logger = logging.getLogger() ++ ++# Our testing module ++import svntest ++ ++# (abbreviation) ++Skip = svntest.testcase.Skip_deco ++SkipUnless = svntest.testcase.SkipUnless_deco ++XFail = svntest.testcase.XFail_deco ++Issues = svntest.testcase.Issues_deco ++Issue = svntest.testcase.Issue_deco ++Wimp = svntest.testcase.Wimp_deco ++ ++ls_of_D_no_H = '''<html><head><title>repos - Revision 1: /A/D</title></head> ++<body> ++ <h2>repos - Revision 1: /A/D</h2> ++ <ul> ++ <li><a href="../">..</a></li> ++ <li><a href="G/">G/</a></li> ++ <li><a href="gamma">gamma</a></li> ++ </ul> ++</body></html>''' ++ ++ls_of_D_H = '''<html><head><title>repos - Revision 1: /A/D</title></head> ++<body> ++ <h2>repos - Revision 1: /A/D</h2> ++ <ul> ++ <li><a href="../">..</a></li> ++ <li><a href="G/">G/</a></li> ++ <li><a href="H/">H/</a></li> ++ <li><a href="gamma">gamma</a></li> ++ </ul> ++</body></html>''' ++ ++ls_of_H = '''<html><head><title>repos - Revision 1: /A/D/H</title></head> ++<body> ++ <h2>repos - Revision 1: /A/D/H</h2> ++ <ul> ++ <li><a href="../">..</a></li> ++ <li><a href="chi">chi</a></li> ++ <li><a href="omega">omega</a></li> ++ <li><a href="psi">psi</a></li> ++ </ul> ++</body></html>''' ++ ++user1 = svntest.main.wc_author ++user1_upper = user1.upper() ++user1_pass = svntest.main.wc_passwd ++user1_badpass = 'XXX' ++assert user1_pass != user1_badpass, "Passwords can't match" ++user2 = svntest.main.wc_author2 ++user2_upper = user2.upper() ++user2_pass = svntest.main.wc_passwd ++user2_badpass = 'XXX' ++assert user2_pass != user2_badpass, "Passwords can't match" ++ ++def write_authz_file(sbox): ++ svntest.main.write_authz_file(sbox, { ++ '/': '$anonymous = r\n' + ++ 'jrandom = rw\n' + ++ 'jconstant = rw', ++ '/A/D/H': '$anonymous =\n' + ++ '$authenticated =\n' + ++ 'jrandom = rw' ++ }) ++ ++def write_authz_file_groups(sbox): ++ authz_name = sbox.authz_name() ++ svntest.main.write_authz_file(sbox,{ ++ '/': '* =', ++ }) ++ ++def verify_get(test_area_url, path, user, pw, ++ expected_status, expected_body, headers): ++ import httplib ++ from urlparse import urlparse ++ import base64 ++ ++ req_url = test_area_url + path ++ ++ loc = urlparse(req_url) ++ ++ if loc.scheme == 'http': ++ h = httplib.HTTPConnection(loc.hostname, loc.port) ++ else: ++ h = httplib.HTTPSConnection(loc.hostname, loc.port) ++ ++ if headers is None: ++ headers = {} ++ ++ if user and pw: ++ auth_info = user + ':' + pw ++ headers['Authorization'] = 'Basic ' + base64.b64encode(auth_info) ++ else: ++ auth_info = "anonymous" ++ ++ h.request('GET', req_url, None, headers) ++ ++ r = h.getresponse() ++ ++ actual_status = r.status ++ if expected_status and expected_status != actual_status: ++ ++ logger.warn("Expected status '" + str(expected_status) + ++ "' but got '" + str(actual_status) + ++ "' on url '" + req_url + "' (" + ++ auth_info + ").") ++ raise svntest.Failure ++ ++ if expected_body: ++ actual_body = r.read() ++ if expected_body != actual_body: ++ logger.warn("Expected body:") ++ logger.warn(expected_body) ++ logger.warn("But got:") ++ logger.warn(actual_body) ++ logger.warn("on url '" + req_url + "' (" + auth_info + ").") ++ raise svntest.Failure ++ ++def verify_gets(test_area_url, tests): ++ for test in tests: ++ verify_get(test_area_url, test['path'], test.get('user'), test.get('pw'), ++ test['status'], test.get('body'), test.get('headers')) ++ ++ ++###################################################################### ++# Tests ++# ++# Each test must return on success or raise on failure. ++ ++ ++#---------------------------------------------------------------------- ++ ++ ++@SkipUnless(svntest.main.is_ra_type_dav) ++def anon(sbox): ++ "test anonymous access" ++ sbox.build(read_only = True, create_wc = False) ++ ++ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos', ++ '/authz-test-work/anon') ++ ++ write_authz_file(sbox) ++ ++ anon_tests = ( ++ { 'path': '', 'status': 301 }, ++ { 'path': '/', 'status': 200 }, ++ { 'path': '/repos', 'status': 301 }, ++ { 'path': '/repos/', 'status': 200 }, ++ { 'path': '/repos/A', 'status': 301 }, ++ { 'path': '/repos/A/', 'status': 200 }, ++ { 'path': '/repos/A/D', 'status': 301 }, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H }, ++ { 'path': '/repos/A/D/gamma', 'status': 200 }, ++ { 'path': '/repos/A/D/H', 'status': 403 }, ++ { 'path': '/repos/A/D/H/', 'status': 403 }, ++ { 'path': '/repos/A/D/H/chi', 'status': 403 }, ++ # auth isn't configured so nothing should change when passing ++ # authn details ++ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '', 'status': 301, 'user': user2, 'pw': user1_pass}, ++ { 'path': '/', 'status': 200, 'user': user2, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user2, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ ) ++ ++ verify_gets(test_area_url, anon_tests) ++ ++ ++@SkipUnless(svntest.main.is_ra_type_dav) ++def mixed(sbox): ++ "test mixed anonymous and authenticated access" ++ sbox.build(read_only = True, create_wc = False) ++ ++ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos', ++ '/authz-test-work/mixed') ++ ++ write_authz_file(sbox) ++ ++ mixed_tests = ( ++ { 'path': '', 'status': 301, }, ++ { 'path': '/', 'status': 200, }, ++ { 'path': '/repos', 'status': 301, }, ++ { 'path': '/repos/', 'status': 200, }, ++ { 'path': '/repos/A', 'status': 301, }, ++ { 'path': '/repos/A/', 'status': 200, }, ++ { 'path': '/repos/A/D', 'status': 301, }, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ }, ++ { 'path': '/repos/A/D/gamma', 'status': 200, }, ++ { 'path': '/repos/A/D/H', 'status': 401, }, ++ { 'path': '/repos/A/D/H/', 'status': 401, }, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, }, ++ # auth is configured and user1 is allowed access to H ++ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H, ++ 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ # try with the wrong password for user1 ++ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ # auth is configured and user2 is not allowed access to H ++ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ # try with the wrong password for user2 ++ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ ) ++ ++ verify_gets(test_area_url, mixed_tests) ++ ++@SkipUnless(svntest.main.is_ra_type_dav) ++@XFail(svntest.main.is_httpd_authz_provider_enabled) ++# uses the AuthzSVNNoAuthWhenAnonymousAllowed On directive ++# this is broken with httpd 2.3.x+ since it requires the auth system to accept ++# r->user == NULL and there is a test for this in server/request.c now. It ++# was intended as a workaround for the lack of Satisfy Any in 2.3.x+ which ++# was resolved by httpd with mod_access_compat in 2.3.x+. ++def mixed_noauthwhenanon(sbox): ++ "test mixed with noauthwhenanon directive" ++ sbox.build(read_only = True, create_wc = False) ++ ++ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos', ++ '/authz-test-work/mixed-noauthwhenanon') ++ ++ write_authz_file(sbox) ++ ++ noauthwhenanon_tests = ( ++ { 'path': '', 'status': 301, }, ++ { 'path': '/', 'status': 200, }, ++ { 'path': '/repos', 'status': 301, }, ++ { 'path': '/repos/', 'status': 200, }, ++ { 'path': '/repos/A', 'status': 301, }, ++ { 'path': '/repos/A/', 'status': 200, }, ++ { 'path': '/repos/A/D', 'status': 301, }, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ }, ++ { 'path': '/repos/A/D/gamma', 'status': 200, }, ++ { 'path': '/repos/A/D/H', 'status': 401, }, ++ { 'path': '/repos/A/D/H/', 'status': 401, }, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, }, ++ # auth is configured and user1 is allowed access to H ++ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H, ++ 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ # try with the wrong password for user1 ++ # note that unlike doing this with Satisfy Any this case ++ # actually provides anon access when provided with an invalid ++ # password ++ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ # auth is configured and user2 is not allowed access to H ++ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ # try with the wrong password for user2 ++ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ ) ++ ++ verify_gets(test_area_url, noauthwhenanon_tests) ++ ++ ++@SkipUnless(svntest.main.is_ra_type_dav) ++def authn(sbox): ++ "test authenticated only access" ++ sbox.build(read_only = True, create_wc = False) ++ ++ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos', ++ '/authz-test-work/authn') ++ ++ write_authz_file(sbox) ++ ++ authn_tests = ( ++ { 'path': '', 'status': 401, }, ++ { 'path': '/', 'status': 401, }, ++ { 'path': '/repos', 'status': 401, }, ++ { 'path': '/repos/', 'status': 401, }, ++ { 'path': '/repos/A', 'status': 401, }, ++ { 'path': '/repos/A/', 'status': 401, }, ++ { 'path': '/repos/A/D', 'status': 401, }, ++ { 'path': '/repos/A/D/', 'status': 401, }, ++ { 'path': '/repos/A/D/gamma', 'status': 401, }, ++ { 'path': '/repos/A/D/H', 'status': 401, }, ++ { 'path': '/repos/A/D/H/', 'status': 401, }, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, }, ++ # auth is configured and user1 is allowed access to H ++ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H, ++ 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ # try with upper case username for user1 ++ { 'path': '', 'status': 301, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/', 'status': 200, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ # try with the wrong password for user1 ++ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ # auth is configured and user2 is not allowed access to H ++ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ # try with upper case username for user2 ++ { 'path': '', 'status': 301, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/', 'status': 200, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ # try with the wrong password for user2 ++ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ ) ++ ++ verify_gets(test_area_url, authn_tests) ++ ++@SkipUnless(svntest.main.is_ra_type_dav) ++def authn_anonoff(sbox): ++ "test authenticated only access with anonoff" ++ sbox.build(read_only = True, create_wc = False) ++ ++ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos', ++ '/authz-test-work/authn-anonoff') ++ ++ write_authz_file(sbox) ++ ++ anonoff_tests = ( ++ { 'path': '', 'status': 401, }, ++ { 'path': '/', 'status': 401, }, ++ { 'path': '/repos', 'status': 401, }, ++ { 'path': '/repos/', 'status': 401, }, ++ { 'path': '/repos/A', 'status': 401, }, ++ { 'path': '/repos/A/', 'status': 401, }, ++ { 'path': '/repos/A/D', 'status': 401, }, ++ { 'path': '/repos/A/D/', 'status': 401, }, ++ { 'path': '/repos/A/D/gamma', 'status': 401, }, ++ { 'path': '/repos/A/D/H', 'status': 401, }, ++ { 'path': '/repos/A/D/H/', 'status': 401, }, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, }, ++ # auth is configured and user1 is allowed access to H ++ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H, ++ 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ # try with upper case username for user1 ++ { 'path': '', 'status': 301, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/', 'status': 200, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1_upper, 'pw': user1_pass}, ++ # try with the wrong password for user1 ++ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ # auth is configured and user2 is not allowed access to H ++ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ # try with upper case username for user2 ++ { 'path': '', 'status': 301, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/', 'status': 200, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ # try with the wrong password for user2 ++ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ ) ++ ++ verify_gets(test_area_url, anonoff_tests) ++ ++@SkipUnless(svntest.main.is_ra_type_dav) ++def authn_lcuser(sbox): ++ "test authenticated only access with lcuser" ++ sbox.build(read_only = True, create_wc = False) ++ ++ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos', ++ '/authz-test-work/authn-lcuser') ++ ++ write_authz_file(sbox) ++ ++ lcuser_tests = ( ++ # try with upper case username for user1 (works due to lcuser option) ++ { 'path': '', 'status': 301, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/', 'status': 200, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H, ++ 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1_upper, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1_upper, 'pw': user1_pass}, ++ # try with upper case username for user2 (works due to lcuser option) ++ { 'path': '', 'status': 301, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/', 'status': 200, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2_upper, 'pw': user2_pass}, ++ ) ++ ++ verify_gets(test_area_url, lcuser_tests) ++ ++# authenticated access only by group - a excuse to use AuthzSVNAuthoritative Off ++# this is terribly messed up, Require group runs after mod_authz_svn. ++# so if mod_authz_svn grants the access then it doesn't matter what the group ++# requirement says. If we reject the access then you can use the AuthzSVNAuthoritative Off ++# directive to fall through to the group check. Overall the behavior of setups like this ++# is almost guaranteed to not be what users expect. ++@SkipUnless(svntest.main.is_ra_type_dav) ++def authn_group(sbox): ++ "test authenticated only access via groups" ++ sbox.build(read_only = True, create_wc = False) ++ ++ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos', ++ '/authz-test-work/authn-group') ++ ++ # Can't use write_authz_file() as most tests because we want to deny all ++ # access with mod_authz_svn so the tests fall through to the group handling ++ authz_name = sbox.authz_name() ++ svntest.main.write_authz_file(sbox, { ++ '/': '* =', ++ }) ++ ++ group_tests = ( ++ { 'path': '', 'status': 401, }, ++ { 'path': '/', 'status': 401, }, ++ { 'path': '/repos', 'status': 401, }, ++ { 'path': '/repos/', 'status': 401, }, ++ { 'path': '/repos/A', 'status': 401, }, ++ { 'path': '/repos/A/', 'status': 401, }, ++ { 'path': '/repos/A/D', 'status': 401, }, ++ { 'path': '/repos/A/D/', 'status': 401, }, ++ { 'path': '/repos/A/D/gamma', 'status': 401, }, ++ { 'path': '/repos/A/D/H', 'status': 401, }, ++ { 'path': '/repos/A/D/H/', 'status': 401, }, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, }, ++ # auth is configured and user1 is allowed access repo including H ++ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H, ++ 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ ) ++ ++ verify_gets(test_area_url, group_tests) ++ ++# This test exists to validate our behavior when used with the new authz ++# provider system introduced in httpd 2.3.x. The Satisfy directive ++# determines how older authz hooks are combined and the RequireA(ll|ny) ++# blocks handles how new authz providers are combined. The overall results of ++# all the authz providers (combined per the Require* blocks) are then ++# combined with the other authz hooks via the Satisfy directive. ++# Meaning this test requires that mod_authz_svn says yes and there is ++# either a valid user or the ALLOW header is 1. The header may seem ++# like a silly test but it's easier to excercise than say a host directive ++# in a repeatable test. ++@SkipUnless(svntest.main.is_httpd_authz_provider_enabled) ++def authn_sallrany(sbox): ++ "test satisfy all require any config" ++ sbox.build(read_only = True, create_wc = False) ++ ++ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos', ++ '/authz-test-work/sallrany') ++ ++ write_authz_file(sbox) ++ ++ allow_header = { 'ALLOW': '1' } ++ ++ sallrany_tests = ( ++ #anon access isn't allowed without ALLOW header ++ { 'path': '', 'status': 401, }, ++ { 'path': '/', 'status': 401, }, ++ { 'path': '/repos', 'status': 401, }, ++ { 'path': '/repos/', 'status': 401, }, ++ { 'path': '/repos/A', 'status': 401, }, ++ { 'path': '/repos/A/', 'status': 401, }, ++ { 'path': '/repos/A/D', 'status': 401, }, ++ { 'path': '/repos/A/D/', 'status': 401, }, ++ { 'path': '/repos/A/D/gamma', 'status': 401, }, ++ { 'path': '/repos/A/D/H', 'status': 401, }, ++ { 'path': '/repos/A/D/H/', 'status': 401, }, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, }, ++ # auth is configured and user1 is allowed access repo including H ++ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H, ++ 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass}, ++ # try with the wrong password for user1 ++ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass}, ++ # auth is configured and user2 is not allowed access to H ++ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ # try with the wrong password for user2 ++ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass}, ++ # anon is allowed with the ALLOW header ++ { 'path': '', 'status': 301, 'headers': allow_header }, ++ { 'path': '/', 'status': 200, 'headers': allow_header }, ++ { 'path': '/repos', 'status': 301, 'headers': allow_header }, ++ { 'path': '/repos/', 'status': 200, 'headers': allow_header }, ++ { 'path': '/repos/A', 'status': 301, 'headers': allow_header }, ++ { 'path': '/repos/A/', 'status': 200, 'headers': allow_header }, ++ { 'path': '/repos/A/D', 'status': 301, 'headers': allow_header }, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, 'headers': allow_header }, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'headers': allow_header }, ++ # these 3 tests return 403 instead of 401 becasue the config allows ++ # the anon user with the ALLOW header without any auth and the old hook ++ # system has no way of knowing it should return 401 since authentication is ++ # configured and can change the behavior. It could decide to return 401 just on ++ # the basis of authentication being configured but then that leaks info in other ++ # cases so it's better for this case to be "broken". ++ { 'path': '/repos/A/D/H', 'status': 403, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'headers': allow_header }, ++ # auth is configured and user1 is allowed access repo including H ++ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H, ++ 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ # try with the wrong password for user1 ++ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ # auth is configured and user2 is not allowed access to H ++ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ # try with the wrong password for user2 ++ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ ++ ) ++ ++ verify_gets(test_area_url, sallrany_tests) ++ ++# See comments on authn_sallrany test for some background on the interaction ++# of Satisfy Any and the newer Require blocks. ++@SkipUnless(svntest.main.is_httpd_authz_provider_enabled) ++def authn_sallrall(sbox): ++ "test satisfy all require all config" ++ sbox.build(read_only = True, create_wc = False) ++ ++ test_area_url = sbox.repo_url.replace('/svn-test-work/local_tmp/repos', ++ '/authz-test-work/sallrall') ++ ++ write_authz_file(sbox) ++ ++ allow_header = { 'ALLOW': '1' } ++ ++ sallrall_tests = ( ++ #anon access isn't allowed without ALLOW header ++ { 'path': '', 'status': 403, }, ++ { 'path': '/', 'status': 403, }, ++ { 'path': '/repos', 'status': 403, }, ++ { 'path': '/repos/', 'status': 403, }, ++ { 'path': '/repos/A', 'status': 403, }, ++ { 'path': '/repos/A/', 'status': 403, }, ++ { 'path': '/repos/A/D', 'status': 403, }, ++ { 'path': '/repos/A/D/', 'status': 403, }, ++ { 'path': '/repos/A/D/gamma', 'status': 403, }, ++ { 'path': '/repos/A/D/H', 'status': 403, }, ++ { 'path': '/repos/A/D/H/', 'status': 403, }, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, }, ++ # auth is configured but no access is allowed without the ALLOW header ++ { 'path': '', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1, 'pw': user1_pass}, ++ # try with the wrong password for user1 ++ { 'path': '', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user1, 'pw': user1_badpass}, ++ # auth is configured but no access is allowed without the ALLOW header ++ { 'path': '', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass}, ++ # try with the wrong password for user2 ++ { 'path': '', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/gamma', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_badpass}, ++ # anon is not allowed even with ALLOW header ++ { 'path': '', 'status': 401, 'headers': allow_header }, ++ { 'path': '/', 'status': 401, 'headers': allow_header }, ++ { 'path': '/repos', 'status': 401, 'headers': allow_header }, ++ { 'path': '/repos/', 'status': 401, 'headers': allow_header }, ++ { 'path': '/repos/A', 'status': 401, 'headers': allow_header }, ++ { 'path': '/repos/A/', 'status': 401, 'headers': allow_header }, ++ { 'path': '/repos/A/D', 'status': 401, 'headers': allow_header }, ++ { 'path': '/repos/A/D/', 'status': 401, 'headers': allow_header }, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H', 'status': 401, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'headers': allow_header }, ++ # auth is configured and user1 is allowed access repo including H ++ { 'path': '', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_H, ++ 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H', 'status': 301, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/', 'status': 200, 'body': ls_of_H, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/chi', 'status': 200, 'user': user1, 'pw': user1_pass, 'headers': allow_header }, ++ # try with the wrong password for user1 ++ { 'path': '', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user1, 'pw': user1_badpass, 'headers': allow_header }, ++ # auth is configured and user2 is not allowed access to H ++ { 'path': '', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D', 'status': 301, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/', 'status': 200, 'body': ls_of_D_no_H, ++ 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/gamma', 'status': 200, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/chi', 'status': 403, 'user': user2, 'pw': user2_pass, 'headers': allow_header }, ++ # try with the wrong password for user2 ++ { 'path': '', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/gamma', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ { 'path': '/repos/A/D/H/chi', 'status': 401, 'user': user2, 'pw': user2_badpass, 'headers': allow_header }, ++ ++ ) ++ ++ verify_gets(test_area_url, sallrall_tests) ++ ++ ++######################################################################## ++# Run the tests ++ ++ ++# list all tests here, starting with None: ++test_list = [ None, ++ anon, ++ mixed, ++ mixed_noauthwhenanon, ++ authn, ++ authn_anonoff, ++ authn_lcuser, ++ authn_group, ++ authn_sallrany, ++ authn_sallrall, ++ ] ++serial_only = True ++ ++if __name__ == '__main__': ++ svntest.main.run_tests(test_list) ++ # NOTREACHED ++ ++ ++### End of file. + +Property changes on: subversion/tests/cmdline/mod_authz_svn_tests.py +___________________________________________________________________ +Added: svn:eol-style +## -0,0 +1 ## ++native +\ No newline at end of property +Index: subversion/tests/cmdline/svntest/main.py +=================================================================== +--- a/subversion/tests/cmdline/svntest/main.py (revision 1691883) ++++ b/subversion/tests/cmdline/svntest/main.py (working copy) +@@ -1378,6 +1378,30 @@ def is_plaintext_password_storage_disabled(): + return False + return True + ++ ++# https://issues.apache.org/bugzilla/show_bug.cgi?id=56480 ++# https://issues.apache.org/bugzilla/show_bug.cgi?id=55397 ++__mod_dav_url_quoting_broken_versions = frozenset([ ++ '2.2.27', ++ '2.2.26', ++ '2.2.25', ++ '2.4.9', ++ '2.4.8', ++ '2.4.7', ++ '2.4.6', ++ '2.4.5', ++]) ++def is_mod_dav_url_quoting_broken(): ++ if is_ra_type_dav(): ++ return (options.httpd_version in __mod_dav_url_quoting_broken_versions) ++ return None ++ ++def is_httpd_authz_provider_enabled(): ++ if is_ra_type_dav(): ++ v = options.httpd_version.split('.') ++ return (v[0] == '2' and int(v[1]) >= 3) or int(v[0]) > 2 ++ return None ++ + ###################################################################### + + +@@ -1435,6 +1459,8 @@ class TestSpawningThread(threading.Thread): + args.append('--ssl-cert=' + options.ssl_cert) + if options.http_proxy: + args.append('--http-proxy=' + options.http_proxy) ++ if options.httpd_version: ++ args.append('--httpd-version=' + options.httpd_version) + + result, stdout_lines, stderr_lines = spawn_process(command, 0, False, None, + *args) +@@ -1600,6 +1626,12 @@ class TestRunner: + sandbox.cleanup_test_paths() + return exit_code + ++def is_httpd_authz_provider_enabled(): ++ if is_ra_type_dav(): ++ v = options.httpd_version.split('.') ++ return (v[0] == '2' and int(v[1]) >= 3) or int(v[0]) > 2 ++ return None ++ + ###################################################################### + # Main testing functions + +@@ -1780,6 +1812,8 @@ def _create_parser(): + help='Path to SSL server certificate.') + parser.add_option('--http-proxy', action='store', + help='Use the HTTP Proxy at hostname:port.') ++ parser.add_option('--httpd-version', action='store', ++ help='Assume HTTPD is this version.') + parser.add_option('--tools-bin', action='store', dest='tools_bin', + help='Use the svn tools installed in this path') + +Index: win-tests.py +=================================================================== +--- a/win-tests.py (revision 1691883) ++++ b/win-tests.py (working copy) +@@ -481,6 +481,7 @@ class Httpd: + self.httpd_config = os.path.join(self.root, 'httpd.conf') + self.httpd_users = os.path.join(self.root, 'users') + self.httpd_mime_types = os.path.join(self.root, 'mime.types') ++ self.httpd_groups = os.path.join(self.root, 'groups') + self.abs_builddir = abs_builddir + self.abs_objdir = abs_objdir + self.service_name = 'svn-test-httpd-' + str(httpd_port) +@@ -494,6 +495,7 @@ class Httpd: + create_target_dir(self.root_dir) + + self._create_users_file() ++ self._create_groups_file() + self._create_mime_types_file() + self._create_dontdothat_file() + +@@ -540,6 +542,8 @@ class Httpd: + if self.httpd_ver >= 2.2: + fp.write(self._sys_module('auth_basic_module', 'mod_auth_basic.so')) + fp.write(self._sys_module('authn_file_module', 'mod_authn_file.so')) ++ fp.write(self._sys_module('authz_groupfile_module', 'mod_authz_groupfile.so')) ++ fp.write(self._sys_module('authz_host_module', 'mod_authz_host.so')) + else: + fp.write(self._sys_module('auth_module', 'mod_auth.so')) + fp.write(self._sys_module('alias_module', 'mod_alias.so')) +@@ -562,6 +566,7 @@ class Httpd: + # Define two locations for repositories + fp.write(self._svn_repo('repositories')) + fp.write(self._svn_repo('local_tmp')) ++ fp.write(self._svn_authz_repo()) + + # And two redirects for the redirect tests + fp.write('RedirectMatch permanent ^/svn-test-work/repositories/' +@@ -592,7 +597,18 @@ class Httpd: + 'jrandom', 'rayjandom']) + os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users, + 'jconstant', 'rayjandom']) ++ os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users, ++ 'JRANDOM', 'rayjandom']) ++ os.spawnv(os.P_WAIT, htpasswd, ['htpasswd.exe', '-bp', self.httpd_users, ++ 'JCONSTANT', 'rayjandom']) + ++ def _create_groups_file(self): ++ "Create groups for mod_authz_svn tests" ++ fp = open(self.httpd_groups, 'w') ++ fp.write('random: jrandom\n') ++ fp.write('constant: jconstant\n') ++ fp.close() ++ + def _create_mime_types_file(self): + "Create empty mime.types file" + fp = open(self.httpd_mime_types, 'w') +@@ -652,6 +668,153 @@ class Httpd: + ' DontDoThatConfigFile ' + self._quote(self.dontdothat_file) + '\n' \ + '</Location>\n' + ++ def _svn_authz_repo(self): ++ local_tmp = os.path.join(self.abs_builddir, ++ CMDLINE_TEST_SCRIPT_NATIVE_PATH, ++ 'svn-test-work', 'local_tmp') ++ return \ ++ '<Location /authz-test-work/anon>' + '\n' \ ++ ' DAV svn' + '\n' \ ++ ' SVNParentPath ' + local_tmp + '\n' \ ++ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ ++ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ ++ ' SVNListParentPath On' + '\n' \ ++ ' <IfModule mod_authz_core.c>' + '\n' \ ++ ' Require all granted' + '\n' \ ++ ' </IfModule>' + '\n' \ ++ ' <IfModule !mod_authz_core.c>' + '\n' \ ++ ' Allow from all' + '\n' \ ++ ' </IfModule>' + '\n' \ ++ ' SVNPathAuthz ' + self.path_authz_option + '\n' \ ++ '</Location>' + '\n' \ ++ '<Location /authz-test-work/mixed>' + '\n' \ ++ ' DAV svn' + '\n' \ ++ ' SVNParentPath ' + local_tmp + '\n' \ ++ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ ++ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ ++ ' SVNListParentPath On' + '\n' \ ++ ' AuthType Basic' + '\n' \ ++ ' AuthName "Subversion Repository"' + '\n' \ ++ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ ++ ' Require valid-user' + '\n' \ ++ ' Satisfy Any' + '\n' \ ++ ' SVNPathAuthz ' + self.path_authz_option + '\n' \ ++ '</Location>' + '\n' \ ++ '<Location /authz-test-work/mixed-noauthwhenanon>' + '\n' \ ++ ' DAV svn' + '\n' \ ++ ' SVNParentPath ' + local_tmp + '\n' \ ++ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ ++ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ ++ ' SVNListParentPath On' + '\n' \ ++ ' AuthType Basic' + '\n' \ ++ ' AuthName "Subversion Repository"' + '\n' \ ++ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ ++ ' Require valid-user' + '\n' \ ++ ' AuthzSVNNoAuthWhenAnonymousAllowed On' + '\n' \ ++ ' SVNPathAuthz On' + '\n' \ ++ '</Location>' + '\n' \ ++ '<Location /authz-test-work/authn>' + '\n' \ ++ ' DAV svn' + '\n' \ ++ ' SVNParentPath ' + local_tmp + '\n' \ ++ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ ++ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ ++ ' SVNListParentPath On' + '\n' \ ++ ' AuthType Basic' + '\n' \ ++ ' AuthName "Subversion Repository"' + '\n' \ ++ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ ++ ' Require valid-user' + '\n' \ ++ ' SVNPathAuthz ' + self.path_authz_option + '\n' \ ++ '</Location>' + '\n' \ ++ '<Location /authz-test-work/authn-anonoff>' + '\n' \ ++ ' DAV svn' + '\n' \ ++ ' SVNParentPath ' + local_tmp + '\n' \ ++ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ ++ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ ++ ' SVNListParentPath On' + '\n' \ ++ ' AuthType Basic' + '\n' \ ++ ' AuthName "Subversion Repository"' + '\n' \ ++ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ ++ ' Require valid-user' + '\n' \ ++ ' AuthzSVNAnonymous Off' + '\n' \ ++ ' SVNPathAuthz On' + '\n' \ ++ '</Location>' + '\n' \ ++ '<Location /authz-test-work/authn-lcuser>' + '\n' \ ++ ' DAV svn' + '\n' \ ++ ' SVNParentPath ' + local_tmp + '\n' \ ++ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ ++ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ ++ ' SVNListParentPath On' + '\n' \ ++ ' AuthType Basic' + '\n' \ ++ ' AuthName "Subversion Repository"' + '\n' \ ++ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ ++ ' Require valid-user' + '\n' \ ++ ' AuthzForceUsernameCase Lower' + '\n' \ ++ ' SVNPathAuthz ' + self.path_authz_option + '\n' \ ++ '</Location>' + '\n' \ ++ '<Location /authz-test-work/authn-lcuser>' + '\n' \ ++ ' DAV svn' + '\n' \ ++ ' SVNParentPath ' + local_tmp + '\n' \ ++ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ ++ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ ++ ' SVNListParentPath On' + '\n' \ ++ ' AuthType Basic' + '\n' \ ++ ' AuthName "Subversion Repository"' + '\n' \ ++ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ ++ ' Require valid-user' + '\n' \ ++ ' AuthzForceUsernameCase Lower' + '\n' \ ++ ' SVNPathAuthz ' + self.path_authz_option + '\n' \ ++ '</Location>' + '\n' \ ++ '<Location /authz-test-work/authn-group>' + '\n' \ ++ ' DAV svn' + '\n' \ ++ ' SVNParentPath ' + local_tmp + '\n' \ ++ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ ++ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ ++ ' SVNListParentPath On' + '\n' \ ++ ' AuthType Basic' + '\n' \ ++ ' AuthName "Subversion Repository"' + '\n' \ ++ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ ++ ' AuthGroupFile ' + self._quote(self.httpd_groups) + '\n' \ ++ ' Require group random' + '\n' \ ++ ' AuthzSVNAuthoritative Off' + '\n' \ ++ ' SVNPathAuthz On' + '\n' \ ++ '</Location>' + '\n' \ ++ '<IfModule mod_authz_core.c>' + '\n' \ ++ '<Location /authz-test-work/sallrany>' + '\n' \ ++ ' DAV svn' + '\n' \ ++ ' SVNParentPath ' + local_tmp + '\n' \ ++ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ ++ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ ++ ' SVNListParentPath On' + '\n' \ ++ ' AuthType Basic' + '\n' \ ++ ' AuthName "Subversion Repository"' + '\n' \ ++ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ ++ ' AuthzSendForbiddenOnFailure On' + '\n' \ ++ ' Satisfy All' + '\n' \ ++ ' <RequireAny>' + '\n' \ ++ ' Require valid-user' + '\n' \ ++ ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \ ++ ' </RequireAny>' + '\n' \ ++ ' SVNPathAuthz ' + self.path_authz_option + '\n' \ ++ '</Location>' + '\n' \ ++ '<Location /authz-test-work/sallrall>'+ '\n' \ ++ ' DAV svn' + '\n' \ ++ ' SVNParentPath ' + local_tmp + '\n' \ ++ ' AuthzSVNAccessFile ' + self._quote(self.authz_file) + '\n' \ ++ ' SVNAdvertiseV2Protocol ' + self.httpv2_option + '\n' \ ++ ' SVNListParentPath On' + '\n' \ ++ ' AuthType Basic' + '\n' \ ++ ' AuthName "Subversion Repository"' + '\n' \ ++ ' AuthUserFile ' + self._quote(self.httpd_users) + '\n' \ ++ ' AuthzSendForbiddenOnFailure On' + '\n' \ ++ ' Satisfy All' + '\n' \ ++ ' <RequireAll>' + '\n' \ ++ ' Require valid-user' + '\n' \ ++ ' Require expr req(\'ALLOW\') == \'1\'' + '\n' \ ++ ' </RequireAll>' + '\n' \ ++ ' SVNPathAuthz ' + self.path_authz_option + '\n' \ ++ '</Location>' + '\n' \ ++ '</IfModule>' + '\n' \ ++ + def start(self): + if self.service: + self._start_service() +@@ -786,6 +949,10 @@ if not test_javahl: + log_file = os.path.join(abs_builddir, log) + fail_log_file = os.path.join(abs_builddir, faillog) + ++ if run_httpd: ++ httpd_version = "%.1f" % daemon.httpd_ver ++ else: ++ httpd_version = None + th = run_tests.TestHarness(abs_srcdir, abs_builddir, + log_file, + fail_log_file, +@@ -795,6 +962,7 @@ if not test_javahl: + fsfs_sharding, fsfs_packing, + list_tests, svn_bin, mode_filter, + milestone_filter, ++ httpd_version=httpd_version, + set_log_level=log_level, ssl_cert=ssl_cert) + old_cwd = os.getcwd() + try:
meta/recipes-devtools/subversion/subversion_1.8.13.bb +1 -0
@@ -14,6 +14,7 @@ SRC_URI = "${APACHE_MIRROR}/${BPN}/${BPN}-${PV}.tar.bz2 \ file://libtool2.patch \ file://disable_macos.patch \ file://serf.m4-Regex-modified-to-allow-D-in-paths.patch \ + file://subversion-CVE-2015-3184.patch \ " SRC_URI[md5sum] = "4413417b529d7bdf82f74e50df02e88b" SRC_URI[sha256sum] = "1099cc68840753b48aedb3a27ebd1e2afbcc84ddb871412e5d500e843d607579"
Signature · 1 line
1.7.9.5
PATCH 14/15 subversion: fix CVE-2015-3187
In reply to Robert YangFrom: Wenzong Fan <[email protected]>
The svn_repos_trace_node_locations function in Apache Subversion before 1.7.21 and 1.8.x before 1.8.14, when path-based authorization is used, allows remote authenticated users to obtain sensitive path information by reading the history of a node that has been moved from a hidden path.
Patch is from:
http://subversion.apache.org/security/CVE-2015-3187-advisory.txt
(From OE-Core master rev: 6da25614edcad30fdb4bea8ff47b81ff81cdaed2)
--- .../subversion-CVE-2015-3187.patch | 346 ++++++++++++++++++++ .../subversion/subversion_1.8.13.bb | 1 + 2 files changed, 347 insertions(+) create mode 100644 meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3187.patch
meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3187.patch new file +346 -0
@@ -0,0 +1,346 @@ +Fix CVE-2015-3187 + +Patch is from: +http://subversion.apache.org/security/CVE-2015-3187-advisory.txt + +Upstream-Status: Backport + +Signed-off-by: Wenzong Fan <[email protected]> + +Index: subversion/libsvn_repos/rev_hunt.c +=================================================================== +--- a/subversion/libsvn_repos/rev_hunt.c (revision 1685077) ++++ b/subversion/libsvn_repos/rev_hunt.c (working copy) +@@ -726,23 +726,6 @@ svn_repos_trace_node_locations(svn_fs_t *fs, + if (! prev_path) + break; + +- if (authz_read_func) +- { +- svn_boolean_t readable; +- svn_fs_root_t *tmp_root; +- +- SVN_ERR(svn_fs_revision_root(&tmp_root, fs, revision, currpool)); +- SVN_ERR(authz_read_func(&readable, tmp_root, path, +- authz_read_baton, currpool)); +- if (! readable) +- { +- svn_pool_destroy(lastpool); +- svn_pool_destroy(currpool); +- +- return SVN_NO_ERROR; +- } +- } +- + /* Assign the current path to all younger revisions until we reach + the copy target rev. */ + while ((revision_ptr < revision_ptr_end) +@@ -765,6 +748,20 @@ svn_repos_trace_node_locations(svn_fs_t *fs, + path = prev_path; + revision = prev_rev; + ++ if (authz_read_func) ++ { ++ svn_boolean_t readable; ++ SVN_ERR(svn_fs_revision_root(&root, fs, revision, currpool)); ++ SVN_ERR(authz_read_func(&readable, root, path, ++ authz_read_baton, currpool)); ++ if (!readable) ++ { ++ svn_pool_destroy(lastpool); ++ svn_pool_destroy(currpool); ++ return SVN_NO_ERROR; ++ } ++ } ++ + /* Clear last pool and switch. */ + svn_pool_clear(lastpool); + tmppool = lastpool; +Index: subversion/tests/cmdline/authz_tests.py +=================================================================== +--- a/subversion/tests/cmdline/authz_tests.py (revision 1685077) ++++ b/subversion/tests/cmdline/authz_tests.py (working copy) +@@ -609,8 +609,10 @@ def authz_log_and_tracing_test(sbox): + + ## cat + ++ expected_err2 = ".*svn: E195012: Unable to find repository location.*" ++ + # now see if we can look at the older version of rho +- svntest.actions.run_and_verify_svn(None, None, expected_err, ++ svntest.actions.run_and_verify_svn(None, None, expected_err2, + 'cat', '-r', '2', D_url+'/rho') + + if sbox.repo_url.startswith('http'): +@@ -627,10 +629,11 @@ def authz_log_and_tracing_test(sbox): + svntest.actions.run_and_verify_svn(None, None, expected_err, + 'diff', '-r', 'HEAD', G_url+'/rho') + +- svntest.actions.run_and_verify_svn(None, None, expected_err, ++ # diff treats the unreadable path as indicating an add so no error ++ svntest.actions.run_and_verify_svn(None, None, [], + 'diff', '-r', '2', D_url+'/rho') + +- svntest.actions.run_and_verify_svn(None, None, expected_err, ++ svntest.actions.run_and_verify_svn(None, None, [], + 'diff', '-r', '2:4', D_url+'/rho') + + # test whether read access is correctly granted and denied +Index: subversion/tests/libsvn_repos/repos-test.c +=================================================================== +--- a/subversion/tests/libsvn_repos/repos-test.c (revision 1685077) ++++ b/subversion/tests/libsvn_repos/repos-test.c (working copy) +@@ -3524,6 +3524,245 @@ test_load_r0_mergeinfo(const svn_test_opts_t *opts + return SVN_NO_ERROR; + } + ++static svn_error_t * ++mkdir_delete_copy(svn_repos_t *repos, ++ const char *src, ++ const char *dst, ++ apr_pool_t *pool) ++{ ++ svn_fs_t *fs = svn_repos_fs(repos); ++ svn_revnum_t youngest_rev; ++ svn_fs_txn_t *txn; ++ svn_fs_root_t *txn_root, *rev_root; ++ ++ SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool)); ++ ++ SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool)); ++ SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); ++ SVN_ERR(svn_fs_make_dir(txn_root, "A/T", pool)); ++ SVN_ERR(svn_repos_fs_commit_txn(NULL, repos, &youngest_rev, txn, pool)); ++ ++ SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool)); ++ SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); ++ SVN_ERR(svn_fs_delete(txn_root, "A/T", pool)); ++ SVN_ERR(svn_repos_fs_commit_txn(NULL, repos, &youngest_rev, txn, pool)); ++ ++ SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool)); ++ SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); ++ SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev - 1, pool)); ++ SVN_ERR(svn_fs_copy(rev_root, src, txn_root, dst, pool)); ++ SVN_ERR(svn_repos_fs_commit_txn(NULL, repos, &youngest_rev, txn, pool)); ++ ++ return SVN_NO_ERROR; ++} ++ ++struct authz_read_baton_t { ++ apr_hash_t *paths; ++ apr_pool_t *pool; ++ const char *deny; ++}; ++ ++static svn_error_t * ++authz_read_func(svn_boolean_t *allowed, ++ svn_fs_root_t *root, ++ const char *path, ++ void *baton, ++ apr_pool_t *pool) ++{ ++ struct authz_read_baton_t *b = baton; ++ ++ if (b->deny && !strcmp(b->deny, path)) ++ *allowed = FALSE; ++ else ++ *allowed = TRUE; ++ ++ svn_hash_sets(b->paths, apr_pstrdup(b->pool, path), (void*)1); ++ ++ return SVN_NO_ERROR; ++} ++ ++static svn_error_t * ++verify_locations(apr_hash_t *actual, ++ apr_hash_t *expected, ++ apr_hash_t *checked, ++ apr_pool_t *pool) ++{ ++ apr_hash_index_t *hi; ++ ++ for (hi = apr_hash_first(pool, expected); hi; hi = apr_hash_next(hi)) ++ { ++ const svn_revnum_t *rev = svn__apr_hash_index_key(hi); ++ const char *path = apr_hash_get(actual, rev, sizeof(svn_revnum_t)); ++ ++ if (!path) ++ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, ++ "expected %s for %d found (null)", ++ (char*)svn__apr_hash_index_val(hi), ++ (int)*rev); ++ else if (strcmp(path, svn__apr_hash_index_val(hi))) ++ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, ++ "expected %s for %d found %s", ++ (char*)svn__apr_hash_index_val(hi), ++ (int)*rev, path); ++ ++ } ++ ++ for (hi = apr_hash_first(pool, actual); hi; hi = apr_hash_next(hi)) ++ { ++ const svn_revnum_t *rev = svn__apr_hash_index_key(hi); ++ const char *path = apr_hash_get(expected, rev, sizeof(svn_revnum_t)); ++ ++ if (!path) ++ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, ++ "found %s for %d expected (null)", ++ (char*)svn__apr_hash_index_val(hi), ++ (int)*rev); ++ else if (strcmp(path, svn__apr_hash_index_val(hi))) ++ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, ++ "found %s for %d expected %s", ++ (char*)svn__apr_hash_index_val(hi), ++ (int)*rev, path); ++ ++ if (!svn_hash_gets(checked, path)) ++ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, ++ "did not check %s", path); ++ } ++ ++ return SVN_NO_ERROR; ++} ++ ++static void ++set_expected(apr_hash_t *expected, ++ svn_revnum_t rev, ++ const char *path, ++ apr_pool_t *pool) ++{ ++ svn_revnum_t *rp = apr_palloc(pool, sizeof(svn_revnum_t)); ++ *rp = rev; ++ apr_hash_set(expected, rp, sizeof(svn_revnum_t), path); ++} ++ ++static svn_error_t * ++trace_node_locations_authz(const svn_test_opts_t *opts, ++ apr_pool_t *pool) ++{ ++ svn_repos_t *repos; ++ svn_fs_t *fs; ++ svn_revnum_t youngest_rev = 0; ++ svn_fs_txn_t *txn; ++ svn_fs_root_t *txn_root; ++ struct authz_read_baton_t arb; ++ apr_array_header_t *revs = apr_array_make(pool, 10, sizeof(svn_revnum_t)); ++ apr_hash_t *locations; ++ apr_hash_t *expected = apr_hash_make(pool); ++ int i; ++ ++ /* Create test repository. */ ++ SVN_ERR(svn_test__create_repos(&repos, "test-repo-trace-node-locations-authz", ++ opts, pool)); ++ fs = svn_repos_fs(repos); ++ ++ /* r1 create A */ ++ SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool)); ++ SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); ++ SVN_ERR(svn_fs_make_dir(txn_root, "A", pool)); ++ SVN_ERR(svn_fs_make_file(txn_root, "A/f", pool)); ++ SVN_ERR(svn_test__set_file_contents(txn_root, "A/f", "foobar", pool)); ++ SVN_ERR(svn_repos_fs_commit_txn(NULL, repos, &youngest_rev, txn, pool)); ++ ++ /* r4 copy A to B */ ++ SVN_ERR(mkdir_delete_copy(repos, "A", "B", pool)); ++ ++ /* r7 copy B to C */ ++ SVN_ERR(mkdir_delete_copy(repos, "B", "C", pool)); ++ ++ /* r10 copy C to D */ ++ SVN_ERR(mkdir_delete_copy(repos, "C", "D", pool)); ++ ++ SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool)); ++ SVN_ERR_ASSERT(youngest_rev == 10); ++ ++ arb.paths = apr_hash_make(pool); ++ arb.pool = pool; ++ arb.deny = NULL; ++ ++ apr_array_clear(revs); ++ for (i = 0; i <= youngest_rev; ++i) ++ APR_ARRAY_PUSH(revs, svn_revnum_t) = i; ++ set_expected(expected, 10, "/D/f", pool); ++ set_expected(expected, 8, "/C/f", pool); ++ set_expected(expected, 7, "/C/f", pool); ++ set_expected(expected, 5, "/B/f", pool); ++ set_expected(expected, 4, "/B/f", pool); ++ set_expected(expected, 2, "/A/f", pool); ++ set_expected(expected, 1, "/A/f", pool); ++ apr_hash_clear(arb.paths); ++ SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, ++ authz_read_func, &arb, pool)); ++ SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); ++ ++ apr_array_clear(revs); ++ for (i = 1; i <= youngest_rev; ++i) ++ APR_ARRAY_PUSH(revs, svn_revnum_t) = i; ++ apr_hash_clear(arb.paths); ++ SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, ++ authz_read_func, &arb, pool)); ++ SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); ++ ++ apr_array_clear(revs); ++ for (i = 2; i <= youngest_rev; ++i) ++ APR_ARRAY_PUSH(revs, svn_revnum_t) = i; ++ set_expected(expected, 1, NULL, pool); ++ apr_hash_clear(arb.paths); ++ SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, ++ authz_read_func, &arb, pool)); ++ SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); ++ ++ apr_array_clear(revs); ++ for (i = 3; i <= youngest_rev; ++i) ++ APR_ARRAY_PUSH(revs, svn_revnum_t) = i; ++ set_expected(expected, 2, NULL, pool); ++ apr_hash_clear(arb.paths); ++ SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, ++ authz_read_func, &arb, pool)); ++ SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); ++ ++ apr_array_clear(revs); ++ for (i = 6; i <= youngest_rev; ++i) ++ APR_ARRAY_PUSH(revs, svn_revnum_t) = i; ++ set_expected(expected, 5, NULL, pool); ++ set_expected(expected, 4, NULL, pool); ++ apr_hash_clear(arb.paths); ++ SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, ++ authz_read_func, &arb, pool)); ++ SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); ++ ++ arb.deny = "/B/f"; ++ apr_array_clear(revs); ++ for (i = 0; i <= youngest_rev; ++i) ++ APR_ARRAY_PUSH(revs, svn_revnum_t) = i; ++ apr_hash_clear(arb.paths); ++ SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, ++ authz_read_func, &arb, pool)); ++ SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); ++ ++ apr_array_clear(revs); ++ for (i = 6; i <= youngest_rev; ++i) ++ APR_ARRAY_PUSH(revs, svn_revnum_t) = i; ++ apr_hash_clear(arb.paths); ++ SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, ++ authz_read_func, &arb, pool)); ++ SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); ++ ++ APR_ARRAY_PUSH(revs, svn_revnum_t) = 0; ++ apr_hash_clear(arb.paths); ++ SVN_ERR(svn_repos_trace_node_locations(fs, &locations, "D/f", 10, revs, ++ authz_read_func, &arb, pool)); ++ SVN_ERR(verify_locations(locations, expected, arb.paths, pool)); ++ ++ return SVN_NO_ERROR; ++} ++ + /* The test table. */ + + struct svn_test_descriptor_t test_funcs[] = +@@ -3573,5 +3812,7 @@ struct svn_test_descriptor_t test_funcs[] = + "test dumping with r0 mergeinfo"), + SVN_TEST_OPTS_PASS(test_load_r0_mergeinfo, + "test loading with r0 mergeinfo"), ++ SVN_TEST_OPTS_PASS(trace_node_locations_authz, ++ "authz for svn_repos_trace_node_locations"), + SVN_TEST_NULL + };
meta/recipes-devtools/subversion/subversion_1.8.13.bb +1 -0
@@ -15,6 +15,7 @@ SRC_URI = "${APACHE_MIRROR}/${BPN}/${BPN}-${PV}.tar.bz2 \ file://disable_macos.patch \ file://serf.m4-Regex-modified-to-allow-D-in-paths.patch \ file://subversion-CVE-2015-3184.patch \ + file://subversion-CVE-2015-3187.patch \ " SRC_URI[md5sum] = "4413417b529d7bdf82f74e50df02e88b" SRC_URI[sha256sum] = "1099cc68840753b48aedb3a27ebd1e2afbcc84ddb871412e5d500e843d607579"
Signature · 1 line
1.7.9.5
PATCH 15/15 rpcbind: Security Advisory - rpcbind - CVE-2015-7236
In reply to Robert YangFrom: Li Zhou <[email protected]>
rpcbind: Fix memory corruption in PMAP_CALLIT code
Use-after-free vulnerability in xprt_set_caller in rpcb_svc_com.c in rpcbind 0.2.1 and earlier allows remote attackers to cause a denial of service (daemon crash) via crafted packets, involving a PMAP_CALLIT code.
The patch comes from
<http://www.openwall.com/lists/oss-security/2015/09/18/7>, and it hasn't been in rpcbind upstream yet.
(From OE-Core master rev: cc4f62f3627f3804907e8ff9c68d9321979df32b)
--- .../rpcbind/rpcbind/cve-2015-7236.patch | 83 ++++++++++++++++++++ meta/recipes-extended/rpcbind/rpcbind_0.2.3.bb | 1 + 2 files changed, 84 insertions(+) create mode 100644 meta/recipes-extended/rpcbind/rpcbind/cve-2015-7236.patch
meta/recipes-extended/rpcbind/rpcbind/cve-2015-7236.patch new file +83 -0
@@ -0,0 +1,83 @@ +commit 06f7ebb1dade2f0dbf872ea2bedf17cff4734bdd +Author: Olaf Kirch <[email protected]> +Date: Thu Aug 6 16:27:20 2015 +0200 + + Fix memory corruption in PMAP_CALLIT code + + - A PMAP_CALLIT call comes in on IPv4 UDP + - rpcbind duplicates the caller's address to a netbuf and stores it in + FINFO[0].caller_addr. caller_addr->buf now points to a memory region A + with a size of 16 bytes + - rpcbind forwards the call to the local service, receives a reply + - when processing the reply, it does this in xprt_set_caller: + xprt->xp_rtaddr = *FINFO[0].caller_addr + It sends out the reply, and then frees the netbuf caller_addr and + caller_addr.buf. + However, it does not clear xp_rtaddr, so xp_rtaddr.buf now refers + to memory region A, which is free. + - When the next call comes in on the UDP/IPv4 socket, svc_dg_recv will + be called, which will set xp_rtaddr to the client's address. + It will reuse the buffer inside xp_rtaddr, ie it will write a + sockaddr_in to region A + + Some time down the road, an incoming TCP connection is accepted, + allocating a fresh SVCXPRT. The memory region A is inside the + new SVCXPRT + + - While processing the TCP call, another UDP call comes in, again + overwriting region A with the client's address + - TCP client closes connection. In svc_destroy, we now trip over + the garbage left in region A + + We ran into the case where a commercial scanner was triggering + occasional rpcbind segfaults. The core file that was captured showed + a corrupted xprt->xp_netid pointer that was really a sockaddr_in. + + Signed-off-by: Olaf Kirch <[email protected]> + + Upstream-Status: Backport + + Signed-off-by: Li Zhou <[email protected]> +--- + src/rpcb_svc_com.c | 23 ++++++++++++++++++++++- + 1 file changed, 22 insertions(+), 1 deletion(-) + +Index: rpcbind-0.1.6+git20080930/src/rpcb_svc_com.c +=================================================================== +--- rpcbind-0.1.6+git20080930.orig/src/rpcb_svc_com.c ++++ rpcbind-0.1.6+git20080930/src/rpcb_svc_com.c +@@ -1298,12 +1298,33 @@ check_rmtcalls(struct pollfd *pfds, int + return (ncallbacks_found); + } + ++/* ++ * This is really a helper function defined in libtirpc, but unfortunately, it hasn't ++ * been exported yet. ++ */ ++static struct netbuf * ++__rpc_set_netbuf(struct netbuf *nb, const void *ptr, size_t len) ++{ ++ if (nb->len != len) { ++ if (nb->len) ++ mem_free(nb->buf, nb->len); ++ nb->buf = mem_alloc(len); ++ if (nb->buf == NULL) ++ return NULL; ++ ++ nb->maxlen = nb->len = len; ++ } ++ memcpy(nb->buf, ptr, len); ++ return nb; ++} ++ + static void + xprt_set_caller(SVCXPRT *xprt, struct finfo *fi) + { ++ const struct netbuf *caller = fi->caller_addr; + u_int32_t *xidp; + +- *(svc_getrpccaller(xprt)) = *(fi->caller_addr); ++ __rpc_set_netbuf(svc_getrpccaller(xprt), caller->buf, caller->len); + xidp = __rpcb_get_dg_xidp(xprt); + *xidp = fi->caller_xid; + }
meta/recipes-extended/rpcbind/rpcbind_0.2.3.bb +1 -0
@@ -19,6 +19,7 @@ SRC_URI = "${SOURCEFORGE_MIRROR}/rpcbind/rpcbind-${PV}.tar.bz2 \ file://rpcbind.conf \ file://rpcbind.socket \ file://rpcbind.service \ + file://cve-2015-7236.patch \ " MUSLPATCHES_libc-musl = "file://musl-sunrpc.patch"
Signature · 1 line
1.7.9.5