diff --git a/qubesagent/firewall.py b/qubesagent/firewall.py index 66407a5ce..9ffc5f9d3 100755 --- a/qubesagent/firewall.py +++ b/qubesagent/firewall.py @@ -167,7 +167,18 @@ def update_dns_info(self, source, dns): self.qdb.rm('/dns/{}/'.format(source)) for host, hostaddrs in dns.items(): - self.qdb.write('/dns/{}/{}'.format(source, host), str(hostaddrs)) + path = '/dns/{}/{}'.format(source, host) + try: + self.qdb.write(path, str(hostaddrs)) + except Exception as err: + if len(path) > 64 and err.args == (0, 'Error'): + self.log.error(('Unable to add DNS information for {} ({})' + ' due to qubesdb path length limit').format( + host, source)) + self.log.error('See https://github.com/QubesOS/' + 'qubes-issues/issues/9084') + else: + raise def update_handled(self, addr): """ diff --git a/qubesagent/test_firewall.py b/qubesagent/test_firewall.py index 4938d4516..98106d30d 100644 --- a/qubesagent/test_firewall.py +++ b/qubesagent/test_firewall.py @@ -40,6 +40,8 @@ def rm(self, path): self.entries.pop(path) def write(self, path, val): + if len(path) > 64: + raise DummyQubesDBError(0, 'Error') self.entries[path] = val def multiread(self, prefix): @@ -65,6 +67,8 @@ def read_watch(self): except IndexError: return None +class DummyQubesDBError(Exception): + "Raised by QubesDB" class FirewallWorker(qubesagent.firewall.FirewallWorker): def __init__(self): @@ -159,6 +163,23 @@ def test_701_dns_info(self): self.obj.apply_rules('10.137.0.1', [{'action': 'drop'}]) self.assertIsNone(self.obj.qdb.read('/dns/10.137.0.1/ripe.net')) + def test_702_dns_info_qubesdb_path_length_crash(self): + self.obj.conntrack_get_connections = Mock(return_value=[]) + rules = [ + {'action': 'accept', 'proto': 'tcp', + 'dstports': '443-443', 'dsthost': 'www.google.com'}, + {'action': 'accept', 'proto': 'tcp', + 'dstports': '443-443', 'dsthost': 'prod-dynamite-prod-05-us-signaler-pa.clients6.google.com'}, + {'action': 'drop'}, + ] + self.obj.apply_rules('10.137.0.22', rules) + self.assertIsNotNone(self.obj.qdb.read('/dns/10.137.0.22/www.google.com')) + # Unfortunately, this is assertIsNone until the QubesDB path length limit is raised. + self.assertIsNone(self.obj.qdb.read('/dns/10.137.0.22/prod-dynamite-prod-05-us-signaler-pa.clients6.google.com')) + self.obj.apply_rules('10.137.0.22', [{'action': 'drop'}]) + self.assertIsNone(self.obj.qdb.read('/dns/10.137.0.22/www.google.com')) + self.assertIsNone(self.obj.qdb.read('/dns/10.137.0.22/prod-dynamite-prod-05-us-signaler-pa.clients6.google.com')) + class TestNftablesWorker(TestCase, WorkerCommon): def setUp(self): super(TestNftablesWorker, self).setUp()