djdelorie / rpms / glibc

Forked from rpms/glibc 3 years ago
Clone
8597553
commit c803cb9b24c6cea15698768e4301e963b98e742c
8597553
Author: Florian Weimer <fweimer@redhat.com>
8597553
Date:   Thu Apr 13 11:56:28 2017 +0200
8597553
8597553
    resolv: Support an exactly sized buffer in ns_name_pack [BZ #21359]
8597553
    
8597553
    This bug did not affect name resolution because those functions
8597553
    indirectly call ns_name_pack with a buffer which is always larger
8597553
    than the generated query packet, even in the case of the
8597553
    longest-possible domain name.
8597553
8597553
diff --git a/resolv/Makefile b/resolv/Makefile
8597553
index db287947a4f7e9a6..d41fd4603d7b7d77 100644
8597553
--- a/resolv/Makefile
8597553
+++ b/resolv/Makefile
8597553
@@ -44,6 +44,7 @@ tests += \
8597553
   tst-bug18665 \
8597553
   tst-bug18665-tcp \
8597553
   tst-ns_name \
8597553
+  tst-ns_name_compress \
8597553
   tst-res_hconf_reorder \
8597553
   tst-res_use_inet6 \
8597553
   tst-resolv-basic \
8597553
@@ -142,6 +143,7 @@ $(objpfx)tst-resolv-canonname: \
8597553
 
8597553
 $(objpfx)tst-ns_name: $(objpfx)libresolv.so
8597553
 $(objpfx)tst-ns_name.out: tst-ns_name.data
8597553
+$(objpfx)tst-ns_name_compress: $(objpfx)libresolv.so
8597553
 
8597553
 
8597553
 # This test case uses the deprecated RES_USE_INET6 resolver option.
8597553
diff --git a/resolv/ns_name.c b/resolv/ns_name.c
8597553
index 0d76fe53a6f2245f..08a75e2fe0b4edd6 100644
8597553
--- a/resolv/ns_name.c
8597553
+++ b/resolv/ns_name.c
8597553
@@ -475,7 +475,7 @@ ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
8597553
 			goto cleanup;
8597553
 		}
8597553
 		n = labellen(srcp);
