Browse Source

[master] [1299] (not directly related fix): be sure to check the AA flag of SOA response

JINMEI Tatuya 13 years ago
parent
commit
8376919647
3 changed files with 26 additions and 11 deletions
  1. 10 3
      src/bin/xfrin/tests/xfrin_test.py
  2. 15 7
      src/bin/xfrin/xfrin.py.in
  3. 1 1
      src/bin/xfrin/xfrin_messages.mes

+ 10 - 3
src/bin/xfrin/tests/xfrin_test.py

@@ -36,11 +36,9 @@ TEST_RRCLASS_STR = 'IN'
 TEST_DB_FILE = 'db_file'
 TEST_MASTER_IPV4_ADDRESS = '127.0.0.1'
 TEST_MASTER_IPV4_ADDRINFO = (socket.AF_INET, socket.SOCK_STREAM,
-                             socket.IPPROTO_TCP, '',
                              (TEST_MASTER_IPV4_ADDRESS, 53))
 TEST_MASTER_IPV6_ADDRESS = '::1'
 TEST_MASTER_IPV6_ADDRINFO = (socket.AF_INET6, socket.SOCK_STREAM,
-                             socket.IPPROTO_TCP, '',
                              (TEST_MASTER_IPV6_ADDRESS, 53))
 
 TESTDATA_SRCDIR = os.getenv("TESTDATASRCDIR")
@@ -274,7 +272,7 @@ class MockXfrinConnection(XfrinConnection):
                 self.response_generator()
         return len(data)
 
-    def create_response_data(self, response=True, bad_qid=False,
+    def create_response_data(self, response=True, auth=True, bad_qid=False,
                              rcode=Rcode.NOERROR(),
                              questions=default_questions,
                              answers=default_answers,
@@ -288,6 +286,8 @@ class MockXfrinConnection(XfrinConnection):
         resp.set_rcode(rcode)
         if response:
             resp.set_header_flag(Message.HEADERFLAG_QR)
+        if auth:
+            resp.set_header_flag(Message.HEADERFLAG_AA)
         [resp.add_question(q) for q in questions]
         [resp.add_rrset(Message.SECTION_ANSWER, a) for a in answers]
 
@@ -599,6 +599,7 @@ class TestXfrinConnection(unittest.TestCase):
             'questions': [example_soa_question],
             'bad_qid': False,
             'response': True,
+            'auth': True,
             'rcode': Rcode.NOERROR(),
             'tsig': False,
             'axfr_after_soa': self._create_normal_response_data
@@ -656,6 +657,7 @@ class TestXfrinConnection(unittest.TestCase):
         self.conn.reply_data = self.conn.create_response_data(
             bad_qid=self.soa_response_params['bad_qid'],
             response=self.soa_response_params['response'],
+            auth=self.soa_response_params['auth'],
             rcode=self.soa_response_params['rcode'],
             questions=self.soa_response_params['questions'],
             tsig_ctx=verify_ctx)
@@ -941,6 +943,11 @@ class TestAXFR(TestXfrinConnection):
         self.conn.response_generator = self._create_soa_response_data
         self.assertRaises(XfrinException, self.conn._check_soa_serial)
 
+    def test_soacheck_notauth(self):
+        self.soa_response_params['auth'] = False
+        self.conn.response_generator = self._create_soa_response_data
+        self.assertRaises(XfrinException, self.conn._check_soa_serial)
+
     def test_soacheck_with_tsig(self):
         # Use a mock tsig context emulating a validly signed response
         self.conn._tsig_key = TSIG_KEY

+ 15 - 7
src/bin/xfrin/xfrin.py.in

@@ -618,14 +618,19 @@ class XfrinConnection(asyncore.dispatcher):
         msg.set_opcode(Opcode.QUERY())
         msg.set_rcode(Rcode.NOERROR())
         msg.add_question(Question(self._zone_name, self._rrclass, query_type))
+
+        # Remember our serial, if known
+        self._request_serial = get_soa_serial(self._zone_soa.get_rdata()[0]) \
+            if self._zone_soa is not None else None
+
+        # Set the authority section with our SOA for IXFR
         if query_type == RRType.IXFR():
             if self._zone_soa is None:
                 # (incremental) IXFR doesn't work without known SOA
                 raise XfrinException('Failed to create IXFR query due to no ' +
                                      'SOA for ' + self.zone_str())
             msg.add_rrset(Message.SECTION_AUTHORITY, self._zone_soa)
-            self._request_serial = \
-                get_soa_serial(self._zone_soa.get_rdata()[0])
+
         return msg
 
     def _send_data(self, data):
@@ -707,9 +712,11 @@ class XfrinConnection(asyncore.dispatcher):
         # TSIG related checks, including an unexpected signed response
         self._check_response_tsig(msg, soa_response)
 
-        # perform some minimal level validation.  It's an open issue how
-        # strict we should be (see the comment in _check_response_header())
+        # Validate the header.  Unlike AXFR/IXFR, we should be more strict
+        # for SOA queries and check the AA flag, too.
         self._check_response_header(msg)
+        if not msg.get_header_flag(Message.HEADERFLAG_AA):
+            raise XfrinException('non-authoritative answer to SOA query')
 
         # TODO, need select soa record from data source then compare the two
         # serial, current just return OK, since this function hasn't been used
@@ -739,7 +746,8 @@ class XfrinConnection(asyncore.dispatcher):
 
         except (XfrinException, XfrinProtocolError) as e:
             logger.error(XFRIN_XFR_TRANSFER_FAILURE, request_str,
-                         self.zone_str(), str(e))
+                         self.zone_str(),
+                         format_addrinfo(self._master_addrinfo), str(e))
             ret = XFRIN_FAIL
         except Exception as e:
             # Catching all possible exceptions like this is generally not a
@@ -828,8 +836,8 @@ class XfrinConnection(asyncore.dispatcher):
         return False
 
 def __process_xfrin(server, zone_name, rrclass, db_file,
-                  shutdown_event, master_addrinfo, check_soa, tsig_key,
-                  request_type, conn_class):
+                    shutdown_event, master_addrinfo, check_soa, tsig_key,
+                    request_type, conn_class):
     conn = None
     exception = None
     ret = XFRIN_FAIL

+ 1 - 1
src/bin/xfrin/xfrin_messages.mes

@@ -50,7 +50,7 @@ The XFR transfer for the given zone has failed due to a problem outside
 of the xfrin module.  Possible reasons are a broken DNS message or failure
 in database connection.  The error is shown in the log message.
 
-% XFRIN_XFR_TRANSFER_FAILURE %1 transfer of zone %2 failed: %3
+% XFRIN_XFR_TRANSFER_FAILURE %1 transfer of zone %2 with %3 failed: %4
 The XFR transfer for the given zone has failed due to a protocol error.
 The error is shown in the log message.