summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhyunuktak <hyunuk.tak@samsung.com>2016-10-31 13:35:37 +0900
committerhyunuktak <hyunuk.tak@samsung.com>2016-10-31 13:36:03 +0900
commitca8ee1372d34c7dbb67aad0219530625ec2f7483 (patch)
tree40651af3ea38c3904ae73edbb59bc537d85b3a42
parent9dafee4d154c8d0f68275af2542b52d04228e4bf (diff)
downloadc-ares-ca8ee1372d34c7dbb67aad0219530625ec2f7483.tar.gz
c-ares-ca8ee1372d34c7dbb67aad0219530625ec2f7483.tar.bz2
c-ares-ca8ee1372d34c7dbb67aad0219530625ec2f7483.zip
CVE-2016-5180
ares_create_query single byte out of buffer write Change-Id: I42baed5e1354095b27eab3fa90dc7433f6ba8362 Signed-off-by: hyunuktak <hyunuk.tak@samsung.com>
-rwxr-xr-x[-rw-r--r--]ares_create_query.c84
1 files changed, 39 insertions, 45 deletions
diff --git a/ares_create_query.c b/ares_create_query.c
index 8624e2f..b9204e6 100644..100755
--- a/ares_create_query.c
+++ b/ares_create_query.c
@@ -85,57 +85,31 @@
*/
int ares_create_query(const char *name, int dnsclass, int type,
- unsigned short id, int rd, unsigned char **buf,
- int *buflen, int max_udp_size)
+ unsigned short id, int rd, unsigned char **bufp,
+ int *buflenp, int max_udp_size)
{
- int len;
+ size_t len;
unsigned char *q;
const char *p;
+ size_t buflen;
+ unsigned char *buf;
/* Set our results early, in case we bail out early with an error. */
- *buflen = 0;
- *buf = NULL;
+ *buflenp = 0;
+ *bufp = NULL;
- /* Compute the length of the encoded name so we can check buflen.
- * Start counting at 1 for the zero-length label at the end. */
- len = 1;
- 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++;
-
- /* Immediately 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."). 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.
+ /* Allocate a memory area for the maximum size this packet might need. +2
+ * is for the length byte and zero termination if no dots or ecscaping is
+ * used.
*/
- if (len > MAXCDNAME)
- return ARES_EBADNAME;
-
- *buflen = len + HFIXEDSZ + QFIXEDSZ + (max_udp_size ? EDNSFIXEDSZ : 0);
- *buf = malloc(*buflen);
- if (!*buf)
- return ARES_ENOMEM;
+ len = strlen(name) + 2 + HFIXEDSZ + QFIXEDSZ +
+ (max_udp_size ? EDNSFIXEDSZ : 0);
+ buf = ares_malloc(len);
+ if (!buf)
+ return ARES_ENOMEM;
/* Set up the header. */
- q = *buf;
+ q = buf;
memset(q, 0, HFIXEDSZ);
DNS_HEADER_SET_QID(q, id);
DNS_HEADER_SET_OPCODE(q, QUERY);
@@ -159,8 +133,10 @@ int ares_create_query(const char *name, int dnsclass, int type,
q += HFIXEDSZ;
while (*name)
{
- if (*name == '.')
+ if (*name == '.') {
+ free(buf);
return ARES_EBADNAME;
+ }
/* Count the number of bytes in this label. */
len = 0;
@@ -170,8 +146,10 @@ int ares_create_query(const char *name, int dnsclass, int type,
p++;
len++;
}
- if (len > MAXLABEL)
+ if (len > MAXLABEL) {
+ free(buf);
return ARES_EBADNAME;
+ }
/* Encode the length and copy the data. */
*q++ = (unsigned char)len;
@@ -195,14 +173,30 @@ int ares_create_query(const char *name, int dnsclass, int type,
DNS_QUESTION_SET_TYPE(q, type);
DNS_QUESTION_SET_CLASS(q, dnsclass);
+ q += QFIXEDSZ;
if (max_udp_size)
{
- q += QFIXEDSZ;
memset(q, 0, EDNSFIXEDSZ);
q++;
DNS_RR_SET_TYPE(q, T_OPT);
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;
}