The bug occurs when reading objects that are bigger than the SSL
record size from a SSL bio:
ASN1_d2i_bio calls BIO_read without checking that the amount received
is equal to the amount requested (This causes a problem becuase
BIO_read will never return more than a record worth of data when
reading from a ssl bio). The following patch seems to fix the problem.
To make things clearer here is the current bit of problematic code in
ASN1_d2i_bio with a few comments:
for (;;)
{
if (want >= (len-off))
{
want-=(len-off);
if (!BUF_MEM_grow(b,len+want))
{
ASN1err(ASN1_F_ASN1_D2I_BIO,ERR_R_MALLOC_FAILURE);
goto err;
}
i=BIO_read(in,&(b->data[len]),want);
if ((i < 0) && ((len-off) == 0))
{
ASN1err(ASN1_F_ASN1_D2I_BIO,ASN1_R_NOT_ENOUGH_DATA);
goto err;
}
if (i > 0)
len+=i;
=============================================================================
When the header stradles a ssl record boundary, we will have i>0, but
i<want. This can cause ASN1_get_object to return ASN1_R_TOO_LONG
(which is seemingly ignored here) and will (I think) lead to accessing
uninitialized data. Note that my patch doesn't address this one as I'm
less sure of it.
=============================================================================
}
/* else data already loaded */
p=(unsigned char *)&(b->data[off]);
c.p=p;
c.inf=ASN1_get_object(&(c.p),&(c.slen),&(c.tag),&(c.xclass),
len-off);
if (c.inf & 0x80)
{
unsigned long e;
e=ERR_GET_REASON(ERR_peek_error());
if (e != ASN1_R_TOO_LONG)
goto err;
else
ERR_get_error(); /* clear error */
}
i=c.p-p;/* header length */
off+=i; /* end of data */
if (c.inf & 1)
{
/* no data body so go round again */
eos++;
want=HEADER_SIZE;
}
else if (eos && (c.slen == 0) && (c.tag == V_ASN1_EOC))
{
/* eos value, so go back and read another header */
eos--;
if (eos <= 0)
break;
else
want=HEADER_SIZE;
}
else
{
/* suck in c.slen bytes of data */
want=(int)c.slen;
if (want > (len-off))
{
want-=(len-off);
if (!BUF_MEM_grow(b,len+want))
{
ASN1err(ASN1_F_ASN1_D2I_BIO,ERR_R_MALLOC_FAILURE);
goto err;
}
i=BIO_read(in,&(b->data[len]),want);
if (i <= 0)
{
ASN1err(ASN1_F_ASN1_D2I_BIO,ASN1_R_NOT_ENOUGH_DATA);
goto err;
}
len+=i;
=============================================================================
Here we have a definite problem. Again if we hit the situation where
i>0, but i<want, we just ignore this and return successfully.
=============================================================================
}
off+=(int)c.slen;
if (eos <= 0)
{
break;
}
else
want=HEADER_SIZE;
}
}
I've attached a patch for the second problem, though I believe a
similar thing should be done for the first.
/Sam
diff -u -r openssl-0.9.6c/crypto/asn1/a_d2i_fp.c
openssl-0.9.6c-patched/crypto/asn1/a_d2i_fp.c
--- openssl-0.9.6c/crypto/asn1/a_d2i_fp.c Fri Apr 23 17:08:07 1999
+++ openssl-0.9.6c-patched/crypto/asn1/a_d2i_fp.c Sun Feb 3 14:26:45 2002
@@ -169,13 +169,17 @@
ASN1err(ASN1_F_ASN1_D2I_BIO,ERR_R_MALLOC_FAILURE);
goto err;
}
- i=BIO_read(in,&(b->data[len]),want);
- if (i <= 0)
- {
-
ASN1err(ASN1_F_ASN1_D2I_BIO,ASN1_R_NOT_ENOUGH_DATA);
- goto err;
- }
- len+=i;
+ while(want)
+ {
+ i=BIO_read(in,&(b->data[len]),want);
+ if (i <= 0)
+ {
+
+ASN1err(ASN1_F_ASN1_D2I_BIO,ASN1_R_NOT_ENOUGH_DATA);
+ goto err;
+ }
+ len+=i;
+ want-=i;
+ }
}
off+=(int)c.slen;
if (eos <= 0)