]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - sysdeps/posix/getaddrinfo.c
CVE-2016-10739: getaddrinfo: Fully parse IPv4 address strings [BZ #20018]
[thirdparty/glibc.git] / sysdeps / posix / getaddrinfo.c
index 0e79ac2d914afff4976176f15c88a66963544514..aa054b620f2af75efcabd14cf96e12ccb3063451 100644 (file)
@@ -1,5 +1,5 @@
 /* Host and service name lookups using Name Service Switch modules.
-   Copyright (C) 1996-2017 Free Software Foundation, Inc.
+   Copyright (C) 1996-2019 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -85,11 +85,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <scratch_buffer.h>
 #include <inet/net-internal.h>
 
-#ifdef HAVE_LIBIDN
-extern int __idna_to_ascii_lz (const char *input, char **output, int flags);
-extern int __idna_to_unicode_lzlz (const char *input, char **output,
-                                  int flags);
-# include <libidn/idna.h>
+/* Former AI_IDN_ALLOW_UNASSIGNED and AI_IDN_USE_STD3_ASCII_RULES
+   flags, now ignored.  */
+#define DEPRECATED_AI_IDN 0x300
+
+#if IS_IN (libc)
+# define feof_unlocked(fp) __feof_unlocked (fp)
 #endif
 
 struct gaih_service
@@ -241,47 +242,43 @@ convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
 
 #define gethosts(_family, _type) \
  {                                                                           \
-  int herrno;                                                                \
   struct hostent th;                                                         \
-  struct hostent *h;                                                         \
   char *localcanon = NULL;                                                   \
   no_data = 0;                                                               \
-  while (1) {                                                                \
-    status = DL_CALL_FCT (fct, (name, _family, &th,                          \
-                               tmpbuf->data, tmpbuf->length,                 \
-                               &errno, &herrno, NULL, &localcanon));         \
-    if (errno != ERANGE || herrno != NETDB_INTERNAL)                         \
-      break;                                                                 \
-    if (!scratch_buffer_grow (tmpbuf))                                       \
-      {                                                                              \
-       __resolv_context_enable_inet6 (res_ctx, res_enable_inet6);            \
-       __resolv_context_put (res_ctx);                                       \
-       result = -EAI_MEMORY;                                                 \
-       goto free_and_return;                                                 \
-      }                                                                              \
-  }                                                                          \
-  if (status == NSS_STATUS_SUCCESS && errno == 0)                            \
-    h = &th;                                                                 \
-  else                                                                       \
-    h = NULL;                                                                \
-  if (errno != 0)                                                            \
+  while (1)                                                                  \
+    {                                                                        \
+      status = DL_CALL_FCT (fct, (name, _family, &th,                        \
+                                 tmpbuf->data, tmpbuf->length,               \
+                                 &errno, &h_errno, NULL, &localcanon));      \
+      if (status != NSS_STATUS_TRYAGAIN || h_errno != NETDB_INTERNAL         \
+         || errno != ERANGE)                                                 \
+       break;                                                                \
+      if (!scratch_buffer_grow (tmpbuf))                                     \
+       {                                                                     \
+         __resolv_context_enable_inet6 (res_ctx, res_enable_inet6);          \
+         __resolv_context_put (res_ctx);                                     \
+         result = -EAI_MEMORY;                                               \
+         goto free_and_return;                                               \
+       }                                                                     \
+    }                                                                        \
+  if (status == NSS_STATUS_NOTFOUND                                          \
+      || status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)              \
     {                                                                        \
-      if (herrno == NETDB_INTERNAL)                                          \
+      if (h_errno == NETDB_INTERNAL)                                         \
        {                                                                     \
-         __set_h_errno (herrno);                                             \
          __resolv_context_enable_inet6 (res_ctx, res_enable_inet6);          \
          __resolv_context_put (res_ctx);                                     \
          result = -EAI_SYSTEM;                                               \
          goto free_and_return;                                               \
        }                                                                     \
-      if (herrno == TRY_AGAIN)                                               \
+      if (h_errno == TRY_AGAIN)                                                      \
        no_data = EAI_AGAIN;                                                  \
       else                                                                   \
-       no_data = herrno == NO_DATA;                                          \
+       no_data = h_errno == NO_DATA;                                         \
     }                                                                        \
-  else if (h != NULL)                                                        \
+  else if (status == NSS_STATUS_SUCCESS)                                     \
     {                                                                        \
-      if (!convert_hostent_to_gaih_addrtuple (req, _family,h, &addrmem))      \
+      if (!convert_hostent_to_gaih_addrtuple (req, _family, &th, &addrmem))   \
        {                                                                     \
          __resolv_context_enable_inet6 (res_ctx, res_enable_inet6);          \
          __resolv_context_put (res_ctx);                                     \
@@ -332,9 +329,8 @@ getcanonname (service_user *nip, struct gaih_addrtuple *at, const char *name)
   if (cfct != NULL)
     {
       char buf[256];
-      int herrno;
       if (DL_CALL_FCT (cfct, (at->name ?: name, buf, sizeof (buf),
-                             &s, &errno, &herrno)) != NSS_STATUS_SUCCESS)
+                             &s, &errno, &h_errno)) != NSS_STATUS_SUCCESS)
        /* If the canonical name cannot be determined, use the passed
           string.  */
        s = (char *) name;
@@ -482,37 +478,17 @@ gaih_inet (const char *name, const struct gaih_service *service,
       at->scopeid = 0;
       at->next = NULL;
 
-#ifdef HAVE_LIBIDN
       if (req->ai_flags & AI_IDN)
        {
-         int idn_flags = 0;
-         if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
-           idn_flags |= IDNA_ALLOW_UNASSIGNED;
-         if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
-           idn_flags |= IDNA_USE_STD3_ASCII_RULES;
-
-         char *p = NULL;
-         int rc = __idna_to_ascii_lz (name, &p, idn_flags);
-         if (rc != IDNA_SUCCESS)
-           {
-             /* No need to jump to free_and_return here.  */
-             if (rc == IDNA_MALLOC_ERROR)
-               return -EAI_MEMORY;
-             if (rc == IDNA_DLOPEN_ERROR)
-               return -EAI_SYSTEM;
-             return -EAI_IDN_ENCODE;
-           }
-         /* In case the output string is the same as the input string
-            no new string has been allocated.  */
-         if (p != name)
-           {
-             name = p;
-             malloc_name = true;
-           }
+         char *out;
+         result = __idna_to_dns_encoding (name, &out);
+         if (result != 0)
+           return -result;
+         name = out;
+         malloc_name = true;
        }
-#endif
 
-      if (__inet_aton (name, (struct in_addr *) at->addr) != 0)
+      if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
        {
          if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
            at->family = AF_INET;
@@ -593,14 +569,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
              int rc;
              struct hostent th;
              struct hostent *h;
-             int herrno;
 
              while (1)
                {
                  rc = __gethostbyname2_r (name, AF_INET, &th,
                                           tmpbuf->data, tmpbuf->length,
-                                          &h, &herrno);
-                 if (rc != ERANGE || herrno != NETDB_INTERNAL)
+                                          &h, &h_errno);
+                 if (rc != ERANGE || h_errno != NETDB_INTERNAL)
                    break;
                  if (!scratch_buffer_grow (tmpbuf))
                    {
@@ -622,15 +597,20 @@ gaih_inet (const char *name, const struct gaih_service *service,
                        }
                      *pat = addrmem;
                    }
+                 else
+                   {
+                     if (h_errno == NO_DATA)
+                       result = -EAI_NODATA;
+                     else
+                       result = -EAI_NONAME;
+                     goto free_and_return;
+                   }
                }
              else
                {
-                 if (herrno == NETDB_INTERNAL)
-                   {
-                     __set_h_errno (herrno);
-                     result = -EAI_SYSTEM;
-                   }
-                 else if (herrno == TRY_AGAIN)
+                 if (h_errno == NETDB_INTERNAL)
+                   result = -EAI_SYSTEM;
+                 else if (h_errno == TRY_AGAIN)
                    result = -EAI_AGAIN;
                  else
                    /* We made requests but they turned out no data.
@@ -653,8 +633,7 @@ gaih_inet (const char *name, const struct gaih_service *service,
            {
              /* Try to use nscd.  */
              struct nscd_ai_result *air = NULL;
-             int herrno;
-             int err = __nscd_getai (name, &air, &herrno);
+             int err = __nscd_getai (name, &air, &h_errno);
              if (air != NULL)
                {
                  /* Transform into gaih_addrtuple list.  */
@@ -745,9 +724,9 @@ gaih_inet (const char *name, const struct gaih_service *service,
                goto free_and_return;
              else if (__nss_not_use_nscd_hosts == 0)
                {
-                 if (herrno == NETDB_INTERNAL && errno == ENOMEM)
+                 if (h_errno == NETDB_INTERNAL && errno == ENOMEM)
                    result = -EAI_MEMORY;
-                 else if (herrno == TRY_AGAIN)
+                 else if (h_errno == TRY_AGAIN)
                    result = -EAI_AGAIN;
                  else
                    result = -EAI_SYSTEM;
@@ -786,23 +765,21 @@ gaih_inet (const char *name, const struct gaih_service *service,
 
              if (fct4 != NULL)
                {
-                 int herrno;
-
                  while (1)
                    {
                      status = DL_CALL_FCT (fct4, (name, pat,
                                                   tmpbuf->data, tmpbuf->length,
-                                                  &errno, &herrno,
+                                                  &errno, &h_errno,
                                                   NULL));
                      if (status == NSS_STATUS_SUCCESS)
                        break;
                      if (status != NSS_STATUS_TRYAGAIN
-                         || errno != ERANGE || herrno != NETDB_INTERNAL)
+                         || errno != ERANGE || h_errno != NETDB_INTERNAL)
                        {
-                         if (herrno == TRY_AGAIN)
+                         if (h_errno == TRY_AGAIN)
                            no_data = EAI_AGAIN;
                          else
-                           no_data = herrno == NO_DATA;
+                           no_data = h_errno == NO_DATA;
                          break;
                        }
 
@@ -932,13 +909,17 @@ gaih_inet (const char *name, const struct gaih_service *service,
                    }
                  else
                    {
+                     /* Could not locate any of the lookup functions.
+                        The NSS lookup code does not consistently set
+                        errno, so we need to supply our own error
+                        code here.  The root cause could either be a
+                        resource allocation failure, or a missing
+                        service function in the DSO (so it should not
+                        be listed in /etc/nsswitch.conf).  Assume the
+                        former, and return EBUSY.  */
                      status = NSS_STATUS_UNAVAIL;
-                     /* Could not load any of the lookup functions.  Indicate
-                        an internal error if the failure was due to a system
-                        error other than the file not being found.  We use the
-                        errno from the last failed callback.  */
-                     if (errno != 0 && errno != ENOENT)
-                       __set_h_errno (NETDB_INTERNAL);
+                    __set_h_errno (NETDB_INTERNAL);
+                    __set_errno (EBUSY);
                    }
                }
 
@@ -954,7 +935,10 @@ gaih_inet (const char *name, const struct gaih_service *service,
          __resolv_context_enable_inet6 (res_ctx, res_enable_inet6);
          __resolv_context_put (res_ctx);
 
-         if (h_errno == NETDB_INTERNAL)
+         /* If we have a failure which sets errno, report it using
+            EAI_SYSTEM.  */
+         if ((status == NSS_STATUS_TRYAGAIN || status == NSS_STATUS_UNAVAIL)
+             && h_errno == NETDB_INTERNAL)
            {
              result = -EAI_SYSTEM;
              goto free_and_return;
@@ -1028,40 +1012,24 @@ gaih_inet (const char *name, const struct gaih_service *service,
                 the passed in string.  */
              canon = orig_name;
 
-#ifdef HAVE_LIBIDN
-           if (req->ai_flags & AI_CANONIDN)
+           bool do_idn = req->ai_flags & AI_CANONIDN;
+           if (do_idn)
              {
-               int idn_flags = 0;
-               if (req->ai_flags & AI_IDN_ALLOW_UNASSIGNED)
-                 idn_flags |= IDNA_ALLOW_UNASSIGNED;
-               if (req->ai_flags & AI_IDN_USE_STD3_ASCII_RULES)
-                 idn_flags |= IDNA_USE_STD3_ASCII_RULES;
-
                char *out;
-               int rc = __idna_to_unicode_lzlz (canon, &out, idn_flags);
-               if (rc != IDNA_SUCCESS)
+               int rc = __idna_from_dns_encoding (canon, &out);
+               if (rc == 0)
+                 canon = out;
+               else if (rc == EAI_IDN_ENCODE)
+                 /* Use the punycode name as a fallback.  */
+                 do_idn = false;
+               else
                  {
-                   if (rc == IDNA_MALLOC_ERROR)
-                     result = -EAI_MEMORY;
-                   else if (rc == IDNA_DLOPEN_ERROR)
-                     result = -EAI_SYSTEM;
-                   else
-                     result = -EAI_IDN_ENCODE;
+                   result = -rc;
                    goto free_and_return;
                  }
-               /* In case the output string is the same as the input
-                  string no new string has been allocated and we
-                  make a copy.  */
-               if (out == canon)
-                 goto make_copy;
-               canon = out;
              }
-           else
-#endif
+           if (!do_idn)
              {
-#ifdef HAVE_LIBIDN
-             make_copy:
-#endif
                if (canonbuf != NULL)
                  /* We already allocated the string using malloc, but
                     the buffer is now owned by canon.  */
@@ -2222,10 +2190,7 @@ getaddrinfo (const char *name, const char *service,
 
   if (hints->ai_flags
       & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|AI_ADDRCONFIG|AI_V4MAPPED
-#ifdef HAVE_LIBIDN
-         |AI_IDN|AI_CANONIDN|AI_IDN_ALLOW_UNASSIGNED
-         |AI_IDN_USE_STD3_ASCII_RULES
-#endif
+         |AI_IDN|AI_CANONIDN|DEPRECATED_AI_IDN
          |AI_NUMERICSERV|AI_ALL))
     return EAI_BADFLAGS;