Control: tags 870187 + pending

Dear maintainer,

I've prepared an NMU for supervisor (versioned as 3.3.1-1.1) and
uploaded it to DELAYED/2. Please feel free to tell me if I
should delay it longer.

Regards,
Salvatore
diff -Nru supervisor-3.3.1/debian/changelog supervisor-3.3.1/debian/changelog
--- supervisor-3.3.1/debian/changelog	2016-09-07 15:47:05.000000000 +0200
+++ supervisor-3.3.1/debian/changelog	2017-08-12 10:55:14.000000000 +0200
@@ -1,3 +1,11 @@
+supervisor (3.3.1-1.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * Disable object traversal in XML-RPC dispatch (CVE-2017-11610)
+    (Closes: #870187)
+
+ -- Salvatore Bonaccorso <car...@debian.org>  Sat, 12 Aug 2017 10:55:14 +0200
+
 supervisor (3.3.1-1) unstable; urgency=medium
 
   * New upstream release.
diff -Nru supervisor-3.3.1/debian/patches/0001-Fix-CVE-2017-11610-by-disabling-object-traversal-in-.patch supervisor-3.3.1/debian/patches/0001-Fix-CVE-2017-11610-by-disabling-object-traversal-in-.patch
--- supervisor-3.3.1/debian/patches/0001-Fix-CVE-2017-11610-by-disabling-object-traversal-in-.patch	1970-01-01 01:00:00.000000000 +0100
+++ supervisor-3.3.1/debian/patches/0001-Fix-CVE-2017-11610-by-disabling-object-traversal-in-.patch	2017-08-12 10:55:14.000000000 +0200
@@ -0,0 +1,166 @@
+From: Mike Naberezny <m...@naberezny.com>
+Date: Mon, 24 Jul 2017 13:02:38 -0600
+Subject: Fix CVE-2017-11610 by disabling object traversal in XML-RPC dispatch
+Origin: https://github.com/Supervisor/supervisor/commit/058f46141e346b18dee0497ba11203cb81ecb19e
+Bug: https://github.com/Supervisor/supervisor/issues/964
+Bug-Debian: https://bugs.debian.org/870187
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2017-11610
+
+---
+ supervisor/tests/test_xmlrpc.py | 89 ++++++++++++++++++++++++++++++++++-------
+ supervisor/xmlrpc.py            | 29 +++++++++-----
+ 2 files changed, 94 insertions(+), 24 deletions(-)
+
+diff --git a/supervisor/tests/test_xmlrpc.py b/supervisor/tests/test_xmlrpc.py
+index 22042ad..856b5f9 100644
+--- a/supervisor/tests/test_xmlrpc.py
++++ b/supervisor/tests/test_xmlrpc.py
+@@ -269,28 +269,89 @@ class XMLRPCHandlerTests(unittest.TestCase):
+         self.assertEqual(request._error, 500)
+ 
+ class TraverseTests(unittest.TestCase):
+-    def test_underscore(self):
++    def test_security_disallows_underscore_methods(self):
+         from supervisor import xmlrpc
+-        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, None, '_', None)
++        class Root:
++            pass
++        class A:
++            def _danger(self):
++                return True
++        root = Root()
++        root.a = A()
++        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse,
++            root, 'a._danger', [])
++
++    def test_security_disallows_object_traversal(self):
++        from supervisor import xmlrpc
++        class Root:
++            pass
++        class A:
++            pass
++        class B:
++            def danger(self):
++                return True
++        root = Root()
++        root.a = A()
++        root.a.b = B()
++        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse,
++            root, 'a.b.danger', [])
++
++    def test_namespace_name_not_found(self):
++        from supervisor import xmlrpc
++        class Root:
++            pass
++        root = Root()
++        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse,
++            root, 'notfound.hello', None)
+ 
+-    def test_notfound(self):
++    def test_method_name_not_found(self):
+         from supervisor import xmlrpc
+-        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, None, 'foo', None)
++        class Root:
++            pass
++        class A:
++            pass
++        root = Root()
++        root.a = A()
++        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse,
++            root, 'a.notfound', [])
+ 
+-    def test_badparams(self):
++    def test_method_name_exists_but_is_not_a_method(self):
+         from supervisor import xmlrpc
+-        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, self,
+-                          'test_badparams', (1, 2, 3))
++        class Root:
++            pass
++        class A:
++            pass
++        class B:
++            pass
++        root = Root()
++        root.a = A()
++        root.a.b = B()
++        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse,
++            root, 'a.b', [])  # b is not a method
++
++    def test_bad_params(self):
++        from supervisor import xmlrpc
++        class Root:
++            pass
++        class A:
++            def hello(self, name):
++                return "Hello %s" % name
++        root = Root()
++        root.a = A()
++        self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse,
++            root, 'a.hello', ["there", "extra"])  # too many params
+ 
+     def test_success(self):
+         from supervisor import xmlrpc
+-        L = []
+-        class Dummy:
+-            def foo(self, a):
+-                L.append(a)
+-        dummy = Dummy()
+-        xmlrpc.traverse(dummy, 'foo', [1])
+-        self.assertEqual(L, [1])
++        class Root:
++            pass
++        class A:
++            def hello(self, name):
++                return "Hello %s" % name
++        root = Root()
++        root.a = A()
++        result = xmlrpc.traverse(root, 'a.hello', ["there"])
++        self.assertEqual(result, "Hello there")
+ 
+ class SupervisorTransportTests(unittest.TestCase):
+     def _getTargetClass(self):
+diff --git a/supervisor/xmlrpc.py b/supervisor/xmlrpc.py
+index 339e19e..9a37c73 100644
+--- a/supervisor/xmlrpc.py
++++ b/supervisor/xmlrpc.py
+@@ -428,18 +428,27 @@ class supervisor_xmlrpc_handler(xmlrpc_handler):
+         return traverse(self.rpcinterface, method, params)
+ 
+ def traverse(ob, method, params):
+-    path = method.split('.')
+-    for name in path:
+-        if name.startswith('_'):
+-            # security (don't allow things that start with an underscore to
+-            # be called remotely)
+-            raise RPCError(Faults.UNKNOWN_METHOD)
+-        ob = getattr(ob, name, None)
+-        if ob is None:
+-            raise RPCError(Faults.UNKNOWN_METHOD)
++    dotted_parts = method.split('.')
++    # security (CVE-2017-11610, don't allow object traversal)
++    if len(dotted_parts) != 2:
++        raise RPCError(Faults.UNKNOWN_METHOD)
++    namespace, method = dotted_parts
++
++    # security (don't allow methods that start with an underscore to
++    # be called remotely)
++    if method.startswith('_'):
++        raise RPCError(Faults.UNKNOWN_METHOD)
++
++    rpcinterface = getattr(ob, namespace, None)
++    if rpcinterface is None:
++        raise RPCError(Faults.UNKNOWN_METHOD)
++
++    func = getattr(rpcinterface, method, None)
++    if not isinstance(func, types.MethodType):
++        raise RPCError(Faults.UNKNOWN_METHOD)
+ 
+     try:
+-        return ob(*params)
++        return func(*params)
+     except TypeError:
+         raise RPCError(Faults.INCORRECT_PARAMETERS)
+ 
+-- 
+2.14.1
+
diff -Nru supervisor-3.3.1/debian/patches/series supervisor-3.3.1/debian/patches/series
--- supervisor-3.3.1/debian/patches/series	1970-01-01 01:00:00.000000000 +0100
+++ supervisor-3.3.1/debian/patches/series	2017-08-12 10:55:14.000000000 +0200
@@ -0,0 +1 @@
+0001-Fix-CVE-2017-11610-by-disabling-object-traversal-in-.patch

Reply via email to