Hello dear maintainer(s), the Debian LTS team would like to fix the security issues which are currently open in the Squeeze version of subversion: https://security-tracker.debian.org/tracker/CVE-2015-3187
I have prepared the attached patch and I am ready to upload, unless you want to do it by yourselves. If you do, please follow the workflow we have defined here: http://wiki.debian.org/LTS/Development Otherwise, I'll upload the package in the next couple of days. Thank you very much. Santiago Ruano Rincón, on behalf of the Debian LTS team.
diff -u subversion-1.6.12dfsg/debian/changelog subversion-1.6.12dfsg/debian/changelog --- subversion-1.6.12dfsg/debian/changelog +++ subversion-1.6.12dfsg/debian/changelog @@ -1,3 +1,11 @@ +subversion (1.6.12dfsg-7+deb6u3) squeeze-lts; urgency=high + + * Non-maintainer upload. + * CVE-2015-3187: svn_repos_trace_node_locations revelaed hidden path + information. + + -- Santiago Ruano Rincón <santiag...@riseup.net> Fri, 14 Aug 2015 19:39:24 +0200 + subversion (1.6.12dfsg-7+deb6u2) squeeze-lts; urgency=high * Add patches for following CVEs diff -u subversion-1.6.12dfsg/debian/patches/series subversion-1.6.12dfsg/debian/patches/series --- subversion-1.6.12dfsg/debian/patches/series +++ subversion-1.6.12dfsg/debian/patches/series @@ -41,0 +42 @@ +CVE-2015-3187 only in patch2: unchanged: --- subversion-1.6.12dfsg.orig/debian/patches/CVE-2015-3187 +++ subversion-1.6.12dfsg/debian/patches/CVE-2015-3187 @@ -0,0 +1,415 @@ + svn_repos_trace_node_locations() reveals paths hidden by authz + +Summary +======= + + Subversion servers, both httpd and svnserve, will reveal some paths + that should be hidden by path-based authz. When a node is copied + from an unreadable location to a readable location the unreadable + path may be revealed. This vulnerablity only reveals the path, it + does not reveal the contents of the path. + +Known vulnerable +================ + + Subversion 1.8.0 to 1.8.13 + Subversion 1.7.0 to 1.7.20 + All older versions + +Known fixed +=========== + + Subversion 1.8.14 + Subversion 1.7.21 + +Details +======= + + The function svn_repos_trace_node_locations() follows the history of + a node through earlier revisions and shows any copies. It should + stop following history when an unreadable path is encountered but an + implementation error causes the first unreadable path to be returned + in some circumstances. This only reveals the unreadable path, it + does not reveal the content of the file or directory at that path. + Any attempts to obtain the content will fail with an authorization + error. + + Examples: + + 1. After copying "unreadable-path" to "readable-path" then following + the history of "readable-path" may reveal "unreadable-path". + + 2. After copying "unreadable/some-path" to "readable/other-path" + then following the history of "readable/other-path" may reveal + "unreadable/some-path". + +Severity +======== + + CVSSv2 Base Score: 3.5 + CVSSv2 Base Vector: AV:N/AC:M/Au:S/C:P/I:N/A:N + +Recommendations +=============== + + All Subversion servers using path-based authz to deny read access + to sensitive paths should be upgraded. + +References +========== + + CVE-2015-3187 (Subversion) + +Reported by +=========== + + C. Michael Pilato, CollabNet + +Index: subversion-1.6.12dfsg/subversion/libsvn_repos/rev_hunt.c +=================================================================== +--- subversion-1.6.12dfsg.orig/subversion/libsvn_repos/rev_hunt.c ++++ subversion-1.6.12dfsg/subversion/libsvn_repos/rev_hunt.c +@@ -716,20 +716,6 @@ svn_repos_trace_node_locations(svn_fs_t + 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) +- { +- 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) +@@ -752,6 +738,18 @@ svn_repos_trace_node_locations(svn_fs_t + 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) ++ { ++ return SVN_NO_ERROR; ++ } ++ } ++ + /* Clear last pool and switch. */ + svn_pool_clear(lastpool); + tmppool = lastpool; +Index: subversion-1.6.12dfsg/subversion/tests/cmdline/authz_tests.py +=================================================================== +--- subversion-1.6.12dfsg.orig/subversion/tests/cmdline/authz_tests.py ++++ subversion-1.6.12dfsg/subversion/tests/cmdline/authz_tests.py +@@ -559,8 +559,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'): +@@ -577,10 +579,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-1.6.12dfsg/subversion/tests/libsvn_repos/repos-test.c +=================================================================== +--- subversion-1.6.12dfsg.orig/subversion/tests/libsvn_repos/repos-test.c ++++ subversion-1.6.12dfsg/subversion/tests/libsvn_repos/repos-test.c +@@ -2424,6 +2424,262 @@ get_logs(const char **msg, + 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; ++ ++ apr_hash_set(b->paths, apr_pstrdup(b->pool, path), APR_HASH_KEY_STRING, ++ (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; ++ void *key; ++ const char *expected_path; ++ ++ apr_hash_this(hi, &key, NULL, (void *)&expected_path); ++ rev = key; ++ 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)", ++ expected_path, ++ (int)*rev); ++ else if (strcmp(path, expected_path)) ++ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, ++ "expected %s for %d found %s", ++ expected_path, ++ (int)*rev, path); ++ ++ } ++ ++ for (hi = apr_hash_first(pool, actual); hi; hi = apr_hash_next(hi)) ++ { ++ const svn_revnum_t *rev; ++ void *key; ++ const char *expected_path; ++ ++ apr_hash_this(hi, &key, NULL, (void *)&expected_path); ++ rev = key; ++ 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)", ++ expected_path, ++ (int)*rev); ++ else if (strcmp(path, expected_path)) ++ return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, ++ "found %s for %d expected %s", ++ expected_path, ++ (int)*rev, path); ++ ++ if (!apr_hash_get(checked, path, APR_HASH_KEY_STRING)) ++ 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 char **msg, ++ svn_boolean_t msg_only, ++ 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; ++ ++ *msg = "authz for svn_repos_trace_node_locations"; ++ ++ if (msg_only) ++ return SVN_NO_ERROR; ++ ++ /* 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. */ +@@ -2444,5 +2700,6 @@ struct svn_test_descriptor_t test_funcs[ + SVN_TEST_PASS(reporter_depth_exclude), + SVN_TEST_PASS(prop_validation), + SVN_TEST_PASS(get_logs), ++ SVN_TEST_PASS(trace_node_locations_authz), + SVN_TEST_NULL + };
signature.asc
Description: Digital signature