8597553
-		if (dstp + 1 + n >= eob) {
8597553
+		if (n + 1 > eob - dstp) {
8597553
 			goto cleanup;
8597553
 		}
8597553
 		memcpy(dstp, srcp, n + 1);
8597553
diff --git a/resolv/tst-ns_name.c b/resolv/tst-ns_name.c
8597553
index 66d9a6666b2ebde2..65eea4c827780826 100644
8597553
--- a/resolv/tst-ns_name.c
8597553
+++ b/resolv/tst-ns_name.c
8597553
@@ -195,6 +195,7 @@ print_hex (const char *label, struct buffer buffer)
8597553
 static void
8597553
 run_test_case (struct test_case *t)
8597553
 {
8597553
+  /* Test ns_name_unpack.  */
8597553
   unsigned char *unpacked = xmalloc (NS_MAXCDNAME);
8597553
   int consumed = ns_name_unpack
8597553
     (t->input.data, t->input.data + t->input.length,
8597553
@@ -211,16 +212,19 @@ run_test_case (struct test_case *t)
8597553
     }
8597553
   if (consumed != -1)
8597553
     {
8597553
-      if (memcmp (unpacked, t->unpack_output.data, consumed) != 0)
8597553
+      if (memcmp (unpacked, t->unpack_output.data,
8597553
+                  t->unpack_output.length) != 0)
8597553
         {
8597553
           support_record_failure ();
8597553
           printf ("%s:%zu: error: wrong data from ns_name_unpack\n",
8597553
                   t->path, t->lineno);
8597553
           print_hex ("expected:", t->unpack_output);
8597553
-          print_hex ("actual:  ", (struct buffer) { unpacked, consumed });
8597553
+          print_hex ("actual:  ",
8597553
+                     (struct buffer) { unpacked, t->unpack_output.length });
8597553
           return;
8597553
         }
8597553
 
8597553
+      /* Test ns_name_ntop.  */
8597553
       char *text = xmalloc (NS_MAXDNAME);
8597553
       int ret = ns_name_ntop (unpacked, text, NS_MAXDNAME);
8597553
       if (ret != t->ntop_result)
8597553
@@ -243,6 +247,137 @@ run_test_case (struct test_case *t)
8597553
               printf ("  actual:   \"%s\"\n", text);
8597553
               return;
8597553
             }
8597553
+
8597553
+          /* Test ns_name_pton.  Unpacking does not check the
8597553
+             NS_MAXCDNAME limit, but packing does, so we need to
8597553
+             adjust the expected result.  */
8597553
+          int expected;
8597553
+          if (t->unpack_output.length > NS_MAXCDNAME)
8597553
+            expected = -1;
8597553
+          else if (strcmp (text, ".") == 0)
8597553
+            /* The root domain is fully qualified.  */
8597553
+            expected = 1;
8597553
+          else
8597553
+            /* The domain name is never fully qualified.  */
8597553
+            expected = 0;
8597553
+          unsigned char *repacked = xmalloc (NS_MAXCDNAME);
8597553
+          ret = ns_name_pton (text, repacked, NS_MAXCDNAME);
8597553
+          if (ret != expected)
8597553
+            {
8597553
+              support_record_failure ();
8597553
+              printf ("%s:%zu: error: wrong result from ns_name_pton\n"
8597553
+                      "  expected: %d\n"
8597553
+                      "  actual:   %d\n",
8597553
+                      t->path, t->lineno, expected, ret);
8597553
+              return;
8597553
+            }
8597553
+          if (ret >= 0
8597553
+              && memcmp (repacked, unpacked, t->unpack_output.length) != 0)
8597553
+            {
8597553
+              support_record_failure ();
8597553
+              printf ("%s:%zu: error: wrong data from ns_name_pton\n",
8597553
+                      t->path, t->lineno);
8597553
+              print_hex ("expected:", t->unpack_output);
8597553
+              print_hex ("actual:  ",
8597553
+                         (struct buffer) { repacked, t->unpack_output.length });
8597553
+              return;
8597553
+            }
8597553
+
8597553
+          /* Test ns_name_compress, no compression case.  */
8597553
+          if (t->unpack_output.length > NS_MAXCDNAME)
8597553
+            expected = -1;
8597553
+          else
8597553
+            expected = t->unpack_output.length;
8597553
+          memset (repacked, '$', NS_MAXCDNAME);
8597553
+          {
8597553
+            enum { ptr_count = 5 };
8597553
+            const unsigned char *dnptrs[ptr_count] = { repacked, };
8597553
+            ret = ns_name_compress (text, repacked, NS_MAXCDNAME,
8597553
+                                    dnptrs, dnptrs + ptr_count);
8597553
+            if (ret != expected)
8597553
+              {
8597553
+                support_record_failure ();
8597553
+                printf ("%s:%zu: error: wrong result from ns_name_compress\n"
8597553
+                        "  expected: %d\n"
8597553
+                        "  actual:   %d\n",
8597553
+                        t->path, t->lineno, expected, ret);
8597553
+                return;
8597553
+              }
8597553
+            if (ret < 0)
8597553
+              {
8597553
+                TEST_VERIFY (dnptrs[0] == repacked);
8597553
+                TEST_VERIFY (dnptrs[1] == NULL);
8597553
+              }
8597553
+            else
8597553
+              {
8597553
+                if (memcmp (repacked, unpacked, t->unpack_output.length) != 0)
8597553
+                  {
8597553
+                    support_record_failure ();
8597553
+                    printf ("%s:%zu: error: wrong data from ns_name_compress\n",
8597553
+                            t->path, t->lineno);
8597553
+                    print_hex ("expected:", t->unpack_output);
8597553
+                    print_hex ("actual:  ", (struct buffer) { repacked, ret });
8597553
+                    return;
8597553
+                  }
8597553
+                TEST_VERIFY (dnptrs[0] == repacked);
8597553
+                if (unpacked[0] == '\0')
8597553
+                  /* The root domain is not a compression target.  */
8597553
+                  TEST_VERIFY (dnptrs[1] == NULL);
8597553
+                else
8597553
+                  {
8597553
+                    TEST_VERIFY (dnptrs[1] == repacked);
8597553
+                    TEST_VERIFY (dnptrs[2] == NULL);
8597553
+                  }
8597553
+              }
8597553
+          }
8597553
+
8597553
+          /* Test ns_name_compress, full compression case.  Skip this
8597553
+             test for invalid names and the root domain.  */
8597553
+          if (expected >= 0 && unpacked[0] != '\0')
8597553
+            {
8597553
+              /* The destination buffer needs additional room for the
8597553
+                 offset, the initial name, and the compression
8597553
+                 reference.  */
8597553
+              enum { name_offset = 259 };
8597553
+              size_t target_offset = name_offset + t->unpack_output.length;
8597553
+              size_t repacked_size = target_offset + 2;
8597553
+              repacked = xrealloc (repacked, repacked_size);
8597553
+              memset (repacked, '@', repacked_size);
8597553
+              memcpy (repacked + name_offset,
8597553
+                      t->unpack_output.data, t->unpack_output.length);
8597553
+              enum { ptr_count = 5 };
8597553
+              const unsigned char *dnptrs[ptr_count]
8597553
+                = { repacked, repacked + name_offset, };
8597553
+              ret = ns_name_compress
8597553
+                (text, repacked + target_offset, NS_MAXCDNAME,
8597553
+                 dnptrs, dnptrs + ptr_count);
8597553
+              if (ret != 2)
8597553
+                {
8597553
+                  support_record_failure ();
8597553
+                  printf ("%s:%zu: error: wrong result from ns_name_compress"
8597553
+                          " (2)\n"
8597553
+                          "  expected: 2\n"
8597553
+                          "  actual:   %d\n",
8597553
+                          t->path, t->lineno, ret);
8597553
+                  return;
8597553
+                }
8597553
+              if (memcmp (repacked + target_offset, "\xc1\x03", 2) != 0)
8597553
+                {
8597553
+                  support_record_failure ();
8597553
+                  printf ("%s:%zu: error: wrong data from ns_name_compress"
8597553
+                          " (2)\n"
8597553
+                          "  expected: C103\n",
8597553
+                          t->path, t->lineno);
8597553
+                  print_hex ("actual:  ",
8597553
+                             (struct buffer) { repacked + target_offset, ret });
8597553
+                  return;
8597553
+                }
8597553
+              TEST_VERIFY (dnptrs[0] == repacked);
8597553
+              TEST_VERIFY (dnptrs[1] == repacked + name_offset);
8597553
+              TEST_VERIFY (dnptrs[2] == NULL);
8597553
+            }
8597553
+
8597553
+          free (repacked);
8597553
         }
8597553
       free (text);
8597553
     }
