summary refs log tree commit diff
path: root/support
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2017-04-13 13:09:38 +0200
committerFlorian Weimer <fweimer@redhat.com>2017-04-13 13:09:38 +0200
commite14a27723cc3a154d67f3f26e719d08c0ba9ad25 (patch)
treec4706acf27f91784a8b592772d03e0c8da0b4731 /support
parentc803cb9b24c6cea15698768e4301e963b98e742c (diff)
downloadglibc-e14a27723cc3a154d67f3f26e719d08c0ba9ad25.tar.gz
glibc-e14a27723cc3a154d67f3f26e719d08c0ba9ad25.tar.xz
glibc-e14a27723cc3a154d67f3f26e719d08c0ba9ad25.zip
resolv: Reduce EDNS payload size to 1200 bytes [BZ #21361]
This hardens the stub resolver against fragmentation-based attacks.
Diffstat (limited to 'support')
-rw-r--r--support/resolv_test.c56
-rw-r--r--support/resolv_test.h11
2 files changed, 64 insertions, 3 deletions
diff --git a/support/resolv_test.c b/support/resolv_test.c
index 49ed210191..5c5a46353d 100644
--- a/support/resolv_test.c
+++ b/support/resolv_test.c
@@ -429,6 +429,7 @@ struct query_info
   char qname[MAXDNAME];
   uint16_t qclass;
   uint16_t qtype;
+  struct resolv_edns_info edns;
 };
 
 /* Update *INFO from the specified DNS packet.  */
@@ -436,10 +437,26 @@ static void
 parse_query (struct query_info *info,
              const unsigned char *buffer, size_t length)
 {
-  if (length < 12)
+  HEADER hd;
+  _Static_assert (sizeof (hd) == 12, "DNS header size");
+  if (length < sizeof (hd))
     FAIL_EXIT1 ("malformed DNS query: too short: %zu bytes", length);
-
-  int ret = dn_expand (buffer, buffer + length, buffer + 12,
+  memcpy (&hd, buffer, sizeof (hd));
+
+  if (ntohs (hd.qdcount) != 1)
+    FAIL_EXIT1 ("malformed DNS query: wrong question count: %d",
+                (int) ntohs (hd.qdcount));
+  if (ntohs (hd.ancount) != 0)
+    FAIL_EXIT1 ("malformed DNS query: wrong answer count: %d",
+                (int) ntohs (hd.ancount));
+  if (ntohs (hd.nscount) != 0)
+    FAIL_EXIT1 ("malformed DNS query: wrong authority count: %d",
+                (int) ntohs (hd.nscount));
+  if (ntohs (hd.arcount) > 1)
+    FAIL_EXIT1 ("malformed DNS query: wrong additional count: %d",
+                (int) ntohs (hd.arcount));
+
+  int ret = dn_expand (buffer, buffer + length, buffer + sizeof (hd),
                        info->qname, sizeof (info->qname));
   if (ret < 0)
     FAIL_EXIT1 ("malformed DNS query: cannot uncompress QNAME");
@@ -457,6 +474,37 @@ parse_query (struct query_info *info,
   memcpy (&qtype_qclass, buffer + 12 + ret, sizeof (qtype_qclass));
   info->qclass = ntohs (qtype_qclass.qclass);
   info->qtype = ntohs (qtype_qclass.qtype);
+
+  memset (&info->edns, 0, sizeof (info->edns));
+  if (ntohs (hd.arcount) > 0)
+    {
+      /* Parse EDNS record.  */
+      struct __attribute__ ((packed, aligned (1)))
+      {
+        uint8_t root;
+        uint16_t rtype;
+        uint16_t payload;
+        uint8_t edns_extended_rcode;
+        uint8_t edns_version;
+        uint16_t flags;
+        uint16_t rdatalen;
+      } rr;
+      _Static_assert (sizeof (rr) == 11, "EDNS record size");
+
+      if (remaining < 4 + sizeof (rr))
+        FAIL_EXIT1 ("mailformed DNS query: no room for EDNS record");
+      memcpy (&rr, buffer + 12 + ret + 4, sizeof (rr));
+      if (rr.root != 0)
+        FAIL_EXIT1 ("malformed DNS query: invalid OPT RNAME: %d\n", rr.root);
+      if (rr.rtype != htons (41))
+        FAIL_EXIT1 ("malformed DNS query: invalid OPT type: %d\n",
+                    ntohs (rr.rtype));
+      info->edns.active = true;
+      info->edns.extended_rcode = rr.edns_extended_rcode;
+      info->edns.version = rr.edns_version;
+      info->edns.flags = ntohs (rr.flags);
+      info->edns.payload_size = ntohs (rr.payload);
+    }
 }
 
 
@@ -586,6 +634,7 @@ server_thread_udp_process_one (struct resolv_test *obj, int server_index)
       .query_length = length,
       .server_index = server_index,
       .tcp = false,
+      .edns = qinfo.edns,
     };
   struct resolv_response_builder *b = response_builder_allocate (query, length);
   obj->config.response_callback
@@ -821,6 +870,7 @@ server_thread_tcp_client (void *arg)
           .query_length = query_length,
           .server_index = closure->server_index,
           .tcp = true,
+          .edns = qinfo.edns,
         };
       struct resolv_response_builder *b = response_builder_allocate
         (query_buffer, query_length);
diff --git a/support/resolv_test.h b/support/resolv_test.h
index 7a9f1f7ae8..6498751569 100644
--- a/support/resolv_test.h
+++ b/support/resolv_test.h
@@ -25,6 +25,16 @@
 
 __BEGIN_DECLS
 
+/* Information about EDNS properties of a DNS query.  */
+struct resolv_edns_info
+{
+  bool active;
+  uint8_t extended_rcode;
+  uint8_t version;
+  uint16_t flags;
+  uint16_t payload_size;
+};
+
 /* This struct provides context information when the response callback
    specified in struct resolv_redirect_config is invoked. */
 struct resolv_response_context
@@ -33,6 +43,7 @@ struct resolv_response_context
   size_t query_length;
   int server_index;
   bool tcp;
+  struct resolv_edns_info edns;
 };
 
 /* This opaque struct is used to construct responses from within the