CVE-2019-15149 (published 2019-08-18T20:15Z)

core.py in Mitogen before 0.2.8 has a typo that drops the unidirectional-routing protection mechanism in the case of a child that is initiated by another child. The Ansible extension is unaffected. NOTE: the vendor disputes this issue because it is exploitable only in conjunction with hypothetical other factors, i.e., an affected use case within a library caller, and a bug in the message receiver policy code that led to reliance on this extra protection mechanism

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-254
cwe_name: 7PK - Security Features
description: Software security is not security software. Here we're concerned with topics like authentication, access control, confidentiality, cryptography, and privilege management.

Fix information:
repo_url: https://github.com/dw/mitogen
commit_details: repo_url: https://github.com/dw/mitogen
author: David Wilson
author_date: 2019-08-08 23:03:43+01:00
msg: [security] core: undirectional routing wasn't respected in some cases

When creating a context using Router.method(via=somechild),
unidirectional mode was set on the new child correctly, however if the
child were to call Router.method(), due to a typing mistake the new
child would start without it.

This doesn't impact the Ansible extension, as only forked tasks are
started directly by children, and they are not responsible for routing
messages.

Add test so it can't happen again.
repository: repo_url: https://github.com/dw/mitogen
repo_name: mitogen-hq/mitogen
description: Distributed self-replicating programs in Python
repo_language: Python
owner: dw
file_changes: filename: core.py
change_type: ModificationType.MODIFY
diff: @@ -3623,7 +3623,7 @@ class ExternalContext(object):
         self.broker = Broker(activate_compat=False)
         self.router = Router(self.broker)
         self.router.debug = self.config.get('debug', False)
-        self.router.undirectional = self.config['unidirectional']
+        self.router.unidirectional = self.config['unidirectional']
         self.router.add_handler(
             fn=self._on_shutdown_msg,
             handle=SHUTDOWN,

programming_language: Python
filename: router_test.py
change_type: ModificationType.MODIFY
diff: @@ -1,9 +1,11 @@
+import sys
 import time
 import zlib
 
 import unittest2
 
 import testlib
+import mitogen.core
 import mitogen.master
 import mitogen.parent
 import mitogen.utils
@@ -341,22 +343,42 @@ class NoRouteTest(testlib.RouterMixin, testlib.TestCase):
         ))
 
 
+def test_siblings_cant_talk(router):
+    l1 = router.local()
+    l2 = router.local()
+    logs = testlib.LogCapturer()
+    logs.start()
+
+    try:
+        l2.call(ping_context, l1)
+    except mitogen.core.CallError:
+        e = sys.exc_info()[1]
+
+    msg = mitogen.core.Router.unidirectional_msg % (
+        l2.context_id,
+        l1.context_id,
+    )
+    assert msg in str(e)
+    assert 'routing mode prevents forward of ' in logs.stop()
+
+
+@mitogen.core.takes_econtext
+def test_siblings_cant_talk_remote(econtext):
+    mitogen.parent.upgrade_router(econtext)
+    test_siblings_cant_talk(econtext.router)
+
+
 class UnidirectionalTest(testlib.RouterMixin, testlib.TestCase):
-    def test_siblings_cant_talk(self):
+    def test_siblings_cant_talk_master(self):
         self.router.unidirectional = True
-        l1 = self.router.local()
-        l2 = self.router.local()
-        logs = testlib.LogCapturer()
-        logs.start()
-        e = self.assertRaises(mitogen.core.CallError,
-                              lambda: l2.call(ping_context, l1))
+        test_siblings_cant_talk(self.router)
 
-        msg = self.router.unidirectional_msg % (
-            l2.context_id,
-            l1.context_id,
-        )
-        self.assertTrue(msg in str(e))
-        self.assertTrue('routing mode prevents forward of ' in logs.stop())
+    def test_siblings_cant_talk_parent(self):
+        # ensure 'unidirectional' attribute is respected for contexts started
+        # by children.
+        self.router.unidirectional = True
+        parent = self.router.local()
+        parent.call(test_siblings_cant_talk_remote)
 
     def test_auth_id_can_talk(self):
         self.router.unidirectional = True

programming_language: Python