From bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Sat, 15 Mar 2014 01:49:50 +0100 Subject: Lean and mean initial commit Not much functionality yet --- scan_asn1generic.c | 270 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 scan_asn1generic.c (limited to 'scan_asn1generic.c') diff --git a/scan_asn1generic.c b/scan_asn1generic.c new file mode 100644 index 0000000..c246efd --- /dev/null +++ b/scan_asn1generic.c @@ -0,0 +1,270 @@ +#include +#include +#include +#include +#include "asn1.h" +#include + +size_t scan_asn1generic(const char* src,const char* max,const char* fmt,...) { + size_t curlen,seqlen; + const char* maxstack[100]; + size_t curmax=0; + va_list args; + int optional=0; + unsigned long* application=NULL; + unsigned long tag; + enum asn1_tagclass tc; + enum asn1_tagtype tt; + unsigned int wantedtag; + unsigned long* desttag=NULL; + const char* orig=src; + va_start(args,fmt); + maxstack[0]=max; + while (*fmt) { + switch (*fmt) { + case '?': // ? = rest is optional (until end of sequence) + optional=1; + break; + case 'i': // i = INTEGER + { + long* dest=va_arg(args,long*); + *dest=0; + curlen=scan_asn1int(src,maxstack[curmax],&tc,&tt,&tag,dest); + if (application) { + if (tc!=APPLICATION) return 0; + *application=tag; + } else { + if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=INTEGER) + return 0; + } + if (!curlen) { if (optional) break; else return 0; } + src+=curlen; + application=NULL; + break; + } + case 'I': // I = INTEGER, but for bignum integers; writes to an array of size_t, first one contains number of digits after it + { + size_t* dest=va_arg(args,size_t*); + size_t len,tmp,tlen,j,t; + if (!(len=scan_asn1tag(src,maxstack[curmax],&tc,&tt,&tag))) return 0; + if (!(tmp=scan_asn1length(src+len,maxstack[curmax],&tlen))) return 0; + len+=tmp; + j=0; t=1; + src+=len; + /* asn.1 sends n bytes, most significant first. + * we want m digits, most significant first. + * if n is not a multiple of sizeof(digit) then we need to + * insert a few 0 bytes in the first word + */ + while (tlen) { + j=(j<<8)+(unsigned char)(*src); + ++src; + --tlen; + if ((tlen%sizeof(j))==0 && (j || t>1)) { + dest[t]=j; + j=0; + ++t; + } + } + if (j) dest[t++]=j; + dest[0]=t-1; + break; + } + case 'b': + wantedtag=BIT_STRING; goto stringmain; + case 'u': + wantedtag=UTCTIME; goto stringmain; + case 'p': + wantedtag=PrintableString; goto stringmain; + case 'a': + wantedtag=IA5String; goto stringmain; + case 's': + wantedtag=OCTET_STRING; goto stringmain; +stringmain: + { + struct string* dest; + struct string temp; + time_t* desttime=NULL; + size_t i; + if (wantedtag==UTCTIME) { + dest=&temp; + desttime=va_arg(args,time_t*); + } else + dest=va_arg(args,struct string*); + dest->l=0; + dest->s=0; + curlen=scan_asn1string(src,maxstack[curmax],&tc,&tt,&tag,&dest->s,&dest->l); + if (!curlen) { if (optional) break; else return 0; } + if (application) { + if (tc!=APPLICATION) return 0; + *application=tag; + } else { + if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=wantedtag) + return 0; + } + if (wantedtag==BIT_STRING) { // 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; + } else if (wantedtag==PrintableString) { + for (i=0; il; ++i) // RFC 2252 section 4.1 production p + if (!isalnum(dest->s[i]) + && dest->s[i]!='"' + && dest->s[i]!='(' + && dest->s[i]!=')' + && dest->s[i]!='+' + && dest->s[i]!=',' + && dest->s[i]!='-' + && dest->s[i]!='.' + && dest->s[i]!='/' + && dest->s[i]!=':' + && dest->s[i]!='?' + && dest->s[i]!=' ') return 0; + } else if (wantedtag==IA5String) { + for (i=0; il; ++i) // IA5String is an ASCII string, which means 0 <= s[i] <= 127 + if ((unsigned char)(dest->s[i]) > 127) return 0; + } else if (wantedtag==UTCTIME) { + size_t j; + struct tm t; + memset(&t,0,sizeof(t)); + /* + YYMMDDhhmmZ + YYMMDDhhmm+hh'mm' + YYMMDDhhmm-hh'mm' + YYMMDDhhmmssZ + YYMMDDhhmmss+hh'mm' + YYMMDDhhmmss-hh'mm' + */ + if (dest->l<11 || dest->l>17) return 0; + j=(dest->s[0]-'0')*10+dest->s[1]-'0'; + t.tm_year=j+(j<70)*100; + + for (i=0; i<10; ++i) + if (!isdigit(dest->s[i])) return 0; + j=(dest->s[2]-'0')*10+dest->s[3]-'0'; // is the month plausible? + if (j<1 || j>12) return 0; + t.tm_mon=j-1; + j=(dest->s[4]-'0')*10+dest->s[5]-'0'; // is the day plausible? + if (j<1 || j>31) return 0; + t.tm_mday=j; + j=(dest->s[6]-'0')*10+dest->s[7]-'0'; // is the hour plausible? + if (j>23) return 0; + t.tm_hour=j; + j=(dest->s[8]-'0')*10+dest->s[9]-'0'; // is the minutes plausible? + if (j>59) return 0; + t.tm_min=j; + i=10; + if (isdigit(dest->s[10])) { + i+=2; + j=(dest->s[10]-'0')*10+dest->s[11]-'0'; // is the seconds plausible? + if (j>59) return 0; + t.tm_sec=j; + } + *desttime=mktime(&t); + if (dest->s[i]=='+' || dest->s[i]=='-') { + size_t j; + if (dest->l!=15) return 0; + for (j=i; js[j])) return 0; + j=(dest->s[i]-'0')*10+dest->s[i+1]-'0'; // is the offset minutes plausible? + if (j>59) return 0; + if (dest->s[i]=='+') + *desttime+=j*60; + else + *desttime-=j*60; + j=(dest->s[i+2]-'0')*10+dest->s[i+3]-'0'; // is the offset seconds plausible? + if (j>59) return 0; + if (dest->s[i]=='+') + *desttime+=j; + else + *desttime-=j; + } else if (dest->s[i]!='Z') return 0; + } + src+=curlen; + application=NULL; + break; + } + case 'o': // o == OID + { + struct string* dest=va_arg(args,struct string*); + 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; + dest->s=src; + dest->l=seqlen; + src+=seqlen; + application=NULL; + break; + } + case '*': // next tag class is APPLICATION instead of UNIVERSAL; write tag to unsigned long* + { + application=va_arg(args,unsigned long*); + break; + } + case 'c': // c = context specific; PRIVATE CONSTRUCTED 0, close with '}' + desttag=va_arg(args,unsigned long*); + // fall through + case '[': // [ = SET + case '{': // { = SEQUENCE + { + curlen=scan_asn1tag(src,maxstack[curmax],&tc,&tt,&tag); + if (!curlen) { if (optional) break; else return 0; } + if (application) { + if (tc!=APPLICATION || tt!=CONSTRUCTED) return 0; + *application=tag; + } else { + if (*fmt=='c') { + if (tc!=PRIVATE || tt!=CONSTRUCTED) + return 0; + *desttag=tag; + } else { + if (tc!=UNIVERSAL || tt!=CONSTRUCTED || tag!=(*fmt=='{'?SEQUENCE_OF:SET_OF)) + return 0; + } + } + src+=curlen; + curlen=scan_asn1length(src,maxstack[curmax],&seqlen); + if (!curlen) return 0; + if (curmax>99) return 0; + maxstack[++curmax]=src+curlen+seqlen; + src+=curlen; + application=NULL; + break; + } + case '!': // save current src and max-src into struct string* + // useful for optional parts or CHOICEs + { + struct string* dest=va_arg(args,struct string*); + dest->s=src; + dest->l=maxstack[curmax]-src; + break; + } + case ']': // ] = end of SET + case '}': // } = end of SEQUENCE + { + optional=0; + if (curmax==0) return 0; + src=maxstack[curmax]; + --curmax; + break; + } + default: + return 0; + } + ++fmt; + } + va_end(args); + return src-orig; +} -- cgit v1.2.3-55-g7522