aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYang Tse <yangsita@gmail.com>2009-10-07 18:47:04 +0000
committerYang Tse <yangsita@gmail.com>2009-10-07 18:47:04 +0000
commit052dac0d3fe02f4f3e838b0848e3e6f2901e6e42 (patch)
tree5a1a11bb8a41b2b6693729df14c99c857cdb406f
parent2eeafcf9a6f1bd43bbe05e20b58cbb2a937f2b26 (diff)
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.
-rw-r--r--ares/CHANGES6
-rw-r--r--ares/RELEASE-NOTES1
-rw-r--r--ares/ares__get_hostent.c252
3 files changed, 163 insertions, 96 deletions
diff --git a/ares/CHANGES b/ares/CHANGES
index 0fa138cdf..f76edc858 100644
--- a/ares/CHANGES
+++ b/ares/CHANGES
@@ -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
diff --git a/ares/RELEASE-NOTES b/ares/RELEASE-NOTES
index 4eef9541f..215c4d2ee 100644
--- a/ares/RELEASE-NOTES
+++ b/ares/RELEASE-NOTES
@@ -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:
diff --git a/ares/ares__get_hostent.c b/ares/ares__get_hostent.c
index 90fd88f48..8402714c4 100644
--- a/ares/ares__get_hostent.c
+++ b/ares/ares__get_hostent.c
@@ -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
@@ -17,25 +17,18 @@
#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"
@@ -43,136 +36,203 @@
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;
}