From dae3ea8024afa583f34dc08e8a40d1fa24f25621 Mon Sep 17 00:00:00 2001 From: leitner Date: Thu, 28 Apr 2011 21:33:10 +0000 Subject: [PATCH] add oid and bitstring parsing and formatting to "generic" format string routines, test them in t10.c --- Makefile | 4 +++- THANKS | 4 ++-- asn1.h | 16 +++++++++++++-- fmt_asn1OID.c | 4 ++-- fmt_asn1bitstring.c | 19 ++++++++++++++++++ fmt_asn1generic.c | 17 ++++++++++++++++ ldap.h | 2 +- scan_asn1BITSTRING.c | 2 +- scan_asn1generic.c | 38 +++++++++++++++++++++++++++++++++++- scan_asn1oid.c | 46 +++++++++----------------------------------- scan_asn1rawoid.c | 40 ++++++++++++++++++++++++++++++++++++++ t10.c | 39 ++++++++++++++++++++++++++++++++++--- 12 files changed, 181 insertions(+), 50 deletions(-) create mode 100644 fmt_asn1bitstring.c create mode 100644 scan_asn1rawoid.c diff --git a/Makefile b/Makefile index 1302447..ad7aced 100644 --- a/Makefile +++ b/Makefile @@ -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 \ fmt_asn1sintpayload.o scan_asn1oid.o scan_asn1BITSTRING.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 \ 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_asn1OID.o: fmt_asn1OID.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_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_asn1length.o: scan_asn1length.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_asn1string.o: scan_asn1string.c asn1.h scan_asn1tag.o: scan_asn1tag.c asn1.h diff --git a/THANKS b/THANKS index bba4bdb..2787c67 100644 --- a/THANKS +++ b/THANKS @@ -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. -Ö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. @@ -10,5 +10,5 @@ Dirk Meyer helped BSD portability. 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. diff --git a/asn1.h b/asn1.h index 49cc9aa..ef3c52a 100644 --- a/asn1.h +++ b/asn1.h @@ -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, 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 */ #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 */ #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: @@ -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. * 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 */ -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 { size_t l; const char* s; }; +struct oid { + size_t l; + size_t* a; +}; + #endif diff --git a/fmt_asn1OID.c b/fmt_asn1OID.c index 5bd9c00..6f6fc10 100644 --- a/fmt_asn1OID.c +++ b/fmt_asn1OID.c @@ -1,12 +1,12 @@ #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; if (len<2) return 0; for (l=1,i=2; i(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; +} diff --git a/fmt_asn1generic.c b/fmt_asn1generic.c index 886bcd8..4a11417 100644 --- a/fmt_asn1generic.c +++ b/fmt_asn1generic.c @@ -9,6 +9,7 @@ size_t fmt_asn1generic(char* dest,const char* fmt,...) { va_start(args,fmt); unsigned long* application=0; struct string* s; + struct oid* o; struct string S; size_t curlen=0; size_t cursor=0; @@ -31,6 +32,14 @@ size_t fmt_asn1generic(char* dest,const char* fmt,...) { application=NULL; 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 s=va_arg(args,struct string*); copystring: @@ -45,6 +54,14 @@ copystring: S.l=strlen(S.s); s=&S; 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 if (application) curlen=fmt_asn1tag(realdest,APPLICATION,CONSTRUCTED,*application); diff --git a/ldap.h b/ldap.h index 47d5edc..ef70fae 100644 --- a/ldap.h +++ b/ldap.h @@ -1,9 +1,9 @@ #ifndef _LDAP_H #define _LDAP_H -#include "asn1.h" #include #include +#include "asn1.h" int matchstring(struct string* s,const char* c); int matchcasestring(struct string* s,const char* c); diff --git a/scan_asn1BITSTRING.c b/scan_asn1BITSTRING.c index 4553ee5..b42901a 100644 --- a/scan_asn1BITSTRING.c +++ b/scan_asn1BITSTRING.c @@ -19,7 +19,7 @@ size_t scan_asn1BITSTRING(const char* src,const char* max,const char** s,size_t* lastbyte=(*s)[*l+1]; if (lastbyte & (0xff >> (8-**s))) return 0; - *l=*l*8-(unsigned char)(**s); + *l=(*l-1)*8-(unsigned char)(**s); ++*s; return tmp; } diff --git a/scan_asn1generic.c b/scan_asn1generic.c index 21ea582..a4a9b0b 100644 --- a/scan_asn1generic.c +++ b/scan_asn1generic.c @@ -1,3 +1,4 @@ +#include #include #include "asn1.h" @@ -36,6 +37,7 @@ size_t scan_asn1generic(const char* src,const char* max,const char* fmt,...) { application=0; break; } + case 'b': // s = BIT STRING case 's': // s = 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; *application=tag; } else { - if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=OCTET_STRING) + if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=(*fmt=='s'?OCTET_STRING:BIT_STRING)) 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; application=0; 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* { application=va_arg(args,unsigned long*); diff --git a/scan_asn1oid.c b/scan_asn1oid.c index 89f85b9..acb9387 100644 --- a/scan_asn1oid.c +++ b/scan_asn1oid.c @@ -1,49 +1,21 @@ #include "asn1.h" -size_t scan_asn1oid(const char* src,const char* max,unsigned long* array,unsigned long* arraylen) { - const char* orig=src; - size_t res,tlen,cur=0,al; +size_t scan_asn1oid(const char* src,const char* max,unsigned long* array,size_t* arraylen) { + size_t res,tlen; unsigned long tag,tmp; enum asn1_tagclass tc; enum asn1_tagtype tt; if (!arraylen) return 0; - al=*arraylen; *arraylen=0; - if (!(res=scan_asn1tag(src,max,&tc,&tt,&tag))) return 0; - if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=OBJECT_IDENTIFIER) return 0; - if (!(tmp=scan_asn1length(src+res,max,&tlen))) return 0; - if (tlen<1) return 0; /* there has to be at least one octet */ + if (!(res=scan_asn1tag(src,max,&tc,&tt,&tag)) || + (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=OBJECT_IDENTIFIER) || + !(tmp=scan_asn1length(src+res,max,&tlen)) || tlen<1) { + *arraylen=0; + return 0; + } res+=tmp; if (max>src+res+tlen) max=src+res+tlen; /* clamp max down */ src+=res; - { - 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 && cural) /* did not fit */ - return 0; - return src-orig; + return scan_asn1rawoid(src,max,array,arraylen); } diff --git a/scan_asn1rawoid.c b/scan_asn1rawoid.c new file mode 100644 index 0000000..36b0eb7 --- /dev/null +++ b/scan_asn1rawoid.c @@ -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 && cural) /* did not fit */ + return 0; + return src-orig; +} + diff --git a/t10.c b/t10.c index eb88038..916e87a 100644 --- a/t10.c +++ b/t10.c @@ -41,6 +41,7 @@ void printasn1(const char* buf,const char* max) { case INTEGER: printf("INTEGER"); break; case BIT_STRING: printf("BIT_STRING"); break; case OCTET_STRING: printf("OCTET_STRING"); break; + case OBJECT_IDENTIFIER: printf("OBJECT_IDENTIFIER"); break; case ENUMERATED: printf("ENUMERATED"); break; case SEQUENCE_OF: printf("SEQUENCE_OF"); break; case SET_OF: printf("SET_OF"); break; @@ -68,11 +69,23 @@ void printasn1(const char* buf,const char* max) { printf("%*s-> \"",indent,""); for (i=0; i ",indent,""); + for (i=0; i "); @@ -117,13 +139,24 @@ main() { unsigned long a2; unsigned long b; 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); if (l) { printf("got application tag %d (should be 8)\n",a); printf("got sequence length %d\n",a2); printf("got integer %d (should be 23)\n",b); printf("got string \"%.*s\" (should be \"fnord\")\n",c.l,c.s); + + printf("got bitstring length %d: ",e.l); + for (i=0; i*8