8597553
diff --git a/resolv/tst-ns_name_compress.c b/resolv/tst-ns_name_compress.c
8597553
new file mode 100644
8597553
index 0000000000000000..300ba0bf8f0b3ad1
8597553
--- /dev/null
8597553
+++ b/resolv/tst-ns_name_compress.c
8597553
@@ -0,0 +1,75 @@
8597553
+/* Test ns_name_compress corner cases.
8597553
+   Copyright (C) 2017 Free Software Foundation, Inc.
8597553
+   This file is part of the GNU C Library.
8597553
+
8597553
+   The GNU C Library is free software; you can redistribute it and/or
8597553
+   modify it under the terms of the GNU Lesser General Public
8597553
+   License as published by the Free Software Foundation; either
8597553
+   version 2.1 of the License, or (at your option) any later version.
8597553
+
8597553
+   The GNU C Library is distributed in the hope that it will be useful,
8597553
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
8597553
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
8597553
+   Lesser General Public License for more details.
8597553
+
8597553
+   You should have received a copy of the GNU Lesser General Public
8597553
+   License along with the GNU C Library; if not, see
8597553
+   <http://www.gnu.org/licenses/>.  */
8597553
+
8597553
+#include <resolv.h>
8597553
+#include <stdlib.h>
8597553
+#include <support/check.h>
8597553
+#include <support/support.h>
8597553
+
8597553
+/* Check that we can process names which fit into the destination
8597553
+   buffer exactly.  See bug 21359.  */
8597553
+static void
8597553
+test_exact_fit (const char *name, size_t length)
8597553
+{
8597553
+  unsigned char *buf = xmalloc (length + 1);
8597553
+  memset (buf, '$', length + 1);
8597553
+  enum { ptr_count = 5 };
8597553
+  const unsigned char *dnptrs[ptr_count] = { buf, };
8597553
+  int ret = ns_name_compress (name, buf, length,
8597553
+                          dnptrs, dnptrs + ptr_count);
8597553
+  if (ret < 0)
8597553
+    {
8597553
+      support_record_failure ();
8597553
+      printf ("error: ns_name_compress for %s/%zu failed\n", name, length);
8597553
+      return;
8597553
+    }
8597553
+  if ((size_t) ret != length)
8597553
+    {
8597553
+      support_record_failure ();
8597553
+      printf ("error: ns_name_compress for %s/%zu result mismatch: %d\n",
8597553
+              name, length, ret);
8597553
+    }
8597553
+  if (buf[length] != '$')
8597553
+    {
8597553
+      support_record_failure ();
8597553
+      printf ("error: ns_name_compress for %s/%zu padding write\n",
8597553
+              name, length);
8597553
+    }
8597553
+  free (buf);
8597553
+}
8597553
+
8597553
+static int
8597553
+do_test (void)
8597553
+{
8597553
+  test_exact_fit ("abc", 5);
8597553
+  test_exact_fit ("abc.", 5);
8597553
+  {
8597553
+    char long_name[]
8597553
+      = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
8597553
+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
8597553
+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
8597553
+      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.";
8597553
+    TEST_VERIFY (strlen (long_name) == NS_MAXCDNAME - 1);
8597553
+    test_exact_fit (long_name, NS_MAXCDNAME);
8597553
+    long_name[sizeof (long_name) - 1] = '\0';
8597553
+    test_exact_fit (long_name, NS_MAXCDNAME);
8597553
+  }
8597553
+  return 0;
8597553
+}
8597553
+
8597553
+#include <support/test-driver.c>
8597553
diff --git a/resolv/tst-resolv-basic.c b/resolv/tst-resolv-basic.c
8597553
index 94b1631121de01f3..f2b1fc7490430bce 100644
8597553
--- a/resolv/tst-resolv-basic.c
8597553
+++ b/resolv/tst-resolv-basic.c
8597553
@@ -25,6 +25,12 @@
8597553
 #include <support/resolv_test.h>
