aboutsummaryrefslogtreecommitdiffstats
path: root/community/sphinx
diff options
context:
space:
mode:
Diffstat (limited to 'community/sphinx')
-rw-r--r--community/sphinx/APKBUILD35
-rw-r--r--community/sphinx/CVE-2020-29050.patch416
2 files changed, 438 insertions, 13 deletions
diff --git a/community/sphinx/APKBUILD b/community/sphinx/APKBUILD
index 0f56d82ca55..22a73b2eb24 100644
--- a/community/sphinx/APKBUILD
+++ b/community/sphinx/APKBUILD
@@ -3,13 +3,18 @@
# Maintainer: Francesco Colista <fcolista@alpinelinux.org>
pkgname=sphinx
pkgver=2.2.11
-pkgrel=2
+pkgrel=7
pkgdesc="Free open-source SQL full-text search engine"
url="http://www.sphinxsearch.com"
arch="all"
license="GPL-2.0-only"
-makedepends="postgresql-dev mariadb-connector-c-dev unixodbc-dev expat-dev
- re2-dev snowball-dev"
+makedepends="
+ expat-dev
+ libpq-dev
+ libstemmer-dev
+ mariadb-connector-c-dev
+ unixodbc-dev
+ "
pkgusers="$pkgname"
pkggroups="$pkgname"
subpackages="$pkgname-doc $pkgname-php::noarch"
@@ -17,10 +22,14 @@ source="http://sphinxsearch.com/files/sphinx-$pkgver-release.tar.gz
sphinx.initd
sphinx.confd
sphinx-pagesize.patch
+ CVE-2020-29050.patch
"
-
builddir="$srcdir"/$pkgname-$pkgver-release
+# secfixes:
+# 2.2.11-r7:
+# - CVE-2020-29059
+
build() {
./configure \
--prefix=/usr \
@@ -36,29 +45,26 @@ build() {
--with-iconv \
--with-libstemmer \
--with-syslog \
- --with-re2 \
+ --without-re2 \
--enable-id64
make
}
package() {
make DESTDIR="$pkgdir" install
- install -Dm755 "$srcdir"/$pkgname.confd "$pkgdir"/etc/conf.d/$pkgname
install -Dm755 "$srcdir"/$pkgname.initd "$pkgdir"/etc/init.d/$pkgname
+ install -Dm644 "$srcdir"/$pkgname.confd "$pkgdir"/etc/conf.d/$pkgname
chown -R $pkgusers:$pkggroups "$pkgdir"/var/lib/sphinx
}
-
php() {
pkgdesc="PHP api for sphinx search engine"
mkdir -p "$subpkgdir"/usr/share/php/$pkgname/api
- for i in $(ls $builddir/api/*.php); do
- mv $i "$subpkgdir"/usr/share/php/$pkgname/api ;
- done
+ find $builddir/api/ -type f -maxdepth 1 -name "*.php" \
+ -exec mv {} "$subpkgdir"/usr/share/php/$pkgname/api/ \;
}
-
doc() {
default_doc;
mkdir -p "$subpkgdir"/usr/share/doc/$pkgname/misc
@@ -66,7 +72,10 @@ doc() {
}
-sha512sums="cf1a262a5b0fbf0bd2827ec6ec629edeaf709ce855a6e7b509b65342baaeb26c02717ca63f1578d32c83d21e2fd6d1e92dceb34660e6351b93cd96fd4e623689 sphinx-2.2.11-release.tar.gz
+sha512sums="
+cf1a262a5b0fbf0bd2827ec6ec629edeaf709ce855a6e7b509b65342baaeb26c02717ca63f1578d32c83d21e2fd6d1e92dceb34660e6351b93cd96fd4e623689 sphinx-2.2.11-release.tar.gz
583601ff63e663099ae40048b8a216d0bc815a50a82370a42d3e7b923c90c650d58951636041ff9000141d897357767b7895a238a4edc49c328e46241b391350 sphinx.initd
8dbbb3b75bfbde5c6d2bee801df8c7a82650d3943dd667a4330cae473cbf18390aff5eb8d6aa6e5d69c4c995065d48289047b9166fa756c6015bf71f2b13a8f0 sphinx.confd
-9563c5a926e5be30477781038ccf115a809a32bbcbc02c5b82e7985fca76185005968b5f0442772ec598b2ff17ef5c185582e24ae74775e5358abc88192345f2 sphinx-pagesize.patch"
+9563c5a926e5be30477781038ccf115a809a32bbcbc02c5b82e7985fca76185005968b5f0442772ec598b2ff17ef5c185582e24ae74775e5358abc88192345f2 sphinx-pagesize.patch
+b3e27a556789af966ca779136122f8d0ebb6999ef9766029a0492bcf918d3e6f94cda01797c8715ad41f6fa8f8afc755672734b4bdcacb17b35009ac9a5f05aa CVE-2020-29050.patch
+"
diff --git a/community/sphinx/CVE-2020-29050.patch b/community/sphinx/CVE-2020-29050.patch
new file mode 100644
index 00000000000..62355266ce3
--- /dev/null
+++ b/community/sphinx/CVE-2020-29050.patch
@@ -0,0 +1,416 @@
+Patch-Source: https://salsa.debian.org/debian/sphinxsearch/-/blob/5810cd80b48ec7915458939f992030f8b6f3bef9/debian/patches/06-CVE-2020-29050.patch
+--
+From: klirichek <alexey@manticoresearch.com>
+Date: Tue, 2 Jul 2019 19:06:09 +0700
+Subject: [PATCH] Fix random file reading by scattered snippets
+
+Issue was that load_files_scattered option just concatenated prefix with
+given name and then opened the file. So, if you provide something like
+'/etc/password' as file, or '../../../etc/passwd' even with non-zero
+prefix, it will unfortunately work. After this commit such behavior
+possible ONLY if user explicitly set `snippets_file_prefix` to empty
+string or '/'; another cases will not cause uncontroled reading.
+That fixes #866.
+
+[basilgello: Back-port to 2.2.11:
+ - Refactor sphNormalizePath to use only available classes.
+ - Do not use 'g_sSnippetsFilePrefix' in src/sphinxexcerpt.h,
+ use 'tOptions.m_sFilePrefix' instead, as for 2.2.11, all
+ two instances are assigned to 'g_sSnippetsFilePrefix'.
+ - Adjust context. ]
+
+The following test passes, adapted from commit
+66b5761ad258c60b1866a8e1333f86e74f48035 at
+https://github.com/manticoresoftware/manticoresearch :
+
+====
+/*
+ cd test &&
+ g++ -o test test.cpp -I../src -I../config -I/usr/include/postgresql \
+ -I/usr/include/mariadb -DHAVE_CONFIG_H -L../src -lsphinx -pthread \
+ -lm -ldl -lstemmer -lmariadb -lpq -lexpat -lz
+*/
+
+void ASSERT_STREQ(const char *src, const char *dst)
+{
+ auto normalized = sphNormalizePath(src);
+ if (!strcmp(dst, normalized.scstr())) {
+ auto _src = src;
+ auto _dst = dst;
+
+ if (src == nullptr) _src = "nullptr";
+ if (dst == nullptr) _dst = "nullptr";
+
+ std::cout << "OK : '" << _src << "' = '" << _dst <<
+ "'" << std::endl;
+ } else {
+ auto _src = src;
+ auto _dst = dst;
+
+ if (src == nullptr) _src = "nullptr";
+ if (dst == nullptr) _dst = "nullptr";
+
+ std::cout << "FAIL: '" << _src << "' != '" << _dst << "' ( = '" <<
+ normalized.scstr() << "')" << std::endl;
+ }
+}
+
+int main()
+{
+ ASSERT_STREQ ( "/", "/" );
+ ASSERT_STREQ ( "/..//bbb", "/bbb" );
+ ASSERT_STREQ ( "/quite/long/path/../../../etc/passwd", "/etc/passwd" );
+ ASSERT_STREQ ( "/aaa/bbb/ccc/ddd/../../../../../../../", "/" );
+
+ ASSERT_STREQ ( "", "" );
+ ASSERT_STREQ ( nullptr, "" );
+ ASSERT_STREQ ( "aaa/", "aaa" );
+ ASSERT_STREQ ( "aaa/.", "aaa" );
+ ASSERT_STREQ ( "aaa/././././////././", "aaa" );
+ ASSERT_STREQ ( "aaa/////", "aaa" );
+ ASSERT_STREQ ( "aaa/bbb/ccc", "aaa/bbb/ccc" );
+ ASSERT_STREQ ( "aaa/bbb/ccc/ddd/..", "aaa/bbb/ccc" );
+ ASSERT_STREQ ( "aaa/bbb/ccc/ddd/../../..", "aaa" );
+ ASSERT_STREQ ( "aaa/bbb/ccc/ddd/../../../xxx", "aaa/xxx" );
+ ASSERT_STREQ ( "aaa/bbb/ccc/ddd/../../../..", "" );
+ ASSERT_STREQ ( "aaa/bbb/ccc/ddd/../../../../", "" );
+ ASSERT_STREQ ( "aaa/bbb/ccc/ddd/../../../../../../../", "../../.." );
+ ASSERT_STREQ ( "..//bbb", "../bbb" );
+ return 0;
+}
+====
+
+Fixes CVE-2020-29050.
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ doc/sphinx.html | 19 +++++++++++++-
+ doc/sphinx.txt | 19 +++++++++++++-
+ sphinx.conf.in | 4 +--
+ src/searchd.cpp | 8 +++++-
+ src/sphinx.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++
+ src/sphinxexcerpt.cpp | 14 ++++++++++
+ src/sphinxexcerpt.h | 3 +++
+ src/sphinxstd.h | 3 +++
+ test/test_130/test.xml | 7 ++---
+ 9 files changed, 140 insertions(+), 8 deletions(-)
+
+diff --git a/doc/sphinx.html b/doc/sphinx.html
+index b56e445..8ad44dd 100644
+--- a/doc/sphinx.html
++++ b/doc/sphinx.html
+@@ -11652,7 +11652,7 @@ binlog_max_log_size = 16M
+ <div class="sect2" title="12.4.28.&nbsp;snippets_file_prefix"><div class="titlepage"><div><div><h3 class="title"><a name="conf-snippets-file-prefix"></a>12.4.28.&nbsp;snippets_file_prefix</h3></div></div></div>
+ <p>
+ A prefix to prepend to the local file names when generating snippets.
+-Optional, default is empty.
++Optional, default is current working folder.
+ Introduced in version 2.1.1-beta.
+ </p><p>
+ This prefix can be used in distributed snippets generation along with
+@@ -11663,6 +11663,19 @@ is set to "server1" and the request refers to "file23", <code class="filename">s
+ will attempt to open "server1file23" (all of that without quotes). So if you
+ need it to be a path, you have to mention the trailing slash.
+ </p><p>
++After constructing final file path, daemon unwinds all relative dirs and
++compares final result with the value of ``snippet_file_prefix``. If result
++is not begin with the prefix, such file will be rejected with error message.
++
++So, if you set it to '/mnt/data' and somebody calls snippet generation with file
++'../../../etc/passwd', as the source, it will get error message
++`File '/mnt/data/../../../etc/passwd' escapes '/mnt/data/' scope`
++instead of content of the file.
++
++Also, with non-set parameter and reading '/etc/passwd' it will actually read
++/daemon/working/folder/etc/passwd since default for param is exactly daemon's
++working folder.
++</p><p>
+ Note also that this is a local option, it does not affect the agents anyhow.
+ So you can safely set a prefix on a master server. The requests routed to the
+ agents will not be affected by the master's setting. They will however be affected
+@@ -11673,6 +11686,10 @@ This might be useful, for instance, when the document storage locations
+ </p><h4><a name="idp33320288"></a>Example:</h4><pre class="programlisting">
+ snippets_file_prefix = /mnt/common/server1/
+ </pre></div>
++<p><span class="bold"><strong>WARNING:</strong></span>
++If you still want to access files from the FS root, you have to explicitly set
++'snippets_file_prefix' to empty value (by 'snippets_file_prefix=' line), or to
++root (by 'snippets_file_prefix=/').
+ <div class="sect2" title="12.4.29.&nbsp;collation_server"><div class="titlepage"><div><div><h3 class="title"><a name="conf-collation-server"></a>12.4.29.&nbsp;collation_server</h3></div></div></div>
+ <p>
+ Default server collation.
+diff --git a/doc/sphinx.txt b/doc/sphinx.txt
+index ed994f9..f750f4e 100644
+--- a/doc/sphinx.txt
++++ b/doc/sphinx.txt
+@@ -12832,7 +12832,7 @@ Example:
+ -----------------------------
+
+ A prefix to prepend to the local file names when generating snippets.
+-Optional, default is empty. Introduced in version 2.1.1-beta.
++Optional, default is current working folder. Introduced in version 2.1.1-beta.
+
+ This prefix can be used in distributed snippets generation along with
+ load_files or load_files_scattered options.
+@@ -12842,6 +12842,19 @@ to "server1" and the request refers to "file23", searchd will attempt to
+ open "server1file23" (all of that without quotes). So if you need it to be
+ a path, you have to mention the trailing slash.
+
++After constructing final file path, daemon unwinds all relative dirs and
++compares final result with the value of ``snippet_file_prefix``. If result
++is not begin with the prefix, such file will be rejected with error message.
++
++So, if you set it to '/mnt/data' and somebody calls snippet generation with file
++'../../../etc/passwd', as the source, it will get error message
++`File '/mnt/data/../../../etc/passwd' escapes '/mnt/data/' scope`
++instead of content of the file.
++
++Also, with non-set parameter and reading '/etc/passwd' it will actually read
++/daemon/working/folder/etc/passwd since default for param is exactly daemon's
++working folder.
++
+ Note also that this is a local option, it does not affect the agents
+ anyhow. So you can safely set a prefix on a master server. The requests
+ routed to the agents will not be affected by the master's setting. They
+@@ -12855,6 +12868,10 @@ Example:
+
+ | snippets_file_prefix = /mnt/common/server1/
+
++WARNING: If you still want to access files from the FS root, you have to
++explicitly set 'snippets_file_prefix' to empty value (by 'snippets_file_prefix='
++line), or to root (by 'snippets_file_prefix=/').
++
+ 12.4.29. collation_server
+ -------------------------
+
+diff --git a/sphinx.conf.in b/sphinx.conf.in
+index 0ccf858..9d83897 100644
+--- a/sphinx.conf.in
++++ b/sphinx.conf.in
+@@ -604,7 +604,7 @@ index test1
+ # snippet document file name prefix
+ # preprended to file names when generating snippets using load_files option
+ # WARNING, this is a prefix (not a path), trailing slash matters!
+- # optional, default is empty
++ # optional, default is current working directory of a running process
+ #
+ # snippets_file_prefix = /mnt/mydocs/server1
+
+@@ -1042,7 +1042,7 @@ searchd
+
+ # a prefix to prepend to the local file names when creating snippets
+ # with load_files and/or load_files_scatter options
+- # optional, default is empty
++ # optional, default is current working directory of a running process
+ #
+ # snippets_file_prefix = /mnt/common/server1/
+ }
+diff --git a/src/searchd.cpp b/src/searchd.cpp
+index 85b1cd6..6462b16 100644
+--- a/src/searchd.cpp
++++ b/src/searchd.cpp
+@@ -13330,6 +13330,12 @@ bool MakeSnippets ( CSphString sIndex, CSphVector<ExcerptQuery_t> & dQueries, CS
+ struct stat st;
+ CSphString sFilename;
+ sFilename.SetSprintf ( "%s%s", g_sSnippetsFilePrefix.cstr(), dQueries[i].m_sSource.cstr() );
++ if ( !TestEscaping ( g_sSnippetsFilePrefix, sFilename ))
++ {
++ sError.SetSprintf( "File '%s' escapes '%s' scope",
++ sFilename.scstr(), g_sSnippetsFilePrefix.scstr());
++ return false;
++ }
+ if ( ::stat ( sFilename.cstr(), &st )<0 )
+ {
+ if ( !bScattered )
+@@ -23719,7 +23725,7 @@ int WINAPI ServiceMain ( int argc, char **argv )
+ if ( hSearchd.Exists ( "snippets_file_prefix" ) )
+ g_sSnippetsFilePrefix = hSearchd["snippets_file_prefix"].cstr();
+ else
+- g_sSnippetsFilePrefix = "";
++ g_sSnippetsFilePrefix.SetSprintf("%s/", sphGetCwd().scstr());
+
+ const char* sLogFormat = hSearchd.GetStr ( "query_log_format", "plain" );
+ if ( !strcmp ( sLogFormat, "sphinxql" ) )
+diff --git a/src/sphinx.cpp b/src/sphinx.cpp
+index 698b8af..4257d0f 100644
+--- a/src/sphinx.cpp
++++ b/src/sphinx.cpp
+@@ -81,9 +81,11 @@
+ #include <io.h> // for open()
+
+ // workaround Windows quirks
++ #include <direct.h>
+ #define popen _popen
+ #define pclose _pclose
+ #define snprintf _snprintf
++ #define getcwd _getcwd
+ #define sphSeek _lseeki64
+
+ #define stat _stat64
+@@ -12420,6 +12422,75 @@ static bool sphTruncate ( int iFD )
+ #endif
+ }
+
++CSphString sphNormalizePath( const CSphString& sOrigPath )
++{
++ CSphVector<CSphString> dChunks;
++ const char* sBegin = sOrigPath.scstr();
++ const char* sEnd = sBegin + sOrigPath.Length();
++ const char* sPath = sBegin;
++ int iLevel = 0;
++
++ while ( sPath<sEnd )
++ {
++ const char* sSlash = ( char* ) memchr( sPath, '/', sEnd - sPath );
++ if ( !sSlash )
++ sSlash = sEnd;
++
++ auto iChunkLen = sSlash - sPath;
++
++ switch ( iChunkLen )
++ {
++ case 0: // empty chunk skipped
++ ++sPath;
++ continue;
++ case 1: // simple dot chunk skipped
++ if ( *sPath=='.' )
++ {
++ sPath += 2;
++ continue;
++ }
++ break;
++ case 2: // double dot abandons chunks, then decrease level
++ if ( sPath[0]=='.' && sPath[1]=='.' )
++ {
++ if ( dChunks.GetLength() <= 0 )
++ --iLevel;
++ else
++ dChunks.Pop();
++ sPath += 3;
++ continue;
++ }
++ default: break;
++ }
++ CSphString temp( "" );
++ temp.SetBinary( sPath, iChunkLen );
++ dChunks.Add( temp );
++ sPath = sSlash + 1;
++ }
++
++ CSphStringBuilder sResult;
++ if ( *sBegin=='/' )
++ sResult += "/";
++ else
++ while ( iLevel++<0 )
++ dChunks.Insert(0, "..");
++
++ int i;
++ for ( i=0; i<dChunks.GetLength(); i++ ) {
++ sResult += dChunks[i].scstr();
++ if (i<dChunks.GetLength()-1)
++ sResult += "/";
++ }
++
++ return sResult.cstr();
++}
++
++CSphString sphGetCwd()
++{
++ CSphVector<char> sBuf (65536);
++ return getcwd( sBuf.Begin(), sBuf.GetLength());
++}
++
+ class DeleteOnFail : public ISphNoncopyable
+ {
+ public:
+diff --git a/src/sphinxexcerpt.cpp b/src/sphinxexcerpt.cpp
+index b42c6a2..ff593f0 100644
+--- a/src/sphinxexcerpt.cpp
++++ b/src/sphinxexcerpt.cpp
+@@ -3817,6 +3817,11 @@ void sphBuildExcerpt ( ExcerptQuery_t & tOptions, const CSphIndex * pIndex, cons
+ {
+ CSphString sFilename;
+ sFilename.SetSprintf ( "%s%s", tOptions.m_sFilePrefix.cstr(), tOptions.m_sSource.cstr() );
++ if ( !TestEscaping( tOptions.m_sFilePrefix.scstr(), sFilename ))
++ {
++ sError.SetSprintf( "File '%s' escapes '%s' scope", sFilename.scstr(), tOptions.m_sFilePrefix.scstr());
++ return;
++ }
+ if ( tFile.Open ( sFilename.cstr(), SPH_O_READ, sError )<0 )
+ return;
+ } else if ( tOptions.m_sSource.IsEmpty() )
+@@ -3859,6 +3864,15 @@ void sphBuildExcerpt ( ExcerptQuery_t & tOptions, const CSphIndex * pIndex, cons
+ sWarning, sError, pQueryTokenizer, tOptions.m_dRes );
+ }
+
++// check whether filepath from sPath does not escape area of sPrefix
++bool TestEscaping( const CSphString& sPrefix, const CSphString& sPath )
++{
++ if ( sPrefix.IsEmpty() || sPrefix==sPath )
++ return true;
++ auto sNormalized = sphNormalizePath( sPath );
++ return sPrefix==sNormalized.SubString( 0, sPrefix.Length());
++}
++
+ //
+ // $Id$
+ //
+diff --git a/src/sphinxexcerpt.h b/src/sphinxexcerpt.h
+index cf48b1f..87a55d4 100644
+--- a/src/sphinxexcerpt.h
++++ b/src/sphinxexcerpt.h
+@@ -81,6 +81,9 @@ struct XQQuery_t;
+ void sphBuildExcerpt ( ExcerptQuery_t & tOptions, const CSphIndex * pIndex, const CSphHTMLStripper * pStripper, const XQQuery_t & tExtQuery,
+ DWORD eExtQuerySPZ, CSphString & sWarning, CSphString & sError, CSphDict * pDict, ISphTokenizer * pDocTokenizer, ISphTokenizer * pQueryTokenizer );
+
++// helper whether filepath from sPath does not escape area of sPrefix
++bool TestEscaping( const CSphString& sPrefix, const CSphString& sPath );
++
+ #endif // _sphinxexcerpt_
+
+ //
+diff --git a/src/sphinxstd.h b/src/sphinxstd.h
+index 39cc7ee..c1f15c1 100644
+--- a/src/sphinxstd.h
++++ b/src/sphinxstd.h
+@@ -2294,6 +2294,9 @@ int sphOpenFile ( const char * sFile, CSphString & sError );
+ /// return size of file descriptor
+ int64_t sphGetFileSize ( int iFD, CSphString & sError );
+
++// unwind different tricks like "../../../etc/passwd"
++CSphString sphNormalizePath ( const CSphString& sOrigPath );
++CSphString sphGetCwd();
+
+ /// buffer trait that neither own buffer nor clean-up it on destroy
+ template < typename T >
+diff --git a/test/test_130/test.xml b/test/test_130/test.xml
+index d8f746c..41e4961 100644
+--- a/test/test_130/test.xml
++++ b/test/test_130/test.xml
+@@ -7,6 +7,7 @@
+ searchd
+ {
+ <searchd_settings/>
++ snippets_file_prefix=<this_test/>/
+ }
+
+ source test
+@@ -30,15 +31,15 @@ index test
+
+ $results = array();
+
+-$docs = array( 'test_130/load_file.txt' );
++$docs = array( "load_file.txt" );
+ $opts = array( 'load_files'=>true, 'limit'=>0 );
+
+ $results[] = $client->BuildExcerpts($docs, 'test', 'end point', $opts );
+ $results[] = $client->BuildExcerpts($docs, 'test', 'not_found', $opts );
+-$results[] = $client->BuildExcerpts(array( 'test_130/empty.txt' ), 'test', 'end point', $opts );
++$results[] = $client->BuildExcerpts(array( 'empty.txt' ), 'test', 'end point', $opts );
+ $results[] = $client->BuildExcerpts(array( '' ), 'test', 'not_found', $opts );
+ $results[] = $client->GetLastError();
+-$results[] = $client->BuildExcerpts ( array ( 'test_130/512k.xml' ), 'test', 'it builds', array ( "limit" => 100, "load_files" => true ) );
++$results[] = $client->BuildExcerpts ( array ( '512k.xml' ), 'test', 'it builds', array ( "limit" => 100, "load_files" => true ) );
+
+ ]]></custom_test>
+