Package: release.debian.org Severity: normal Tags: jessie User: release.debian....@packages.debian.org Usertags: pu
Hello, I'd like to upload a new version of dulwich to testing-proposed-updates. unstable already has a new upstream version (0.9.8) from an upload in November, and has diverged from testing. This upload would fix two serious security bugs: #780958 CVE-2015-0838: buffer overflow in C implementation of pack apply_delta() #780989 CVE-2014-9706: does not prevent to write files in commits with invalid paths to working tree debdiff attached -- System Information: Debian Release: jessie/sid APT prefers unstable APT policy: (500, 'unstable'), (1, 'experimental') Architecture: amd64 (x86_64) Foreign Architectures: i386 Kernel: Linux 3.16-1-amd64 (SMP w/4 CPU cores) Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash Init: sysvinit (via /sbin/init)
diff -Nru dulwich-0.9.7/debian/changelog dulwich-0.9.7/debian/changelog --- dulwich-0.9.7/debian/changelog 2014-07-09 01:02:43.000000000 +0000 +++ dulwich-0.9.7/debian/changelog 2015-03-24 23:43:05.000000000 +0000 @@ -1,3 +1,12 @@ +dulwich (0.9.7-3) unstable; urgency=medium + + * Add 02_cve_2015-0838: Fix buffer overflow in C implementation of + apply_delta (CVE-2015-0838). Closes: #780958 + * Add 03_cve_2014-9706: Don't allow writing to files under .git/ when + checking out working trees (CVE-2014-9706). Closes: #780989 + + -- Jelmer Vernooij <jel...@debian.org> Tue, 24 Mar 2015 22:34:34 +0000 + dulwich (0.9.7-2) unstable; urgency=medium * Require at least Python 2.7. Closes: #753519 diff -Nru dulwich-0.9.7/debian/patches/02_cve_2015-0838 dulwich-0.9.7/debian/patches/02_cve_2015-0838 --- dulwich-0.9.7/debian/patches/02_cve_2015-0838 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.9.7/debian/patches/02_cve_2015-0838 2015-03-24 23:37:51.000000000 +0000 @@ -0,0 +1,111 @@ +commit b25e8390074060ea2aed25cf070b8e98b85a3875 +Author: Jelmer Vernooij <jel...@google.com> +Date: Fri Mar 6 12:29:07 2015 +0000 + + Fix buffer overflow in C version of apply_delta(). + + This is CVE-2015-0838. + + Thanks to Ivan Fratric of the Google Security Team for + reporting this issue. + +diff --git a/dulwich/_pack.c b/dulwich/_pack.c +index d1534a5..440a9a9 100644 +--- a/dulwich/_pack.c ++++ b/dulwich/_pack.c +@@ -20,6 +20,8 @@ + #include <Python.h> + #include <stdint.h> + ++static PyObject *PyExc_ApplyDeltaError = NULL; ++ + static int py_is_sha(PyObject *sha) + { + if (!PyString_CheckExact(sha)) +@@ -103,7 +105,7 @@ static PyObject *py_apply_delta(PyObject *self, PyObject *args) + index = 0; + src_size = get_delta_header_size(delta, &index, delta_len); + if (src_size != src_buf_len) { +- PyErr_Format(PyExc_ValueError, ++ PyErr_Format(PyExc_ApplyDeltaError, + "Unexpected source buffer size: %lu vs %d", src_size, src_buf_len); + Py_DECREF(py_src_buf); + Py_DECREF(py_delta); +@@ -146,12 +148,16 @@ static PyObject *py_apply_delta(PyObject *self, PyObject *args) + break; + memcpy(out+outindex, src_buf+cp_off, cp_size); + outindex += cp_size; ++ dest_size -= cp_size; + } else if (cmd != 0) { ++ if (cmd > dest_size) ++ break; + memcpy(out+outindex, delta+index, cmd); + outindex += cmd; + index += cmd; ++ dest_size -= cmd; + } else { +- PyErr_SetString(PyExc_ValueError, "Invalid opcode 0"); ++ PyErr_SetString(PyExc_ApplyDeltaError, "Invalid opcode 0"); + Py_DECREF(ret); + Py_DECREF(py_delta); + Py_DECREF(py_src_buf); +@@ -162,13 +168,13 @@ static PyObject *py_apply_delta(PyObject *self, PyObject *args) + Py_DECREF(py_delta); + + if (index != delta_len) { +- PyErr_SetString(PyExc_ValueError, "delta not empty"); ++ PyErr_SetString(PyExc_ApplyDeltaError, "delta not empty"); + Py_DECREF(ret); + return NULL; + } + +- if (dest_size != outindex) { +- PyErr_SetString(PyExc_ValueError, "dest size incorrect"); ++ if (dest_size != 0) { ++ PyErr_SetString(PyExc_ApplyDeltaError, "dest size incorrect"); + Py_DECREF(ret); + return NULL; + } +@@ -236,6 +242,15 @@ static PyMethodDef py_pack_methods[] = { + void init_pack(void) + { + PyObject *m; ++ PyObject *errors_module; ++ ++ errors_module = PyImport_ImportModule("dulwich.errors"); ++ if (errors_module == NULL) ++ return; ++ ++ PyExc_ApplyDeltaError = PyObject_GetAttrString(errors_module, "ApplyDeltaError"); ++ if (PyExc_ApplyDeltaError == NULL) ++ return; + + m = Py_InitModule3("_pack", py_pack_methods, NULL); + if (m == NULL) +diff --git a/dulwich/tests/test_pack.py b/dulwich/tests/test_pack.py +index d33104f..393b931 100644 +--- a/dulwich/tests/test_pack.py ++++ b/dulwich/tests/test_pack.py +@@ -28,6 +28,7 @@ import tempfile + import zlib + + from dulwich.errors import ( ++ ApplyDeltaError, + ChecksumMismatch, + ) + from dulwich.file import ( +@@ -181,6 +182,14 @@ class TestPackDeltas(TestCase): + self.skipTest("big strings don't work yet") + self._test_roundtrip(self.test_string_huge, self.test_string_huge) + ++ def test_dest_overflow(self): ++ self.assertRaises( ++ ApplyDeltaError, ++ apply_delta, 'a'*0x10000, '\x80\x80\x04\x80\x80\x04\x80' + 'a'*0x10000) ++ self.assertRaises( ++ ApplyDeltaError, ++ apply_delta, '', '\x00\x80\x02\xb0\x11\x11') ++ + + class TestPackData(PackTests): + """Tests getting the data from the packfile.""" diff -Nru dulwich-0.9.7/debian/patches/03_cve_2014-9706 dulwich-0.9.7/debian/patches/03_cve_2014-9706 --- dulwich-0.9.7/debian/patches/03_cve_2014-9706 1970-01-01 00:00:00.000000000 +0000 +++ dulwich-0.9.7/debian/patches/03_cve_2014-9706 2015-03-24 23:37:04.000000000 +0000 @@ -0,0 +1,92 @@ +commit 598be9170c5e21ba408ba139a2b7bd7da6a04c70 +Author: Jelmer Vernooij <jel...@samba.org> +Date: Thu Jan 15 23:30:28 2015 +0100 + + By default refuse to create index entries with a path starting with .git/. + +diff --git a/dulwich/index.py b/dulwich/index.py +index f32d70f..3c7488e 100644 +--- a/dulwich/index.py ++++ b/dulwich/index.py +@@ -402,8 +402,14 @@ def index_entry_from_stat(stat_val, hex_sha, flags, mode=None): + stat_val.st_gid, stat_val.st_size, hex_sha, flags) + + ++def validate_path_default(path): ++ """Default path validator that just checks for .git/.""" ++ return not path.startswith(".git/") ++ ++ + def build_index_from_tree(prefix, index_path, object_store, tree_id, +- honor_filemode=True): ++ honor_filemode=True, ++ validate_path=validate_path_default): + """Generate and materialize index from a tree + + :param tree_id: Tree to materialize +@@ -412,6 +418,8 @@ def build_index_from_tree(prefix, index_path, object_store, tree_id, + :param object_store: Non-empty object store holding tree contents + :param honor_filemode: An optional flag to honor core.filemode setting in + config file, default is core.filemode=True, change executable bit ++ :param validate_path: Function to validate paths to check out; ++ default just refuses filenames starting with .git/. + + :note:: existing index is wiped and contents are not merged + in a working dir. Suiteable only for fresh clones. +@@ -420,6 +428,8 @@ def build_index_from_tree(prefix, index_path, object_store, tree_id, + index = Index(index_path) + + for entry in object_store.iter_tree_contents(tree_id): ++ if not validate_path(entry.path): ++ continue + full_path = os.path.join(prefix, entry.path) + + if not os.path.exists(os.path.dirname(full_path)): +diff --git a/dulwich/tests/test_index.py b/dulwich/tests/test_index.py +index 89b5d54..1852b9a 100644 +--- a/dulwich/tests/test_index.py ++++ b/dulwich/tests/test_index.py +@@ -284,6 +284,43 @@ class BuildIndexTests(TestCase): + # Verify no files + self.assertEqual(['.git'], os.listdir(repo.path)) + ++ def test_git_dir(self): ++ if os.name != 'posix': ++ self.skipTest("test depends on POSIX shell") ++ ++ repo_dir = tempfile.mkdtemp() ++ repo = Repo.init(repo_dir) ++ self.addCleanup(shutil.rmtree, repo_dir) ++ ++ # Populate repo ++ filea = Blob.from_string('file a') ++ filee = Blob.from_string('d') ++ ++ tree = Tree() ++ tree['.git/a'] = (stat.S_IFREG | 0o644, filea.id) ++ tree['c/e'] = (stat.S_IFREG | 0o644, filee.id) ++ ++ repo.object_store.add_objects([(o, None) ++ for o in [filea, filee, tree]]) ++ ++ build_index_from_tree(repo.path, repo.index_path(), ++ repo.object_store, tree.id) ++ ++ # Verify index entries ++ index = repo.open_index() ++ self.assertEqual(len(index), 1) ++ ++ # filea ++ apath = os.path.join(repo.path, '.git', 'a') ++ self.assertFalse(os.path.exists(apath)) ++ ++ # filee ++ epath = os.path.join(repo.path, 'c', 'e') ++ self.assertTrue(os.path.exists(epath)) ++ self.assertReasonableIndexEntry(index['c/e'], ++ stat.S_IFREG | 0o644, 1, filee.id) ++ self.assertFileContents(epath, 'd') ++ + def test_nonempty(self): + if os.name != 'posix': + self.skipTest("test depends on POSIX shell") diff -Nru dulwich-0.9.7/debian/patches/series dulwich-0.9.7/debian/patches/series --- dulwich-0.9.7/debian/patches/series 2014-07-09 01:02:32.000000000 +0000 +++ dulwich-0.9.7/debian/patches/series 2015-03-24 22:32:43.000000000 +0000 @@ -1 +1,3 @@ 01_setup_cfg +02_cve_2015-0838 +03_cve_2014-9706