diff --git a/Makefile b/Makefile index ad7aced..374ccc7 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 scan_asn1rawoid.o fmt_asn1bitstring.o +fmt_asn1generic.o scan_asn1rawoid.o fmt_asn1bitstring.o asn1oid.o ldap.a: scan_ldapmessage.o fmt_ldapmessage.o fmt_ldapbindrequest.o \ scan_ldapbindrequest.o scan_ldapbindresponse.o scan_ldapresult.o \ @@ -159,4 +159,6 @@ scan_ldapsearchresultentry.o: scan_ldapsearchresultentry.c asn1.h ldap.h scan_ldapstring.o: scan_ldapstring.c asn1.h ldap.h scan_asn1generic.o: scan_asn1generic.c asn1.h +asn1oid.o: asn1oid.c asn1.h + ldap_match_sre.o: ldap_match_sre.c ldap.h diff --git a/asn1.h b/asn1.h index ef3c52a..4ddd68e 100644 --- a/asn1.h +++ b/asn1.h @@ -23,10 +23,13 @@ enum asn1_tag { INTEGER=2, BIT_STRING=3, OCTET_STRING=4, + _NULL=5, OBJECT_IDENTIFIER=6, ENUMERATED=10, SEQUENCE_OF=16, SET_OF=17, + PrintableString=19, + IA5String=22, UTCTIME=23 }; @@ -160,4 +163,58 @@ struct oid { size_t* a; }; +enum x509_oid { + X509_ATTR_COMMONNAME, X509_ATTR_SURNAME, X509_ATTR_SERIALNUMBER, + X509_ATTR_COUNTRY, X509_ATTR_LOCALITY, X509_ATTR_STATEPROVINCE, + X509_ATTR_STREET, X509_ATTR_ORG, X509_ATTR_ORGUNIT, X509_ATTR_TITLE, + X509_ATTR_DESC, X509_ATTR_GIVENNAME, X509_ATTR_INITIALS, + X509_ATTR_GENERATIONQUALIFIER, X509_ATTR_UNIQID, X509_ATTR_DNQUALIFIER, + X509_ATTR_EMAIL, + + X509_EXT_SUBJKEYID, X509_EXT_KEYUSAGE, X509_EXT_PRIVKEYUSAGEPERIOD, + X509_EXT_SUBJALTNAME, X509_EXT_ISSUERALTNAME, X509_EXT_BASICCONSTRAINTS, + X509_EXT_CRL_NUMBER, X509_EXT_REASONCODE, X509_EXT_INSTRUCTIONCODE, + X509_EXT_INVALIDITYDATE, X509_EXT_DELTA_CRL_INDICATOR, + X509_EXT_ISSUING_DISTRIBUTION_POINT, X509_EXT_NAME_CONSTRAINTS, + X509_EXT_CRL_DISTRIBUTION_POINTS, X509_EXT_CERT_POLICIES, + X509_EXT_AUTH_KEY_ID, X509_EXT_KEY_USAGE, + + X509_ALG_MD2RSA, X509_ALG_MD4RSA, X509_ALG_MD5RSA, X509_ALG_SHA1RSA, + X509_ALG_SHA256RSA, X509_ALG_SHA384RSA, X509_ALG_SHA512RSA, + X509_ALG_SHA224RSA, X509_ALG_RSA, +}; + +extern const struct oidlookup { + size_t l; + const char* oid,* name; + enum x509_oid id; +} oid2string[]; + +size_t lookupoid(const char* oid,size_t l); + +/* Generic parser and formatter routines: */ +size_t scan_asn1generic(const char* src,const char* max,const char* fmt,...); +size_t fmt_asn1generic(char* dest,const char* fmt,...); +/* the format string works like this: + * 'i' next argument is a long* (scan) or unsigned long (fmt) + * '*' (fmt only) next argument is an unsigned long, tag type is set to APPLICATION and tag is set to that argument + * '*' (scan only) next argument is an unsigned long*; for next tag, expect tag type to be APPLICATION and write tag to this unsigned long* + * 'b' next argument is a struct string* but the length l in it is in bits, not bytes; if the length is not a multiple of 8, the unused bits are at the end of the last byte in the string + * 'S' (fmt only) next argument is struct string *, send as OCTET_STRING + * 's' (fmt only) next argument is const char*, use strlen and send as OCTET_STRING + * 's' (scan only) next argument is struct string*, parse OCTET_STRING into it + * 'o' (fmt only) next argument is struct oid*, send OBJECT_IDENTIFIER + * 'o' (scan only) next argument is struct string*, parse raw OBJECT_IDENTIFIER into it; you have to call scan_asn1rawoid on contents of string to process further + * '[' start set + * ']' end set + * '{' start sequence + * '}' end sequence + * '?' from here till end of input / set / sequence is optional and can be missing + * 'u' (scan only) next argument is time_t*, parse UTCTIME into it + * 'p' (scan only) like 's' but check that contents of string is printable + * 'a' (scan only) like 's' but check that contents of string is ascii + * '!' (scan only) next argument is struct string*, fill in region until end of current sequence / set (for optional data) + * 'c' context specific value (tag class PRIVATE, type CONSTRUCTED, tag taken from unsigned long arg / written to unsigned long* argument) + */ + #endif diff --git a/asn1oid.c b/asn1oid.c new file mode 100644 index 0000000..dfbdede --- /dev/null +++ b/asn1oid.c @@ -0,0 +1,68 @@ +#include +#include "asn1.h" + +#define ENTRY(a,b,c) { sizeof(a)-1, a, b, c } + +const struct oidlookup oid2string[] = { + + /* naming attribute OIDs */ + ENTRY("\x55\x04\x03", "commonName", X509_ATTR_COMMONNAME), + ENTRY("\x55\x04\x04", "surname", X509_ATTR_SURNAME), + ENTRY("\x55\x04\x05", "serialNumber", X509_ATTR_SERIALNUMBER), + ENTRY("\x55\x04\x06", "countryName", X509_ATTR_COUNTRY), + ENTRY("\x55\x04\x07", "localityName", X509_ATTR_LOCALITY), + ENTRY("\x55\x04\x08", "stateOrProvinceName", X509_ATTR_STATEPROVINCE), + ENTRY("\x55\x04\x09", "street", X509_ATTR_STREET), + ENTRY("\x55\x04\x0a", "organizationName", X509_ATTR_ORG), + ENTRY("\x55\x04\x0b", "organizationalUnitName", X509_ATTR_ORGUNIT), + ENTRY("\x55\x04\x0c", "title", X509_ATTR_TITLE), + ENTRY("\x55\x04\x0d", "description", X509_ATTR_DESC), + ENTRY("\x55\x04\x2a", "givenName", X509_ATTR_GIVENNAME), + ENTRY("\x55\x04\x2b", "initials", X509_ATTR_INITIALS), + ENTRY("\x55\x04\x2c", "generationQualifier", X509_ATTR_GENERATIONQUALIFIER), + ENTRY("\x55\x04\x2d", "uniqueIdentifier", X509_ATTR_UNIQID), + ENTRY("\x55\x04\x2e", "dnQualifier", X509_ATTR_DNQUALIFIER), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01", "emailAddress", X509_ATTR_EMAIL), + + /* X.509v3 extension OIDs */ + ENTRY("\x55\x1d\x0e", "subject_key_identifier", X509_EXT_SUBJKEYID), + ENTRY("\x55\x1d\x0f", "key_usage", X509_EXT_KEYUSAGE), + ENTRY("\x55\x1d\x10", "private_key_usage_period", X509_EXT_PRIVKEYUSAGEPERIOD), + ENTRY("\x55\x1d\x11", "subject_alt_name", X509_EXT_SUBJALTNAME), + ENTRY("\x55\x1d\x12", "issuer_alt_name", X509_EXT_ISSUERALTNAME), + ENTRY("\x55\x1d\x13", "basic_constraints", X509_EXT_BASICCONSTRAINTS), + ENTRY("\x55\x1d\x14", "crl_number", X509_EXT_CRL_NUMBER), + ENTRY("\x55\x1d\x15", "reasonCode", X509_EXT_REASONCODE), + ENTRY("\x55\x1d\x17", "instruction_code", X509_EXT_INSTRUCTIONCODE), + ENTRY("\x55\x1d\x18", "invalidity_date", X509_EXT_INVALIDITYDATE), + ENTRY("\x55\x1d\x1b", "delta_crl_indicator", X509_EXT_DELTA_CRL_INDICATOR), + ENTRY("\x55\x1d\x1c", "issuing_distribution_point", X509_EXT_ISSUING_DISTRIBUTION_POINT), + ENTRY("\x55\x1d\x1e", "name_constraints", X509_EXT_NAME_CONSTRAINTS), + ENTRY("\x55\x1d\x1f", "crl_distribution_points", X509_EXT_CRL_DISTRIBUTION_POINTS), + ENTRY("\x55\x1d\x20", "certificate_policies", X509_EXT_CERT_POLICIES), + ENTRY("\x55\x1d\x23", "authority_key_identifier", X509_EXT_AUTH_KEY_ID), + ENTRY("\x55\x1d\x25", "ext_key_usage", X509_EXT_KEY_USAGE), + + /* X.509 algorithms */ + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x01\x02", "md2WithRSAEncryption", X509_ALG_MD2RSA), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x01\x03", "md4WithRSAEncryption", X509_ALG_MD4RSA), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x01\x04", "md5WithRSAEncryption", X509_ALG_MD5RSA), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05", "SHA1WithRSAEncryption", X509_ALG_SHA1RSA), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b", "SHA256WithRSAEncryption", X509_ALG_SHA256RSA), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0c", "SHA384WithRSAEncryption", X509_ALG_SHA384RSA), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0d", "SHA512WithRSAEncryption", X509_ALG_SHA512RSA), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0e", "SHA224WithRSAEncryption", X509_ALG_SHA224RSA), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01", "rsaEncryption", X509_ALG_RSA), + +}; + +#undef ENTRY + +size_t lookupoid(const char* oid,size_t l) { + size_t i; + for (i=0; ia,o->l); application=NULL; break; + case 'c': // start context specific section + desttag=va_arg(args,unsigned long); + // fall through + case '[': // start SET case '{': // start SEQUENCE if (application) curlen=fmt_asn1tag(realdest,APPLICATION,CONSTRUCTED,*application); + else if (*fmt=='c') + curlen=fmt_asn1tag(realdest,PRIVATE,CONSTRUCTED,desttag); else - curlen=fmt_asn1tag(realdest,UNIVERSAL,CONSTRUCTED,SEQUENCE_OF); + curlen=fmt_asn1tag(realdest,UNIVERSAL,CONSTRUCTED,*fmt=='{'?SEQUENCE_OF:SET_OF); containerstack[curinstack++]=cursor+curlen; application=NULL; break; + case ']': // end of SET case '}': // end of SEQUENCE /* we just wrote the tag and the sequence. Now that we wrote the * sequence, we know the length it took, and we need to move the diff --git a/printasn1.c b/printasn1.c new file mode 100644 index 0000000..facd2eb --- /dev/null +++ b/printasn1.c @@ -0,0 +1,123 @@ +/* needs stdio.h and asn1.h included */ + +void printasn1(const char* buf,const char* max) { + const char* maxstack[100]; + size_t sptr=0; + size_t indent=0; + unsigned long tag; + enum asn1_tagclass tc; + enum asn1_tagtype tt; + size_t cl,len; + maxstack[sptr]=max; + while (buf %ld\n",indent,"",l); + } else if (tag==OCTET_STRING || tag==PrintableString || tag==IA5String || tag==UTCTIME) { + printf("%*s-> \"",indent,""); + for (i=0; i ",indent,""); + for (i=0; i=99) { + printf("too many nested constructed elements!\n"); + return; + } + maxstack[++sptr]=buf+len; + } else + buf+=len; + + while (sptr && maxstack[sptr]<=buf) { + --sptr; + indent-=2; + printf("%*s}\n",indent,""); + } + + } +} + + diff --git a/scan_asn1generic.c b/scan_asn1generic.c index a4a9b0b..b00d0f2 100644 --- a/scan_asn1generic.c +++ b/scan_asn1generic.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include "asn1.h" size_t scan_asn1generic(const char* src,const char* max,const char* fmt,...) { @@ -12,6 +14,8 @@ size_t scan_asn1generic(const char* src,const char* max,const char* fmt,...) { 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; @@ -34,13 +38,58 @@ size_t scan_asn1generic(const char* src,const char* max,const char* fmt,...) { } if (!curlen) { if (optional) break; else return 0; } src+=curlen; - application=0; + application=NULL; break; } - case 'b': // s = BIT STRING - case 's': // s = STRING + case 'I': // I = INTEGER, but for bignum integers; writes to an array of size_t, first one contains number of digits after it { - struct string* dest=va_arg(args,struct string*); + 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); @@ -49,23 +98,96 @@ 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!=(*fmt=='s'?OCTET_STRING:BIT_STRING)) + if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=wantedtag) return 0; } - if (*fmt=='b') { // additional checks for bit strings + 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=0; + application=NULL; break; } case 'o': // o == OID { - struct oid* dest=va_arg(args,struct 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) { @@ -79,32 +201,37 @@ size_t scan_asn1generic(const char* src,const char* max,const char* fmt,...) { 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; + dest->s=src; + dest->l=seqlen; + src+=seqlen; + application=NULL; + break; } - case 'a': // next tag class is APPLICATION instead of UNIVERSAL; write tag to unsigned long* + 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) return 0; + if (tc!=APPLICATION || tt!=CONSTRUCTED) return 0; *application=tag; } else { - if (tc!=UNIVERSAL || tt!=CONSTRUCTED || tag!=SEQUENCE_OF) - return 0; + 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); @@ -112,24 +239,23 @@ size_t scan_asn1generic(const char* src,const char* max,const char* fmt,...) { if (curmax>99) return 0; maxstack[++curmax]=src+curlen+seqlen; src+=curlen; - application=0; + application=NULL; break; } - case '!': // save current max-src into size_t - // useful for ldap, where you have an application sequence - // and the tag defines which encoding you have inside the - // sequence, so you can't put it in the format string. - // you still need to know the length so you can call this function - // again on the rest of the data. + case '!': // save current src and max-src into struct string* + // useful for optional parts or CHOICEs { - size_t* dest=va_arg(args,size_t*); - *dest=maxstack[curmax]-src; + 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; } diff --git a/scan_certificate.c b/scan_certificate.c new file mode 100644 index 0000000..8821f39 --- /dev/null +++ b/scan_certificate.c @@ -0,0 +1,254 @@ +#include +#include +#include "asn1.h" +#include "str.h" +#include "textcode.h" + +struct x509signature { + struct string oid; /* you are not expected to actually decode this */ + size_t oididx; /* if this is (size_t)-1, then the parser did not know the OID. + Otherwise it's the index into oid2string. oid2string[oididx].id + should be something like X509_ALG_SHA1RSA (see asn1.h) */ + struct string bitstring; /* In this string, the length is in bits, not bytes! */ + /* If the length is not a multiple of 8, then the unused bits are missing in the last byte. + * The parser already validated that the last byte is padded with 0 bits */ +}; + +struct x509cert { + enum { v1=0, v1988=0, v2=1, v3=2, v1996=2 } version; + size_t serial; + struct x509signature algid; + struct string issuer; /* this is the raw asn.1 structure, a SET of "[{op}]" in scan_asn1generic terms */ + time_t notbefore, notafter; + struct string subject; /* this is the raw asn.1 structure, a SET of "[{op}]" in scan_asn1generic terms */ + struct x509signature sig; +}; + +void printasn1(const char* buf,const char* max); + +static int findindn(struct string* dn,enum x509_oid id,struct string* dest) { + size_t i; + const char* c=dn->s; + const char* max=dn->s+dn->l; + for (;;) { + struct string oid; + size_t l=scan_asn1generic(c,max,"[{op}]",&oid,dest); + if (l) { + i=lookupoid(oid.s,oid.l); + if (i!=(size_t)-1) { // recognized the oid! + if (oid2string[i].id==id) + return 1; + } + c+=l; + } else break; + } + return 0; +} + +size_t scan_certificate(const char* cert,size_t l, struct x509cert* C) { + char* c=0,* x; + /* if it's base64 encoded, decode first */ + if (l > 27+25+2 && str_start(cert,"-----BEGIN CERTIFICATE-----")) +certfound: + { + size_t cur,used; + /* "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----" and newlines */ + c=malloc((l-27-25-2)/4*3); + if (!c) return 0; + x=c; + for (cur=27; cur+26'~')) { + a=0; + break; + } + if (str_start(cert+i,"-----BEGIN CERTIFICATE-----")) { + cert+=i; + l-=i; + goto certfound; + } + } + if (a) /* if we end up here, it was ascii but did not contain a certificate. fail. */ + return 0; + } + /* if we end up here, we decoded some base64 data or we found some + * binary data. See if it looks like x.509 at all. If it does, it + * starts with a SEQUENCE_OF, which encodes as '0'. */ + if (*cert!='0') { +parseerror: + free(c); + return 0; + } + + /* now for the heavy lifting */ + { + unsigned long tagforversion; // must be 0 + unsigned long version; + struct string oidalg,algparams,pubkeyalg,extensions,oidsig,sigrest,sigdata; + size_t i; + if (scan_asn1generic(cert,cert+l,"{{ci]i{o!}{!}{uu}{!}{!}!}{o!}b}", + &tagforversion, + &version, + &C->serial, + &oidalg, &algparams, + &C->issuer, + &C->notbefore, &C->notafter, + &C->subject, + &pubkeyalg, + &extensions, + &oidsig, &sigrest, &sigdata)) { + + if (version==0) + printf("X.509 certificate\n"); + else if (version==1) + printf("X.509v2 certificate\n"); + else if (version==2) + printf("X.509v3 certificate\n"); + else + printf("unsupported version %ld (must be 0, 1 or 2)\n",version); + + printf("serial number %lu\n",C->serial); + + printf("issuer: "); + { + struct string s; + if (findindn(&C->issuer,X509_ATTR_COUNTRY,&s)) + printf("C=%.*s ",(int)s.l,s.s); + if (findindn(&C->issuer,X509_ATTR_ORG,&s)) + printf("O=%.*s ",(int)s.l,s.s); + if (findindn(&C->issuer,X509_ATTR_COMMONNAME,&s)) + printf("CN=%.*s ",(int)s.l,s.s); + } + printf("\n"); + + { + char a[100],b[100]; + a[fmt_httpdate(a,C->notbefore)]=0; + b[fmt_httpdate(b,C->notafter)]=0; + printf("valid not before %s and not after %s\n",a,b); + } + + printf("subject: "); + { + struct string s; + if (findindn(&C->issuer,X509_ATTR_COUNTRY,&s)) + printf("C=%.*s ",(int)s.l,s.s); + if (findindn(&C->issuer,X509_ATTR_ORG,&s)) + printf("O=%.*s ",(int)s.l,s.s); + if (findindn(&C->issuer,X509_ATTR_COMMONNAME,&s)) + printf("CN=%.*s ",(int)s.l,s.s); + } + printf("\n"); + + i=lookupoid(oidalg.s,oidalg.l); + if (i!=(size_t)-1) + printf("signature algorithm %s\n",oid2string[i].name); + else { + unsigned long temp[100]; + size_t len=100; + if (scan_asn1rawoid(oidalg.s,oidalg.s+oidalg.l,temp,&len)) { + printf("Unknown signature algorithm (oid "); + for (i=0; i>((sizeof(modulus[0])*8)-(j+1)*8))&0xff); + } + if ((i-1)%4==3 || i==modulus[0]) printf("\n"); + } + } else + printf("bignum scanning failed!\n"); + } + /* for RSA, bits is actually another sequence with two integers, modulus and publicExponent */ + printf("pubkeyparams len %lu, bits len %lu\n",pubkeyparams.l,bits.l); + } + } else { + unsigned long temp[100]; + size_t len=100; + if (scan_asn1rawoid(pubkeyoid.s,pubkeyoid.s+pubkeyoid.l,temp,&len)) { + printf("Unknown public key algorithm (oid "); + for (i=0; i + +#include "printasn1.c" + +int main(int argc,char* argv[]) { + char* buf; + size_t l,n; + struct x509cert c; + + buf=mmap_read(argc>1?argv[1]:"test.pem",&l); + if (!buf) { puts("test.pem not found"); return 1; } + + n=scan_certificate(buf,l,&c); +} diff --git a/t10.c b/t10.c index 916e87a..bc0b060 100644 --- a/t10.c +++ b/t10.c @@ -1,115 +1,11 @@ #include #include +#include #include "asn1.h" -void printasn1(const char* buf,const char* max) { - const char* maxstack[100]; - size_t sptr=0; - size_t indent=0; - unsigned long tag; - enum asn1_tagclass tc; - enum asn1_tagtype tt; - size_t cl,len; - maxstack[sptr]=max; - while (buf %ld\n",indent,"",l); - } else if (tag==OCTET_STRING) { - printf("%*s-> \"",indent,""); - for (i=0; i ",indent,""); - for (i=0; i=99) { - printf("too many nested constructed elements!\n"); - return; - } - maxstack[++sptr]=buf+len; - } else - buf+=len; - - while (sptr && maxstack[sptr]<=buf) { - --sptr; - indent-=2; - printf("%*s}\n",indent,""); - } - - } -} - -const unsigned long oid[]={1,2,840,113549,1}; +unsigned long oid[]={1,2,840,113549,1}; const unsigned long oidlen = sizeof(oid) / sizeof(oid[0]); main() { @@ -122,7 +18,7 @@ main() { o.l=oidlen; o.a=oid; byte_zero(buf,1024); - l=fmt_asn1generic(buf,"a{isbo}",8,23,"fnord",&B,&o); + l=fmt_asn1generic(buf,"*{isbo}",8,23,"fnord",&B,&o); printf("formatted into %d bytes\n",l); { printf("-> "); @@ -139,9 +35,9 @@ main() { unsigned long a2; unsigned long b; struct string c; - struct oid d; + struct string d; struct string e; - l=scan_asn1generic(buf,buf+l,"a{!isbo}",&a,&a2,&b,&c,&e,&d); + l=scan_asn1generic(buf,buf+l,"*{!isbo}",&a,&a2,&b,&c,&e,&d); printf("%lu\n",l); if (l) { printf("got application tag %d (should be 8)\n",a); @@ -155,8 +51,22 @@ main() { printf("\n"); printf("got oid "); - for (i=0; i