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

Reply via email to