add oid and bitstring parsing and formatting to "generic" format string

routines, test them in t10.c
This commit is contained in:
leitner
2011-04-28 21:33:10 +00:00
parent e04ca78ff8
commit dae3ea8024
12 changed files with 181 additions and 50 deletions

View File

@@ -12,7 +12,7 @@ scan_asn1STRING.o scan_asn1SEQUENCE.o scan_asn1ENUMERATED.o \
scan_asn1BOOLEAN.o scan_asn1rawint.o scan_asn1SET.o fmt_asn1sint.o \ scan_asn1BOOLEAN.o scan_asn1rawint.o scan_asn1SET.o fmt_asn1sint.o \
fmt_asn1sintpayload.o scan_asn1oid.o scan_asn1BITSTRING.o \ fmt_asn1sintpayload.o scan_asn1oid.o scan_asn1BITSTRING.o \
scan_asn1tagint.o fmt_asn1tagint.o fmt_asn1OID.o scan_asn1generic.o \ scan_asn1tagint.o fmt_asn1tagint.o fmt_asn1OID.o scan_asn1generic.o \
fmt_asn1generic.o fmt_asn1generic.o scan_asn1rawoid.o fmt_asn1bitstring.o
ldap.a: scan_ldapmessage.o fmt_ldapmessage.o fmt_ldapbindrequest.o \ ldap.a: scan_ldapmessage.o fmt_ldapmessage.o fmt_ldapbindrequest.o \
scan_ldapbindrequest.o scan_ldapbindresponse.o scan_ldapresult.o \ scan_ldapbindrequest.o scan_ldapbindresponse.o scan_ldapresult.o \
@@ -128,6 +128,7 @@ fmt_ldapsearchresultentry.o: fmt_ldapsearchresultentry.c asn1.h ldap.h
fmt_ldapstring.o: fmt_ldapstring.c asn1.h ldap.h fmt_ldapstring.o: fmt_ldapstring.c asn1.h ldap.h
fmt_asn1OID.o: fmt_asn1OID.c asn1.h fmt_asn1OID.o: fmt_asn1OID.c asn1.h
fmt_asn1generic.o: fmt_asn1generic.c asn1.h fmt_asn1generic.o: fmt_asn1generic.c asn1.h
fmt_asn1bitstring.o: fmt_asn1bitstring.c asn1.h
scan_asn1BOOLEAN.o: scan_asn1BOOLEAN.c asn1.h scan_asn1BOOLEAN.o: scan_asn1BOOLEAN.c asn1.h
scan_asn1ENUMERATED.o: scan_asn1ENUMERATED.c asn1.h scan_asn1ENUMERATED.o: scan_asn1ENUMERATED.c asn1.h
@@ -139,6 +140,7 @@ scan_asn1BITSTRING.o: scan_asn1BITSTRING.c asn1.h
scan_asn1int.o: scan_asn1int.c asn1.h scan_asn1int.o: scan_asn1int.c asn1.h
scan_asn1length.o: scan_asn1length.c asn1.h scan_asn1length.o: scan_asn1length.c asn1.h
scan_asn1oid.o: scan_asn1oid.c asn1.h scan_asn1oid.o: scan_asn1oid.c asn1.h
scan_asn1rawoid.o: scan_asn1rawoid.c asn1.h
scan_asn1rawint.o: scan_asn1rawint.c asn1.h scan_asn1rawint.o: scan_asn1rawint.c asn1.h
scan_asn1string.o: scan_asn1string.c asn1.h scan_asn1string.o: scan_asn1string.c asn1.h
scan_asn1tag.o: scan_asn1tag.c asn1.h scan_asn1tag.o: scan_asn1tag.c asn1.h

4
THANKS
View File

@@ -2,7 +2,7 @@ Trevor Harrison found a bug in fmt_asn1tag for the multibyte encoding.
David Lichteblau found lots of problems in the ASN.1 code. David Lichteblau found lots of problems in the ASN.1 code.
Özgür Kesim helped fix the substring search. Özgür Kesim helped fix the substring search.
Thomas Walpuski has found lots of problems with the LDAP code. Thomas Walpuski has found lots of problems with the LDAP code.
@@ -10,5 +10,5 @@ Dirk Meyer helped BSD portability.
Zak Johnson fixed substring matches in ldap_match_mapped. Zak Johnson fixed substring matches in ldap_match_mapped.
Andreas Stührk found a possible integer underflow in the indexing code. Andreas Stührk found a possible integer underflow in the indexing code.
I'd be astounded if someone found a way to exploit it, though. I'd be astounded if someone found a way to exploit it, though.