8597553
 #include <support/support.h>
8597553
 
8597553
+#define LONG_NAME                                                       \
8597553
+  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax."    \
8597553
+  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay."    \
8597553
+  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz."    \
8597553
+  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat"
8597553
+
8597553
 static void
8597553
 response (const struct resolv_response_context *ctx,
8597553
           struct resolv_response_builder *b,
8597553
@@ -43,13 +49,15 @@ response (const struct resolv_response_context *ctx,
8597553
     qname_compare = qname + 2;
8597553
   else
8597553
     qname_compare = qname;
8597553
-  enum {www, alias, nxdomain} requested_qname;
8597553
+  enum {www, alias, nxdomain, long_name} requested_qname;
8597553
   if (strcmp (qname_compare, "www.example") == 0)
8597553
     requested_qname = www;
8597553
   else if (strcmp (qname_compare, "alias.example") == 0)
8597553
     requested_qname = alias;
8597553
   else if (strcmp (qname_compare, "nxdomain.example") == 0)
8597553
     requested_qname = nxdomain;
8597553
+  else if (strcmp (qname_compare, LONG_NAME) == 0)
8597553
+    requested_qname = long_name;
8597553
   else
8597553
     {
8597553
       support_record_failure ();
8597553
@@ -69,6 +77,7 @@ response (const struct resolv_response_context *ctx,
8597553
   switch (requested_qname)
8597553
     {
8597553
     case www:
8597553
+    case long_name:
8597553
       resolv_response_open_record (b, qname, qclass, qtype, 0);
8597553
       break;
8597553
     case alias:
8597553
@@ -209,6 +218,10 @@ do_test (void)
8597553
            "name: www.example\n"
8597553
            "alias: alias.example\n"
8597553
            "address: 2001:db8::2\n");
8597553
+  check_h (LONG_NAME, AF_INET,
8597553
+           "name: " LONG_NAME "\n"
8597553
+           "address: 192.0.2.20\n");
8597553
+
8597553
   check_ai ("www.example", "80", AF_UNSPEC,
8597553
             "address: STREAM/TCP 192.0.2.17 80\n"
8597553
             "address: DGRAM/UDP 192.0.2.17 80\n"
8597553
@@ -223,6 +236,13 @@ do_test (void)
8597553
             "address: STREAM/TCP 2001:db8::2 80\n"
8597553
             "address: DGRAM/UDP 2001:db8::2 80\n"
8597553
             "address: RAW/IP 2001:db8::2 80\n");
8597553
+  check_ai (LONG_NAME, "80", AF_UNSPEC,
8597553
+            "address: STREAM/TCP 192.0.2.20 80\n"
8597553
+            "address: DGRAM/UDP 192.0.2.20 80\n"
8597553
+            "address: RAW/IP 192.0.2.20 80\n"
8597553
+            "address: STREAM/TCP 2001:db8::4 80\n"
8597553
+            "address: DGRAM/UDP 2001:db8::4 80\n"
8597553
+            "address: RAW/IP 2001:db8::4 80\n");
8597553
   check_ai ("www.example", "80", AF_INET,
8597553
             "address: STREAM/TCP 192.0.2.17 80\n"
8597553
             "address: DGRAM/UDP 192.0.2.17 80\n"
8597553
@@ -231,6 +251,10 @@ do_test (void)
8597553
             "address: STREAM/TCP 192.0.2.18 80\n"
8597553
             "address: DGRAM/UDP 192.0.2.18 80\n"
8597553
             "address: RAW/IP 192.0.2.18 80\n");
8597553
+  check_ai (LONG_NAME, "80", AF_INET,
8597553
+            "address: STREAM/TCP 192.0.2.20 80\n"
8597553
+            "address: DGRAM/UDP 192.0.2.20 80\n"
8597553
+            "address: RAW/IP 192.0.2.20 80\n");
8597553
   check_ai ("www.example", "80", AF_INET6,
8597553
             "address: STREAM/TCP 2001:db8::1 80\n"
8597553
             "address: DGRAM/UDP 2001:db8::1 80\n"
8597553
@@ -239,6 +263,10 @@ do_test (void)
8597553
             "address: STREAM/TCP 2001:db8::2 80\n"
8597553
             "address: DGRAM/UDP 2001:db8::2 80\n"
8597553
             "address: RAW/IP 2001:db8::2 80\n");
8597553
+  check_ai (LONG_NAME, "80", AF_INET6,
8597553
+            "address: STREAM/TCP 2001:db8::4 80\n"
8597553
+            "address: DGRAM/UDP 2001:db8::4 80\n"
8597553
+            "address: RAW/IP 2001:db8::4 80\n");
8597553
 
8597553
   check_h ("t.www.example", AF_INET,
8597553
            "name: t.www.example\n"