* [PATCH 00/15] [jethro] Consolidated pull
@ 2015-12-08 9:56 Robert Yang
2015-12-08 9:56 ` [PATCH 01/15] opkg: add cache filename length fixes Robert Yang
` (14 more replies)
0 siblings, 15 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
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.patch
--
1.7.9.5
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 01/15] opkg: add cache filename length fixes
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 02/15] binutils: Fix octeon3 disassembly patch Robert Yang
` (13 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Alejandro del Castillo <alejandro.delcastillo@ni.com>
(From OE-Core master rev: 8e53500a7c05204fc63759f456639545a022e82b)
Signed-off-by: Alejandro del Castillo <alejandro.delcastillo@ni.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Nicolas Dechesne <nicolas.dechesne@linaro.org>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
...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
diff --git a/meta/recipes-devtools/opkg/opkg/0001-string_util-New-file-with-bin_to_hex-function.patch b/meta/recipes-devtools/opkg/opkg/0001-string_util-New-file-with-bin_to_hex-function.patch
new file mode 100644
index 0000000..fb3ac46
--- /dev/null
+++ b/meta/recipes-devtools/opkg/opkg/0001-string_util-New-file-with-bin_to_hex-function.patch
@@ -0,0 +1,122 @@
+From 646b80024567a6245c598be3374653fa1fa09a12 Mon Sep 17 00:00:00 2001
+From: Paul Barker <paul@paulbarker.me.uk>
+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 <paul@paulbarker.me.uk>
+Signed-off-by: Alejandro del Castillo <alejandro.delcastillo@ni.com>
+
+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
+
diff --git a/meta/recipes-devtools/opkg/opkg/0002-md5-Add-md5_to_string-function.patch b/meta/recipes-devtools/opkg/opkg/0002-md5-Add-md5_to_string-function.patch
new file mode 100644
index 0000000..3b823c6
--- /dev/null
+++ b/meta/recipes-devtools/opkg/opkg/0002-md5-Add-md5_to_string-function.patch
@@ -0,0 +1,110 @@
+From ecad8afab377d8be95eeaafc08afa228c8e030c3 Mon Sep 17 00:00:00 2001
+From: Paul Barker <paul@paulbarker.me.uk>
+Date: Sat, 7 Nov 2015 10:23:50 +0000
+Subject: [PATCH 2/4] md5: Add md5_to_string function
+
+Signed-off-by: Paul Barker <paul@paulbarker.me.uk>
+Signed-off-by: Alejandro del Castillo <alejandro.delcastillo@ni.com>
+
+Upstream-Status: Accepted
+---
+ libopkg/file_util.c | 28 +++-------------------------
+ libopkg/md5.c | 7 +++++++
+ libopkg/md5.h | 3 +++
+ 3 files changed, 13 insertions(+), 25 deletions(-)
+
+diff --git a/libopkg/file_util.c b/libopkg/file_util.c
+index 5eff469..cb3dbf0 100644
+--- a/libopkg/file_util.c
++++ b/libopkg/file_util.c
+@@ -349,27 +349,13 @@ int file_mkdir_hier(const char *path, long mode)
+
+ char *file_md5sum_alloc(const char *file_name)
+ {
+- static const int md5sum_bin_len = 16;
+- static const int md5sum_hex_len = 32;
+-
+- static const unsigned char bin2hex[16] = {
+- '0', '1', '2', '3',
+- '4', '5', '6', '7',
+- '8', '9', 'a', 'b',
+- 'c', 'd', 'e', 'f'
+- };
+-
+- int i, err;
++ int err;
+ FILE *file;
+- char *md5sum_hex;
+- unsigned char md5sum_bin[md5sum_bin_len];
+-
+- md5sum_hex = xcalloc(1, md5sum_hex_len + 1);
++ unsigned char md5sum_bin[16];
+
+ file = fopen(file_name, "r");
+ if (file == NULL) {
+ opkg_perror(ERROR, "Failed to open file %s", file_name);
+- free(md5sum_hex);
+ return NULL;
+ }
+
+@@ -377,20 +363,12 @@ char *file_md5sum_alloc(const char *file_name)
+ if (err) {
+ opkg_msg(ERROR, "Could't compute md5sum for %s.\n", file_name);
+ fclose(file);
+- free(md5sum_hex);
+ return NULL;
+ }
+
+ fclose(file);
+
+- for (i = 0; i < md5sum_bin_len; i++) {
+- md5sum_hex[i * 2] = bin2hex[md5sum_bin[i] >> 4];
+- md5sum_hex[i * 2 + 1] = bin2hex[md5sum_bin[i] & 0xf];
+- }
+-
+- md5sum_hex[md5sum_hex_len] = '\0';
+-
+- return md5sum_hex;
++ return md5_to_string(md5sum_bin);
+ }
+
+ #ifdef HAVE_SHA256
+diff --git a/libopkg/md5.c b/libopkg/md5.c
+index d476b8b..bc2b229 100644
+--- a/libopkg/md5.c
++++ b/libopkg/md5.c
+@@ -30,6 +30,8 @@
+ #include <string.h>
+ #include <sys/types.h>
+
++#include "string_util.h"
++
+ #if USE_UNLOCKED_IO
+ #include "unlocked-io.h"
+ #endif
+@@ -431,3 +433,8 @@ void md5_process_block(const void *buffer, size_t len, struct md5_ctx *ctx)
+ ctx->C = C;
+ ctx->D = D;
+ }
++
++char *md5_to_string(const void *md5sum_bin)
++{
++ return bin_to_hex(md5sum_bin, 16);
++}
+diff --git a/libopkg/md5.h b/libopkg/md5.h
+index 01320f5..2a7274d 100644
+--- a/libopkg/md5.h
++++ b/libopkg/md5.h
+@@ -118,6 +118,9 @@ extern int __md5_stream(FILE * stream, void *resblock) __THROW;
+ extern void *__md5_buffer(const char *buffer, size_t len,
+ void *resblock) __THROW;
+
++/* Convert a binary md5sum value to an ASCII string. */
++char *md5_to_string(const void *md5sum_bin);
++
+ #ifdef __cplusplus
+ }
+ #endif
+--
+1.9.1
+
diff --git a/meta/recipes-devtools/opkg/opkg/0003-sha256-Add-sha256_to_string-function.patch b/meta/recipes-devtools/opkg/opkg/0003-sha256-Add-sha256_to_string-function.patch
new file mode 100644
index 0000000..16e82d7
--- /dev/null
+++ b/meta/recipes-devtools/opkg/opkg/0003-sha256-Add-sha256_to_string-function.patch
@@ -0,0 +1,110 @@
+From 92e8378103bba3b91f2dec4e6fda3e1755a7c0fd Mon Sep 17 00:00:00 2001
+From: Paul Barker <paul@paulbarker.me.uk>
+Date: Sat, 7 Nov 2015 10:23:51 +0000
+Subject: [PATCH 3/4] sha256: Add sha256_to_string function
+
+Signed-off-by: Paul Barker <paul@paulbarker.me.uk>
+Signed-off-by: Alejandro del Castillo <alejandro.delcastillo@ni.com>
+
+Upstream-Status: Accepted
+---
+ libopkg/file_util.c | 28 +++-------------------------
+ libopkg/sha256.c | 7 +++++++
+ libopkg/sha256.h | 3 +++
+ 3 files changed, 13 insertions(+), 25 deletions(-)
+
+diff --git a/libopkg/file_util.c b/libopkg/file_util.c
+index cb3dbf0..864aedb 100644
+--- a/libopkg/file_util.c
++++ b/libopkg/file_util.c
+@@ -374,27 +374,13 @@ char *file_md5sum_alloc(const char *file_name)
+ #ifdef HAVE_SHA256
+ char *file_sha256sum_alloc(const char *file_name)
+ {
+- static const int sha256sum_bin_len = 32;
+- static const int sha256sum_hex_len = 64;
+-
+- static const unsigned char bin2hex[16] = {
+- '0', '1', '2', '3',
+- '4', '5', '6', '7',
+- '8', '9', 'a', 'b',
+- 'c', 'd', 'e', 'f'
+- };
+-
+- int i, err;
++ int err;
+ FILE *file;
+- char *sha256sum_hex;
+- unsigned char sha256sum_bin[sha256sum_bin_len];
+-
+- sha256sum_hex = xcalloc(1, sha256sum_hex_len + 1);
++ unsigned char sha256sum_bin[32];
+
+ file = fopen(file_name, "r");
+ if (file == NULL) {
+ opkg_perror(ERROR, "Failed to open file %s", file_name);
+- free(sha256sum_hex);
+ return NULL;
+ }
+
+@@ -402,20 +388,12 @@ char *file_sha256sum_alloc(const char *file_name)
+ if (err) {
+ opkg_msg(ERROR, "Could't compute sha256sum for %s.\n", file_name);
+ fclose(file);
+- free(sha256sum_hex);
+ return NULL;
+ }
+
+ fclose(file);
+
+- for (i = 0; i < sha256sum_bin_len; i++) {
+- sha256sum_hex[i * 2] = bin2hex[sha256sum_bin[i] >> 4];
+- sha256sum_hex[i * 2 + 1] = bin2hex[sha256sum_bin[i] & 0xf];
+- }
+-
+- sha256sum_hex[sha256sum_hex_len] = '\0';
+-
+- return sha256sum_hex;
++ return sha256_to_string(sha256sum_bin);
+ }
+
+ #endif
+diff --git a/libopkg/sha256.c b/libopkg/sha256.c
+index 0816858..bceed72 100644
+--- a/libopkg/sha256.c
++++ b/libopkg/sha256.c
+@@ -29,6 +29,8 @@
+ #include <stddef.h>
+ #include <string.h>
+
++#include "string_util.h"
++
+ #if USE_UNLOCKED_IO
+ #include "unlocked-io.h"
+ #endif
+@@ -517,3 +519,8 @@ void sha256_process_block(const void *buffer, size_t len,
+ h = ctx->state[7] += h;
+ }
+ }
++
++char *sha256_to_string(const void *sha256sum_bin)
++{
++ return bin_to_hex(sha256sum_bin, 32);
++}
+diff --git a/libopkg/sha256.h b/libopkg/sha256.h
+index 734ab54..0d1e9e5 100644
+--- a/libopkg/sha256.h
++++ b/libopkg/sha256.h
+@@ -85,6 +85,9 @@ extern int sha224_stream(FILE * stream, void *resblock);
+ extern void *sha256_buffer(const char *buffer, size_t len, void *resblock);
+ extern void *sha224_buffer(const char *buffer, size_t len, void *resblock);
+
++/* Convert a binary sha256sum value to an ASCII string. */
++char *sha256_to_string(const void *sha256sum_bin);
++
+ #ifdef __cplusplus
+ }
+ #endif
+--
+1.9.1
+
diff --git a/meta/recipes-devtools/opkg/opkg/0004-opkg_download-Use-short-cache-file-name.patch b/meta/recipes-devtools/opkg/opkg/0004-opkg_download-Use-short-cache-file-name.patch
new file mode 100644
index 0000000..7ea661d
--- /dev/null
+++ b/meta/recipes-devtools/opkg/opkg/0004-opkg_download-Use-short-cache-file-name.patch
@@ -0,0 +1,85 @@
+From 61636f15718edc7ea17b91f22f1d97b905eaf951 Mon Sep 17 00:00:00 2001
+From: Paul Barker <paul@paulbarker.me.uk>
+Date: Sat, 7 Nov 2015 10:23:52 +0000
+Subject: [PATCH 4/4] opkg_download: Use short cache file name
+
+Source URIs can be very long. The cache directory itself may already have a very
+long path, especially if we're installing packages into an offline rootfs.
+Therefore it's not a good idea to simply tag the source URI onto the cache
+directory path to create a cache file name.
+
+To create shorter cache file names which are deterministic and very likely to be
+unique, we use the md5sum of the source URI along with the basename of the
+source URI. The basename is length limited to ensure that it the resulting
+filename length is always reasonable.
+
+Signed-off-by: Paul Barker <paul@paulbarker.me.uk>
+Signed-off-by: Alejandro del Castillo <alejandro.delcastillo@ni.com>
+
+Upstream-Status: Accepted
+---
+ libopkg/opkg_download.c | 35 ++++++++++++++++++++++++++++-------
+ 1 file changed, 28 insertions(+), 7 deletions(-)
+
+diff --git a/libopkg/opkg_download.c b/libopkg/opkg_download.c
+index e9b86a5..a37b10d 100644
+--- a/libopkg/opkg_download.c
++++ b/libopkg/opkg_download.c
+@@ -29,10 +29,18 @@
+ #include "opkg_verify.h"
+ #include "opkg_utils.h"
+
++#include "md5.h"
+ #include "sprintf_alloc.h"
+ #include "file_util.h"
+ #include "xfuncs.h"
+
++/* Limit the short file name used to generate cache file names to 90 characters
++ * so that when added to the md5sum (32 characters) and an underscore, the
++ * resulting length is below 128 characters. The maximum file name length
++ * differs between plaforms but 128 characters should be reasonable.
++ */
++#define MAX_SHORT_FILE_NAME_LENGTH 90
++
+ static int opkg_download_set_env()
+ {
+ int r;
+@@ -135,15 +143,28 @@ int opkg_download_internal(const char *src, const char *dest,
+ */
+ char *get_cache_location(const char *src)
+ {
+- char *cache_name = xstrdup(src);
+- char *cache_location, *p;
++ unsigned char md5sum_bin[16];
++ char *md5sum_hex;
++ char *cache_location;
++ char *short_file_name;
++ char *tmp = xstrdup(src);
+
+- for (p = cache_name; *p; p++)
+- if (*p == '/')
+- *p = '_';
++ md5_buffer(src, strlen(src), md5sum_bin);
++ md5sum_hex = md5_to_string(md5sum_bin);
+
+- sprintf_alloc(&cache_location, "%s/%s", opkg_config->cache_dir, cache_name);
+- free(cache_name);
++ /* Generate a short file name which will be used along with an md5sum of the
++ * full src URI in the cache file name. This short file name is limited to
++ * MAX_SHORT_FILE_NAME_LENGTH to ensure that the total cache file name
++ * length is reasonable.
++ */
++ short_file_name = basename(tmp);
++ if (strlen(short_file_name) > MAX_SHORT_FILE_NAME_LENGTH)
++ short_file_name[MAX_SHORT_FILE_NAME_LENGTH] = '\0';
++
++ sprintf_alloc(&cache_location, "%s/%s_%s", opkg_config->cache_dir,
++ md5sum_hex, short_file_name);
++ free(md5sum_hex);
++ free(tmp);
+ return cache_location;
+ }
+
+--
+1.9.1
+
diff --git a/meta/recipes-devtools/opkg/opkg_0.3.0.bb b/meta/recipes-devtools/opkg/opkg_0.3.0.bb
index 588250e..5ad3e92 100644
--- a/meta/recipes-devtools/opkg/opkg_0.3.0.bb
+++ b/meta/recipes-devtools/opkg/opkg_0.3.0.bb
@@ -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"
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 02/15] binutils: Fix octeon3 disassembly patch
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
2015-12-08 9:56 ` [PATCH 01/15] opkg: add cache filename length fixes Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 03/15] libarchive: rename patch to reflect CVE Robert Yang
` (12 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Mark Hatle <mark.hatle@windriver.com>
The structure has apparently changed, and there was a missing
setting. This corrects a segfault when disassembling code.
(From OE-Core master rev: 2e8f1ffe3a8d7740b0ac68eefbba3fe28f7ba6d4)
Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
.../binutils/binutils/binutils-octeon3.patch | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/meta/recipes-devtools/binutils/binutils/binutils-octeon3.patch b/meta/recipes-devtools/binutils/binutils/binutils-octeon3.patch
index 6108c0d..4e8c69f 100644
--- a/meta/recipes-devtools/binutils/binutils/binutils-octeon3.patch
+++ b/meta/recipes-devtools/binutils/binutils/binutils-octeon3.patch
@@ -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,
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 03/15] libarchive: rename patch to reflect CVE
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
2015-12-08 9:56 ` [PATCH 01/15] opkg: add cache filename length fixes Robert Yang
2015-12-08 9:56 ` [PATCH 02/15] binutils: Fix octeon3 disassembly patch Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 04/15] readline: rename patch to contain CVE reference Robert Yang
` (11 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Ross Burton <ross.burton@intel.com>
This patch is a CVE fix, so rename it to help CVE detection tools identify it as
such.
(From OE-Core master rev: 3fd05ce1f709cbbd8fdeb1dbfdffbd39922eca6e)
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
...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%)
diff --git a/meta/recipes-extended/libarchive/libarchive/0001-Add-ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS-option.patch b/meta/recipes-extended/libarchive/libarchive/libarchive-CVE-2015-2304.patch
similarity index 100%
rename from meta/recipes-extended/libarchive/libarchive/0001-Add-ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS-option.patch
rename to meta/recipes-extended/libarchive/libarchive/libarchive-CVE-2015-2304.patch
diff --git a/meta/recipes-extended/libarchive/libarchive_3.1.2.bb b/meta/recipes-extended/libarchive/libarchive_3.1.2.bb
index aaa3255..716db9a 100644
--- a/meta/recipes-extended/libarchive/libarchive_3.1.2.bb
+++ b/meta/recipes-extended/libarchive/libarchive_3.1.2.bb
@@ -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 \
"
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 04/15] readline: rename patch to contain CVE reference
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (2 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 03/15] libarchive: rename patch to reflect CVE Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 05/15] unzip: rename patch to reflect CVE fix Robert Yang
` (10 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Ross Burton <ross.burton@intel.com>
To help automated scanning of CVEs, put the CVE ID in the filename.
(From OE-Core master rev: 211bce4f23230c7898cccdb73b582420f830f977)
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
...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%)
diff --git a/meta/recipes-core/readline/readline-6.3/readline63-003.patch b/meta/recipes-core/readline/readline-6.3/readline-cve-2014-2524.patch
similarity index 100%
rename from meta/recipes-core/readline/readline-6.3/readline63-003.patch
rename to meta/recipes-core/readline/readline-6.3/readline-cve-2014-2524.patch
diff --git a/meta/recipes-core/readline/readline_6.3.bb b/meta/recipes-core/readline/readline_6.3.bb
index 6ba1c18..fc362ae 100644
--- a/meta/recipes-core/readline/readline_6.3.bb
+++ b/meta/recipes-core/readline/readline_6.3.bb
@@ -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"
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 05/15] unzip: rename patch to reflect CVE fix
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (3 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 04/15] readline: rename patch to contain CVE reference Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 06/15] libxslt: CVE-2015-7995 Robert Yang
` (9 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Ross Burton <ross.burton@intel.com>
(From OE-Core rev: e3d2974348bd830ec2fcf84ea08cbf38abbc0327)
(master rev: 78e05984b1ac48b1f25547ccd9740611cd5890a9)
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
...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%)
diff --git a/meta/recipes-extended/unzip/unzip/unzip-6.0_overflow3.diff b/meta/recipes-extended/unzip/unzip/cve-2014-9636.patch
similarity index 100%
rename from meta/recipes-extended/unzip/unzip/unzip-6.0_overflow3.diff
rename to meta/recipes-extended/unzip/unzip/cve-2014-9636.patch
diff --git a/meta/recipes-extended/unzip/unzip_6.0.bb b/meta/recipes-extended/unzip/unzip_6.0.bb
index 9e63d3a..b386323 100644
--- a/meta/recipes-extended/unzip/unzip_6.0.bb
+++ b/meta/recipes-extended/unzip/unzip_6.0.bb
@@ -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 \
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 06/15] libxslt: CVE-2015-7995
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (4 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 05/15] unzip: rename patch to reflect CVE fix Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 07/15] libsndfile: fix CVE-2014-9756 Robert Yang
` (8 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Armin Kuster <akuster@mvista.com>
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)
Signed-off-by: Armin Kuster <akuster@mvista.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
.../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
diff --git a/meta/recipes-support/libxslt/libxslt/CVE-2015-7995.patch b/meta/recipes-support/libxslt/libxslt/CVE-2015-7995.patch
new file mode 100644
index 0000000..e4d09c2
--- /dev/null
+++ b/meta/recipes-support/libxslt/libxslt/CVE-2015-7995.patch
@@ -0,0 +1,33 @@
+From 7ca19df892ca22d9314e95d59ce2abdeff46b617 Mon Sep 17 00:00:00 2001
+From: Daniel Veillard <veillard@redhat.com>
+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 <akuster@mvista.com>
+
+---
+ 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"))) {
diff --git a/meta/recipes-support/libxslt/libxslt_1.1.28.bb b/meta/recipes-support/libxslt/libxslt_1.1.28.bb
index 166bcd8..87fabec 100644
--- a/meta/recipes-support/libxslt/libxslt_1.1.28.bb
+++ b/meta/recipes-support/libxslt/libxslt_1.1.28.bb
@@ -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"
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 07/15] libsndfile: fix CVE-2014-9756
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (5 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 06/15] libxslt: CVE-2015-7995 Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 08/15] allarch: Force TARGET_*FLAGS variable values Robert Yang
` (7 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: "Maxin B. John" <maxin.john@intel.com>
Fix divide by zero bug (CVE-2014-9756)
(From OE-Core master rev: f47cf07ab9d00ed7eddc8e867138481f7bd2bb7d)
Signed-off-by: Maxin B. John <maxin.john@intel.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
.../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
diff --git a/meta/recipes-multimedia/libsndfile/files/libsndfile-fix-CVE-2014-9756.patch b/meta/recipes-multimedia/libsndfile/files/libsndfile-fix-CVE-2014-9756.patch
new file mode 100644
index 0000000..b54b3ba
--- /dev/null
+++ b/meta/recipes-multimedia/libsndfile/files/libsndfile-fix-CVE-2014-9756.patch
@@ -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 <erikd@mega-nerd.com>
+Signed-off-by: Maxin B. John <maxin.john@intel.com>
+---
+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 ;
+
diff --git a/meta/recipes-multimedia/libsndfile/libsndfile1_1.0.25.bb b/meta/recipes-multimedia/libsndfile/libsndfile1_1.0.25.bb
index 3e02f4e..be875c2 100644
--- a/meta/recipes-multimedia/libsndfile/libsndfile1_1.0.25.bb
+++ b/meta/recipes-multimedia/libsndfile/libsndfile1_1.0.25.bb
@@ -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"
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 08/15] allarch: Force TARGET_*FLAGS variable values
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (6 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 07/15] libsndfile: fix CVE-2014-9756 Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 09/15] pulseaudio: Fix HDMI profile selection Robert Yang
` (6 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Mike Crowe <mac@mcrowe.com>
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.)
Signed-off-by: Mike Crowe <mac@mcrowe.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
meta/classes/allarch.bbclass | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/meta/classes/allarch.bbclass b/meta/classes/allarch.bbclass
index 2fea7c0..4af38d7 100644
--- a/meta/classes/allarch.bbclass
+++ b/meta/classes/allarch.bbclass
@@ -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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 09/15] pulseaudio: Fix HDMI profile selection
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (7 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 08/15] allarch: Force TARGET_*FLAGS variable values Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 10/15] init-install-efi: fix script for eMMC installation Robert Yang
` (5 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Jussi Kukkonen <jussi.kukkonen@intel.com>
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]
Signed-off-by: Jussi Kukkonen <jussi.kukkonen@intel.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Saul Wold <sgw@linux.intel.com>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
.../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
diff --git a/meta/recipes-multimedia/pulseaudio/pulseaudio/0001-card-add-pa_card_profile.ports.patch b/meta/recipes-multimedia/pulseaudio/pulseaudio/0001-card-add-pa_card_profile.ports.patch
new file mode 100644
index 0000000..97b2e40
--- /dev/null
+++ b/meta/recipes-multimedia/pulseaudio/pulseaudio/0001-card-add-pa_card_profile.ports.patch
@@ -0,0 +1,245 @@
+From 6f814b40a01d03f93b36184c19339033949de472 Mon Sep 17 00:00:00 2001
+From: Tanu Kaskinen <tanuk@iki.fi>
+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 <jussi.kukkonen@intel.com>
+---
+ 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
+
diff --git a/meta/recipes-multimedia/pulseaudio/pulseaudio/0002-alsa-bluetooth-fail-if-user-requested-profile-doesn-.patch b/meta/recipes-multimedia/pulseaudio/pulseaudio/0002-alsa-bluetooth-fail-if-user-requested-profile-doesn-.patch
new file mode 100644
index 0000000..c3f217b
--- /dev/null
+++ b/meta/recipes-multimedia/pulseaudio/pulseaudio/0002-alsa-bluetooth-fail-if-user-requested-profile-doesn-.patch
@@ -0,0 +1,60 @@
+From 339eb179baa7810113f6456accc05b3a32c1cdba Mon Sep 17 00:00:00 2001
+From: Tanu Kaskinen <tanuk@iki.fi>
+Date: Fri, 23 Oct 2015 12:36:34 +0300
+Subject: [PATCH 2/4] alsa, bluetooth: fail if user-requested profile doesn't
+ exist
+
+If we can't fulfill the user request fully, I think we shouldn't
+fulfill it at all, to make it clear that the requested operation
+didn't succeed.
+
+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 <jussi.kukkonen@intel.com>
+---
+ src/modules/alsa/module-alsa-card.c | 10 ++++++++--
+ src/modules/bluetooth/module-bluez4-device.c | 6 ++++--
+ 2 files changed, 12 insertions(+), 4 deletions(-)
+
+diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
+index a7fec04..32f517e 100644
+--- a/src/modules/alsa/module-alsa-card.c
++++ b/src/modules/alsa/module-alsa-card.c
+@@ -754,8 +754,14 @@ int pa__init(pa_module *m) {
+ goto fail;
+ }
+
+- if ((profile = pa_modargs_get_value(u->modargs, "profile", NULL)))
+- pa_card_new_data_set_profile(&data, profile);
++ if ((profile = pa_modargs_get_value(u->modargs, "profile", NULL))) {
++ if (pa_hashmap_get(data.profiles, profile))
++ pa_card_new_data_set_profile(&data, profile);
++ else {
++ pa_log("No such profile: %s", profile);
++ goto fail;
++ }
++ }
+
+ u->card = pa_card_new(m->core, &data);
+ pa_card_new_data_done(&data);
+diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c
+index b40c6a0..94e6988 100644
+--- a/src/modules/bluetooth/module-bluez4-device.c
++++ b/src/modules/bluetooth/module-bluez4-device.c
+@@ -2310,8 +2310,10 @@ static int add_card(struct userdata *u) {
+ if ((default_profile = pa_modargs_get_value(u->modargs, "profile", NULL))) {
+ if (pa_hashmap_get(data.profiles, default_profile))
+ pa_card_new_data_set_profile(&data, default_profile);
+- else
+- pa_log_warn("Profile '%s' not valid or not supported by device.", default_profile);
++ else {
++ pa_log("Profile '%s' not valid or not supported by device.", default_profile);
++ return -1;
++ }
+ }
+
+ u->card = pa_card_new(u->core, &data);
+--
+2.1.4
+
diff --git a/meta/recipes-multimedia/pulseaudio/pulseaudio/0003-card-move-profile-selection-after-pa_card_new.patch b/meta/recipes-multimedia/pulseaudio/pulseaudio/0003-card-move-profile-selection-after-pa_card_new.patch
new file mode 100644
index 0000000..9585f3d
--- /dev/null
+++ b/meta/recipes-multimedia/pulseaudio/pulseaudio/0003-card-move-profile-selection-after-pa_card_new.patch
@@ -0,0 +1,363 @@
+From cc41c8a3149ef04d4aa2db3d15032605a5504658 Mon Sep 17 00:00:00 2001
+From: Tanu Kaskinen <tanuk@iki.fi>
+Date: Fri, 23 Oct 2015 12:59:53 +0300
+Subject: [PATCH 3/4] card: move profile selection after pa_card_new()
+
+I want module-alsa-card to set the availability of unavailable
+profiles before the initial card profile gets selected, so that the
+selection logic can use correct availability information.
+module-alsa-card initializes the jack state after calling
+pa_card_new(), however, and the profile selection happens in
+pa_card_new(). This patch solves that by introducing pa_card_put() and
+moving the profile selection code there.
+
+An alternative solution would have been to move the jack
+initialization to happen before pa_card_new() and use pa_card_new_data
+instead of pa_card in the jack initialization code, but I disliked
+that idea (I want to get rid of the "new data" pattern eventually).
+
+The CARD_NEW hook is used when applying the initial profile policy, so
+that was moved to pa_card_put(). That required changing the hook data
+from pa_card_new_data to pa_card. module-card-restore now uses
+pa_card_set_profile() instead of pa_card_new_data_set_profile(). That
+required adding a state variable to pa_card, because
+pa_card_set_profile() needs to distinguish between setting the initial
+profile and setting the profile in other situations.
+
+The order in which the initial profile policy is applied is reversed
+in this patch. Previously the first one to set it won, now the last
+one to set it wins. I think this is better, because if you have N
+parties that want to set the profile, we avoid checking N times
+whether someone else has already set the profile.
+
+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 <jussi.kukkonen@intel.com>
+---
+ src/modules/alsa/module-alsa-card.c | 19 +++---
+ src/modules/bluetooth/module-bluez4-device.c | 18 +++---
+ src/modules/bluetooth/module-bluez5-device.c | 1 +
+ src/modules/macosx/module-coreaudio-device.c | 1 +
+ src/modules/module-card-restore.c | 24 ++++----
+ src/pulsecore/card.c | 86 +++++++++++++++-------------
+ src/pulsecore/card.h | 7 +++
+ 7 files changed, 87 insertions(+), 69 deletions(-)
+
+diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
+index 32f517e..5b39654 100644
+--- a/src/modules/alsa/module-alsa-card.c
++++ b/src/modules/alsa/module-alsa-card.c
+@@ -754,15 +754,6 @@ int pa__init(pa_module *m) {
+ goto fail;
+ }
+
+- if ((profile = pa_modargs_get_value(u->modargs, "profile", NULL))) {
+- if (pa_hashmap_get(data.profiles, profile))
+- pa_card_new_data_set_profile(&data, profile);
+- else {
+- pa_log("No such profile: %s", profile);
+- goto fail;
+- }
+- }
+-
+ u->card = pa_card_new(m->core, &data);
+ pa_card_new_data_done(&data);
+
+@@ -773,6 +764,16 @@ int pa__init(pa_module *m) {
+ u->card->set_profile = card_set_profile;
+
+ init_jacks(u);
++ pa_card_put(u->card);
++
++ if ((profile = pa_modargs_get_value(u->modargs, "profile", NULL))) {
++ u->card->active_profile = pa_hashmap_get(u->card->profiles, profile);
++ if (!u->card->active_profile) {
++ pa_log("No such profile: %s", profile);
++ goto fail;
++ }
++ }
++
+ init_profile(u);
+ init_eld_ctls(u);
+
+diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c
+index 94e6988..5efc5dc 100644
+--- a/src/modules/bluetooth/module-bluez4-device.c
++++ b/src/modules/bluetooth/module-bluez4-device.c
+@@ -2307,15 +2307,6 @@ static int add_card(struct userdata *u) {
+ *d = PA_BLUEZ4_PROFILE_OFF;
+ pa_hashmap_put(data.profiles, p->name, p);
+
+- if ((default_profile = pa_modargs_get_value(u->modargs, "profile", NULL))) {
+- if (pa_hashmap_get(data.profiles, default_profile))
+- pa_card_new_data_set_profile(&data, default_profile);
+- else {
+- pa_log("Profile '%s' not valid or not supported by device.", default_profile);
+- return -1;
+- }
+- }
+-
+ u->card = pa_card_new(u->core, &data);
+ pa_card_new_data_done(&data);
+
+@@ -2326,6 +2317,15 @@ static int add_card(struct userdata *u) {
+
+ u->card->userdata = u;
+ u->card->set_profile = card_set_profile;
++ pa_card_put(u->card);
++
++ if ((default_profile = pa_modargs_get_value(u->modargs, "profile", NULL))) {
++ u->card->active_profile = pa_hashmap_get(u->card->profiles, default_profile);
++ if (!u->card->active_profile) {
++ pa_log("Profile '%s' not valid or not supported by device.", default_profile);
++ return -1;
++ }
++ }
+
+ d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+
+diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c
+index 3321785..0081a21 100644
+--- a/src/modules/bluetooth/module-bluez5-device.c
++++ b/src/modules/bluetooth/module-bluez5-device.c
+@@ -1959,6 +1959,7 @@ static int add_card(struct userdata *u) {
+
+ u->card->userdata = u;
+ u->card->set_profile = set_profile_cb;
++ pa_card_put(u->card);
+
+ p = PA_CARD_PROFILE_DATA(u->card->active_profile);
+ u->profile = *p;
+diff --git a/src/modules/macosx/module-coreaudio-device.c b/src/modules/macosx/module-coreaudio-device.c
+index 4bbb5d5..41f151f 100644
+--- a/src/modules/macosx/module-coreaudio-device.c
++++ b/src/modules/macosx/module-coreaudio-device.c
+@@ -764,6 +764,7 @@ int pa__init(pa_module *m) {
+ pa_card_new_data_done(&card_new_data);
+ u->card->userdata = u;
+ u->card->set_profile = card_set_profile;
++ pa_card_put(u->card);
+
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
+index baa2f4f..0501ac8 100644
+--- a/src/modules/module-card-restore.c
++++ b/src/modules/module-card-restore.c
+@@ -485,34 +485,38 @@ static pa_hook_result_t port_offset_change_callback(pa_core *c, pa_device_port *
+ return PA_HOOK_OK;
+ }
+
+-static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
++static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card *card, struct userdata *u) {
+ struct entry *e;
+ void *state;
+ pa_device_port *p;
+ struct port_info *p_info;
+
+- pa_assert(new_data);
++ pa_assert(c);
++ pa_assert(card);
++ pa_assert(u);
+
+- if (!(e = entry_read(u, new_data->name)))
++ if (!(e = entry_read(u, card->name)))
+ return PA_HOOK_OK;
+
+ if (e->profile[0]) {
+- if (!new_data->active_profile) {
+- pa_card_new_data_set_profile(new_data, e->profile);
+- pa_log_info("Restored profile '%s' for card %s.", new_data->active_profile, new_data->name);
+- new_data->save_profile = true;
++ pa_card_profile *profile;
+
++ profile = pa_hashmap_get(card->profiles, e->profile);
++ if (profile) {
++ pa_card_set_profile(card, profile, true);
++ pa_log_info("Restored profile '%s' for card %s.", card->active_profile->name, card->name);
+ } else
+- pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name);
++ pa_log_debug("Tried to restore profile %s for card %s, but the card doesn't have such profile.",
++ e->profile, card->name);
+ }
+
+ /* Always restore the latency offsets because their
+ * initial value is always 0 */
+
+- pa_log_info("Restoring port latency offsets for card %s.", new_data->name);
++ pa_log_info("Restoring port latency offsets for card %s.", card->name);
+
+ PA_HASHMAP_FOREACH(p_info, e->ports, state)
+- if ((p = pa_hashmap_get(new_data->ports, p_info->name)))
++ if ((p = pa_hashmap_get(card->ports, p_info->name)))
+ p->latency_offset = p_info->offset;
+
+ entry_free(e);
+diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
+index cc4c784..1b7f71b 100644
+--- a/src/pulsecore/card.c
++++ b/src/pulsecore/card.c
+@@ -151,6 +151,7 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
+ pa_assert(!pa_hashmap_isempty(data->profiles));
+
+ c = pa_xnew(pa_card, 1);
++ c->state = PA_CARD_STATE_INIT;
+
+ if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_CARD, c, data->namereg_fail))) {
+ pa_xfree(c);
+@@ -159,12 +160,6 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
+
+ pa_card_new_data_set_name(data, name);
+
+- if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_NEW], data) < 0) {
+- pa_xfree(c);
+- pa_namereg_unregister(core, name);
+- return NULL;
+- }
+-
+ c->core = core;
+ c->name = pa_xstrdup(data->name);
+ c->proplist = pa_proplist_copy(data->proplist);
+@@ -187,30 +182,6 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
+ PA_HASHMAP_FOREACH(port, c->ports, state)
+ port->card = c;
+
+- c->active_profile = NULL;
+- c->save_profile = false;
+-
+- if (data->active_profile)
+- if ((c->active_profile = pa_hashmap_get(c->profiles, data->active_profile)))
+- c->save_profile = data->save_profile;
+-
+- if (!c->active_profile) {
+- PA_HASHMAP_FOREACH(profile, c->profiles, state) {
+- if (profile->available == PA_AVAILABLE_NO)
+- continue;
+-
+- if (!c->active_profile || profile->priority > c->active_profile->priority)
+- c->active_profile = profile;
+- }
+- /* If all profiles are not available, then we still need to pick one */
+- if (!c->active_profile) {
+- PA_HASHMAP_FOREACH(profile, c->profiles, state)
+- if (!c->active_profile || profile->priority > c->active_profile->priority)
+- c->active_profile = profile;
+- }
+- pa_assert(c->active_profile);
+- }
+-
+ c->userdata = NULL;
+ c->set_profile = NULL;
+ c->active_profile = NULL;
+@@ -219,13 +190,39 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
+ pa_device_init_icon(c->proplist, true);
+ pa_device_init_intended_roles(c->proplist);
+
+- pa_assert_se(pa_idxset_put(core->cards, c, &c->index) >= 0);
++ return c;
++}
+
+- pa_log_info("Created %u \"%s\"", c->index, c->name);
+- pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW, c->index);
++void pa_card_put(pa_card *card) {
++ pa_card_profile *profile;
++ void *state;
+
+- pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_PUT], c);
+- return c;
++ pa_assert(card);
++
++ PA_HASHMAP_FOREACH(profile, card->profiles, state) {
++ if (profile->available == PA_AVAILABLE_NO)
++ continue;
++
++ if (!card->active_profile || profile->priority > card->active_profile->priority)
++ card->active_profile = profile;
++ }
++
++ /* If all profiles are unavailable, then we still need to pick one */
++ if (!card->active_profile) {
++ PA_HASHMAP_FOREACH(profile, card->profiles, state)
++ if (!card->active_profile || profile->priority > card->active_profile->priority)
++ card->active_profile = profile;
++ }
++ pa_assert(card->active_profile);
++
++ pa_hook_fire(&card->core->hooks[PA_CORE_HOOK_CARD_NEW], card);
++
++ pa_assert_se(pa_idxset_put(card->core->cards, card, &card->index) >= 0);
++ card->state = PA_CARD_STATE_LINKED;
++
++ pa_log_info("Created %u \"%s\"", card->index, card->name);
++ pa_hook_fire(&card->core->hooks[PA_CORE_HOOK_CARD_PUT], card);
++ pa_subscription_post(card->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW, card->index);
+ }
+
+ void pa_card_free(pa_card *c) {
+@@ -292,17 +289,24 @@ int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save) {
+ return 0;
+ }
+
+- if ((r = c->set_profile(c, profile)) < 0)
++ /* If we're setting the initial profile, we shouldn't call set_profile(),
++ * because the implementations don't expect that (for historical reasons).
++ * We should just set c->active_profile, and the implementations will
++ * properly set up that profile after pa_card_put() has returned. It would
++ * be probably good to change this so that also the initial profile can be
++ * set up in set_profile(), but if set_profile() fails, that would need
++ * some better handling than what we do here currently. */
++ if (c->state != PA_CARD_STATE_INIT && (r = c->set_profile(c, profile)) < 0)
+ return r;
+
+- pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
+-
+- pa_log_info("Changed profile of card %u \"%s\" to %s", c->index, c->name, profile->name);
+-
+ c->active_profile = profile;
+ c->save_profile = save;
+
+- pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], c);
++ if (c->state != PA_CARD_STATE_INIT) {
++ pa_log_info("Changed profile of card %u \"%s\" to %s", c->index, c->name, profile->name);
++ pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], c);
++ pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
++ }
+
+ return 0;
+ }
+diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
+index 1c33958..dbbc1c2 100644
+--- a/src/pulsecore/card.h
++++ b/src/pulsecore/card.h
+@@ -37,6 +37,11 @@ typedef enum pa_available {
+ #include <pulsecore/module.h>
+ #include <pulsecore/idxset.h>
+
++typedef enum pa_card_state {
++ PA_CARD_STATE_INIT,
++ PA_CARD_STATE_LINKED,
++} pa_card_state_t;
++
+ typedef struct pa_card_profile {
+ pa_card *card;
+ char *name;
+@@ -61,6 +66,7 @@ typedef struct pa_card_profile {
+
+ struct pa_card {
+ uint32_t index;
++ pa_card_state_t state;
+ pa_core *core;
+
+ char *name;
+@@ -115,6 +121,7 @@ void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile);
+ void pa_card_new_data_done(pa_card_new_data *data);
+
+ pa_card *pa_card_new(pa_core *c, pa_card_new_data *data);
++void pa_card_put(pa_card *c);
+ void pa_card_free(pa_card *c);
+
+ void pa_card_add_profile(pa_card *c, pa_card_profile *profile);
+--
+2.1.4
+
diff --git a/meta/recipes-multimedia/pulseaudio/pulseaudio/0004-alsa-set-availability-for-some-unavailable-profiles.patch b/meta/recipes-multimedia/pulseaudio/pulseaudio/0004-alsa-set-availability-for-some-unavailable-profiles.patch
new file mode 100644
index 0000000..bb318aa
--- /dev/null
+++ b/meta/recipes-multimedia/pulseaudio/pulseaudio/0004-alsa-set-availability-for-some-unavailable-profiles.patch
@@ -0,0 +1,75 @@
+From 0136b73158f60d5dc630ae348b18df3b59a2a5c2 Mon Sep 17 00:00:00 2001
+From: Tanu Kaskinen <tanuk@iki.fi>
+Date: Fri, 23 Oct 2015 13:37:11 +0300
+Subject: [PATCH 4/4] alsa: set availability for (some) unavailable profiles
+
+The alsa card hasn't so far set any availability for profiles. That
+caused an issue with some HDMI hardware: the sound card has two HDMI
+outputs, but only the second of them is actually usable. The
+unavailable port is marked as unavailable and the available port is
+marked as available, but this information isn't propagated to the
+profile availability. Without profile availability information, the
+initial profile policy picks the unavailable one, since it has a
+higher priority value.
+
+This patch adds simple logic for marking some profiles unavailable:
+if the profile only contains unavailable ports, the profile is
+unavailable too. This can be improved in the future so that if a
+profile contains sinks or sources that only contain unavailable ports,
+the profile should be marked as unavailable. Implementing that
+requires adding more information about the sinks and sources to
+pa_card_profile, however.
+
+BugLink: https://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 <jussi.kukkonen@intel.com>
+---
+ src/modules/alsa/module-alsa-card.c | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
+index 5b39654..73a846c 100644
+--- a/src/modules/alsa/module-alsa-card.c
++++ b/src/modules/alsa/module-alsa-card.c
+@@ -366,6 +366,7 @@ static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) {
+ void *state;
+ pa_alsa_jack *jack;
+ pa_device_port *port;
++ pa_card_profile *profile;
+
+ pa_assert(u);
+
+@@ -396,6 +397,29 @@ static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) {
+ }
+ report_port_state(port, u);
+ }
++
++ /* Update profile availabilities. The logic could be improved; for now we
++ * only set obviously unavailable profiles (those that contain only
++ * unavailable ports) to PA_AVAILABLE_NO and all others to
++ * PA_AVAILABLE_UNKNOWN. */
++ PA_HASHMAP_FOREACH(profile, u->card->profiles, state) {
++ void *state2;
++ pa_available_t available = PA_AVAILABLE_NO;
++
++ /* Don't touch the "off" profile. */
++ if (pa_hashmap_size(profile->ports) == 0)
++ continue;
++
++ PA_HASHMAP_FOREACH(port, profile->ports, state2) {
++ if (port->available != PA_AVAILABLE_NO) {
++ available = PA_AVAILABLE_UNKNOWN;
++ break;
++ }
++ }
++
++ pa_card_profile_set_available(profile, available);
++ }
++
+ return 0;
+ }
+
+--
+2.1.4
+
diff --git a/meta/recipes-multimedia/pulseaudio/pulseaudio_6.0.bb b/meta/recipes-multimedia/pulseaudio/pulseaudio_6.0.bb
index 31e9096..ec629aa 100644
--- a/meta/recipes-multimedia/pulseaudio/pulseaudio_6.0.bb
+++ b/meta/recipes-multimedia/pulseaudio/pulseaudio_6.0.bb
@@ -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"
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 10/15] init-install-efi: fix script for eMMC installation
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (8 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 09/15] pulseaudio: Fix HDMI profile selection Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 11/15] init-install-efi: fix script for gummiboot loader Robert Yang
` (4 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: "Ng, Mei Yeen" <mei.yeen.ng@intel.com>
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)
Signed-off-by: Ng, Mei Yeen <mei.yeen.ng@intel.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Saul Wold <sgw@linux.intel.com>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
.../initrdscripts/files/init-install-efi.sh | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/meta/recipes-core/initrdscripts/files/init-install-efi.sh b/meta/recipes-core/initrdscripts/files/init-install-efi.sh
index fc4908e..ac26bce 100644
--- a/meta/recipes-core/initrdscripts/files/init-install-efi.sh
+++ b/meta/recipes-core/initrdscripts/files/init-install-efi.sh
@@ -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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 11/15] init-install-efi: fix script for gummiboot loader
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (9 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 10/15] init-install-efi: fix script for eMMC installation Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 12/15] linux-firmware: rtl8192cx: Add latest available firmware Robert Yang
` (3 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: "Ng, Mei Yeen" <mei.yeen.ng@intel.com>
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.
[YOCTO #8709]
Signed-off-by: Ng, Mei Yeen <mei.yeen.ng@intel.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Saul Wold <sgw@linux.intel.com>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
.../initrdscripts/files/init-install-efi.sh | 1 +
1 file changed, 1 insertion(+)
diff --git a/meta/recipes-core/initrdscripts/files/init-install-efi.sh b/meta/recipes-core/initrdscripts/files/init-install-efi.sh
index ac26bce..0443a9d 100644
--- a/meta/recipes-core/initrdscripts/files/init-install-efi.sh
+++ b/meta/recipes-core/initrdscripts/files/init-install-efi.sh
@@ -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
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 12/15] linux-firmware: rtl8192cx: Add latest available firmware
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (10 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 11/15] init-install-efi: fix script for gummiboot loader Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 13/15] subversion: fix CVE-2015-3184 Robert Yang
` (2 subsequent siblings)
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Bhuvanchandra DV <bhuvanchandra.dv@toradex.com>
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)
Signed-off-by: Bhuvanchandra DV <bhuvanchandra.dv@toradex.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
.../linux-firmware/linux-firmware_git.bb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/meta/recipes-kernel/linux-firmware/linux-firmware_git.bb b/meta/recipes-kernel/linux-firmware/linux-firmware_git.bb
index 4939ca6..0878ab1 100644
--- a/meta/recipes-kernel/linux-firmware/linux-firmware_git.bb
+++ b/meta/recipes-kernel/linux-firmware/linux-firmware_git.bb
@@ -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 \
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 13/15] subversion: fix CVE-2015-3184
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (11 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 12/15] linux-firmware: rtl8192cx: Add latest available firmware Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 14/15] subversion: fix CVE-2015-3187 Robert Yang
2015-12-08 9:56 ` [PATCH 15/15] rpcbind: Security Advisory - rpcbind - CVE-2015-7236 Robert Yang
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Wenzong Fan <wenzong.fan@windriver.com>
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)
Signed-off-by: Wenzong Fan <wenzong.fan@windriver.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
.../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
diff --git a/meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3184.patch b/meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3184.patch
new file mode 100644
index 0000000..0663bd2
--- /dev/null
+++ b/meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3184.patch
@@ -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 <wenzong.fan@windriver.com>
+
+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:
diff --git a/meta/recipes-devtools/subversion/subversion_1.8.13.bb b/meta/recipes-devtools/subversion/subversion_1.8.13.bb
index 9c9bdb1..9505247 100644
--- a/meta/recipes-devtools/subversion/subversion_1.8.13.bb
+++ b/meta/recipes-devtools/subversion/subversion_1.8.13.bb
@@ -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"
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 14/15] subversion: fix CVE-2015-3187
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (12 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 13/15] subversion: fix CVE-2015-3184 Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
2015-12-08 9:56 ` [PATCH 15/15] rpcbind: Security Advisory - rpcbind - CVE-2015-7236 Robert Yang
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Wenzong Fan <wenzong.fan@windriver.com>
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)
Signed-off-by: Wenzong Fan <wenzong.fan@windriver.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
.../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
diff --git a/meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3187.patch b/meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3187.patch
new file mode 100644
index 0000000..494e11c
--- /dev/null
+++ b/meta/recipes-devtools/subversion/subversion-1.8.13/subversion-CVE-2015-3187.patch
@@ -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 <wenzong.fan@windriver.com>
+
+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
+ };
diff --git a/meta/recipes-devtools/subversion/subversion_1.8.13.bb b/meta/recipes-devtools/subversion/subversion_1.8.13.bb
index 9505247..68934b7 100644
--- a/meta/recipes-devtools/subversion/subversion_1.8.13.bb
+++ b/meta/recipes-devtools/subversion/subversion_1.8.13.bb
@@ -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"
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 15/15] rpcbind: Security Advisory - rpcbind - CVE-2015-7236
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
` (13 preceding siblings ...)
2015-12-08 9:56 ` [PATCH 14/15] subversion: fix CVE-2015-3187 Robert Yang
@ 2015-12-08 9:56 ` Robert Yang
14 siblings, 0 replies; 16+ messages in thread
From: Robert Yang @ 2015-12-08 9:56 UTC (permalink / raw)
To: openembedded-core
From: Li Zhou <li.zhou@windriver.com>
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)
Signed-off-by: Li Zhou <li.zhou@windriver.com>
Signed-off-by: Wenzong Fan <wenzong.fan@windriver.com>
Signed-off-by: Ross Burton <ross.burton@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
---
.../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
diff --git a/meta/recipes-extended/rpcbind/rpcbind/cve-2015-7236.patch b/meta/recipes-extended/rpcbind/rpcbind/cve-2015-7236.patch
new file mode 100644
index 0000000..f156290
--- /dev/null
+++ b/meta/recipes-extended/rpcbind/rpcbind/cve-2015-7236.patch
@@ -0,0 +1,83 @@
+commit 06f7ebb1dade2f0dbf872ea2bedf17cff4734bdd
+Author: Olaf Kirch <okir@...e.de>
+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 <okir@...e.de>
+
+ Upstream-Status: Backport
+
+ Signed-off-by: Li Zhou <li.zhou@windriver.com>
+---
+ 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;
+ }
diff --git a/meta/recipes-extended/rpcbind/rpcbind_0.2.3.bb b/meta/recipes-extended/rpcbind/rpcbind_0.2.3.bb
index 237018b..ecd3ba8 100644
--- a/meta/recipes-extended/rpcbind/rpcbind_0.2.3.bb
+++ b/meta/recipes-extended/rpcbind/rpcbind_0.2.3.bb
@@ -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"
--
1.7.9.5
^ permalink raw reply related [flat|nested] 16+ messages in thread
end of thread, other threads:[~2015-12-08 9:56 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-12-08 9:56 [PATCH 00/15] [jethro] Consolidated pull Robert Yang
2015-12-08 9:56 ` [PATCH 01/15] opkg: add cache filename length fixes Robert Yang
2015-12-08 9:56 ` [PATCH 02/15] binutils: Fix octeon3 disassembly patch Robert Yang
2015-12-08 9:56 ` [PATCH 03/15] libarchive: rename patch to reflect CVE Robert Yang
2015-12-08 9:56 ` [PATCH 04/15] readline: rename patch to contain CVE reference Robert Yang
2015-12-08 9:56 ` [PATCH 05/15] unzip: rename patch to reflect CVE fix Robert Yang
2015-12-08 9:56 ` [PATCH 06/15] libxslt: CVE-2015-7995 Robert Yang
2015-12-08 9:56 ` [PATCH 07/15] libsndfile: fix CVE-2014-9756 Robert Yang
2015-12-08 9:56 ` [PATCH 08/15] allarch: Force TARGET_*FLAGS variable values Robert Yang
2015-12-08 9:56 ` [PATCH 09/15] pulseaudio: Fix HDMI profile selection Robert Yang
2015-12-08 9:56 ` [PATCH 10/15] init-install-efi: fix script for eMMC installation Robert Yang
2015-12-08 9:56 ` [PATCH 11/15] init-install-efi: fix script for gummiboot loader Robert Yang
2015-12-08 9:56 ` [PATCH 12/15] linux-firmware: rtl8192cx: Add latest available firmware Robert Yang
2015-12-08 9:56 ` [PATCH 13/15] subversion: fix CVE-2015-3184 Robert Yang
2015-12-08 9:56 ` [PATCH 14/15] subversion: fix CVE-2015-3187 Robert Yang
2015-12-08 9:56 ` [PATCH 15/15] rpcbind: Security Advisory - rpcbind - CVE-2015-7236 Robert Yang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox