summaryrefslogtreecommitdiffstats
path: root/scan_asn1generic.c
diff options
context:
space:
mode:
authorSimon Rettberg2014-03-15 01:49:50 +0100
committerSimon Rettberg2014-03-15 01:49:50 +0100
commitbedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad (patch)
treec7d1995a09f6ed0c4e6873252e957d72f5d07d07 /scan_asn1generic.c
downloadldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.tar.gz
ldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.tar.xz
ldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.zip
Lean and mean initial commit
Not much functionality yet
Diffstat (limited to 'scan_asn1generic.c')
-rw-r--r--scan_asn1generic.c270
1 files changed, 270 insertions, 0 deletions
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 <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+#include <ctype.h>
+#include "asn1.h"
+#include <string.h>
+
+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; i<dest->l; ++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; i<dest->l; ++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; j<i+4; ++j)
+ if (!isdigit(dest->s[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;
+}