Skip to content

Commit

Permalink
ares_create_query: avoid single-byte buffer overwrite
Browse files Browse the repository at this point in the history
... when the name ends with an escaped dot.

CVE-2016-5180

Bug: https://c-ares.haxx.se/adv_20160929.html
  • Loading branch information
bagder committed Sep 29, 2016
1 parent 51fbb47 commit 65c71be
Showing 1 changed file with 39 additions and 45 deletions.
84 changes: 39 additions & 45 deletions ares_create_query.c
Expand Up @@ -85,57 +85,31 @@
*/ */


int ares_create_query(const char *name, int dnsclass, int type, int ares_create_query(const char *name, int dnsclass, int type,
unsigned short id, int rd, unsigned char **buf, unsigned short id, int rd, unsigned char **bufp,
int *buflen, int max_udp_size) int *buflenp, int max_udp_size)
{ {
int len; size_t len;
unsigned char *q; unsigned char *q;
const char *p; const char *p;
size_t buflen;
unsigned char *buf;


/* Set our results early, in case we bail out early with an error. */ /* Set our results early, in case we bail out early with an error. */
*buflen = 0; *buflenp = 0;
*buf = NULL; *bufp = NULL;


/* Compute the length of the encoded name so we can check buflen. /* Allocate a memory area for the maximum size this packet might need. +2
* Start counting at 1 for the zero-length label at the end. */ * is for the length byte and zero termination if no dots or ecscaping is
len = 1; * used.
for (p = name; *p; p++)
{
if (*p == '\\' && *(p + 1) != 0)
p++;
len++;
}
/* If there are n periods in the name, there are n + 1 labels, and
* thus n + 1 length fields, unless the name is empty or ends with a
* period. So add 1 unless name is empty or ends with a period.
*/ */
if (*name && *(p - 1) != '.') len = strlen(name) + 2 + HFIXEDSZ + QFIXEDSZ +
len++; (max_udp_size ? EDNSFIXEDSZ : 0);

buf = ares_malloc(len);
/* Immediately reject names that are longer than the maximum of 255 if (!buf)
* bytes that's specified in RFC 1035 ("To simplify implementations, return ARES_ENOMEM;
* the total length of a domain name (i.e., label octets and label
* length octets) is restricted to 255 octets or less."). We aren't
* doing this just to be a stickler about RFCs. For names that are
* too long, 'dnscache' closes its TCP connection to us immediately
* (when using TCP) and ignores the request when using UDP, and
* BIND's named returns ServFail (TCP or UDP). Sending a request
* that we know will cause 'dnscache' to close the TCP connection is
* painful, since that makes any other outstanding requests on that
* connection fail. And sending a UDP request that we know
* 'dnscache' will ignore is bad because resources will be tied up
* until we time-out the request.
*/
if (len > MAXCDNAME)
return ARES_EBADNAME;

*buflen = len + HFIXEDSZ + QFIXEDSZ + (max_udp_size ? EDNSFIXEDSZ : 0);
*buf = ares_malloc(*buflen);
if (!*buf)
return ARES_ENOMEM;


/* Set up the header. */ /* Set up the header. */
q = *buf; q = buf;
memset(q, 0, HFIXEDSZ); memset(q, 0, HFIXEDSZ);
DNS_HEADER_SET_QID(q, id); DNS_HEADER_SET_QID(q, id);
DNS_HEADER_SET_OPCODE(q, QUERY); DNS_HEADER_SET_OPCODE(q, QUERY);
Expand All @@ -159,8 +133,10 @@ int ares_create_query(const char *name, int dnsclass, int type,
q += HFIXEDSZ; q += HFIXEDSZ;
while (*name) while (*name)
{ {
if (*name == '.') if (*name == '.') {
free (buf);
return ARES_EBADNAME; return ARES_EBADNAME;
}


/* Count the number of bytes in this label. */ /* Count the number of bytes in this label. */
len = 0; len = 0;
Expand All @@ -170,8 +146,10 @@ int ares_create_query(const char *name, int dnsclass, int type,
p++; p++;
len++; len++;
} }
if (len > MAXLABEL) if (len > MAXLABEL) {
free (buf);
return ARES_EBADNAME; return ARES_EBADNAME;
}


/* Encode the length and copy the data. */ /* Encode the length and copy the data. */
*q++ = (unsigned char)len; *q++ = (unsigned char)len;
Expand All @@ -195,14 +173,30 @@ int ares_create_query(const char *name, int dnsclass, int type,
DNS_QUESTION_SET_TYPE(q, type); DNS_QUESTION_SET_TYPE(q, type);
DNS_QUESTION_SET_CLASS(q, dnsclass); DNS_QUESTION_SET_CLASS(q, dnsclass);


q += QFIXEDSZ;
if (max_udp_size) if (max_udp_size)
{ {
q += QFIXEDSZ;
memset(q, 0, EDNSFIXEDSZ); memset(q, 0, EDNSFIXEDSZ);
q++; q++;
DNS_RR_SET_TYPE(q, T_OPT); DNS_RR_SET_TYPE(q, T_OPT);
DNS_RR_SET_CLASS(q, max_udp_size); DNS_RR_SET_CLASS(q, max_udp_size);
q += (EDNSFIXEDSZ-1);
}
buflen = (q - buf);

/* Reject names that are longer than the maximum of 255 bytes that's
* specified in RFC 1035 ("To simplify implementations, the total length of
* a domain name (i.e., label octets and label length octets) is restricted
* to 255 octets or less."). */
if (buflen > (MAXCDNAME + HFIXEDSZ + QFIXEDSZ +
(max_udp_size ? EDNSFIXEDSZ : 0))) {
free (buf);
return ARES_EBADNAME;
} }


/* we know this fits in an int at this point */
*buflenp = (int) buflen;
*bufp = buf;

return ARES_SUCCESS; return ARES_SUCCESS;
} }

3 comments on commit 65c71be

@sumitb
Copy link

@sumitb sumitb commented on 65c71be Feb 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting following warnings after this patch, are these ignorable?

c-ares/ares_create_query.c: In function ‘ares_create_query’:
c-ares/ares_create_query.c:107:3: warning: implicit declaration of function ‘ares_malloc’ [-Wimplicit-function-declaration]
c-ares/ares_create_query.c:107:7: warning: assignment makes pointer from integer without a cast

@bagder
Copy link
Member Author

@bagder bagder commented on 65c71be Feb 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit didn't introduce the use of ares_malloc so surely it happened already before then?

This source file includes ares_private.h where the ares_malloc function pointer is extern-declared. I get no warnings with current git.

@sumitb
Copy link

@sumitb sumitb commented on 65c71be Feb 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I apologize I had an earlier version of this file which was missing few commits.

Please sign in to comment.