(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
This commit is contained in:
@@ -113,10 +113,10 @@ dns_free_data(struct dns_reply *r)
|
|||||||
static struct dns_reply*
|
static struct dns_reply*
|
||||||
parse_reply(unsigned char *data, int len)
|
parse_reply(unsigned char *data, int len)
|
||||||
{
|
{
|
||||||
unsigned char *p;
|
const unsigned char *p;
|
||||||
char host[128];
|
char host[128];
|
||||||
int status;
|
int status;
|
||||||
|
const unsigned char *end_data = data + len;
|
||||||
struct dns_reply *r;
|
struct dns_reply *r;
|
||||||
struct resource_record **rr;
|
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 */
|
memcpy(&r->h, p, 12); /* XXX this will probably be mostly garbage */
|
||||||
p += 12;
|
p += 12;
|
||||||
#endif
|
#endif
|
||||||
status = dn_expand(data, data + len, p, host, sizeof(host));
|
status = dn_expand(data, end_data, p, host, sizeof(host));
|
||||||
if(status < 0){
|
if(status < 0){
|
||||||
dns_free_data(r);
|
dns_free_data(r);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -143,19 +143,27 @@ parse_reply(unsigned char *data, int len)
|
|||||||
dns_free_data(r);
|
dns_free_data(r);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (p + status + 4 > end_data) {
|
||||||
|
dns_free_data(r);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
p += status;
|
p += status;
|
||||||
r->q.type = (p[0] << 8 | p[1]);
|
r->q.type = (p[0] << 8 | p[1]);
|
||||||
p += 2;
|
p += 2;
|
||||||
r->q.class = (p[0] << 8 | p[1]);
|
r->q.class = (p[0] << 8 | p[1]);
|
||||||
p += 2;
|
p += 2;
|
||||||
rr = &r->head;
|
rr = &r->head;
|
||||||
while(p < data + len){
|
while(p < end_data){
|
||||||
int type, class, ttl, size;
|
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){
|
if(status < 0){
|
||||||
dns_free_data(r);
|
dns_free_data(r);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (p + status + 10 > end_data) {
|
||||||
|
dns_free_data(r);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
p += status;
|
p += status;
|
||||||
type = (p[0] << 8) | p[1];
|
type = (p[0] << 8) | p[1];
|
||||||
p += 2;
|
p += 2;
|
||||||
@@ -165,6 +173,12 @@ parse_reply(unsigned char *data, int len)
|
|||||||
p += 4;
|
p += 4;
|
||||||
size = (p[0] << 8) | p[1];
|
size = (p[0] << 8) | p[1];
|
||||||
p += 2;
|
p += 2;
|
||||||
|
|
||||||
|
if (p + size > end_data) {
|
||||||
|
dns_free_data(r);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
*rr = (struct resource_record*)calloc(1,
|
*rr = (struct resource_record*)calloc(1,
|
||||||
sizeof(struct resource_record));
|
sizeof(struct resource_record));
|
||||||
if(*rr == NULL) {
|
if(*rr == NULL) {
|
||||||
@@ -184,7 +198,7 @@ parse_reply(unsigned char *data, int len)
|
|||||||
case T_NS:
|
case T_NS:
|
||||||
case T_CNAME:
|
case T_CNAME:
|
||||||
case T_PTR:
|
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){
|
if(status < 0){
|
||||||
dns_free_data(r);
|
dns_free_data(r);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -197,11 +211,16 @@ parse_reply(unsigned char *data, int len)
|
|||||||
break;
|
break;
|
||||||
case T_MX:
|
case T_MX:
|
||||||
case T_AFSDB:{
|
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){
|
if(status < 0){
|
||||||
dns_free_data(r);
|
dns_free_data(r);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (status + 2 > size) {
|
||||||
|
dns_free_data(r);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
(*rr)->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) +
|
(*rr)->u.mx = (struct mx_record*)malloc(sizeof(struct mx_record) +
|
||||||
strlen(host));
|
strlen(host));
|
||||||
if((*rr)->u.mx == NULL) {
|
if((*rr)->u.mx == NULL) {
|
||||||
@@ -213,11 +232,16 @@ parse_reply(unsigned char *data, int len)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case T_SRV:{
|
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){
|
if(status < 0){
|
||||||
dns_free_data(r);
|
dns_free_data(r);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (status + 6 > size) {
|
||||||
|
dns_free_data(r);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
(*rr)->u.srv =
|
(*rr)->u.srv =
|
||||||
(struct srv_record*)malloc(sizeof(struct srv_record) +
|
(struct srv_record*)malloc(sizeof(struct srv_record) +
|
||||||
strlen(host));
|
strlen(host));
|
||||||
@@ -244,6 +268,11 @@ parse_reply(unsigned char *data, int len)
|
|||||||
case T_KEY : {
|
case T_KEY : {
|
||||||
size_t key_len;
|
size_t key_len;
|
||||||
|
|
||||||
|
if (size < 4) {
|
||||||
|
dns_free_data (r);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
key_len = size - 4;
|
key_len = size - 4;
|
||||||
(*rr)->u.key = malloc (sizeof(*(*rr)->u.key) + key_len - 1);
|
(*rr)->u.key = malloc (sizeof(*(*rr)->u.key) + key_len - 1);
|
||||||
if ((*rr)->u.key == NULL) {
|
if ((*rr)->u.key == NULL) {
|
||||||
@@ -261,11 +290,16 @@ parse_reply(unsigned char *data, int len)
|
|||||||
case T_SIG : {
|
case T_SIG : {
|
||||||
size_t sig_len;
|
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) {
|
if (status < 0) {
|
||||||
dns_free_data (r);
|
dns_free_data (r);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (status + 18 > size) {
|
||||||
|
dns_free_data(r);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
sig_len = len - 18 - status;
|
sig_len = len - 18 - status;
|
||||||
(*rr)->u.sig = malloc(sizeof(*(*rr)->u.sig)
|
(*rr)->u.sig = malloc(sizeof(*(*rr)->u.sig)
|
||||||
+ strlen(host) + sig_len);
|
+ strlen(host) + sig_len);
|
||||||
@@ -293,6 +327,11 @@ parse_reply(unsigned char *data, int len)
|
|||||||
case T_CERT : {
|
case T_CERT : {
|
||||||
size_t cert_len;
|
size_t cert_len;
|
||||||
|
|
||||||
|
if (size < 5) {
|
||||||
|
dns_free_data(r);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
cert_len = size - 5;
|
cert_len = size - 5;
|
||||||
(*rr)->u.cert = malloc (sizeof(*(*rr)->u.cert) + cert_len - 1);
|
(*rr)->u.cert = malloc (sizeof(*(*rr)->u.cert) + cert_len - 1);
|
||||||
if ((*rr)->u.cert == NULL) {
|
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];
|
unsigned char reply[1024];
|
||||||
int len;
|
int len;
|
||||||
struct dns_reply *r = NULL;
|
|
||||||
#ifdef HAVE__RES
|
#ifdef HAVE__RES
|
||||||
u_long old_options = 0;
|
u_long old_options = 0;
|
||||||
#endif
|
#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",
|
fprintf(stderr, "dns_lookup(%s, %d, %s) --> %d\n",
|
||||||
domain, rr_class, dns_type_to_string(rr_type), len);
|
domain, rr_class, dns_type_to_string(rr_type), len);
|
||||||
}
|
}
|
||||||
if (len >= 0)
|
if(len < 0) {
|
||||||
r = parse_reply(reply, len);
|
return NULL;
|
||||||
return r;
|
} else {
|
||||||
|
len = min(len, sizeof(reply));
|
||||||
|
return parse_reply(reply, len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct dns_reply *
|
struct dns_reply *
|
||||||
|
Reference in New Issue
Block a user