From d6a7b8a83e693b8d722e3118affa5cbb16e8d19c Mon Sep 17 00:00:00 2001 From: Assar Westerlund Date: Tue, 27 Aug 2002 20:07:02 +0000 Subject: [PATCH] (parse_reply): verify the lengths (both external and internal) are consistent and not too long (dns_lookup_int): be conservative in the length sent in to to parse_reply git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@11239 ec53bebd-3082-4978-b11e-865c3cabbd6b --- lib/roken/resolve.c | 67 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/lib/roken/resolve.c b/lib/roken/resolve.c index 6e56cc2c8..e71538d68 100644 --- a/lib/roken/resolve.c +++ b/lib/roken/resolve.c @@ -113,10 +113,10 @@ dns_free_data(struct dns_reply *r) static struct dns_reply* parse_reply(unsigned char *data, int len) { - unsigned char *p; + const unsigned char *p; char host[128]; int status; - + const unsigned char *end_data = data + len; struct dns_reply *r; struct resource_record **rr; @@ -133,7 +133,7 @@ parse_reply(unsigned char *data, int len) memcpy(&r->h, p, 12); /* XXX this will probably be mostly garbage */ p += 12; #endif - status = dn_expand(data, data + len, p, host, sizeof(host)); + status = dn_expand(data, end_data, p, host, sizeof(host)); if(status < 0){ dns_free_data(r); return NULL; @@ -143,19 +143,27 @@ parse_reply(unsigned char *data, int len) dns_free_data(r); return NULL; } + if (p + status + 4 > end_data) { + dns_free_data(r); + return NULL; + } p += status; r->q.type = (p[0] << 8 | p[1]); p += 2; r->q.class = (p[0] << 8 | p[1]); p += 2; rr = &r->head; - while(p < data + len){ + while(p < end_data){ int type, class, ttl, size; - status = dn_expand(data, data + len, p, host, sizeof(host)); + status = dn_expand(data, end_data, p, host, sizeof(host)); if(status < 0){ dns_free_data(r); return NULL; } + if (p + status + 10 > end_data) { + dns_free_data(r); + return NULL; + } p += status; type = (p[0] << 8) | p[1]; p += 2; @@ -165,6 +173,12 @@ parse_reply(unsigned char *data, int len) p += 4; size = (p[0] << 8) | p[1]; p += 2; + + if (p + size > end_data) { + dns_free_data(r); + return NULL; + } + *rr = (struct resource_record*)calloc(1, sizeof(struct resource_record)); if(*rr == NULL) { @@ -184,7 +198,7 @@ parse_reply(unsigned char *data, int len) case T_NS: case T_CNAME: case T_PTR: - status = dn_expand(data, data + len, p, host, sizeof(host)); + status = dn_expand(data, end_data, p, host, sizeof(host)); if(status < 0){ dns_free_data(r); return NULL; @@ -197,11 +211,16 @@ parse_reply(unsigned char *data, int len) break; case T_MX: case T_AFSDB:{ - status = dn_expand(data, data + len, p + 2, host, sizeof(host)); + status = dn_expand(data, end_data, p + 2, host, sizeof(host)); if(status < 0){ dns_free_data(r); return NULL; } + if (status + 2 > size) { + dns_free_data(r); + return NULL; + } + (*rr)->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) + strlen(host)); if((*rr)->u.mx == NULL) { @@ -213,11 +232,16 @@ parse_reply(unsigned char *data, int len) break; } case T_SRV:{ - status = dn_expand(data, data + len, p + 6, host, sizeof(host)); + status = dn_expand(data, end_data, p + 6, host, sizeof(host)); if(status < 0){ dns_free_data(r); return NULL; } + if (status + 6 > size) { + dns_free_data(r); + return NULL; + } + (*rr)->u.srv = (struct srv_record*)malloc(sizeof(struct srv_record) + strlen(host)); @@ -244,6 +268,11 @@ parse_reply(unsigned char *data, int len) case T_KEY : { size_t key_len; + if (size < 4) { + dns_free_data (r); + return NULL; + } + key_len = size - 4; (*rr)->u.key = malloc (sizeof(*(*rr)->u.key) + key_len - 1); if ((*rr)->u.key == NULL) { @@ -261,11 +290,16 @@ parse_reply(unsigned char *data, int len) case T_SIG : { size_t sig_len; - status = dn_expand (data, data + len, p + 18, host, sizeof(host)); + status = dn_expand (data, end_data, p + 18, host, sizeof(host)); if (status < 0) { dns_free_data (r); return NULL; } + if (status + 18 > size) { + dns_free_data(r); + return NULL; + } + sig_len = len - 18 - status; (*rr)->u.sig = malloc(sizeof(*(*rr)->u.sig) + strlen(host) + sig_len); @@ -293,6 +327,11 @@ parse_reply(unsigned char *data, int len) case T_CERT : { size_t cert_len; + if (size < 5) { + dns_free_data(r); + return NULL; + } + cert_len = size - 5; (*rr)->u.cert = malloc (sizeof(*(*rr)->u.cert) + cert_len - 1); if ((*rr)->u.cert == NULL) { @@ -327,7 +366,6 @@ dns_lookup_int(const char *domain, int rr_class, int rr_type) { unsigned char reply[1024]; int len; - struct dns_reply *r = NULL; #ifdef HAVE__RES u_long old_options = 0; #endif @@ -348,9 +386,12 @@ dns_lookup_int(const char *domain, int rr_class, int rr_type) fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n", domain, rr_class, dns_type_to_string(rr_type), len); } - if (len >= 0) - r = parse_reply(reply, len); - return r; + if(len < 0) { + return NULL; + } else { + len = min(len, sizeof(reply)); + return parse_reply(reply, len); + } } struct dns_reply *