]> granicus.if.org Git - curl/commitdiff
Overhauled ares__get_hostent()
authorYang Tse <yangsita@gmail.com>
Wed, 7 Oct 2009 18:47:04 +0000 (18:47 +0000)
committerYang Tse <yangsita@gmail.com>
Wed, 7 Oct 2009 18:47:04 +0000 (18:47 +0000)
- Fixing out of bounds memory overwrite triggered with malformed /etc/hosts file.
- Improving parsing of /etc/hosts file.
- Validating requested address family.
- Ensuring that failures always return a NULL pointer.
- Adjusting header inclusions.

ares/CHANGES
ares/RELEASE-NOTES
ares/ares__get_hostent.c

index 0fa138cdf72f3b4fca2b2ee712e47e3f127a97d8..f76edc8584acf1892dd100900697a53a9a5ca2de 100644 (file)
@@ -1,5 +1,11 @@
   Changelog for the c-ares project
 
+* October 7, 2009 (Yang Tse)
+- Overhauled ares__get_hostent() Fixing out of bounds memory overwrite
+  triggered with malformed /etc/hosts file. Improving parsing of /etc/hosts
+  file. Validating requested address family. Ensuring that failures always
+  return a NULL pointer. Adjusting header inclusions.
+
 * 4 Sep 2009 (Daniel Stenberg)
 - Jakub Hrozek added ares_parse_srv_reply() for SRV parsing
 
index 4eef9541f3a4aa87dbd131b88dcb04c838c67086..215c4d2ee6420599fe3adfe7831475208b0227fa 100644 (file)
@@ -19,6 +19,7 @@ Fixed:
  o only expose/export symbols starting with 'ares_'
  o fix \Device\TCP handle leaks triggered by buggy iphlpapi.dll
  o init without internet gone no longer fails
+ o out of bounds memory overwrite triggered with malformed /etc/hosts file
 
 Thanks go to these friendly people for their efforts and contributions:
 
index 90fd88f48ebdf94cb0bee46380af08f1a868b4e6..8402714c467d61e02bb7369182c63303eee75678 100644 (file)
@@ -1,6 +1,6 @@
 /* $Id$ */
 