16
asn1.h
View File

@@ -76,6 +76,11 @@ size_t fmt_asn1transparent(char* dest,enum asn1_tagclass tc,
size_t fmt_asn1string(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt, size_t fmt_asn1string(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,
enum asn1_tag tag,const char* c,size_t l); enum asn1_tag tag,const char* c,size_t l);
/* same but for bitstrings.
* l in this case means the number of BITS in c, not bytes */
size_t fmt_asn1bitstring(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,
enum asn1_tag tag,const char* c,size_t l);
/* write ASN.1 OCTET STRING */ /* write ASN.1 OCTET STRING */
#define fmt_asn1OCTETSTRING(dest,c,l) fmt_asn1string(dest,UNIVERSAL,PRIMITIVE,OCTET_STRING,c,l) #define fmt_asn1OCTETSTRING(dest,c,l) fmt_asn1string(dest,UNIVERSAL,PRIMITIVE,OCTET_STRING,c,l)
@@ -94,7 +99,7 @@ size_t fmt_asn1string(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,
/* write ASN.1 SET */ /* write ASN.1 SET */
#define fmt_asn1SET(dest,l) fmt_asn1transparent(dest,UNIVERSAL,CONSTRUCTED,SET_OF,l) #define fmt_asn1SET(dest,l) fmt_asn1transparent(dest,UNIVERSAL,CONSTRUCTED,SET_OF,l)
size_t fmt_asn1OID(char* dest,const unsigned long* array,unsigned long len); size_t fmt_asn1OID(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,enum asn1_tag tag,const unsigned long* array,unsigned long len);
/* conventions for the parser routines: /* conventions for the parser routines:
@@ -141,11 +146,18 @@ size_t scan_asn1SET(const char* src,const char* max,size_t* len);
* Return numbers of bytes parsed or 0 on error. * Return numbers of bytes parsed or 0 on error.
* Put at most arraylen longs into array; if the OID is longer, or if array is NULL, return real number in arraylen and return 0 * Put at most arraylen longs into array; if the OID is longer, or if array is NULL, return real number in arraylen and return 0
* If 0 is returned and arraylen is also 0, there was a parse error */ * If 0 is returned and arraylen is also 0, there was a parse error */
size_t scan_asn1oid(const char* src,const char* max,unsigned long* array,unsigned long* arraylen); size_t scan_asn1oid(const char* src,const char* max,unsigned long* array,size_t* arraylen);
/* internal helper, assumes you already read tag and length and max=src+length */
size_t scan_asn1rawoid(const char* src,const char* max,unsigned long* array,size_t* arraylen);
struct string { struct string {
size_t l; size_t l;
const char* s; const char* s;
}; };
struct oid {
size_t l;
size_t* a;
};
#endif #endif

View File

@@ -1,12 +1,12 @@
#include "asn1.h" #include "asn1.h"
size_t fmt_asn1OID(char* dest,const unsigned long* array,unsigned long len) { size_t fmt_asn1OID(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,enum asn1_tag tag,const unsigned long* array,unsigned long len) {
size_t i,l,l2; size_t i,l,l2;
if (len<2) return 0; if (len<2) return 0;
for (l=1,i=2; i<len; ++i) { for (l=1,i=2; i<len; ++i) {
l+=fmt_asn1tagint(dest,array[i]); l+=fmt_asn1tagint(dest,array[i]);
} }
l2=fmt_asn1transparent(dest,UNIVERSAL,PRIMITIVE,OBJECT_IDENTIFIER,l); l2=fmt_asn1transparent(dest,tc,tt,tag,l);
if (!dest) return l+l2; if (!dest) return l+l2;
dest[l2]=array[0]*40+array[1]; dest[l2]=array[0]*40+array[1];
dest+=l2+1; dest+=l2+1;

19
fmt_asn1bitstring.c Normal file
View File

@@ -0,0 +1,19 @@
#include "asn1.h"
#include "byte.h"
/* like fmt_asn1string, but l is in BITS, not BYTES */
size_t fmt_asn1bitstring(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,enum asn1_tag tag,const char* c,size_t l) {
size_t len;
size_t actuallen;
if (l>(size_t)-100) return (size_t)-1;
actuallen=1+(l+7)/8; /* add one octet to specify the unused bits in the last octet, and calculate octets needed */
len=fmt_asn1transparent(dest,tc,tt,tag,actuallen);
if (dest) {
if (l)
dest[len]=7-((l-1)%8);
else
dest[len]=0;
byte_copy(dest+len+1,actuallen-1,c);
}
return len+actuallen;
}

View File

@@ -9,6 +9,7 @@ size_t fmt_asn1generic(char* dest,const char* fmt,...) {
va_start(args,fmt); va_start(args,fmt);
unsigned long* application=0; unsigned long* application=0;
struct string* s; struct string* s;
struct oid* o;
struct string S; struct string S;
size_t curlen=0; size_t curlen=0;
size_t cursor=0; size_t cursor=0;
@@ -31,6 +32,14 @@ size_t fmt_asn1generic(char* dest,const char* fmt,...) {
application=NULL; application=NULL;
break; break;
} }
case 'b': // send BIT_STRING, using struct string* as arg (expect l to be in bits, not bytes)
s=va_arg(args,struct string*);
if (application)
curlen=fmt_asn1bitstring(realdest,APPLICATION,PRIMITIVE,*application,s->s,s->l);
else
curlen=fmt_asn1bitstring(realdest,UNIVERSAL,PRIMITIVE,BIT_STRING,s->s,s->l);
application=NULL;
break;
case 'S': // send OCTET_STRING, using struct string* as arg case 'S': // send OCTET_STRING, using struct string* as arg
s=va_arg(args,struct string*); s=va_arg(args,struct string*);
copystring: copystring:
@@ -45,6 +54,14 @@ copystring:
S.l=strlen(S.s); S.l=strlen(S.s);
s=&S; s=&S;
goto copystring; goto copystring;
case 'o': // send OBJECT_IDENTIFIER, using struct oid* as arg
o=va_arg(args,struct oid*);
if (application)
curlen=fmt_asn1OID(realdest,APPLICATION,PRIMITIVE,*application,o->a,o->l);
else
curlen=fmt_asn1OID(realdest,UNIVERSAL,PRIMITIVE,OBJECT_IDENTIFIER,o->a,o->l);
application=NULL;
break;
case '{': // start SEQUENCE case '{': // start SEQUENCE
if (application) if (application)
curlen=fmt_asn1tag(realdest,APPLICATION,CONSTRUCTED,*application); curlen=fmt_asn1tag(realdest,APPLICATION,CONSTRUCTED,*application);

2
ldap.h
View File

@@ -1,9 +1,9 @@
#ifndef _LDAP_H #ifndef _LDAP_H
#define _LDAP_H #define _LDAP_H
#include "asn1.h"
#include <stddef.h> #include <stddef.h>
#include <inttypes.h> #include <inttypes.h>
#include "asn1.h"
int matchstring(struct string* s,const char* c); int matchstring(struct string* s,const char* c);
int matchcasestring(struct string* s,const char* c); int matchcasestring(struct string* s,const char* c);

View File

@@ -19,7 +19,7 @@ size_t scan_asn1BITSTRING(const char* src,const char* max,const char** s,size_t*
lastbyte=(*s)[*l+1]; lastbyte=(*s)[*l+1];
if (lastbyte & (0xff >> (8-**s))) if (lastbyte & (0xff >> (8-**s)))
return 0; return 0;
*l=*l*8-(unsigned char)(**s); *l=(*l-1)*8-(unsigned char)(**s);
++*s; ++*s;
return tmp; return tmp;
} }

View File

@@ -1,3 +1,4 @@
#include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include "asn1.h" #include "asn1.h"
@@ -36,6 +37,7 @@ size_t scan_asn1generic(const char* src,const char* max,const char* fmt,...) {
application=0; application=0;
break; break;
} }
case 'b': // s = BIT STRING
case 's': // s = STRING case 's': // s = STRING
{ {
struct string* dest=va_arg(args,struct string*); struct string* dest=va_arg(args,struct string*);
@@ -47,13 +49,47 @@ size_t scan_asn1generic(const char* src,const char* max,const char* fmt,...) {
if (tc!=APPLICATION) return 0; if (tc!=APPLICATION) return 0;
*application=tag; *application=tag;
} else { } else {
if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=OCTET_STRING) if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=(*fmt=='s'?OCTET_STRING:BIT_STRING))
return 0; return 0;
} }
if (*fmt=='b') { // additional checks for bit strings
if (dest->l==0 || // length can't be 0 because the format starts with 1 octet that contains the number of unused bits in the last octet
((unsigned char)(dest->s[0])>7) || // it's the number of unused bits in an octet, must be [0..7]
(dest->l==1 && dest->s[0])) return 0; // if there is no last octet, there can't be any unused bits in there
dest->l=(dest->l-1)*8-dest->s[0];
dest->s+=1;
}
src+=curlen; src+=curlen;
application=0; application=0;
break; break;
} }
case 'o': // o == OID
{
struct oid* dest=va_arg(args,struct oid*);
curlen=scan_asn1tag(src,maxstack[curmax],&tc,&tt,&tag);
if (!curlen) { if (optional) break; else return 0; }
if (application) {
if (tc!=APPLICATION) return 0;
*application=tag;
} else {
if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=OBJECT_IDENTIFIER)
return 0;
}
src+=curlen;
curlen=scan_asn1length(src,maxstack[curmax],&seqlen);
if (!curlen) return 0;
src+=curlen;
curlen=scan_asn1rawoid(src,src+seqlen,dest->a,&dest->l);
if (!curlen) {
if (dest->l && !dest->a) {
dest->a=malloc(dest->l*sizeof(dest->a[0]));
curlen=scan_asn1rawoid(src,src+seqlen,dest->a,&dest->l);
}
if (!curlen) return 0;
}
src+=curlen;
application=0;
}
case 'a': // next tag class is APPLICATION instead of UNIVERSAL; write tag to unsigned long* case 'a': // next tag class is APPLICATION instead of UNIVERSAL; write tag to unsigned long*
{ {
application=va_arg(args,unsigned long*); application=va_arg(args,unsigned long*);

View File

@@ -1,49 +1,21 @@
#include "asn1.h" #include "asn1.h"
size_t scan_asn1oid(const char* src,const char* max,unsigned long* array,unsigned long* arraylen) { size_t scan_asn1oid(const char* src,const char* max,unsigned long* array,size_t* arraylen) {
const char* orig=src; size_t res,tlen;
size_t res,tlen,cur=0,al;
unsigned long tag,tmp; unsigned long tag,tmp;
enum asn1_tagclass tc; enum asn1_tagclass tc;
enum asn1_tagtype tt; enum asn1_tagtype tt;
if (!arraylen) return 0; if (!arraylen) return 0;
al=*arraylen; *arraylen=0; if (!(res=scan_asn1tag(src,max,&tc,&tt,&tag)) ||
if (!(res=scan_asn1tag(src,max,&tc,&tt,&tag))) return 0; (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=OBJECT_IDENTIFIER) ||
if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=OBJECT_IDENTIFIER) return 0; !(tmp=scan_asn1length(src+res,max,&tlen)) || tlen<1) {
if (!(tmp=scan_asn1length(src+res,max,&tlen))) return 0; *arraylen=0;
if (tlen<1) return 0; /* there has to be at least one octet */ return 0;
}
res+=tmp; res+=tmp;
if (max>src+res+tlen) max=src+res+tlen; /* clamp max down */ if (max>src+res+tlen) max=src+res+tlen; /* clamp max down */
src+=res; src+=res;
{ return scan_asn1rawoid(src,max,array,arraylen);
int a,b;
a=(unsigned char)*src;
b=a%40;
a/=40;
/* a can be 0, 1 or 2. And b is <=39 if a is 0 or 1.
* So, if a is bigger than 2, it is really 2 */
if (a>2) {
b+=(a-2)*40;
a=2;
}
if (array && cur<al) array[cur]=a; ++cur;
if (array && cur<al) array[cur]=b; ++cur;
}
for (++src; src<max; ) {
size_t i;
unsigned long tmp;
if (!(i=scan_asn1tagint(src,max,&tmp)))
return 0;
src+=i;
if (array && cur<al) array[cur]=tmp; ++cur;
}
/* if we got this far, then we have an OID, but it might not have fit */
*arraylen=cur;
if (cur>al) /* did not fit */
return 0;
return src-orig;
} }

40
scan_asn1rawoid.c Normal file
View File

@@ -0,0 +1,40 @@
#include "asn1.h"
size_t scan_asn1rawoid(const char* src,const char* max,unsigned long* array,size_t* arraylen) {
const char* orig=src;
size_t cur=0,al;
if (!arraylen) return 0;
al=*arraylen; *arraylen=0;
if (max-src<1) return 0; /* there has to be at least one octet */
{
int a,b;
a=(unsigned char)*src;
b=a%40;
a/=40;
/* a can be 0, 1 or 2. And b is <=39 if a is 0 or 1.
* So, if a is bigger than 2, it is really 2 */
if (a>2) {
b+=(a-2)*40;
a=2;
}
if (array && cur<al) array[cur]=a; ++cur;
if (array && cur<al) array[cur]=b; ++cur;
}
for (++src; src<max; ) {
size_t i;
unsigned long tmp;
if (!(i=scan_asn1tagint(src,max,&tmp)))
return 0;
src+=i;
if (array && cur<al) array[cur]=tmp; ++cur;
}
/* if we got this far, then we have an OID, but it might not have fit */
*arraylen=cur;
if (cur>al) /* did not fit */
return 0;
return src-orig;
}

39
t10.c
View File

@@ -41,6 +41,7 @@ void printasn1(const char* buf,const char* max) {
case INTEGER: printf("INTEGER"); break; case INTEGER: printf("INTEGER"); break;
case BIT_STRING: printf("BIT_STRING"); break; case BIT_STRING: printf("BIT_STRING"); break;
case OCTET_STRING: printf("OCTET_STRING"); break; case OCTET_STRING: printf("OCTET_STRING"); break;
case OBJECT_IDENTIFIER: printf("OBJECT_IDENTIFIER"); break;
case ENUMERATED: printf("ENUMERATED"); break; case ENUMERATED: printf("ENUMERATED"); break;
case SEQUENCE_OF: printf("SEQUENCE_OF"); break; case SEQUENCE_OF: printf("SEQUENCE_OF"); break;
case SET_OF: printf("SET_OF"); break; case SET_OF: printf("SET_OF"); break;
@@ -68,11 +69,23 @@ void printasn1(const char* buf,const char* max) {
printf("%*s-> \"",indent,""); printf("%*s-> \"",indent,"");
for (i=0; i<len; ++i) { for (i=0; i<len; ++i) {
if (buf[i]<' ') if (buf[i]<' ')
printf("\\x%02x",buf[i]); printf("\\x%02x",(unsigned char)(buf[i]));
else else
putchar(buf[i]); putchar(buf[i]);
} }
printf("\"\n"); printf("\"\n");
} else if (tag==OBJECT_IDENTIFIER) {
struct oid o;
size_t mlen;
unsigned long fnord[100];
o.l=100;
o.a=fnord;
mlen=scan_asn1rawoid(buf,maxstack[sptr],o.a,&o.l);
if (mlen) {
printf("%*s-> ",indent,"");
for (i=0; i<o.l; ++i)
printf("%d%s",o.a[i],i+1==o.l?"\n":".");
}
} }
} }
@@ -96,11 +109,20 @@ void printasn1(const char* buf,const char* max) {
} }
} }
const unsigned long oid[]={1,2,840,113549,1};
const unsigned long oidlen = sizeof(oid) / sizeof(oid[0]);
main() { main() {
char buf[1024]; char buf[1024];
int l,i; int l,i;
struct oid o;
struct string B;
B.s="\xfe\x74";
B.l=8+6;
o.l=oidlen;
o.a=oid;
byte_zero(buf,1024); byte_zero(buf,1024);
l=fmt_asn1generic(buf,"a{is}",8,23,"fnord"); l=fmt_asn1generic(buf,"a{isbo}",8,23,"fnord",&B,&o);
printf("formatted into %d bytes\n",l); printf("formatted into %d bytes\n",l);
{ {
printf("-> "); printf("-> ");
@@ -117,13 +139,24 @@ main() {
unsigned long a2; unsigned long a2;
unsigned long b; unsigned long b;
struct string c; struct string c;
l=scan_asn1generic(buf,buf+l,"a{!is}",&a,&a2,&b,&c); struct oid d;
struct string e;
l=scan_asn1generic(buf,buf+l,"a{!isbo}",&a,&a2,&b,&c,&e,&d);
printf("%lu\n",l); printf("%lu\n",l);
if (l) { if (l) {
printf("got application tag %d (should be 8)\n",a); printf("got application tag %d (should be 8)\n",a);
printf("got sequence length %d\n",a2); printf("got sequence length %d\n",a2);
printf("got integer %d (should be 23)\n",b); printf("got integer %d (should be 23)\n",b);
printf("got string \"%.*s\" (should be \"fnord\")\n",c.l,c.s); printf("got string \"%.*s\" (should be \"fnord\")\n",c.l,c.s);
printf("got bitstring length %d: ",e.l);
for (i=0; i*8<e.l; ++i)
printf("%02x",(unsigned char)e.s[i]);
printf("\n");
printf("got oid ");
for (i=0; i<d.l; ++i)
printf("%d%s",d.a[i],i+1<d.l?".":" (should be 1.2.840.113549.1)\n");
} }
} }
} }