-/* Copyright 1998 by the Massachusetts Institute of Technology.
+/* Copyright 1998, 2009 by the Massachusetts Institute of Technology.
  *
  * Permission to use, copy, modify, and distribute this
  * software and its documentation for any purpose and without
 
 #include "setup.h"
 
-#if !defined(WIN32) || defined(WATT32)
 #ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
+#  include <sys/socket.h>
 #endif
 #ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
+#  include <netinet/in.h>
 #endif
 #ifdef HAVE_NETDB_H
-#include <netdb.h>
+#  include <netdb.h>
 #endif
 #ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
+#  include <arpa/inet.h>
 #endif
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
 
 #include "ares.h"
 #include "inet_net_pton.h"
 
 int ares__get_hostent(FILE *fp, int family, struct hostent **host)
 {
-  char *line = NULL, *p, *q, *canonical, **alias;
-  int status, linesize, end_at_hostname, naliases;
+  char *line = NULL, *p, *q, **alias;
+  char *txtaddr, *txthost, *txtalias;
+  int status, linesize, addrfam, naliases;
   struct in_addr addr;
   struct in6_addr addr6;
-  size_t addrlen = sizeof(struct in_addr);
+  size_t addrlen;
   struct hostent *hostent = NULL;
 
+  *host = NULL; /* Assume failure */
+
+  /* Validate family */
+  switch (family) {
+    case AF_INET:
+    case AF_INET6:
+    case AF_UNSPEC:
+      break;
+    default:
+      return ARES_EBADFAMILY;
+  }
+
   while ((status = ares__read_line(fp, &line, &linesize)) == ARES_SUCCESS)
     {
-      /* Skip comment lines; terminate line at comment character. */
-      if (*line == '#' || !*line)
-        continue;
-      p = strchr(line, '#');
-      if (p)
-        *p = 0;
 
-      /* Get the address part. */
+      /* Trim line comment. */
       p = line;
-      while (*p && !ISSPACE(*p))
+      while (*p && (*p != '#'))
+        p++;
+      *p = '\0';
+
+      /* Trim trailing whitespace. */
+      q = p - 1;
+      while ((q >= line) && ISSPACE(*q))
+        q--;
+      *++q = '\0';
+
+      /* Skip leading whitespace. */
+      p = line;
+      while (*p && ISSPACE(*p))
         p++;
       if (!*p)
+        /* Ignore line if empty. */
         continue;
-      *p = 0;
-      addr.s_addr = inet_addr(line);
-      if (addr.s_addr == INADDR_NONE)
-      {
-        /* It wasn't an AF_INET dotted address, then AF_UNSPEC and AF_INET6
-           families are subject for this further check */
-        if ((family != AF_INET) &&
-            (ares_inet_pton(AF_INET6, line, &addr6) > 0)) {
-          addrlen = sizeof(struct in6_addr);
-          family = AF_INET6;
-        }
-        else
-          continue;
-      }
-      else if (family == AF_UNSPEC)
-        family = AF_INET; /* now confirmed! */
-      else if (family != AF_INET)
-        /* unknown, keep moving */
+
+      /* Pointer to start of IPv4 or IPv6 address part. */
+      txtaddr = p;
+
+      /* Advance past address part. */
+      while (*p && !ISSPACE(*p))
+        p++;
+      if (!*p)
+        /* Ignore line if reached end of line. */
         continue;
 
-      /* Get the canonical hostname. */
+      /* Null terminate address part. */
+      *p = '\0';
+
+      /* Advance to host name */
       p++;
-      while (ISSPACE(*p))
+      while (*p && ISSPACE(*p))
         p++;
       if (!*p)
+        /* Ignore line if reached end of line. */
         continue;
-      q = p;
-      while (*q && !ISSPACE(*q))
-        q++;
-      end_at_hostname = (*q == 0);
-      *q = 0;
-      canonical = p;
 
+      /* Pointer to start of host name. */
+      txthost = p;
+
+      /* Advance past host name. */
+      while (*p && !ISSPACE(*p))
+        p++;
+
+      /* Pointer to start of first alias. */
+      txtalias = NULL;
+      if (*p)
+        {
+          q = p + 1;
+          while (*q && ISSPACE(*q))
+            q++;
+          if (*q)
+            txtalias = q;
+        }
+
+      /* Null terminate host name. */
+      *p = '\0';
+
+      /* find out number of aliases. */
       naliases = 0;
-      if (!end_at_hostname)
+      if (txtalias)
         {
-          /* Count the aliases. */
-          p = q + 1;
-          while (ISSPACE(*p))
-            p++;
+          p = txtalias;
           while (*p)
             {
               while (*p && !ISSPACE(*p))
                 p++;
-              while (ISSPACE(*p))
+              while (*p && ISSPACE(*p))
                 p++;
               naliases++;
             }
         }
 
-      /* Allocate memory for the host structure. */
+      /* Convert address string to network address for the requested family. */
+      addrlen = 0;
+      addrfam = AF_UNSPEC;
+      if ((family == AF_INET) || (family == AF_UNSPEC))
+        {
+          addr.s_addr = inet_addr(txtaddr);
+          if (addr.s_addr != INADDR_NONE)
+            {
+              /* Actual network address family and length. */
+              addrfam = AF_INET;
+              addrlen = sizeof(struct in_addr);
+            }
+        }
+      if ((family == AF_INET6) || ((family == AF_UNSPEC) && (!addrlen)))
+        {
+          if (ares_inet_pton(AF_INET6, txtaddr, &addr6) > 0)
+            {
+              /* Actual network address family and length. */
+              addrfam = AF_INET6;
+              addrlen = sizeof(struct in6_addr);
+            }
+        }
+      if (!addrlen)
+        /* Ignore line if invalid address string for the requested family. */
+        continue;
+
+      /*
+      ** Actual address family possible values are AF_INET and AF_INET6 only.
+      */
+
+      /* Allocate memory for the hostent structure. */
       hostent = malloc(sizeof(struct hostent));
       if (!hostent)
         break;
+
+      /* Initialize fields for out of memory condition. */
       hostent->h_aliases = NULL;
       hostent->h_addr_list = NULL;
-      hostent->h_name = strdup(canonical);
+
+      /* Copy official host name. */
+      hostent->h_name = strdup(txthost);
       if (!hostent->h_name)
         break;
+
+      /* Copy network address. */
       hostent->h_addr_list = malloc(2 * sizeof(char *));
       if (!hostent->h_addr_list)
         break;
+      hostent->h_addr_list[1] = NULL;
       hostent->h_addr_list[0] = malloc(addrlen);
       if (!hostent->h_addr_list[0])
         break;
+      if (addrfam == AF_INET)
+        memcpy(hostent->h_addr_list[0], &addr, addrlen);
+      else
+        memcpy(hostent->h_addr_list[0], &addr6, addrlen);
+
+      /* Copy aliases. */
       hostent->h_aliases = malloc((naliases + 1) * sizeof(char *));
       if (!hostent->h_aliases)
         break;
-
-      /* Copy in aliases. */
-      naliases = 0;
-      if (!end_at_hostname)
+      alias = hostent->h_aliases;
+      while (naliases >= 0)
+        *(alias + naliases--) = NULL;
+      while (txtalias)
         {
-          p = canonical + strlen(canonical) + 1;
-          while (ISSPACE(*p))
+          p = txtalias;
+          while (*p && !ISSPACE(*p))
             p++;
-          while (*p)
-            {
-              q = p;
-              while (*q && !ISSPACE(*q))
-                q++;
-              hostent->h_aliases[naliases] = malloc(q - p + 1);
-              if (hostent->h_aliases[naliases] == NULL)
-                break;
-              memcpy(hostent->h_aliases[naliases], p, q - p);
-              hostent->h_aliases[naliases][q - p] = 0;
-              p = q;
-              while (ISSPACE(*p))
-                p++;
-              naliases++;
-            }
-          if (*p)
+          q = p;
+          while (*q && ISSPACE(*q))
+            q++;
+          *p = '\0';
+          if ((*alias = strdup(txtalias)) == NULL)
             break;
+          alias++;
+          txtalias = *q ? q : NULL;
         }
-      hostent->h_aliases[naliases] = NULL;
+      if (txtalias)
+        /* Alias memory allocation failure. */
+        break;
 
-      hostent->h_addrtype = family;
+      /* Copy actual network address family and length. */
+      hostent->h_addrtype = addrfam;
       hostent->h_length = (int)addrlen;
-      if (family == AF_INET)
-        memcpy(hostent->h_addr_list[0], &addr, addrlen);
-      else if (family == AF_INET6)
-        memcpy(hostent->h_addr_list[0], &addr6, addrlen);
-      hostent->h_addr_list[1] = NULL;
-      *host = hostent;
+
+      /* Free line buffer. */
       free(line);
+
+      /* Return hostent successfully */
+      *host = hostent;
       return ARES_SUCCESS;
+
     }
-  if(line)
+
+  /* If allocated, free line buffer. */
+  if (line)
     free(line);
 
   if (status == ARES_SUCCESS)
@@ -180,22 +240,22 @@ int ares__get_hostent(FILE *fp, int family, struct hostent **host)
       /* Memory allocation failure; clean up. */
       if (hostent)
         {
-          if(hostent->h_name)
+          if (hostent->h_name)
             free((char *) hostent->h_name);
           if (hostent->h_aliases)
             {
               for (alias = hostent->h_aliases; *alias; alias++)
                 free(*alias);
+              free(hostent->h_aliases);
+            }
+          if (hostent->h_addr_list)
+            {
+              if (hostent->h_addr_list[0])
+                free(hostent->h_addr_list[0]);
+              free(hostent->h_addr_list);
             }
-          if(hostent->h_aliases)
-            free(hostent->h_aliases);
-          if (hostent->h_addr_list && hostent->h_addr_list[0])
-            free(hostent->h_addr_list[0]);
-          if(hostent->h_addr_list)
-            free(hostent->h_addr_list);
           free(hostent);
         }
-      *host = NULL;
       return ARES_ENOMEM;
     }