diff options
94 files changed, 4838 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a7b0622 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +config.test +*.swp +*.o +*.a +/ldadp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..db2a48a --- /dev/null +++ b/Makefile @@ -0,0 +1,120 @@ +#DEBUG=1 +#COVERAGE=1 + +all: ldadp + +asn1.a: fmt_asn1intpayload.o fmt_asn1length.o fmt_asn1tag.o \ +fmt_asn1int.o fmt_asn1string.o fmt_asn1transparent.o scan_asn1tag.o \ +scan_asn1length.o scan_asn1int.o scan_asn1string.o scan_asn1INTEGER.o \ +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 asn1oid.o + +ldap.a: scan_ldapmessage.o fmt_ldapmessage.o fmt_ldapbindrequest.o \ +scan_ldapbindrequest.o scan_ldapbindresponse.o scan_ldapresult.o \ +scan_ldapstring.o scan_ldapsearchfilter.o scan_ldapsearchrequest.o \ +freefilter.o freeava.o scan_ldapava.o fmt_ldapsearchresultentry.o \ +fmt_ldapstring.o freepal.o scan_ldapsearchresultentry.o \ +fmt_ldapresult.o fmt_ldappal.o fmt_ldapadl.o fmt_ldapava.o \ +fmt_ldapsearchfilter.o fmt_ldapsearchrequest.o matchstring.o \ +matchprefix.o matchcasestring.o matchcaseprefix.o \ +scan_ldapmodifyrequest.o scan_ldapaddrequest.o bstrlen.o bstrfirst.o \ +bstrstart.o free_ldapadl.o free_ldappal.o free_ldapsearchfilter.o \ +scan_ldapsearchfilterstring.o free_ldapsearchresultentry.o \ +fmt_ldapsearchfilterstring.o ldap_match_sre.o \ +fmt_ldapdeleterequest.o scan_ldapdeleterequest.o normalize_dn.o + +CC=gcc +CFLAGS=-g -pipe -Wall -W -Wextra -std=gnu99 -Wno-unused-parameter +LIBS+=-lcrypto -lcrypt -lowfat + +%.o: %.c + $(CC) $(CFLAGS) -c $< + +%.a: + ar cru $@ $^ + +%: %.c + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) ${LIBS} + +ldadp: tmpbuffer.o ini.o client.o server.o helper.o proxy.o epoll.o ldap.a asn1.a + +.PHONY: clean tar +clean: + rm -f *.[ao] ldadp + +tar: clean + cd ..; tar cvvf ldadp.tar.bz2 ldadp --use=bzip2 --exclude capture --exclude .git + +bindrequest.o: bindrequest.c ldap.h + +tmpbuffer.o: tmpbuffer.c tmpbuffer.h +ini.o: ini.c ini.h +client.o: client.c client.h +server.o: server.c server.h +helper.o: helper.c helper.h +proxy.o: proxy.c proxy.h +epoll.o: epoll.c epoll.h + +fmt_asn1int.o: fmt_asn1int.c asn1.h +fmt_asn1intpayload.o: fmt_asn1intpayload.c asn1.h +fmt_asn1length.o: fmt_asn1length.c asn1.h +fmt_asn1sint.o: fmt_asn1sint.c asn1.h +fmt_asn1sintpayload.o: fmt_asn1sintpayload.c asn1.h +fmt_asn1string.o: fmt_asn1string.c asn1.h +fmt_asn1tag.o: fmt_asn1tag.c asn1.h +fmt_asn1tagint.o: fmt_asn1tagint.c asn1.h +fmt_asn1transparent.o: fmt_asn1transparent.c asn1.h +fmt_ldapadl.o: fmt_ldapadl.c asn1.h ldap.h +fmt_ldapava.o: fmt_ldapava.c asn1.h ldap.h +fmt_ldapbindrequest.o: fmt_ldapbindrequest.c asn1.h ldap.h +fmt_ldapmessage.o: fmt_ldapmessage.c asn1.h ldap.h +fmt_ldappal.o: fmt_ldappal.c asn1.h ldap.h +fmt_ldapresult.o: fmt_ldapresult.c asn1.h ldap.h +fmt_ldapsearchfilter.o: fmt_ldapsearchfilter.c asn1.h ldap.h +fmt_ldapsearchfilterstring.o: fmt_ldapsearchfilterstring.c ldap.h +fmt_ldapsearchrequest.o: fmt_ldapsearchrequest.c asn1.h ldap.h +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 +scan_asn1INTEGER.o: scan_asn1INTEGER.c asn1.h +scan_asn1SEQUENCE.o: scan_asn1SEQUENCE.c asn1.h +scan_asn1SET.o: scan_asn1SET.c asn1.h +scan_asn1STRING.o: scan_asn1STRING.c asn1.h +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 +scan_asn1tagint.o: scan_asn1tagint.c asn1.h +scan_ldapaddrequest.o: scan_ldapaddrequest.c asn1.h ldap.h +scan_ldapava.o: scan_ldapava.c asn1.h ldap.h +scan_ldapbindrequest.o: scan_ldapbindrequest.c asn1.h ldap.h +scan_ldapbindresponse.o: scan_ldapbindresponse.c asn1.h ldap.h +scan_ldapmessage.o: scan_ldapmessage.c asn1.h ldap.h +scan_ldapmodifyrequest.o: scan_ldapmodifyrequest.c asn1.h ldap.h +scan_ldapresult.o: scan_ldapresult.c asn1.h ldap.h +scan_ldapsearchfilter.o: scan_ldapsearchfilter.c asn1.h ldap.h +scan_ldapsearchfilterstring.o: scan_ldapsearchfilterstring.c ldap.h +scan_ldapsearchrequest.o: scan_ldapsearchrequest.c asn1.h ldap.h +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 + +privatekey.pem: + openssl genrsa -out $@ + @@ -0,0 +1,16 @@ +TODO: This file + +ldadp is an LDAP to Active Directory Proxy. + +Actually it's more of a translator between LDAP schemes, +presenting an LDAP scheme to Unix/Linux clients suitable +for authentication via pam and nsswitch using an Active +Directory server as the data source (which already speaks +LDAP, but doesn't supply mandatory fields for the Unix +world, like uidNumber, loginShell, homeDirectory etc.) + +LDAP and ASN1 related parser/formatter etc. taken from +tinyldap[1] by Felix von Leitner. + +[1] http://www.fefe.de/tinyldap/ + @@ -0,0 +1,232 @@ +#ifndef ASN1_H +#define ASN1_H + +/* parser and formatter for ASN.1 DER encoding. + * The parser can read BER encoding, too. */ + +#include <stddef.h> + +enum asn1_tagclass { + UNIVERSAL=(0<<6), + APPLICATION=(1<<6), + PRIVATE=(2<<6), + CONTEXT_SPECIFIC=(3<<6) +}; + +enum asn1_tagtype { + PRIMITIVE=(0<<5), + CONSTRUCTED=(1<<5) +}; + +enum asn1_tag { + BOOLEAN=1, + 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 +}; + +/* write variable length integer in the encoding used in tag and oid */ +size_t fmt_asn1tagint(char* dest,unsigned long val); + +/* write int in least amount of bytes, return number of bytes */ +/* as used in ASN.1 tag */ +size_t fmt_asn1tag(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt, + unsigned long tag); + +/* write int in least amount of bytes, return number of bytes */ +/* as used in ASN.1 length */ +size_t fmt_asn1length(char* dest,size_t l); + +/* write int in least amount of bytes, return number of bytes */ +/* as used in ASN.1 INTEGER. This only does the payload, not the tag + * and length headers! */ +size_t fmt_asn1intpayload(char* dest,unsigned long val); + +/* write int in least amount of bytes, return number of bytes */ +/* as used in ASN.1 INTEGER. This only does the payload, not the tag + * and length headers! */ +size_t fmt_asn1sintpayload(char* dest,signed long val); + +/* write int in least amount of bytes, return number of bytes */ +/* as used in ASN.1 INTEGER or ENUMERATED. */ +size_t fmt_asn1int(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt, + enum asn1_tag tag,unsigned long val); + +/* write int in least amount of bytes, return number of bytes */ +/* as used in ASN.1 INTEGER or ENUMERATED. */ +size_t fmt_asn1sint(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt, + enum asn1_tag tag,signed long val); + +/* write any data type that does not require transformation in the least + * amount of bytes, return number of bytes */ +/* as used in ASN.1 OCTET STRING, SEQUENCE etc. */ +/* does not wrote the payload itself, just the header! First construct + * the sequence/octet string so you know the length, then use + * fmt_asn1transparent to write the header before it */ +size_t fmt_asn1transparent(char* dest,enum asn1_tagclass tc, + enum asn1_tagtype tt,enum asn1_tag tag,size_t len); + +/* write string in least amount of bytes, return number of bytes */ +/* as used in ASN.1 OCTET STRING. */ +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) + +/* write ASN.1 INTEGER */ +#define fmt_asn1INTEGER(dest,l) fmt_asn1int(dest,UNIVERSAL,PRIMITIVE,INTEGER,l) + +/* write ASN.1 BOOLEAN */ +#define fmt_asn1BOOLEAN(dest,l) fmt_asn1int(dest,UNIVERSAL,PRIMITIVE,BOOLEAN,l) + +/* write ASN.1 ENUMERATED */ +#define fmt_asn1ENUMERATED(dest,l) fmt_asn1int(dest,UNIVERSAL,PRIMITIVE,ENUMERATED,l) + +/* write ASN.1 SEQUENCE */ +#define fmt_asn1SEQUENCE(dest,l) fmt_asn1transparent(dest,UNIVERSAL,CONSTRUCTED,SEQUENCE_OF,l) + +/* write ASN.1 SET */ +#define fmt_asn1SET(dest,l) fmt_asn1transparent(dest,UNIVERSAL,CONSTRUCTED,SET_OF,l) + +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: + * src points to the first byte to parse + * max points to the first byte behind the buffer + * the return value is the number of bytes parsed or 0 for parse error */ + +/* parse ASN.1 variable length integer as used in tag and oid */ +size_t scan_asn1tagint(const char* src,const char* max,unsigned long* val); + +/* parse ASN.1 tag into a tag class, tag type and tag number */ +size_t scan_asn1tag(const char* src,const char* max, + enum asn1_tagclass* tc,enum asn1_tagtype* tt, unsigned long* tag); + +/* parse ASN.1 length */ +size_t scan_asn1length(const char* src,const char* max,size_t* length); + +/* parse ASN.1 integer with tag and length */ +size_t scan_asn1int(const char* src,const char* max, + enum asn1_tagclass* tc,enum asn1_tagtype* tt, unsigned long* tag, + long* val); + +/* parse raw integer (payload after tag and length); internal helper */ +size_t scan_asn1rawint(const char* src,const char* max,size_t len,long* val); + +/* parse string with tag and length. + * Points s to the first byte in the string, and writes the length of + * the string to l. */ +size_t scan_asn1string(const char* src,const char* max, + enum asn1_tagclass* tc,enum asn1_tagtype* tt,unsigned long* tag, + const char** s,size_t* l); + +/* the following expect a specific universal type and return a parse + * error if the tag does not match that type */ +size_t scan_asn1BOOLEAN(const char* src,const char* max,unsigned long* l); +size_t scan_asn1INTEGER(const char* src,const char* max,signed long* l); +size_t scan_asn1ENUMERATED(const char* src,const char* max,unsigned long* l); +size_t scan_asn1STRING(const char* src,const char* max,const char** s,size_t* l); +size_t scan_asn1BITSTRING(const char* src,const char* max,const char** s,size_t* l); +size_t scan_asn1SEQUENCE(const char* src,const char* max,size_t* len); +size_t scan_asn1SET(const char* src,const char* max,size_t* len); + +/* scan an ASN.1 OID and put the numbers into array. + * 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,size_t* 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,size_t* array,size_t* arraylen); + +struct string { + size_t l; + const char* s; +}; + +struct oid { + size_t l; + 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_SIGNEDDATA, X509_DATA, X509_CONTENTTYPE, X509_MESSAGEDIGEST, X509_SIGNINGTIME, + X509_PKCS2, X509_NETSCAPE_CERTTYPE, X509_SMIME_CAPABILITIES, + + 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, X509_ALG_MD4, X509_ALG_MD5, + + X509_ALG_DES_ECB, X509_ALG_DES_CBC, X509_ALG_DES_OFB64, + X509_ALG_DES_CFB64, X509_ALG_RSASIGNATURE, X509_ALG_DSA_2, + X509_ALG_DSASHA, X509_ALG_SHARSA, X509_ALG_DES_EDE_ECB, X509_ALG_SHA, + X509_ALG_SHA1, X509_ALG_DSASHA1_2, X509_ALG_AES256_CBC, + X509_ALG_AES192_CBC, X509_ALG_AES128_CBC, X509_ALG_DES_EDE3_CBC, + X509_ALG_RC2_CBC, X509_ALG_RIPEMD, + + X509_ALG_GOSTR3411_94, X509_ALG_GOST28147_89, +}; + +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..fa1242e --- /dev/null +++ b/asn1oid.c @@ -0,0 +1,100 @@ +#include <string.h> +#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), + + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x07\x02", "signedData", X509_SIGNEDDATA), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x07\x01", "data", X509_DATA), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x09\x03", "contentType", X509_CONTENTTYPE), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x09\x04", "messageDigest", X509_MESSAGEDIGEST), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x09\x05", "signingTime", X509_SIGNINGTIME), + ENTRY("\x60\x86\x48\x01\x86\xf8\x42\x01\x01", "netscapeCertType", X509_NETSCAPE_CERTTYPE), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x01\x09\x0f", "smimeCapabilities", X509_SMIME_CAPABILITIES), + + /* 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\x01", "rsaEncryption", X509_ALG_RSA), + 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("\x2b\x0e\x03\x02\x03", "md5WithRSA", X509_ALG_MD5RSA), + ENTRY("\x2b\x0e\x03\x02\x06", "des_ecb", X509_ALG_DES_ECB), + ENTRY("\x2b\x0e\x03\x02\x07", "des_cbc", X509_ALG_DES_CBC), + ENTRY("\x2b\x0e\x03\x02\x08", "des_ofb64", X509_ALG_DES_OFB64), + ENTRY("\x2b\x0e\x03\x02\x08", "des_cfb64", X509_ALG_DES_CFB64), + ENTRY("\x2b\x0e\x03\x02\x0b", "rsaSignature", X509_ALG_RSASIGNATURE), + ENTRY("\x2b\x0e\x03\x02\x0c", "dsa_2", X509_ALG_DSA_2), + ENTRY("\x2b\x0e\x03\x02\x0d", "dsaWithSha", X509_ALG_DSASHA), + ENTRY("\x2b\x0e\x03\x02\x0f", "shaWithRSA", X509_ALG_SHARSA), + ENTRY("\x2b\x0e\x03\x02\x11", "des_ede_ecb", X509_ALG_DES_EDE_ECB), + ENTRY("\x2b\x0e\x03\x02\x12", "sha", X509_ALG_SHA), + ENTRY("\x2b\x0e\x03\x02\x1a", "sha1", X509_ALG_SHA1), + ENTRY("\x2b\x0e\x03\x02\x1b", "dsaWithSHA1_2", X509_ALG_DSASHA1_2), + ENTRY("\x2b\x0e\x03\x02\x1d", "sha1WithRSA", X509_ALG_SHA1RSA), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x02\x04", "md4", X509_ALG_MD4), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x02\x05", "md5", X509_ALG_MD5), + ENTRY("\x2b\x24\x03\x02\x01", "ripemd", X509_ALG_RIPEMD), + ENTRY("\x60\x86\x48\x01\x65\x03\x04\x01\x2a", "aes256_cbc", X509_ALG_AES256_CBC), + ENTRY("\x2a\x85\x03\x02\x02\x09", "GOST R 34.11-94", X509_ALG_GOSTR3411_94), + ENTRY("\x2a\x85\x03\x02\x02\x15", "GOST 28147-89", X509_ALG_GOST28147_89), + ENTRY("\x60\x86\x48\x01\x65\x03\x04\x01\x16", "aes192_cbc", X509_ALG_AES192_CBC), + ENTRY("\x60\x86\x48\x01\x65\x03\x04\x01\x02", "aes128_cbc", X509_ALG_AES128_CBC), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x03\x07", "des_ede3_cbc", X509_ALG_DES_EDE3_CBC), + ENTRY("\x2a\x86\x48\x86\xf7\x0d\x03\x02", "rc2_cbc", X509_ALG_RC2_CBC), + +}; + +#undef ENTRY + +size_t lookupoid(const char* oid,size_t l) { + size_t i; + for (i=0; i<sizeof(oid2string)/sizeof(oid2string[0]); ++i) + if (oid2string[i].l==l && memcmp(oid,oid2string[i].oid,l)==0) + return i; + return -1; +} + @@ -0,0 +1,12 @@ +#include <stddef.h> + +int bstr_diff(const char* a,const char* b); +#define bstr_equal(s,t) (!bstr_diff((s),(t))) + +int bstr_diff2(const char* a,const char* b,size_t blen); +#define bstr_equal2(s,t,l) (!bstr_diff2((s),(t),(l))) + +size_t bstrlen(const char* a); +size_t bstrstart(const char* a); /* offset of first byte of bstring */ + +const char* bstrfirst(const char* a); /* pointer to first byte of bstring */ diff --git a/bstrfirst.c b/bstrfirst.c new file mode 100644 index 0000000..4d8ab02 --- /dev/null +++ b/bstrfirst.c @@ -0,0 +1,6 @@ +#include "bstr.h" +#include "uint32.h" + +const char* bstrfirst(const char* a) { + if (*a) return a; else return a+5; +} diff --git a/bstrlen.c b/bstrlen.c new file mode 100644 index 0000000..61825d5 --- /dev/null +++ b/bstrlen.c @@ -0,0 +1,8 @@ +#include <string.h> +#include "bstr.h" +#include "uint32.h" +#include "str.h" + +size_t bstrlen(const char* a) { + if (*a) return str_len(a); else return uint32_read(a+1); +} diff --git a/bstrstart.c b/bstrstart.c new file mode 100644 index 0000000..5094d34 --- /dev/null +++ b/bstrstart.c @@ -0,0 +1,6 @@ +#include "bstr.h" +#include "uint32.h" + +size_t bstrstart(const char* a) { + if (*a) return 0; else return 5; +} diff --git a/client.c b/client.c new file mode 100644 index 0000000..b71b69e --- /dev/null +++ b/client.c @@ -0,0 +1,136 @@ +#include "client.h" +#include "proxy.h" +#include "epoll.h" +#include "helper.h" +#include "asn1.h" +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> + +#define MAX_SEND_BUFFER 100000 + +static void client_flush(epoll_client_t *client); + +static void client_free(epoll_client_t *client) +{ + proxy_removeClient(client); + if (client->sendBuffer != NULL) free(client->sendBuffer); + if (client->fd != -1) { + //ePoll_remove(client->fd); // epoll would remove closed descriptors automatically, but just to be sure we don't cause use after free, we remove it explicitly + close(client->fd); + } + free(client); +} + +void client_callback(void *data, int haveIn, int haveOut, int doCleanup) +{ + epoll_client_t *client = (epoll_client_t*)data; + if (doCleanup) { + printf("Client gone.\n"); + client_free(client); + return; + } + // Anything to read? + if (haveIn) { + for (;;) { + if (client->rbPos >= REQLEN) { + printf("[C->Proxy] Read buffer overflow. Disconnecting.\n"); + client_free(client); + return; + } + const size_t buflen = REQLEN - client->rbPos; + const ssize_t ret = read(client->fd, client->readBuffer + client->rbPos, buflen); + if (ret < 0 && errno == EINTR) continue; + if (ret < 0 && errno == EAGAIN) break; + if (ret <= 0) { + printf("Client gone while reading.\n"); + client_free(client); + return; + } + client->rbPos += ret; + // Request complete? + for (;;) { + size_t consumed, len; + consumed = scan_asn1SEQUENCE(client->readBuffer, client->readBuffer + client->rbPos, &len); + if (consumed == 0) break; // Length-Header not complete + len += consumed; + if (len > client->rbPos) break; // Body not complete yet + printf("Received complete requrest...\n"); + if (proxy_fromClient(client, len) == -1) { + printf("Error parsing request from client.\n"); + client_free(client); + return; + } + // Shift remaining buffer contents + if (len == client->rbPos) { + client->rbPos = 0; + break; + } + memmove(client->readBuffer, client->readBuffer + len, client->rbPos - len); + client->rbPos -= len; + } + if ((ssize_t)buflen > ret) break; // Read less than buffer len, epoll will fire again + } + } + // Anything to send? + if (haveOut) client_flush(client); +} + +static void client_flush(epoll_client_t *client) +{ + while (client->sbPos < client->sbFill) { + const int tosend = client->sbFill - client->sbPos; + const int ret = write(client->fd, client->sendBuffer + client->sbPos, tosend); + if (ret < 0 && errno == EINTR) continue; + if (ret < 0 && errno == EAGAIN) return; + if (ret <= 0) { + printf("Cannot send to client (ret: %d, errno: %d)\n", ret, errno); + return; + } + client->sbPos += ret; + if (ret != tosend) return; // Sent less than requested -> buffer full, epoll will trigger us again + } + client->sbPos = client->sbFill = 0; +} + +int client_send(epoll_client_t *client, const char *buffer, size_t len, const BOOL cork) +{ + if (client->sbFill == 0 && !cork) { + // Nothing in send buffer, fire away + const int ret = write(client->fd, buffer, len); + if (ret == 0 || (ret < 0 && errno != EINTR && errno != EAGAIN)) { + printf("Client gone when trying to send.\n"); + return -1; + } + if (ret == (int)len) return 0; + // Couldn't send everything, continue with buffering logic below + if (ret > 0) { + buffer += ret; + len -= (size_t)ret; + } + } + // Sanity + if ((client->sbLen - client->sbPos) + len > MAX_SEND_BUFFER) { + printf("Dropping client as the send buffer would exceed %d bytes.\n", (int)MAX_SEND_BUFFER); + return -1; + } + // Buffer... + if (client->sbLen - client->sbFill < len) { // Buffer too small? + if (client->sbPos != 0) { // See if we can compact it + memmove(client->sendBuffer, client->sendBuffer + client->sbPos, client->sbFill - client->sbPos); + client->sbFill -= client->sbPos; + client->sbPos = 0; + } + if (client->sbLen - client->sbFill < len) { // Still too small after compacting? realloc + if (helper_realloc(&client->sendBuffer, &client->sbLen, client->sbLen + len + 1500, "client_send") == -1) return -1; + } + } + // Finally append to buffer + memcpy(client->sendBuffer + client->sbFill, buffer, len); + client->sbFill += len; + if (!cork) client_flush(client); + return 0; +} + diff --git a/client.h b/client.h new file mode 100644 index 0000000..64f0171 --- /dev/null +++ b/client.h @@ -0,0 +1,10 @@ +#ifndef _CLIENT_H_ +#define _CLIENT_H_ + +#include "types.h" + +void client_callback(void *data, int haveIn, int haveOut, int doCleanup); + +int client_send(epoll_client_t *client, const char *buffer, size_t len, const BOOL cork); + +#endif diff --git a/config/config.example b/config/config.example new file mode 100644 index 0000000..e444a8a --- /dev/null +++ b/config/config.example @@ -0,0 +1,6 @@ +[dc0.example.com] +binddn=CN=blabla,OU=Foo,DC=public,DC=ads,DC=example,DC=com +bindpw=geheim +base=DC=public,DC=ads,DC=example,DC=com +alias=DN=openslx,DN=org + @@ -0,0 +1,43 @@ +#include "epoll.h" +#include <errno.h> + +static int efd = -1; +#define MAXEVENTS 10 +static struct epoll_event events[MAXEVENTS]; + +int ePoll_init() +{ + if (efd != -1) return 0; + efd = epoll_create1(0); + if (efd == -1) return -1; + return 0; +} + +int ePoll_wait(const int timeoutMs) +{ + const int num = epoll_wait(efd, events, MAXEVENTS, timeoutMs); + if (num < 0 && (errno == EINTR || errno == EAGAIN)) return 0; + if (num < 0) return -1; + for (int i = 0; i < num; ++i) { + epoll_item_t *data = (epoll_item_t *)events[i].data.ptr; + (*data->callback)(data, events[i].events & EPOLLIN, events[i].events & EPOLLOUT, events[i].events & (EPOLLERR | EPOLLHUP)); + } + return 0; +} + +/** + * Returns -1 on error, just like epoll_ctl + */ +int ePoll_add(uint32_t flags, epoll_item_t* callback) +{ + struct epoll_event event; + event.data.ptr = (void*)callback; + event.events = flags; + return epoll_ctl(efd, EPOLL_CTL_ADD, callback->fd, &event); +} + +int ePoll_remove(int fd) +{ + return epoll_ctl(efd, EPOLL_CTL_DEL, fd, (struct epoll_event*)67775); +} + @@ -0,0 +1,15 @@ +#ifndef _EPOLL_H_ +#define _EPOLL_H_ + +#include "types.h" +#include <sys/epoll.h> + +int ePoll_init(); + +int ePoll_wait(const int timeoutMs); + +int ePoll_add(uint32_t flags, epoll_item_t* callback); + +int ePoll_remove(int fd); + +#endif diff --git a/fmt_asn1OID.c b/fmt_asn1OID.c new file mode 100644 index 0000000..6f6fc10 --- /dev/null +++ b/fmt_asn1OID.c @@ -0,0 +1,16 @@ +#include "asn1.h" + +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<len; ++i) { + l+=fmt_asn1tagint(dest,array[i]); + } + l2=fmt_asn1transparent(dest,tc,tt,tag,l); + if (!dest) return l+l2; + dest[l2]=array[0]*40+array[1]; + dest+=l2+1; + for (i=2; i<len; ++i) + dest+=fmt_asn1tagint(dest,array[i]); + return l+l2; +} diff --git a/fmt_asn1bitstring.c b/fmt_asn1bitstring.c new file mode 100644 index 0000000..18df19d --- /dev/null +++ b/fmt_asn1bitstring.c @@ -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; +} diff --git a/fmt_asn1generic.c b/fmt_asn1generic.c new file mode 100644 index 0000000..c97ee96 --- /dev/null +++ b/fmt_asn1generic.c @@ -0,0 +1,139 @@ +#include <stdarg.h> +#include <string.h> +#include "asn1.h" + +size_t fmt_asn1generic(char* dest,const char* fmt,...) { + size_t containerstack[100]; + size_t curinstack=0; + va_list args; + 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; + size_t seqlen; + unsigned long desttag=0; + unsigned long appstore; + int stringtype; + while (*fmt) { + char* realdest=dest?dest+cursor:NULL; + switch (*fmt) { + case '*': // make next tag use APPLICATION with this tag + appstore=va_arg(args,unsigned long); + application=&appstore; + break; + case '0': // UNIVERSAL PRIMITIVE NULL length 0 + if (application) + curlen=fmt_asn1tag(realdest,APPLICATION,PRIMITIVE,_NULL); + else + curlen=fmt_asn1tag(realdest,UNIVERSAL,PRIMITIVE,_NULL); + application=NULL; + curlen+=fmt_asn1length(realdest?realdest+curlen:NULL,0); + break; + case 'i': // send integer + { + unsigned long i=va_arg(args,unsigned long); + if (application) + curlen=fmt_asn1int(realdest,APPLICATION,PRIMITIVE,*application,i); + else + curlen=fmt_asn1int(realdest,UNIVERSAL,PRIMITIVE,INTEGER,i); + 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 'B': + stringtype=BIT_STRING; + goto stringcopy; + case 'A': + stringtype=IA5String; + goto stringcopy; + case 'P': + stringtype=PrintableString; + goto stringcopy; + case 'S': // send OCTET_STRING, using struct string* as arg + stringtype=OCTET_STRING; +stringcopy: + s=va_arg(args,struct string*); +copystring: + if (application) + curlen=fmt_asn1string(realdest,APPLICATION,PRIMITIVE,*application,s->s,s->l); + else + curlen=fmt_asn1string(realdest,UNIVERSAL,PRIMITIVE,stringtype,s->s,s->l); + application=NULL; + break; + case 't': + stringtype=UTCTIME; + goto stringcopy_alt; + case 'a': + stringtype=IA5String; + goto stringcopy_alt; + case 'p': + stringtype=PrintableString; + goto stringcopy_alt; + case 's': // send OCTET_STRING, using const char* with strlen() as arg + stringtype=OCTET_STRING; +stringcopy_alt: + S.s=va_arg(args,const char*); + 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 'C': // copy raw ASN.1 DER data, take struct string* + s=va_arg(args,struct string*); + if (realdest) memcpy(realdest,s->s,s->l); + curlen=s->l; + 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,*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 + * sequence data backwards to make room to write the ASN.1 length */ + { + char* anfang; + if (!curinstack) return 0; + anfang=dest+containerstack[--curinstack]; + seqlen=dest+cursor-anfang; + curlen=fmt_asn1length(NULL,seqlen); + if (!dest) break; + memmove(anfang+curlen,anfang,seqlen); + fmt_asn1length(anfang,seqlen); + break; + } + } + cursor+=curlen; + ++fmt; + } + return cursor; +} diff --git a/fmt_asn1int.c b/fmt_asn1int.c new file mode 100644 index 0000000..571c101 --- /dev/null +++ b/fmt_asn1int.c @@ -0,0 +1,11 @@ +#include "asn1.h" + +size_t fmt_asn1int(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,enum asn1_tag tag,unsigned long l) { + size_t len,tmp; + /* first the tag */ + if (!dest) return fmt_asn1tag(0,tc,tt,tag)+1+fmt_asn1intpayload(0,l); + len=fmt_asn1tag(dest,tc,tt,tag); + tmp=fmt_asn1intpayload(dest+len+1,l); + if (fmt_asn1length(dest+len,tmp)!=1) return 0; + return len+tmp+1; +} diff --git a/fmt_asn1intpayload.c b/fmt_asn1intpayload.c new file mode 100644 index 0000000..15e71ca --- /dev/null +++ b/fmt_asn1intpayload.c @@ -0,0 +1,20 @@ +#include "asn1.h" + +size_t fmt_asn1intpayload(char* dest,unsigned long l) { + size_t needed=sizeof l,i,fixup; + for (i=1; i<needed; ++i) { + if (!(l>>(i*8))) + break; + } + fixup=(l>>((i-1)*8))&0x80 ? 1 : 0; + if (dest) { + size_t j=i; + if (fixup) *dest++=0; + while (j) { + --j; + *dest=(l>>(j*8))&0xff; + ++dest; + } + } + return i+fixup; +} diff --git a/fmt_asn1length.c b/fmt_asn1length.c new file mode 100644 index 0000000..3df929b --- /dev/null +++ b/fmt_asn1length.c @@ -0,0 +1,25 @@ +#include "asn1.h" + +/* write int in least amount of bytes, return number of bytes */ +/* as used in ASN.1 length */ +size_t fmt_asn1length(char* dest,size_t l) { + /* encoding is either l%128 or (0x80+number of bytes,bytes) */ + size_t needed=(sizeof l),i; + if (l<128) { + if (dest) *dest=l&0x7f; + return 1; + } + for (i=1; i<needed; ++i) + if (!(l>>(i*8))) + break; + if (dest) { + int j=i; + *dest=0x80+i; ++dest; + while (j) { + --j; + *dest=((l>>(j*8))&0xff); + ++dest; + } + } + return i+1; +} diff --git a/fmt_asn1sint.c b/fmt_asn1sint.c new file mode 100644 index 0000000..986c398 --- /dev/null +++ b/fmt_asn1sint.c @@ -0,0 +1,11 @@ +#include "asn1.h" + +size_t fmt_asn1sint(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,enum asn1_tag tag,signed long l) { + size_t len,tmp; + /* first the tag */ + if (!dest) return fmt_asn1tag(0,tc,tt,tag)+1+fmt_asn1intpayload(0,l); + len=fmt_asn1tag(dest,tc,tt,tag); + tmp=fmt_asn1sintpayload(dest+len+1,l); + if (fmt_asn1length(dest+len,tmp)!=1) return 0; + return len+tmp+1; +} diff --git a/fmt_asn1sintpayload.c b/fmt_asn1sintpayload.c new file mode 100644 index 0000000..257135e --- /dev/null +++ b/fmt_asn1sintpayload.c @@ -0,0 +1,22 @@ +#include "asn1.h" + +size_t fmt_asn1sintpayload(char* dest,signed long l) { + size_t needed=sizeof l,i; + signed long tmp=0x7f; + if (l>=0) return fmt_asn1intpayload(dest,l); + for (i=1; i<needed; ++i) { + /* assumes two's complement */ + if ((l|tmp) == -1) + break; + tmp=(tmp<<8)|0xff; + } + if (dest) { + size_t j=i; + while (j) { + --j; + *dest=(l>>(j*8))&0xff; + ++dest; + } + } + return i; +} diff --git a/fmt_asn1string.c b/fmt_asn1string.c new file mode 100644 index 0000000..e0ef6ac --- /dev/null +++ b/fmt_asn1string.c @@ -0,0 +1,10 @@ +#include "asn1.h" +#include "byte.h" + +size_t fmt_asn1string(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,enum asn1_tag tag,const char* c,size_t l) { + size_t len; + if (l>(size_t)-100) return (size_t)-1; + len=fmt_asn1transparent(dest,tc,tt,tag,l); + if (dest) byte_copy(dest+len,l,c); + return len+l; +} diff --git a/fmt_asn1tag.c b/fmt_asn1tag.c new file mode 100644 index 0000000..b2bd1a0 --- /dev/null +++ b/fmt_asn1tag.c @@ -0,0 +1,15 @@ +#include "asn1.h" + +/* write int in least amount of bytes, return number of bytes */ +/* as used in ASN.1 tags */ +size_t fmt_asn1tag(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,unsigned long l) { + /* encoding is either l%128 or (0x1f,...) */ + if (l<0x1f) { + if (dest) *dest=(int)tc+(int)tt+(l&0x1f); + return 1; + } + if (dest) { + *dest=(int)tc+(int)tt+0x1f; ++dest; + } + return fmt_asn1tagint(dest,l)+1; +} diff --git a/fmt_asn1tagint.c b/fmt_asn1tagint.c new file mode 100644 index 0000000..338d7bf --- /dev/null +++ b/fmt_asn1tagint.c @@ -0,0 +1,17 @@ +#include "asn1.h" + +size_t fmt_asn1tagint(char* dest,unsigned long l) { + size_t needed=((sizeof l)*7)/8,i; + for (i=1; i<needed; ++i) + if (!(l>>(i*7))) + break; + if (dest) { + size_t j=i; + while (j) { + --j; + *dest=((l>>(j*7))&0x7f) + (j?0x80:0); + ++dest; + } + } + return i; +} diff --git a/fmt_asn1transparent.c b/fmt_asn1transparent.c new file mode 100644 index 0000000..d1f9895 --- /dev/null +++ b/fmt_asn1transparent.c @@ -0,0 +1,10 @@ +#include "asn1.h" +#include "byte.h" + +size_t fmt_asn1transparent(char* dest,enum asn1_tagclass tc,enum asn1_tagtype tt,enum asn1_tag tag,size_t l) { + size_t len,tmp; + /* first the tag */ + len=fmt_asn1tag(dest,tc,tt,tag); + tmp=fmt_asn1length(dest?dest+len:dest,l); + return tmp+len; +} diff --git a/fmt_ldapadl.c b/fmt_ldapadl.c new file mode 100644 index 0000000..c8ba076 --- /dev/null +++ b/fmt_ldapadl.c @@ -0,0 +1,32 @@ +#include "ldap.h" + +static size_t doit(char* dest,const struct AttributeDescriptionList* adl,int seq) { + const struct AttributeDescriptionList* x=adl; + size_t sum=0,tmp; + while (x) { + sum+=fmt_asn1OCTETSTRING(0,0,x->a.l); + x=x->next; + } + if (seq) + tmp=fmt_asn1SEQUENCE(dest,sum); + else + tmp=fmt_asn1SET(dest,sum); + sum+=tmp; + if (dest) { + dest+=tmp; + x=adl; + while (x) { + dest+=fmt_ldapstring(dest,&x->a); + x=x->next; + } + } + return sum; +} + +size_t fmt_ldapadl(char* dest,const struct AttributeDescriptionList* adl) { + return doit(dest,adl,1); +} + +size_t fmt_ldapavl(char* dest,const struct AttributeDescriptionList* adl) { + return doit(dest,adl,0); +} diff --git a/fmt_ldapava.c b/fmt_ldapava.c new file mode 100644 index 0000000..956e5b2 --- /dev/null +++ b/fmt_ldapava.c @@ -0,0 +1,9 @@ +#include "ldap.h" + +size_t fmt_ldapava(char* dest,const struct AttributeValueAssertion* a) { + size_t sum,l; + sum=fmt_ldapstring(dest,&a->desc); + if (dest) dest+=sum; + l=fmt_ldapstring(dest,&a->value); + return sum+l; +} diff --git a/fmt_ldapbindrequest.c b/fmt_ldapbindrequest.c new file mode 100644 index 0000000..5e92bf2 --- /dev/null +++ b/fmt_ldapbindrequest.c @@ -0,0 +1,18 @@ +#include <string.h> +#include "ldap.h" +#include <str.h> +#include "rangecheck.h" + +size_t fmt_ldapbindrequest(char* dest,long version,const char* name,const char* simple) { + size_t l,sum; + size_t nlen=str_len(name); + sum=l=fmt_asn1INTEGER(dest,version); + if (dest) dest+=l; + l=fmt_asn1OCTETSTRING(dest,name,nlen); + if (add_of(sum,sum,l)) return (size_t)-1; if (dest) dest+=l; +// sum+=l; if (dest) dest+=l; + nlen=str_len(simple); + l=fmt_asn1string(dest,PRIVATE,PRIMITIVE,0,simple,nlen); + if (add_of(sum,sum,l)) return (size_t)-1; if (dest) dest+=l; + return sum; +} diff --git a/fmt_ldapdeleterequest.c b/fmt_ldapdeleterequest.c new file mode 100644 index 0000000..7690996 --- /dev/null +++ b/fmt_ldapdeleterequest.c @@ -0,0 +1,8 @@ +#include <string.h> +#include "ldap.h" +#include "byte.h" + +size_t fmt_ldapdeleterequest(char* dest,const struct string* s) { + if (dest) byte_copy(dest,s->l,s->s); + return s->l; +} diff --git a/fmt_ldapmessage.c b/fmt_ldapmessage.c new file mode 100644 index 0000000..9a25f7d --- /dev/null +++ b/fmt_ldapmessage.c @@ -0,0 +1,12 @@ +#include "ldap.h" + +size_t fmt_ldapmessage(char* dest,long messageid,long op,size_t len) { + size_t l,l2,l3; + l2=fmt_asn1INTEGER(0,messageid); + l3=fmt_asn1transparent(0,APPLICATION,CONSTRUCTED,op,len); + l=fmt_asn1SEQUENCE(dest,len+l2+l3); + if (!dest) return l+l2+l3; + l+=fmt_asn1INTEGER(dest+l,messageid); + l+=fmt_asn1transparent(dest+l,APPLICATION,CONSTRUCTED,op,len); + return l; +} diff --git a/fmt_ldappal.c b/fmt_ldappal.c new file mode 100644 index 0000000..1a6bef3 --- /dev/null +++ b/fmt_ldappal.c @@ -0,0 +1,23 @@ +#include "ldap.h" + +size_t fmt_ldappal(char* dest,const struct PartialAttributeList* pal) { +// int l,l2,sum; + size_t sum,l,l2; + if (!pal) return 0; + sum=fmt_ldapstring(0,&pal->type); + /* look how much space the adl needs */ + l=fmt_ldapavl(0,pal->values); + /* write sequence header */ + l2=fmt_asn1SEQUENCE(dest,l+sum); + if (dest) { + fmt_ldapstring(dest+l2,&pal->type); + dest+=sum+l2; + } + sum+=l+l2; + if (dest) { + fmt_ldapavl(dest,pal->values); + dest+=l; + } + return sum+fmt_ldappal(dest,pal->next); +} + diff --git a/fmt_ldapresult.c b/fmt_ldapresult.c new file mode 100644 index 0000000..b573089 --- /dev/null +++ b/fmt_ldapresult.c @@ -0,0 +1,20 @@ +#include "ldap.h" +#include <str.h> + +size_t fmt_ldapresult(char* dest,long result,const char* matcheddn,const char* errormessage,const char* referral) { + size_t l,sum=0,nlen; + sum=l=fmt_asn1ENUMERATED(dest,result); + if (dest) dest+=l; + nlen=str_len(matcheddn); + l=fmt_asn1OCTETSTRING(dest,matcheddn,nlen); + sum+=l; if (dest) dest+=l; + nlen=str_len(errormessage); + l=fmt_asn1OCTETSTRING(dest,errormessage,nlen); + sum+=l; if (dest) dest+=l; + if (referral && *referral) { + nlen=str_len(referral); + l=fmt_asn1OCTETSTRING(dest,referral,nlen); + sum+=l; if (dest) dest+=l; + } + return sum; +} diff --git a/fmt_ldapsearchfilter.c b/fmt_ldapsearchfilter.c new file mode 100644 index 0000000..bbd2c55 --- /dev/null +++ b/fmt_ldapsearchfilter.c @@ -0,0 +1,70 @@ +#include <byte.h> +#include "ldap.h" +#include <stdlib.h> + +/* + Filter ::= CHOICE { + and [0] SET OF Filter, + or [1] SET OF Filter, + not [2] Filter, + equalityMatch [3] AttributeValueAssertion, + substrings [4] SubstringFilter, + greaterOrEqual [5] AttributeValueAssertion, + lessOrEqual [6] AttributeValueAssertion, + present [7] AttributeDescription, + approxMatch [8] AttributeValueAssertion, + extensibleMatch [9] MatchingRuleAssertion } +*/ + +size_t fmt_ldapsubstring(char* dest,const struct Substring* s) { + size_t sum=0,tmp=0; + while (s) { + tmp=fmt_asn1string(dest,PRIVATE,PRIMITIVE,s->substrtype,s->s.s,s->s.l); + if (dest) dest+=tmp; sum+=tmp; + s=s->next; + } + return sum; +} + +size_t fmt_ldapsearchfilter(char* dest,const struct Filter* f) { + size_t sum=0,tmp,savesum; + if (!f) + return 0; + switch (f->type) { + case AND: case OR: case NOT: + sum=fmt_ldapsearchfilter(dest,f->x); break; + case EQUAL: case GREATEQUAL: case LESSEQUAL: case APPROX: + sum=fmt_ldapava(dest,&f->ava); break; + case SUBSTRING: + { + char* nd=dest; + size_t l,tmp; + + tmp=fmt_ldapsubstring(0,f->substrings); + l=fmt_ldapstring(nd,&f->ava.desc); + sum+=l; if (nd) nd+=l; + l=fmt_asn1SEQUENCE(nd,tmp); + sum+=l; if (nd) nd+=l; + l=fmt_ldapsubstring(nd,f->substrings); + sum+=l; + } + break; + case PRESENT: + return fmt_asn1string(dest,PRIVATE,PRIMITIVE,(enum asn1_tag)f->type,f->ava.desc.s,f->ava.desc.l); + break; + default: return 0; + } + + savesum=sum; + if(f->next) { + if (dest) sum+=fmt_ldapsearchfilter(dest+sum,f->next); + else sum+=fmt_ldapsearchfilter(dest,f->next); + } + + tmp=fmt_asn1length(0,savesum); + if (!dest) return sum+tmp+1; + if (dest) byte_copyr(dest+tmp+1,sum,dest); + fmt_asn1tag(dest,PRIVATE,CONSTRUCTED,f->type); + fmt_asn1length(dest+1,savesum); + return sum+tmp+1; +} diff --git a/fmt_ldapsearchfilterstring.c b/fmt_ldapsearchfilterstring.c new file mode 100644 index 0000000..cd53ea3 --- /dev/null +++ b/fmt_ldapsearchfilterstring.c @@ -0,0 +1,69 @@ +#include "fmt.h" +#include "byte.h" +#include <str.h> +#include "ldap.h" + +size_t fmt_ldapsearchfilterstring(char* dest,const struct Filter* f) { + size_t len; + len = fmt_str(dest,"("); + switch (f->type) { + case AND: case OR: case NOT: + if (dest) dest[len]="&|!"[f->type]; + ++len; + len += fmt_ldapsearchfilterstring(dest?dest+len:0,f->x); + break; + case EQUAL: case GREATEQUAL: case LESSEQUAL: case APPROX: + if (dest) { + byte_copy(dest+len,f->ava.desc.l,f->ava.desc.s); + len += f->ava.desc.l; + if (f->type!=EQUAL) { + dest[len]="><~"[f->type-GREATEQUAL]; + ++len; + } + dest[len]='='; ++len; + byte_copy(dest+len,f->ava.value.l,f->ava.value.s); + len += f->ava.value.l; + } else + len += f->ava.desc.l + f->ava.value.l + 1 + (f->type>EQUAL); + break; + case SUBSTRING: + { + struct Substring* x=f->substrings; + while (x) { + if (dest) { + byte_copy(dest+len,f->ava.desc.l,f->ava.desc.s); + len += f->ava.desc.l; + dest[len]='='; ++len; + if (x->substrtype != prefix) { + dest[len]='*'; ++len; + } + byte_copy(dest+len,x->s.l,x->s.s); + len += x->s.l; + if (x->substrtype != suffix) { + dest[len]='*'; ++len; + } + if (x->next) { + dest[len]=')'; + dest[len+1]='('; + len+=2; + } + } else + len += f->ava.desc.l + 1 + x->s.l + 1 + (x->substrtype==any) + (x->next?2:0); + x=x->next; + } + } + break; + case PRESENT: + if (dest) { + byte_copy(dest+len,f->ava.desc.l,f->ava.desc.s); + dest[len+f->ava.desc.l]='='; + dest[len+f->ava.desc.l+1]='*'; + } + len += f->ava.desc.l+2; + break; + default: + return -1; + } + if (dest) dest[len]=')'; + return len+1; +} diff --git a/fmt_ldapsearchrequest.c b/fmt_ldapsearchrequest.c new file mode 100644 index 0000000..3792f05 --- /dev/null +++ b/fmt_ldapsearchrequest.c @@ -0,0 +1,21 @@ +#include "ldap.h" + +size_t fmt_ldapsearchrequest(char* dest,const struct SearchRequest* sr) { + size_t l,sum=0; + sum=fmt_ldapstring(dest,&sr->baseObject); + if (dest) dest+=sum; + l=fmt_asn1ENUMERATED(dest,sr->scope); + sum+=l; if (dest) dest+=l; + l=fmt_asn1ENUMERATED(dest,sr->derefAliases); + sum+=l; if (dest) dest+=l; + l=fmt_asn1INTEGER(dest,sr->sizeLimit); + sum+=l; if (dest) dest+=l; + l=fmt_asn1INTEGER(dest,sr->timeLimit); + sum+=l; if (dest) dest+=l; + l=fmt_asn1BOOLEAN(dest,sr->typesOnly); + sum+=l; if (dest) dest+=l; + l=fmt_ldapsearchfilter(dest,sr->filter); + sum+=l; if (dest) dest+=l; + l=fmt_ldapadl(dest,sr->attributes); + return sum+l; +} diff --git a/fmt_ldapsearchresultentry.c b/fmt_ldapsearchresultentry.c new file mode 100644 index 0000000..3a30b53 --- /dev/null +++ b/fmt_ldapsearchresultentry.c @@ -0,0 +1,11 @@ +#include "ldap.h" + +size_t fmt_ldapsearchresultentry(char* dest,const struct SearchResultEntry* sre) { + size_t l,sum=0; + sum=fmt_ldapstring(dest,&sre->objectName); + if (dest) dest+=sum; + l=fmt_asn1SEQUENCE(dest,fmt_ldappal(0,sre->attributes)); + sum+=l; if (dest) dest+=l; + l=fmt_ldappal(dest,sre->attributes); + return sum+l; +} diff --git a/fmt_ldapstring.c b/fmt_ldapstring.c new file mode 100644 index 0000000..48dd546 --- /dev/null +++ b/fmt_ldapstring.c @@ -0,0 +1,5 @@ +#include "ldap.h" + +size_t fmt_ldapstring(char* dest,const struct string* s) { + return fmt_asn1OCTETSTRING(dest,s->s,s->l); +} diff --git a/free_ldapadl.c b/free_ldapadl.c new file mode 100644 index 0000000..c8338a3 --- /dev/null +++ b/free_ldapadl.c @@ -0,0 +1,10 @@ +#include <stdlib.h> +#include "ldap.h" + +void free_ldapadl(struct AttributeDescriptionList* a) { + while (a) { + struct AttributeDescriptionList* tmp=a->next; + free(a); a=tmp; + } +} + diff --git a/free_ldappal.c b/free_ldappal.c new file mode 100644 index 0000000..69548ed --- /dev/null +++ b/free_ldappal.c @@ -0,0 +1,11 @@ +#include <stdlib.h> +#include "ldap.h" + +void free_ldappal(struct PartialAttributeList* a) { + while (a) { + struct PartialAttributeList* tmp=a->next; + free_ldapadl(a->values); + free(a); a=tmp; + } +} + diff --git a/free_ldapsearchfilter.c b/free_ldapsearchfilter.c new file mode 100644 index 0000000..d952f93 --- /dev/null +++ b/free_ldapsearchfilter.c @@ -0,0 +1,22 @@ +#include <stdlib.h> +#include "ldap.h" + +void free_ldapsearchfilter(struct Filter* f) { + while (f) { + struct Filter* tmp=f->next; + switch (f->type) { + case AND: case OR: case NOT: + free_ldapsearchfilter(f->x); + break; + case SUBSTRING: + while (f->substrings) { + struct Substring* s=f->substrings->next; + free(f->substrings); + f->substrings=s; + } + default: + break; + } + free(f); f=tmp; + } +} diff --git a/free_ldapsearchresultentry.c b/free_ldapsearchresultentry.c new file mode 100644 index 0000000..1a4aa6f --- /dev/null +++ b/free_ldapsearchresultentry.c @@ -0,0 +1,5 @@ +#include "ldap.h" + +void free_ldapsearchresultentry(struct SearchResultEntry* e) { + freepal(e->attributes); +} diff --git a/freeava.c b/freeava.c new file mode 100644 index 0000000..fb6530e --- /dev/null +++ b/freeava.c @@ -0,0 +1,10 @@ +#include <stdlib.h> +#include "ldap.h" + +void freeava(struct AttributeDescriptionList* a) { + while (a) { + struct AttributeDescriptionList* tmp=a->next; + free(a); + a=tmp; + } +} diff --git a/freefilter.c b/freefilter.c new file mode 100644 index 0000000..442dfd1 --- /dev/null +++ b/freefilter.c @@ -0,0 +1,16 @@ +#include "ldap.h" +#include <stdlib.h> + +void freefilter(struct Filter* f) { + if (f) { + freeava(f->a); + if (f->x) freefilter(f->x); + if (f->next) freefilter(f->next); + while (f->substrings) { + struct Substring* s=f->substrings->next; + free(f->substrings); + f->substrings=s; + } + free(f); + } +} diff --git a/freepal.c b/freepal.c new file mode 100644 index 0000000..9992a09 --- /dev/null +++ b/freepal.c @@ -0,0 +1,15 @@ +#include <stdlib.h> +#include "ldap.h" + +void freepal(struct PartialAttributeList* l) { + while (l) { + struct PartialAttributeList* x=l->next; + while (l->values) { + struct AttributeDescriptionList* y=l->values->next; + free(l->values); + l->values=y; + } + free(l); + l=x; + } +} diff --git a/helper.c b/helper.c new file mode 100644 index 0000000..1ba11b0 --- /dev/null +++ b/helper.c @@ -0,0 +1,174 @@ +#include "helper.h" +#include <socket.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <fcntl.h> +#include <stdarg.h> + +void bail(char *args, ...) +{ + printf("ERROR: "); + va_list argList; + va_start(argList, args); + vprintf(args, argList); + va_end(argList); + printf("\n"); + exit(1); +} + +/** + * 0 = SUCCESS, -1 = ERROR + * on success, memory pointer and size variable will be updated. + */ +int helper_realloc(char **memory, size_t *curSize, const size_t newSize, const char *location) +{ + void *newBlock = realloc(*memory, newSize); + if (newBlock == NULL) { + printf("OUT OF MEMORY when trying to realloc to %d bytes at %s\n", (int)newSize, location); + return -1; + } + *memory = newBlock; + if (curSize != NULL) *curSize = newSize; + return 0; +} + +int helper_connect4(char *address, int port, char *ip) +{ + if (sizeof(struct in_addr) != 4) bail("Ach nöö ach nöö"); + if (inet_pton(AF_INET, address, ip) == 1) { + const int sock = socket_tcp4b(); + if (sock == -1) return -1; + if (socket_connect4(sock, ip, port) == 0) { + return sock; + } + close(sock); + return -1; + } + struct addrinfo hints; + struct addrinfo *result, *rp; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + const int ret = getaddrinfo(NULL, address, &hints, &result); + if (ret != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); + return -1; + } + for (rp = result; rp != NULL; rp = rp->ai_next) { + const int sock = socket_tcp4b(); + if (sock == -1) continue; + memcpy(ip, rp->ai_addr->sa_data, 4); + if (socket_connect4(sock, ip, port) == 0) { + freeaddrinfo(result); + return sock; // Success + } + close(sock); + } + freeaddrinfo(result); + return -1; +} + +void helper_nonblock(const int fd) +{ + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); +} + +void helper_printava(struct AttributeValueAssertion* a,const char* rel) +{ + putchar('['); + printf("%.*s", (int)a->desc.l,a->desc.s); + putchar(' '); + printf("%s", rel); + putchar(' '); + printf("%.*s", (int)a->value.l,a->value.s); + putchar(']'); +} + +void helper_printal(struct AttributeDescriptionList* a) +{ + while (a) { + if (a->a.s && a->a.l) printf("%.*s", (int)a->a.l,a->a.s); + a=a->next; + if (a) { + putchar(','); + } + } + putchar('\n'); +} + +void helper_printfilter(struct Filter* f) +{ + switch (f->type) { + case AND: + printf("&("); +mergesub: + helper_printfilter(f->x); + printf(")\n"); + break; + case OR: + printf("|("); + goto mergesub; + break; + case NOT: + printf("!("); + goto mergesub; + case EQUAL: + helper_printava(&f->ava,"=="); + break; + case SUBSTRING: { + struct Substring* s=f->substrings; + int first=1; + printf("%.*s",(int)f->ava.desc.l,f->ava.desc.s); + printf(" has "); + while (s) { + if (!first) { + printf(" and "); + } + first=0; + switch(s->substrtype) { + case prefix: + printf("prefix \""); + break; + case any: + printf("substr \""); + break; + case suffix: + printf("suffix \""); + break; + } + printf("%.*s",(int)s->s.l,s->s.s); + putchar('"'); + s=s->next; + } + } + break; + case GREATEQUAL: + helper_printava(&f->ava,">="); + break; + case LESSEQUAL: + helper_printava(&f->ava,"<="); + break; + case PRESENT: + helper_printava(&f->ava,"\\exist"); + break; + case APPROX: + helper_printava(&f->ava,"\\approx"); + break; + case EXTENSIBLE: + printf("[extensible]"); + break; + } + if (f->next) { + putchar(','); + helper_printfilter(f->next); + } + putchar('\n'); + fflush(stdout); +} + diff --git a/helper.h b/helper.h new file mode 100644 index 0000000..840813d --- /dev/null +++ b/helper.h @@ -0,0 +1,27 @@ +#ifndef _HELPER_H_ +#define _HELPER_H_ + +#include "types.h" +#include "ldap.h" + +void bail(char *args, ...); +int helper_realloc(char **memory, size_t *curSize, const size_t newSize, const char *location); +int helper_connect4(char *address, int port, char *ip); +void helper_nonblock(const int fd); + +void helper_printava(struct AttributeValueAssertion* a,const char* rel); +void helper_printal(struct AttributeDescriptionList* a); +void helper_printfilter(struct Filter* f); + +static inline int min(const int a, const int b) +{ + return a < b ? a : b; +} + +static inline int max(const int a, const int b) +{ + return a > b ? a : b; +} + +#endif + @@ -0,0 +1,166 @@ +/* inih -- simple .INI file parser + + inih is released under the New BSD license (see LICENSE.txt). Go to the project + home page for more info: + + http://code.google.com/p/inih/ + + */ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> + +#include "ini.h" + +#if !INI_USE_STACK +#include <stdlib.h> +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen( s ); + while ( p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while ( *s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char c or ';' comment in given string, or pointer to + null at end of string if neither found. ';' must be prefixed by a whitespace + character to register as a comment. */ +static char* find_char_or_comment(const char* s, char c) +{ + int was_whitespace = 0; + while ( *s && *s != c && !(was_whitespace && *s == ';') ) { + was_whitespace = isspace((unsigned char)(*s)); + s++; + } + return (char*)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + strncpy( dest, src, size ); + dest[size - 1] = '\0'; + return dest; +} + +/* See documentation in header file. */ +int ini_parse_file(FILE* file, int (*handler)(void*, const char*, const char*, const char*), void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; +#else + char* line; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)malloc( INI_MAX_LINE ); + if ( !line ) { + return -2; + } +#endif + + /* Scan through file line by line */ + while ( fgets( line, INI_MAX_LINE, file ) != NULL ) { + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip( rstrip( start ) ); + + if ( *start == ';' || *start == '#' ) { + /* Per Python ConfigParser, allow '#' comments at start of line */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-black line with leading whitespace, treat as continuation + of previous name's value (as per Python ConfigParser). */ + if (!handler(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if ( *start == '[' ) { + /* A "[section]" line */ + end = find_char_or_comment( start + 1, ']' ); + if ( *end == ']' ) { + *end = '\0'; + strncpy0( section, start + 1, sizeof(section) ); + *prev_name = '\0'; + } else if ( !error ) { + /* No ']' found on section line */ + error = lineno; + } + } else if ( *start && *start != ';' ) { + /* Not a comment, must be a name[=:]value pair */ + end = find_char_or_comment( start, '=' ); + if ( *end != '=' ) { + end = find_char_or_comment( start, ':' ); + } + if ( *end == '=' || *end == ':' ) { + *end = '\0'; + name = rstrip( start ); + value = lskip( end + 1 ); + end = find_char_or_comment( value, '\0' ); + if ( *end == ';' ) *end = '\0'; + rstrip( value ); + + /* Valid name[=:]value pair found, call handler */ + strncpy0( prev_name, name, sizeof(prev_name) ); + if ( !handler( user, section, name, value ) && !error ) error = lineno; + } else if ( !error ) { + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + } + +#if !INI_USE_STACK + free( line ); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse(const char* filename, int (*handler)(void*, const char*, const char*, const char*), void* user) +{ + FILE* file; + int error; + + file = fopen( filename, "r" ); + if ( !file ) return -1; + error = ini_parse_file( file, handler, user ); + fclose( file ); + return error; +} + @@ -0,0 +1,66 @@ +/* inih -- simple .INI file parser + + inih is released under the New BSD license (see LICENSE.txt). Go to the project + home page for more info: + + http://code.google.com/p/inih/ + + */ + +#ifndef __INI_H__ +#define __INI_H__ + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's ConfigParser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). + */ +int ini_parse(const char* filename, int (*handler)(void* user, const char* section, const char* name, const char* value), void* user); + +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +int ini_parse_file(FILE* file, int (*handler)(void* user, const char* section, const char* name, const char* value), void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + ConfigParser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See http://code.google.com/p/inih/issues/detail?id=21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Nonzero to use stack, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Maximum line length for any line in INI file. */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __INI_H__ */ @@ -0,0 +1,86 @@ +#include "types.h" +#include "epoll.h" +#include "client.h" +#include "server.h" +#include "proxy.h" +#include "ini.h" +#include "helper.h" +#include <stdio.h> +#include <socket.h> +#include <io.h> +#include <string.h> +#include <stdlib.h> + +static void listen_callback(void *data, int haveIn, int haveOut, int doCleanup); +static void loadConfig(char *file); + +int main(int argc, char **argv) +{ + if (argc < 2) { + printf("Nö\n"); + exit(1); + } + loadConfig(argv[1]); + proxy_init(); + char listen_addr[4] = {0, 0, 0, 0}; + // Setup socket + epoll_listen_t lsn; + lsn.callback = &listen_callback; + lsn.fd = socket_tcp4(); + if (lsn.fd == -1) bail("Could not create listen socket"); + if (socket_bind4_reuse(lsn.fd, listen_addr, 1234) == -1) bail("Could not bind to listening port"); + if (socket_listen(lsn.fd, 10) == -1) bail("Could not listen"); + // Setup epoll + if (ePoll_init() == -1) bail("epoll_create failed"); + // Add listener + if (ePoll_add(EPOLLIN, (epoll_item_t*)&lsn) == -1) bail("Could not add listen socket to epoll fd"); + // Init AD uplinks + server_initServers(); + // Do the mainloop + for (;;) { + if (ePoll_wait(-1) == -1) bail("ePoll wait failed."); + } + return 0; +} + +static void listen_callback(void *data, int haveIn, int haveOut, int doCleanup) +{ + if (doCleanup) bail("doCleanup on Listen socket set."); + if (!haveIn) return; + epoll_listen_t *listen = (epoll_listen_t *)data; + char remote[4]; + uint16 port; + int sock = socket_accept4(listen->fd, remote, &port); + if (sock < 0) { + printf("Error accepting new connection.\n"); + return; + } + printf("Accepted connection.\n"); + epoll_client_t *client = calloc(1, sizeof(epoll_client_t)); + client->fd = sock; + client->callback = &client_callback; + ePoll_add(EPOLLIN | EPOLLOUT | EPOLLET, (epoll_item_t*)client); +} + +static int loadConfig_handler(void *stuff, const char *section, const char *key, const char *value) +{ + if (strcmp(key, "binddn") == 0) { + server_setBind(section, value); + } + if (strcmp(key, "bindpw") == 0) { + server_setPassword(section, value); + } + if (strcmp(key, "base") == 0) { + server_setBase(section, value); + } + if (strcmp(key, "alias") == 0) { + server_setAlias(section, value); + } + return 1; +} + +static void loadConfig(char *file) +{ + ini_parse(file, &loadConfig_handler, NULL); +} + @@ -0,0 +1,221 @@ +#ifndef _LDAP_H +#define _LDAP_H + +#include <stddef.h> +#include <inttypes.h> +#include "asn1.h" + +int matchstring(struct string* s,const char* c); +int matchcasestring(struct string* s,const char* c); +int matchprefix(struct string* s,const char* c); +int matchcaseprefix(struct string* s,const char* c); + +/* "ou=fnord; O=fefe; c=de" -> "ou=fnord,o=fefe,c=de" */ +/* returns the length of the new string */ +size_t normalize_dn(char* dest,const char* src,int len); + +struct AttributeValueAssertion { + struct string desc, value; +}; + +struct AttributeDescriptionList { + struct string a; + uint32_t attrofs; + struct AttributeDescriptionList *next; +}; + +struct PartialAttributeList { + struct string type; + struct AttributeDescriptionList* values; + struct PartialAttributeList* next; +}; + +struct Substring { + enum { prefix=0, any=1, suffix=2 } substrtype; + struct string s; + struct Substring* next; +}; + +enum FilterType { + AND=0, OR=1, NOT=2, EQUAL=3, SUBSTRING=4, GREATEQUAL=5, LESSEQUAL=6, PRESENT=7, APPROX=8, EXTENSIBLE=9 +}; + +struct Filter { + enum FilterType type; + struct AttributeValueAssertion ava; // AND,OR,NOT = not used, PRESENT,SUBSTRING = only desc, EQUAL = both, none otherwiese + uint32_t attrofs; /* offset of attribute name in index */ + uint32_t attrflag; /* "case sensitivity" flag from index */ + struct Substring* substrings; + struct AttributeDescriptionList *a; // ??? + struct Filter* x,*next; + /* x is the subject of this filter (AND, OR and NOT) */ + /* next is used to form a linked list of subjects */ +}; + +struct SearchRequest { + struct string baseObject; + enum { baseObject=0, singleLevel=1, wholeSubtree=2 } scope; + enum { + neverDerefAliases=0, + derefInSearching=1, + derefFindingBaseObj=2, + derefAlways=3 + } derefAliases; + unsigned long sizeLimit, timeLimit, typesOnly; + struct Filter* filter; + struct AttributeDescriptionList* attributes; // Attributes the client wants. NULL = all +}; + +struct SearchResultEntry { + struct string objectName; + struct PartialAttributeList* attributes; +}; + +struct Modification { + enum { Add=0, Delete=1, Replace=2 } operation; + struct string AttributeDescription; /* ? */ + struct AttributeDescriptionList* vals; + struct Modification* next; +}; + +struct Addition { + struct string AttributeDescription; + struct AttributeDescriptionList vals; + struct Addition* next; +}; + +struct ModifyRequest { + struct string object; + struct Modification m; +}; + +struct AddRequest { + struct string entry; + struct Addition a; +}; + +struct ModifyDNRequest { + struct string entry, newrdn; + int deleteoldrdn; + struct string newsuperior; +}; + +enum ldapops { + BindRequest=0, BindResponse=1, + UnbindRequest=2, + SearchRequest=3, SearchResultEntry=4, SearchResultDone=5, + ModifyRequest=6, ModifyResponse=7, + AddRequest=8, AddResponse=9, + DelRequest=10, DelResponse=11, + ModifyDNRequest=12, ModifyDNResponse=13, + CompareRequest=14, CompareResponse=15, + AbandonRequest=16, + ExtendedRequest=23 /* coincidence? I think not. */, + ExtendedResponse=24 +}; + +enum ldaperrors { + success=0, + operationsError=1, + protocolError=2, + timeLimitExceeded=3, + sizeLimitExceeded=4, + compareFalse=5, + compareTrue=6, + authMethodNotSupported=7, + strongAuthRequired=8, + referral=10, + adminLimitExceeded=11, + unavailableCriticalExtension=12, + confidentialityRequired=13, + saslBindInProgress=14, + noSuchAttribute=16, + undefinedAttributeType=17, + inappropriateMatching=18, + constraintViolation=19, + attributeOrValueExists=20, + invalidAttributeSyntax=21, + noSuchObject=32, + aliasProblem=33, + invalidDNSyntax=34, + aliasDereferencingProblem=36, + inappropriateAuthentication=48, + invalidCredentials=49, + insufficientAccessRights=50, + busy=51, + unavailable=52, + unwillingToPerform=53, + loopDetect=54, + namingViolation=64, + objectClassViolation=65, + notAllowedOnNonLeaf=66, + notAllowedOnRDN=67, + entryAlreadyExists=68, + objectClassModsProhibited=69, + affectsMultipleDSAs=71, +}; + +void freefilter(struct Filter* f); +void freeava(struct AttributeDescriptionList* a); +void freepal(struct PartialAttributeList* a); + +size_t scan_ldapstring(const char* src,const char* max,struct string* s); +size_t scan_ldapmessage(const char* src,const char* max, + unsigned long* messageid,unsigned long* op, + size_t* len); +size_t scan_ldapbindrequest(const char* src,const char* max, + unsigned long* version,struct string* name, + unsigned long* method); +size_t scan_ldapbindresponse(const char* src,const char* max, + unsigned long* result,struct string* matcheddn, + struct string* errormessage,struct string* referral); +size_t scan_ldapava(const char* src,const char* max,struct AttributeValueAssertion* a); +size_t scan_ldapsearchfilter(const char* src,const char* max,struct Filter** f); +size_t scan_ldapsearchrequest(const char* src,const char* max,struct SearchRequest* s); +size_t scan_ldapsearchresultentry(const char* src,const char* max,struct SearchResultEntry* sre); +size_t scan_ldapresult(const char* src,const char* max,unsigned long* result, + struct string* matcheddn,struct string* errormessage, + struct string* referral); +size_t scan_ldapmodifyrequest(const char* src,const char* max,struct ModifyRequest* m); +size_t scan_ldapaddrequest(const char* src, const char * max, struct AddRequest * a); +size_t scan_ldapsearchfilterstring(const char* src,struct Filter** f); +size_t scan_ldapdeleterequest(const char* src,const char* max,struct string* s); +size_t scan_ldapmodifydnrequest(const char* src,const char* max,struct ModifyDNRequest* mdr); + +size_t fmt_ldapstring(char* dest,const struct string* s); +size_t fmt_ldapmessage(char* dest,long messageid,long op,size_t len); +size_t fmt_ldapbindrequest(char* dest,long version,const char* name,const char* simple); +size_t fmt_ldapsearchfilter(char* dest,const struct Filter* f); +size_t fmt_ldapsearchrequest(char* dest,const struct SearchRequest* s); +size_t fmt_ldapsearchresultentry(char* dest,const struct SearchResultEntry* sre); +size_t fmt_ldapresult(char* dest,long result,const char* matcheddn,const char* errormessage,const char* referral); +size_t fmt_ldappal(char* dest,const struct PartialAttributeList* pal); +size_t fmt_ldapava(char* dest,const struct AttributeValueAssertion* a); +size_t fmt_ldapadl(char* dest,const struct AttributeDescriptionList* adl); +size_t fmt_ldapavl(char* dest,const struct AttributeDescriptionList* adl); +size_t fmt_ldapmodifyrequest(char* dest,const struct ModifyRequest* m); +size_t fmt_ldapsearchfilterstring(char* dest,const struct Filter* f); +size_t fmt_ldapdeleterequest(char* dest,const struct string* s); +size_t fmt_ldapmodifydnrequest(char* dest,const struct ModifyDNRequest* mdr); + +#define fmt_ldapbindresponse(a,b,c,d,e) fmt_ldapresult(a,b,c,d,e) +#define fmt_ldapsearchresultdone(a,b,c,d,e) fmt_ldapresult(a,b,c,d,e) + +void free_ldapadl(struct AttributeDescriptionList* a); +void free_ldappal(struct PartialAttributeList* a); +void free_ldapsearchfilter(struct Filter* f); +/* does not free s itself */ +void free_ldapsearchrequest(struct SearchRequest* s); +/* does not free m itself */ +void free_ldapmodifyrequest(struct ModifyRequest* m); +/* does not free a itself */ +void free_ldapaddrequest(struct AddRequest * a); +/* does not free e itself */ +void free_ldapsearchresultentry(struct SearchResultEntry* e); + +int ldap_matchfilter_sre(struct SearchResultEntry* sre,struct Filter* f); + +int matchint(struct Filter* f,const char* t); +int substringmatch(struct Substring* x,const char* attr,int ignorecase); + +#endif diff --git a/ldap_match_sre.c b/ldap_match_sre.c new file mode 100644 index 0000000..b0e69c1 --- /dev/null +++ b/ldap_match_sre.c @@ -0,0 +1,166 @@ +#include <unistd.h> +#include "ldap.h" +#include "byte.h" +#include "case.h" +#include <ctype.h> + +static int matchcasestr(struct string* a,struct string* b) { + unsigned long l=a->l; + unsigned long r; + if (b->l<l) l=b->l; + if ((r=case_diffb(a->s,l,b->s))) return r; + if (a->l>l) return 1; + if (b->l>l) return -1; + return 0; +} + +static int matchstr(struct string* a,struct string* b) { + unsigned long l=a->l; + unsigned long r; + if (b->l<l) l=b->l; + if ((r=byte_diff(a->s,l,b->s))) return r; + if (a->l>l) return 1; + if (b->l>l) return -1; + return 0; +} + +static int matchstr_sre(struct Filter* f,struct string* s) { + int r; + if (f->attrflag&1) + r=matchcasestr(&f->ava.value,s); + else + r=matchstr(&f->ava.value,s); + if (f->type==EQUAL) return (r==0); + if (f->type==LESSEQUAL) return (r>0); + return (r<0); +} + +static int ldap_match_present_sre(struct SearchResultEntry* sre,struct string* s) { + struct PartialAttributeList* p; + for (p=sre->attributes; p; p=p->next) { + int r; + if ((r=matchstr(&p->type,s))) return r; + } + return 0; +} + +static int substrmatch(struct Substring* x,struct string* s,int ignorecase) { + int (*diff)(const void* a, unsigned long len, const void* b); + if (ignorecase) + diff=case_diffb; + else + diff=byte_diff; + while (x) { + unsigned long i; + if (x->s.l>s->l) return 0; + switch (x->substrtype) { + case prefix: + if (diff(x->s.s,x->s.l,s->s)) return 0; +found: + break; + case any: + if (s->l<x->s.l) return 0; + for (i=0; i<=s->l-x->s.l; ++i) + if (!diff(x->s.s,x->s.l,s->s+i)) + goto found; + return 0; + case suffix: + if (diff(x->s.s,x->s.l,s->s+s->l-x->s.l)) return 0; + } + x=x->next; + } + return 1; +} + +extern uint32_t dn_ofs; + +int ldap_matchfilter_sre(struct SearchResultEntry* sre,struct Filter* f) { + struct PartialAttributeList* p; + struct Filter* y=f->x; + if (!f) return 1; + switch (f->type) { + case AND: + while (y) { + if (!ldap_matchfilter_sre(sre,y)) return 0; + y=y->next; + } + return 1; + case OR: + while (y) { + if (ldap_matchfilter_sre(sre,y)) return 1; + y=y->next; + } + return 0; + case NOT: + return !ldap_matchfilter_sre(sre,y); + case PRESENT: + return ldap_match_present_sre(sre,&f->ava.desc); + case EQUAL: + case LESSEQUAL: + case GREATEQUAL: + if (f->attrofs==dn_ofs) + return matchstr_sre(f,&sre->objectName); + for (p=sre->attributes; p; p=p->next) { + int r; + struct AttributeDescriptionList* a; + if (matchstr(&f->ava.desc,&p->type)) { + for (a=p->values; a; a=a->next) + if ((r=matchstr_sre(f,&a->a))) return r; + return 0; + } + } + return 0; + case SUBSTRING: + if (f->attrofs==dn_ofs) + return substrmatch(f->substrings,&sre->objectName,f->attrflag&1); + for (p=sre->attributes; p; p=p->next) { + if (matchstr(&f->ava.desc,&p->type)) { + struct AttributeDescriptionList* a; + int r; + for (a=p->values; a; a=a->next) + if ((r=substrmatch(f->substrings,&a->a,f->attrflag&1))) return r; + return 0; + } + } + return 0; + default: + write(2,"unsupported query type\n",23); + return 0; + } + return 1; +} + +/* return 0 if they didn't match, otherwise return length in b */ +static int match(const char* a,int len,const char* b,int blen) { + const char* A=a+len; + const char* B=b+blen; + while (len>0 && A>a && B>b) { + --A; --B; --len; + while (*A==' ' && A>a) { --A; --len; } + while (*B==' ' && B>b) --B; + if (tolower(*A) != tolower(*B)) + return 0; + } + return b+blen-B; +} + +int ldap_match_sre(struct SearchResultEntry* sre,struct SearchRequest* sr) { + unsigned long i; + if (sr->baseObject.l>sre->objectName.l) + /* baseObject is longer than dn */ + return 0; + if (sr->baseObject.l && !match(sr->baseObject.s,sr->baseObject.l,sre->objectName.s,sre->objectName.l)) + /* baseObject is not a suffix of dn */ + return 0; + switch (sr->scope) { + case wholeSubtree: break; + case baseObject: if (sre->objectName.l==sr->baseObject.l) break; return 0; + default: + for (i=0; i<sre->objectName.l; ++i) + if (sre->objectName.s[i]==',') + break; + if (i+2>=sre->objectName.l-sr->baseObject.l) break; + return 0; + } + return ldap_matchfilter_sre(sre,sr->filter); +} diff --git a/matchcaseprefix.c b/matchcaseprefix.c new file mode 100644 index 0000000..9f709b7 --- /dev/null +++ b/matchcaseprefix.c @@ -0,0 +1,20 @@ +#include <string.h> +#include "case.h" +#include "asn1.h" +#include "str.h" + +/* behave like strcmp, but also return 0 if s is a prefix of c. */ +int matchcaseprefix(struct string* s,const char* c) { + unsigned int l,l1,i; + if (!c) return -1; + l1=l=str_len(c); + if (s->l<l1) l1=s->l; + i=case_diffb(s->s,l1,c); + if (i) return i; + /* one is a prefix of the other */ + if (l==s->l) return 0; + if (c[l1]) /* is c the longer string? */ + return 0; + return -(int)(s->s[l1]); +} + diff --git a/matchcasestring.c b/matchcasestring.c new file mode 100644 index 0000000..5e75e51 --- /dev/null +++ b/matchcasestring.c @@ -0,0 +1,21 @@ +#include "case.h" +#include "bstr.h" +#include "asn1.h" + +/* like matchstring, but case insensitively */ +int matchcasestring(struct string* s,const char* c) { + unsigned int l,l1,i; + if (!c) return -1; + l1=l=bstrlen(c); + if (s->l<l1) l1=s->l; + c=bstrfirst(c); + i=case_diffb(s->s,l1,c); + if (i) return i; + /* same length? */ + if (l==s->l) return 0; + /* one is a prefix of the other */ + if (l1<l) /* we cut off c */ + return -c[l1]; + return (int)(s->s[l1]); +} + diff --git a/matchprefix.c b/matchprefix.c new file mode 100644 index 0000000..83baebf --- /dev/null +++ b/matchprefix.c @@ -0,0 +1,20 @@ +#include "byte.h" +#include "asn1.h" +#include "bstr.h" + +/* behave like strcmp, but also return 0 if s is a prefix of c. */ +int matchprefix(struct string* s,const char* c) { + unsigned int l,l1,i; + if (!c) return -1; + l1=l=bstrlen(c); + if (s->l<l1) l1=s->l; + c=bstrfirst(c); + i=byte_diff(s->s,l1,c); + if (i) return i; + /* one is a prefix of the other */ + if (l==s->l) return 0; + if (c[l1]) /* is c the longer string? */ + return 0; + return -(int)(s->s[l1]); +} + diff --git a/matchstring.c b/matchstring.c new file mode 100644 index 0000000..61bbec8 --- /dev/null +++ b/matchstring.c @@ -0,0 +1,21 @@ +#include "byte.h" +#include "bstr.h" +#include "asn1.h" + +/* behave like strcmp */ +int matchstring(struct string* s,const char* c) { + unsigned int l,l1,i; + if (!c) return -1; + l1=l=bstrlen(c); + if (s->l<l1) l1=s->l; + c=bstrfirst(c); + i=byte_diff(s->s,l1,c); + if (i) return i; + /* same length? */ + if (l==s->l) return 0; + /* one is a prefix of the other */ + if (l1<l) /* we cut off c */ + return -c[l1]; + return (int)(s->s[l1]); +} + diff --git a/normalize_dn.c b/normalize_dn.c new file mode 100644 index 0000000..0cdf47e --- /dev/null +++ b/normalize_dn.c @@ -0,0 +1,26 @@ +#include <stddef.h> +#include <ctype.h> + +/* "ou=fnord; O=fefe; c=de" -> "ou=fnord,o=fefe,c=de" */ +/* returns the length of the new string */ +size_t normalize_dn(char* dest,const char* src,int len) { + int makelower=1; + char* orig=dest; + while (len) { + if (*src==';' || *src==',') { + *dest=','; + while (len>1 && src[1]==' ') { ++src; --len; } + makelower=1; + } else { + if (makelower) + *dest=tolower(*src); + else + *dest=*src; + if (*dest=='=') makelower=0; + } + ++dest; + ++src; + --len; + } + return dest-orig; +} @@ -0,0 +1,445 @@ +#include "proxy.h" +#include "client.h" +#include "server.h" +#include "helper.h" +#include "tmpbuffer.h" +#include "ldap.h" +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdarg.h> + +#define MAXPENDING 200 +#define BINDLEN 500 + +typedef struct +{ + BOOL homeDirectory; + BOOL gidNumber; + BOOL gecos; + BOOL loginShell; + BOOL uid; + BOOL uidNumber; +} attr_t; + +typedef struct +{ + unsigned long clientMessageId; + unsigned long serverMessageId; + epoll_client_t *client; + attr_t attr; +} pending_t; + +typedef struct +{ + const char *search; + struct string replace; +} replace_t; + +static pending_t _pendingRequest[MAXPENDING]; +static int _pendingCount = 0; + +static struct string s_shadowAccount, s_user, s_uid, s_sAMAccountName, s_objectSid; +static struct string s_objectclass, s_homeDirectory, s_gidNumber, s_gecos, s_dn; +static struct string s_loginShell, s_uidNumber, s_mail, s_objectCategory, s_memberOf, s_distinguishedName; + +// + +static int proxy_clientBindRequest(epoll_client_t *client, const unsigned long messageId, const size_t offset, const size_t maxLen); +static int proxy_serverBindResponse(epoll_server_t *server, const unsigned long messageId, const size_t offset, const size_t maxLen); +static int proxy_clientSearchRequest(epoll_client_t *client, const unsigned long messageId, const size_t offset, const size_t maxLen); +static int proxy_serverSearchResult(epoll_server_t *server, const unsigned long messageId, const unsigned long type, const size_t offset, const size_t maxLen); + +// + +#define SETSTR(x) s_ ## x.s = #x; s_ ## x.l = strlen( #x ) +void proxy_init() +{ + static int done = 0; + if (done) return; + done = 1; + SETSTR(shadowAccount); + SETSTR(user); + SETSTR(uid); + SETSTR(sAMAccountName); + SETSTR(objectSid); + SETSTR(objectclass); + SETSTR(homeDirectory); + SETSTR(gidNumber); + SETSTR(gecos); + SETSTR(loginShell); + SETSTR(uidNumber); + SETSTR(mail); + SETSTR(objectCategory); + SETSTR(memberOf); + SETSTR(distinguishedName); + SETSTR(dn); +} +#undef SETSTR + +int proxy_fromClient(epoll_client_t *client, const size_t maxLen) +{ + unsigned long messageId, op; + size_t len; + const size_t res = scan_ldapmessage(client->readBuffer, client->readBuffer + maxLen, &messageId, &op, &len); + if (res == 0) return -1; + printf("[C] scan_ldapmessage: Consumed %d, remaining length %d, id %lu, op %lu\n", (int)res, (int)len, messageId, op); + // TODO: Caching + switch (op) { + case BindRequest: + return proxy_clientBindRequest(client, messageId, res, maxLen); + case SearchRequest: + return proxy_clientSearchRequest(client, messageId, res, maxLen); + } + return 0; +} + +void proxy_removeClient(const epoll_client_t *client) +{ + int i, lastValid = -1; + for (i = 0; i < _pendingCount; ++i) { + if (_pendingRequest[i].client == client) _pendingRequest[i].client = NULL; + else if (_pendingRequest[i].client != NULL) lastValid = i; + } + _pendingCount = lastValid + 1; +} + +int proxy_fromServer(epoll_server_t *server, const size_t maxLen) +{ + unsigned long messageId, op; + size_t len; + const size_t res = scan_ldapmessage(server->readBuffer, server->readBuffer + maxLen, &messageId, &op, &len); + if (res == 0) return -1; + printf("[AD] scan_ldapmessage: Consumed %d, remaining length %d, id %lu, op %lu\n", (int)res, (int)len, messageId, op); + switch (op) { + case BindResponse: + return proxy_serverBindResponse(server, messageId, res, maxLen); + case SearchResultEntry: + case SearchResultDone: + return proxy_serverSearchResult(server, messageId, op, res, maxLen); + } + printf("Unsupported op: %lu\n", op); + return -1; +} + +// + +static pending_t* proxy_getFreePendingSlot(epoll_client_t *client) +{ + for (int i = 0; i < _pendingCount; ++i) { + if (_pendingRequest[i].client == NULL) { + memset(&_pendingRequest[i], 0, sizeof(pending_t)); + _pendingRequest[i].client = client; + return &_pendingRequest[i]; + } + } + if (_pendingCount < MAXPENDING) { + memset(&_pendingRequest[_pendingCount], 0, sizeof(pending_t)); + _pendingRequest[_pendingCount].client = client; + return &_pendingRequest[_pendingCount++]; + } + return NULL; +} + +static pending_t* proxy_getPendingFromServer(unsigned long serverMessageId) +{ + for (int i = 0; i < _pendingCount; ++i) { + if (_pendingRequest[i].client == NULL) continue; + if (_pendingRequest[i].serverMessageId == serverMessageId) return &_pendingRequest[i]; + } + return NULL; +} + +/* +static void pref(int spaces, char prefix) +{ + for (int i = 0; i < spaces; ++i) putchar(' '); + putchar(prefix); + putchar(' '); +} +*/ + +static inline int equals(struct string *a, struct string *b) +{ + if (a->l != b->l) return 0; + return strncmp(a->s, b->s, a->l) == 0; +} + +static inline int iequals(struct string *a, struct string *b) +{ + if (a->l != b->l) return 0; + for (size_t i = 0; i < a->l; ++i) { + if (tolower(a->s[i]) != b->s[i]) return 0; + } + return 1; +} + +// ---- client to AD replacements + +//#define PREF(...) do { pref(spaces, prefix); printf(__VA_ARGS__); } while (0) +static void request_replaceFilter(struct Filter *filter); +static void request_replaceAdl(struct AttributeDescriptionList **adl, attr_t *attr); +static void request_replaceAttribute(struct string *attribute, struct string *value); + +static void request_replaceFilter(struct Filter *filter) +{ + for (; filter != NULL; filter = filter->next) { + if (filter->type == PRESENT || filter->type == SUBSTRING) { + request_replaceAttribute(&filter->ava.desc, NULL); + } else if (filter->type == EQUAL) { + request_replaceAttribute(&filter->ava.desc, &filter->ava.value); + } + //request_replaceAdl(&filter->a, NULL, spaces + 2, prefix); + request_replaceFilter(filter->x); + } +} + +#define elifSETATTR(x) else if (equals(&(*adl)->a, &s_ ## x)) attr->x = TRUE, next = (*adl)->next, free(*adl), *adl = next +static void request_replaceAdl(struct AttributeDescriptionList **adl, attr_t *attr) +{ + while (*adl != NULL) { + struct AttributeDescriptionList *next = NULL; + if (attr == NULL) { } + elifSETATTR(homeDirectory); + elifSETATTR(gidNumber); + elifSETATTR(gecos); + elifSETATTR(loginShell); + elifSETATTR(uidNumber); + else request_replaceAttribute(&(*adl)->a, NULL); + if (*adl == NULL) break; + if (next == NULL) adl = &(*adl)->next; // If next is not NULL, we removed an entry, so we don't need to shift + } +} +#undef elifSETATTR + +static void request_replaceAttribute(struct string *attribute, struct string *value) +{ + if (equals(attribute, &s_uid)) { + *attribute = s_sAMAccountName; + } else if (iequals(attribute, &s_objectclass)) { + if (value == NULL) return; + if (equals(value, &s_shadowAccount)) *value = s_user; + } +} + +// --------- AD to client replacements + +static void response_replacePal(struct PartialAttributeList **pal, attr_t *attr); +static void response_replaceAdl(struct string *type, struct AttributeDescriptionList **adl, attr_t *attr); +static void response_replaceAttribute(struct string *attribute, struct string *value); +static struct PartialAttributeList* response_addPal(struct PartialAttributeList *pal, struct string *attribute, const char *format, ...); + +#define ADDATTR(x,...) do { if (attr->x) last = response_addPal(last, &s_ ## x, __VA_ARGS__); } while (0) +#define elifDELATTR(x) else if (equals(&(*pal)->type, &s_ ## x)) next = (*pal)->next +static void response_replacePal(struct PartialAttributeList **pal, attr_t *attr) +{ + struct string *username = NULL; + struct PartialAttributeList *last = NULL; + while (*pal != NULL) { + last = *pal; + struct PartialAttributeList *next = NULL; + if (0) { } + elifDELATTR(homeDirectory); + elifDELATTR(gidNumber); + elifDELATTR(gecos); + elifDELATTR(loginShell); + elifDELATTR(uidNumber); + elifDELATTR(mail); + if (next != NULL) { + free_ldapadl((*pal)->values); + free(*pal); + *pal = next; + continue; + } + response_replaceAdl(&(*pal)->type, &(*pal)->values, attr); + // Fetch user name so we can add our fake fields later + if (username == NULL && equals(&(*pal)->type, &s_uid)) { + username = &(*pal)->values->a; + } + pal = &(*pal)->next; + } + if (username != NULL) { + ADDATTR(homeDirectory, "/home/%.*s", (int)username->l, username->s); + ADDATTR(gecos, "%.*s,,,", (int)username->l, username->s); + ADDATTR(loginShell, "/bin/bash"); + ADDATTR(gidNumber, "1001"); + } +} +#undef ADDATTR +#undef elifDELATTR + +static void response_replaceAdl(struct string *type, struct AttributeDescriptionList **adl, attr_t *attr) +{ + while (*adl != NULL) { + struct AttributeDescriptionList *next = NULL; + // Maybe delete entries here later + if (next != NULL) { + free(*adl); + *adl = next; + continue; + } + response_replaceAttribute(type, &(*adl)->a); + adl = &(*adl)->next; // If next is not NULL, we removed an entry, so we don't need to shift + } +} + +static void response_replaceAttribute(struct string *attribute, struct string *value) +{ + if (equals(attribute, &s_sAMAccountName)) { + *attribute = s_uid; + } else if (iequals(attribute, &s_objectclass)) { + if (value == NULL) return; + if (equals(value, &s_user)) *value = s_shadowAccount; + } else if ( + equals(attribute, &s_dn) || + equals(attribute, &s_distinguishedName) || + equals(attribute, &s_memberOf) || + equals(attribute, &s_objectCategory) + ) { + if (value == NULL) return; + struct string alias; + if (server_baseToAlias(value, &alias) != -1) { + *value = alias; + } + } +} + +static struct PartialAttributeList* response_addPal(struct PartialAttributeList *pal, struct string *attribute, const char *format, ...) +{ + struct PartialAttributeList *next = malloc(sizeof(struct PartialAttributeList)); + va_list args; + va_start(args, format); + pal->next = next; + next->next = NULL; + next->type = *attribute; + next->values = malloc(sizeof(struct AttributeDescriptionList)); + tmpbuffer_formatva(&next->values->a, format, args); + next->values->next = NULL; + va_end(args); + return next; +} + +// ----- + +static int proxy_clientSearchRequest(epoll_client_t *client, const unsigned long messageId, const size_t offset, const size_t maxLen) +{ + struct SearchRequest req; + struct string realBase; + const size_t res = scan_ldapsearchrequest(client->readBuffer + offset, client->readBuffer + maxLen, &req); + if (res == 0) return -1; + const int server = server_aliasToBase(&req.baseObject, &realBase); + if (server == -1) { + printf("scan_ldapsearchrequest: baseObj %.*s unknown.\n", (int)req.baseObject.l, req.baseObject.s); + return -1; + } + req.baseObject = realBase; + printf("scan_ldapsearchrequest: baseO: %.*s, scope: %d, derefAliases: %d\n", (int)req.baseObject.l, req.baseObject.s, req.scope, req.derefAliases); + // + pending_t *pending = proxy_getFreePendingSlot(client); + if (pending == NULL) { + printf("No more slots for pending requests\n"); + return -1; + } + if (req.attributes == NULL) { + memset(&pending->attr, -1, sizeof(pending->attr)); + } else { + request_replaceAdl(&req.attributes, &pending->attr); + } + request_replaceFilter(req.filter); + helper_printfilter(req.filter); + helper_printal(req.attributes); + printf("Attrs: "); + for (size_t i = 0; i < sizeof(attr_t); ++i) putchar(((char*)&pending->attr)[i] == 0 ? '0' : '1'); + putchar('\n'); + pending->clientMessageId = messageId; + pending->serverMessageId = server_searchRequest(server, &req); + if (pending->serverMessageId == 0) { + // Failed to forward.. TODO: Fail client + pending->client = NULL; + } + free_ldapsearchrequest(&req); + // + if (pending->client == NULL) return -1; + return 0; +} + +static int proxy_serverSearchResult(epoll_server_t *server, const unsigned long messageId, const unsigned long type, const size_t offset, const size_t maxLen) +{ + static char *bodyBuffer = NULL; + if (bodyBuffer == NULL) bodyBuffer = malloc(MAXMSGLEN); + pending_t *pending = proxy_getPendingFromServer(messageId); + if (pending == NULL) { + printf("No client matching server message id %lu\n", messageId); + return 0; + } + printf("ServerID %lu -> ClientID %lu\n", messageId, pending->clientMessageId); + const char *body; + size_t bodyLen; + if (type == SearchResultDone) { + // Just forward with new header + bodyLen = maxLen - offset; + body = server->readBuffer + offset; + } else { + // Transform reply + struct SearchResultEntry sre; + const size_t res = scan_ldapsearchresultentry(server->readBuffer + offset, server->readBuffer + maxLen, &sre); + if (res == 0) return -1; + struct string alias; + if (server_baseToAlias(&sre.objectName, &alias) != -1) { + sre.objectName = alias; + } + response_replacePal(&sre.attributes, &pending->attr); + bodyLen = fmt_ldapsearchresultentry(NULL, &sre); + if (bodyLen == 0) { + printf("Error formatting ldapsearchresultentry after transformation\n"); + free_ldapsearchresultentry(&sre); + return -1; + } + if (bodyLen > MAXMSGLEN) { + printf("ldapsearchresultentry too large after transformation\n"); + free_ldapsearchresultentry(&sre); + return -1; + } + fmt_ldapsearchresultentry(bodyBuffer, &sre); + free_ldapsearchresultentry(&sre); + body = bodyBuffer; + } + // Build header and fire away + const size_t headerLen = fmt_ldapmessage(NULL, pending->clientMessageId, type, bodyLen); + char buffer[headerLen]; + fmt_ldapmessage(buffer, pending->clientMessageId, type, bodyLen); + client_send(pending->client, buffer, headerLen, TRUE); + client_send(pending->client, body, bodyLen, FALSE); + if (type == SearchResultDone) pending->client = NULL; + return 0; +} + +static int proxy_clientBindRequest(epoll_client_t *client, const unsigned long messageId, const size_t offset, const size_t maxLen) +{ + unsigned long version, method; + struct string name, password; + const size_t res = scan_ldapbindrequest(client->readBuffer + offset, client->readBuffer + maxLen, &version, &name, &method); + if (res == 0) return -1; // Parsing request failed + const size_t res2 = scan_ldapstring(client->readBuffer + offset + res, client->readBuffer + maxLen, &password); + printf("scan_ldapbindrequest: Consumed %d, version %lu, method %lu, name '%.*s', bindpw: '%.*s'\n", (int)(res + res2), version, method, (int)name.l, name.s, (int)password.l, password.s); + char buffer[800]; + char *bufoff = buffer + 100; + const size_t bodyLen = fmt_ldapbindresponse(bufoff, 0, "", "main screen turn on", ""); + const size_t headerLen = fmt_ldapmessage(NULL, messageId, BindResponse, bodyLen); + if (headerLen > 100) return -1; // Too long - don't care + fmt_ldapmessage(bufoff - headerLen, messageId, BindResponse, bodyLen); + return client_send(client, bufoff - headerLen, bodyLen + headerLen, FALSE); +} + +static int proxy_serverBindResponse(epoll_server_t *server, const unsigned long messageId, const size_t offset, const size_t maxLen) +{ + unsigned long result; + struct string binddn, error, refer; + const size_t res = scan_ldapbindresponse(server->readBuffer + offset, server->readBuffer + maxLen, &result, &binddn, &error, &refer); + if (res == 0) return -1; // Parsing request failed + printf("scan_ldapbindresponse: Consumed %d, result: %lu, binddn: %.*s, error: %.*s, referral: %.*s\n", (int)res, result, (int)binddn.l, binddn.s, (int)error.l, error.s, (int)refer.l, refer.s); + if (result == success) server->bound = TRUE; + return 0; +} + @@ -0,0 +1,14 @@ +#ifndef _PROXY_H_ +#define _PROXY_H_ + +#include "types.h" + +void proxy_init(); + +int proxy_fromClient(epoll_client_t *client, const size_t maxLen); + +void proxy_removeClient(const epoll_client_t *client); + +int proxy_fromServer(epoll_server_t *server, const size_t maxLen); + +#endif diff --git a/scan_asn1BITSTRING.c b/scan_asn1BITSTRING.c new file mode 100644 index 0000000..b42901a --- /dev/null +++ b/scan_asn1BITSTRING.c @@ -0,0 +1,27 @@ +#include "asn1.h" + +size_t scan_asn1BITSTRING(const char* src,const char* max,const char** s,size_t* l) { + size_t tmp; + unsigned long tag; + enum asn1_tagclass tc; + enum asn1_tagtype tt; + if ((tmp=scan_asn1string(src,max,&tc,&tt,&tag,s,l))) + if (tc==UNIVERSAL && tt==PRIMITIVE && tag==BIT_STRING) { + unsigned char lastbyte; + if (*l==0 || /* length must be at least 1 because for bit strings, the first octet contains the number of unused bits in the last octet */ + (unsigned char)(**s)>7) /* the number of unused bits in the last octet must not be negative and can be at most 7 */ + return 0; + /* these are DER checks */ + /* can't have unused bits if the length is 0 */ + if (*l==1 && **s) + return 0; + /* now check if the unused bits are 0 */ + lastbyte=(*s)[*l+1]; + if (lastbyte & (0xff >> (8-**s))) + return 0; + *l=(*l-1)*8-(unsigned char)(**s); + ++*s; + return tmp; + } + return 0; +} diff --git a/scan_asn1BOOLEAN.c b/scan_asn1BOOLEAN.c new file mode 100644 index 0000000..8a3958f --- /dev/null +++ b/scan_asn1BOOLEAN.c @@ -0,0 +1,16 @@ +#include "asn1.h" + +size_t scan_asn1BOOLEAN(const char* src,const char* max,unsigned long* val) { + size_t tmp; + unsigned long tag; + enum asn1_tagclass tc; + enum asn1_tagtype tt; + long ltmp; + if ((tmp=scan_asn1int(src,max,&tc,&tt,&tag,<mp))) + if (tc==UNIVERSAL && tt==PRIMITIVE && tag==BOOLEAN) { + if (ltmp!=0 && ltmp!=1) return 0; + *val=(unsigned long)ltmp; + return tmp; + } + return 0; +} diff --git a/scan_asn1ENUMERATED.c b/scan_asn1ENUMERATED.c new file mode 100644 index 0000000..36347cc --- /dev/null +++ b/scan_asn1ENUMERATED.c @@ -0,0 +1,15 @@ +#include "asn1.h" + +size_t scan_asn1ENUMERATED(const char* src,const char* max,unsigned long* val) { + size_t tmp; + unsigned long tag; + enum asn1_tagclass tc; + enum asn1_tagtype tt; + long ltmp; + if ((tmp=scan_asn1int(src,max,&tc,&tt,&tag,<mp))) + if (tc==UNIVERSAL && tt==PRIMITIVE && tag==ENUMERATED) { + *val=(unsigned long)ltmp; + return tmp; + } + return 0; +} diff --git a/scan_asn1INTEGER.c b/scan_asn1INTEGER.c new file mode 100644 index 0000000..61d3ac5 --- /dev/null +++ b/scan_asn1INTEGER.c @@ -0,0 +1,12 @@ +#include "asn1.h" + +size_t scan_asn1INTEGER(const char* src,const char* max,signed long* val) { + size_t tmp; + unsigned long tag; + enum asn1_tagclass tc; + enum asn1_tagtype tt; + if ((tmp=scan_asn1int(src,max,&tc,&tt,&tag,val))) + if (tc==UNIVERSAL && tt==PRIMITIVE && tag==INTEGER) + return tmp; + return 0; +} diff --git a/scan_asn1SEQUENCE.c b/scan_asn1SEQUENCE.c new file mode 100644 index 0000000..41bdb4b --- /dev/null +++ b/scan_asn1SEQUENCE.c @@ -0,0 +1,14 @@ +#include "asn1.h" + +size_t scan_asn1SEQUENCE(const char* src,const char* max,size_t* len) { + size_t res,tmp; + unsigned long tag; + enum asn1_tagclass tc; + enum asn1_tagtype tt; + if (!(res=scan_asn1tag(src,max,&tc,&tt,&tag))) return 0; + if (!(tmp=scan_asn1length(src+res,max,len))) return 0; + res+=tmp; + if (tc==UNIVERSAL && tt==CONSTRUCTED && tag==SEQUENCE_OF) + return res; + return 0; +} diff --git a/scan_asn1SET.c b/scan_asn1SET.c new file mode 100644 index 0000000..7cced52 --- /dev/null +++ b/scan_asn1SET.c @@ -0,0 +1,14 @@ +#include "asn1.h" + +size_t scan_asn1SET(const char* src,const char* max,size_t* len) { + size_t res,tmp; + unsigned long tag; + enum asn1_tagclass tc; + enum asn1_tagtype tt; + if (!(res=scan_asn1tag(src,max,&tc,&tt,&tag))) return 0; + if (!(tmp=scan_asn1length(src+res,max,len))) return 0; + res+=tmp; + if (tc==UNIVERSAL && tt==CONSTRUCTED && tag==SET_OF) + return res; + return 0; +} diff --git a/scan_asn1STRING.c b/scan_asn1STRING.c new file mode 100644 index 0000000..a6edd14 --- /dev/null +++ b/scan_asn1STRING.c @@ -0,0 +1,12 @@ +#include "asn1.h" + +size_t scan_asn1STRING(const char* src,const char* max,const char** s,size_t* l) { + size_t tmp; + unsigned long tag; + enum asn1_tagclass tc; + enum asn1_tagtype tt; + if ((tmp=scan_asn1string(src,max,&tc,&tt,&tag,s,l))) + if (tc==UNIVERSAL && tt==PRIMITIVE && tag==OCTET_STRING) + return tmp; + return 0; +} 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; +} diff --git a/scan_asn1int.c b/scan_asn1int.c new file mode 100644 index 0000000..0a01737 --- /dev/null +++ b/scan_asn1int.c @@ -0,0 +1,10 @@ +#include "asn1.h" + +size_t scan_asn1int(const char* src,const char* max,enum asn1_tagclass* tc,enum asn1_tagtype* tt,unsigned long* tag,signed long* l) { + size_t len,tmp,tlen; + if (!(len=scan_asn1tag(src,max,tc,tt,tag))) return 0; + if (!(tmp=scan_asn1length(src+len,max,&tlen))) return 0; + len+=tmp; + if (!(scan_asn1rawint(src+len,max,tlen,l))) return 0; + return len+tlen; +} diff --git a/scan_asn1length.c b/scan_asn1length.c new file mode 100644 index 0000000..944bd52 --- /dev/null +++ b/scan_asn1length.c @@ -0,0 +1,25 @@ +#include <inttypes.h> +#include "asn1.h" + +size_t scan_asn1length(const char* src,const char* max,size_t* length) { + const char* orig=src; + if (src>=max) return 0; +/* If the highest bit of the first byte is clear, the byte is the length. + * Otherwise the next n bytes are the length (n being the lower 7 bits) */ + if (*src&0x80) { + int chars=*src&0x7f; + size_t l=0; + while (chars>0) { + if (++src>=max) return 0; + if (l>(((unsigned long)-1)>>8)) return 0; /* catch integer overflow */ + l=l*256+(unsigned char)*src; + --chars; + } + *length=l; + } else + *length=*src&0x7f; + src++; + if (src+*length>max) return 0; /* catch integer overflow */ + if ((uintptr_t)src+*length<(uintptr_t)src) return 0; /* gcc 4.1 removes this check without the cast to uintptr_t */ + return src-orig; +} diff --git a/scan_asn1oid.c b/scan_asn1oid.c new file mode 100644 index 0000000..b5abf52 --- /dev/null +++ b/scan_asn1oid.c @@ -0,0 +1,21 @@ +#include "asn1.h" + +size_t scan_asn1oid(const char* src,const char* max,size_t* array,size_t* arraylen) { + size_t res,tlen; + unsigned long tag,tmp; + enum asn1_tagclass tc; + enum asn1_tagtype tt; + if (!arraylen) return 0; + 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; + + return scan_asn1rawoid(src,max,array,arraylen); +} + diff --git a/scan_asn1rawint.c b/scan_asn1rawint.c new file mode 100644 index 0000000..6f6f676 --- /dev/null +++ b/scan_asn1rawint.c @@ -0,0 +1,17 @@ +#include "asn1.h" + +size_t scan_asn1rawint(const char* src,const char* max,size_t len,long* l) { + size_t i,j; + long m; + if (src>=max) return 0; + if (*src<0) m=-1; else m=0; + for (i=j=0; i<len; ++i,++j) { + if ((m==0 && *src==0) || (m==-1 && *src==-1)) --j; + m=(m<<8)|(unsigned char)*src; + ++src; + if (src>max) return 0; + } + if (j>sizeof(long)) return 0; + *l=m; + return len; +} diff --git a/scan_asn1rawoid.c b/scan_asn1rawoid.c new file mode 100644 index 0000000..814009d --- /dev/null +++ b/scan_asn1rawoid.c @@ -0,0 +1,40 @@ +#include "asn1.h" + +size_t scan_asn1rawoid(const char* src,const char* max,size_t* 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; +} + diff --git a/scan_asn1string.c b/scan_asn1string.c new file mode 100644 index 0000000..c409859 --- /dev/null +++ b/scan_asn1string.c @@ -0,0 +1,12 @@ +#include "asn1.h" + +size_t scan_asn1string(const char* src,const char* max, + enum asn1_tagclass* tc,enum asn1_tagtype* tt,unsigned long* tag, + const char** s,size_t* l) { + size_t len,tmp; + if (!(len=scan_asn1tag(src,max,tc,tt,tag))) return 0; + if (!(tmp=scan_asn1length(src+len,max,l))) return 0; + len+=tmp; + *s=src+len; + return len+*l; +} diff --git a/scan_asn1tag.c b/scan_asn1tag.c new file mode 100644 index 0000000..acf0c57 --- /dev/null +++ b/scan_asn1tag.c @@ -0,0 +1,17 @@ +#include "asn1.h" + +size_t scan_asn1tag(const char* src,const char* max,enum asn1_tagclass* tc,enum asn1_tagtype* tt,unsigned long* tag) { + if (max<=src) return 0; + *tc=(*src&0xC0); + *tt=(*src&0x20); +/* The lower 5 bits are the tag, unless it's 0x1f, in which case the + * next bytes are the tag: always take the lower 7 bits; the last byte + * in the sequence is marked by a cleared high bit */ + if ((*src & 0x1f) == 0x1f) { + size_t res=scan_asn1tagint(src+1,max,tag); + return res+!!res; /* add 1 unless it's 0, then leave 0 */ + } else { + *tag=*src&0x1f; + return 1; + } +} diff --git a/scan_asn1tagint.c b/scan_asn1tagint.c new file mode 100644 index 0000000..49a9974 --- /dev/null +++ b/scan_asn1tagint.c @@ -0,0 +1,14 @@ +#include "asn1.h" + +size_t scan_asn1tagint(const char* src,const char* max,unsigned long* val) { + const char* orig=src; + unsigned long l=0; + for (;; ++src) { + if (src>=max) return 0; + if (l>(((unsigned long)-1)>>7)) return 0; /* catch integer overflow */ + l=l*128+(*src&0x7F); + if (!(*src&0x80)) break; + } + *val=l; + return src-orig+1; +} diff --git a/scan_certificate.c b/scan_certificate.c new file mode 100644 index 0000000..4fb1b44 --- /dev/null +++ b/scan_certificate.c @@ -0,0 +1,338 @@ +#include <stdlib.h> +#include <stdio.h> +#include "asn1.h" +#include <str.h> +#include "textcode.h" +#include "fmt.h" +#include "byte.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; +}; + +struct rsaprivatekey { + size_t* modulus,* publicExponent,* privateExponent,* prime1,* prime2,* exponent1,* exponent2,* coefficient; + struct string otherPrimeInfos; + size_t* freewhendone; +}; + +struct dsaprivatekey { +} + + +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; +} + +static size_t base64_decode(const char* cert, size_t l, const char* name, char** dest) { + /* cert should be something like "-----BEGIN CERTIFICATE-----\n[base64 gunk]\n-----END CERTIFICATE-----\n" + * l should be strlen(cert), but cert does not need to be 0-terminated + * name should be something like "CERTIFICATE" or "RSA PRIVATE KEY", what you are trying to decode + * dest will end up pointing to the decoded data. + * return value can be 0 if we can't decode the data and it does not + * look like it's a binary certificate. Or it can be some length + * value, in which case dest points to a malloced area of that length + * with the decoded data in it. */ + size_t taglen=strlen(name)+sizeof("-----BEGIN -----")-1; + char tag[taglen+1]; + char* c=0,* x; + tag[fmt_strm(tag,"-----BEGIN ",name,"-----")]=0; + if (l > 2*taglen && byte_equal(cert,taglen,tag)) +certfound: + { + size_t cur,used; + /* "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----" and newlines */ + c=malloc((l-2*taglen)/4*3); + if (!c) return 0; + x=c; + for (cur=taglen; cur+taglen-1<l;) { + size_t next; + if (cert[cur]=='\r') ++cur; /* skip line ending */ + if (cert[cur]=='\n') ++cur; + next=scan_base64(cert+cur,x,&used); + if (next==0) break; + cur+=next; + x+=used; + } + if (!str_start(cert+cur,"-----END ") || + byte_diff(cert+cur+sizeof("-----END"),taglen-sizeof("----BEGIN")-1,tag+sizeof("-----BEGIN"))) { + free(c); + return 0; + } + cert=c; + l=x-c; + } else { + /* Maybe it has text in front of the BEGIN CERTIFICATE line */ + size_t i,a=1; + for (i=0; i+27+25+2<l; ++i) { + if (cert[i]!='\n' && cert[i]!='\r' && (cert[i]<' ' || cert[i]>'~')) { + a=0; + break; + } + if (byte_equal(cert+i,taglen,tag)) { + 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; + } + *dest=c; + return l; +} + +size_t scan_rsaprivatekey(const char* cert, size_t l, struct rsaprivatekey* C, char** freewhendone) { + char* c; + size_t maxdigits,ret; + unsigned long version; + *freewhendone=NULL; + l=base64_decode(cert,l,"RSA PRIVATE KEY",&c); + if (!l) return 0; + if (c!=cert) *freewhendone=c; + maxdigits=l/sizeof(size_t)+2; + C->freewhendone=malloc(maxdigits*sizeof(size_t)*8); + if (!C->freewhendone) { +fail: + free(*freewhendone); + freewhendone=NULL; + return 0; + } + C->modulus=C->freewhendone; + C->publicExponent=C->modulus+maxdigits; + C->privateExponent=C->publicExponent+maxdigits; + C->prime1=C->privateExponent+maxdigits; + C->prime2=C->prime1+maxdigits; + C->exponent1=C->prime2+maxdigits; + C->exponent2=C->exponent1+maxdigits; + C->coefficient=C->exponent2+maxdigits; + C->otherPrimeInfos.l=0; + C->otherPrimeInfos.s=NULL; + if ((ret=scan_asn1generic(c,c+l,"{iIIIIIIII!}",&version, + C->modulus,C->publicExponent,C->privateExponent, + C->prime1,C->prime2,C->exponent1,C->exponent2, + C->coefficient,&C->otherPrimeInfos))) { + if (version!=0 && version!=1) goto fail; + if (version==0 && C->otherPrimeInfos.l) goto fail; + if (version==1 && !C->otherPrimeInfos.l) goto fail; + if (version==0) C->otherPrimeInfos.s=NULL; + return ret; + } else + goto fail; +} + +size_t scan_certificate(const char* cert, size_t l, struct x509cert* C, char** freewhendone) { + char* c=0,* x; + *freewhendone=NULL; + l=base64_decode(cert,l,"CERTIFICATE",&c); + if (!l) return 0; + if (c!=cert) *freewhendone=c; + cert=c; + + /* 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<len; ++i) + printf("%lu%s",temp[i],i+1<len?".":")\n"); + } else + printf("I don't know the algorithm and I can't parse/print the OID\n"); + } + + /* pubkeyalg is a SubjectPublicKeyInfo: + SubjectPublicKeyInfo ::= SEQUENCE{ + algorithm AlgorithmIdentifier, + subjectPublicKey BIT STRING} + + AlgorithmIdentifier ::= SEQUENCE{ + algorithm OBJECT IDENTIFIER, + parameters ANY DEFINED BY algorithm OPTIONAL} + */ + + { + struct string pubkeyoid, pubkeyparams, bits; + if (scan_asn1generic(pubkeyalg.s,pubkeyalg.s+pubkeyalg.l,"{o!}b",&pubkeyoid,&pubkeyparams,&bits)) { + + i=lookupoid(pubkeyoid.s,pubkeyoid.l); + if (i!=(size_t)-1) { + printf("public key algorithm %s\n",oid2string[i].name); + if (oid2string[i].id==X509_ALG_RSA) { + size_t* modulus,* publicExponent; + size_t allocsize=bits.l/8+2*sizeof(modulus[0]); + modulus=malloc(allocsize); + publicExponent=malloc(allocsize); + if (!modulus || !publicExponent) + printf("malloc for RSA bignums failed!\n"); + else { + if (scan_asn1generic(bits.s,bits.s+bits.l/8,"{II}",modulus,publicExponent)) { + if (publicExponent[0]==1) + printf("public exponent %lu\n",publicExponent[1]); + else + printf("public exponent is larger than a word?!\n"); + printf("modulus: "); + for (i=1; i<=modulus[0]; ++i) { + size_t j,k; + for (j=0, k=modulus[i]; j<sizeof(modulus[0]); ++j) { + printf("%02lx:",(k>>((sizeof(modulus[0])*8)-(j+1)*8))&0xff); + } + if ((i-1)%4==3 || i==modulus[0]) printf("\n"); + } + } else + printf("bignum scanning failed!\n"); + } + free(modulus); free(publicExponent); + /* 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<len; ++i) + printf("%lu%s",temp[i],i+1<len?".":")\n"); + } else + printf("I don't know the public key algorithm and I can't parse/print the OID\n"); + } + + } else + printf("could not parse public key part!\n"); + } + + } + } + +// printasn1(cert,cert+l); + +} + +#include "mmap.h" +#include <stdio.h> + +#include "printasn1.c" + +int main(int argc,char* argv[]) { + char* freewhendone; + char* buf; + size_t l,n; + struct x509cert c; + struct rsaprivatekey k; + + 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,&freewhendone); + free(freewhendone); + + buf=mmap_read(argc>1?argv[1]:"privatekey.pem",&l); + if (!buf) { puts("privatekey.pem not found"); return 1; } + + n=scan_rsaprivatekey(buf,l,&k,&freewhendone); + free(freewhendone); + free(k.freewhendone); +} diff --git a/scan_ldapaddrequest.c b/scan_ldapaddrequest.c new file mode 100644 index 0000000..9fb4b1d --- /dev/null +++ b/scan_ldapaddrequest.c @@ -0,0 +1,95 @@ +#include <stdlib.h> +#include "ldap.h" +#include "buffer.h" +#include "byte.h" + +#if 0 + AddRequest ::= [APPLICATION 8] SEQUENCE { + entry LDAPDN, + attributes SEQUENCE OF SEQUENCE { + type AttributeDescription, + vals SET OF AttributeValue } } + + AttributeList ::= SEQUENCE OF SEQUENCE { + type AttributeDescription, + vals SET OF AttributeValue } +#endif + +size_t scan_ldapaddrequest(const char* src,const char* max,struct AddRequest* a) { + size_t res,tmp,oslen; + struct Addition* last=0; + byte_zero(a,sizeof(*a)); + if (!(res=scan_ldapstring(src,max,&a->entry))) goto error; + if (!(tmp=scan_asn1SEQUENCE(src+res,max,&oslen))) goto error; + res+=tmp; + if (src+res+oslen>max) goto error; + max=src+res+oslen; + if (src+res>=max) goto error; /* need at least one record */ + do { + size_t islen; + if (last) { + struct Addition* cur; + if (!(cur=malloc(sizeof(struct Addition)))) + goto error; + last->next=cur; + last=cur; + } else { + last=&a->a; + } + last->next=0; + if (!(tmp=scan_asn1SEQUENCE(src+res,max,&islen))) + goto error; + res+=tmp; + /* scan AttributeDescription: */ + if (!(tmp=scan_ldapstring(src+res,max,&last->AttributeDescription))) + goto error; + res+=tmp; + + /* scan set of AttributeValue: */ + { + size_t set_len; + const char* set_max; + struct AttributeDescriptionList* ilast=0; + if (!(tmp=scan_asn1SET(src+res,max,&set_len))) { + goto error; + } + res+=tmp; + set_max=src+res+set_len; + if (src+res+set_len!=set_max) { + goto error; + } + while (src+res<set_max) { + if (ilast) { + struct AttributeDescriptionList* x; + if (!(x=malloc(sizeof(struct AttributeDescriptionList)))) goto error; + ilast->next=x; + ilast = ilast->next; + } else { + ilast=&last->vals; + } + ilast->next=0; + if (!(tmp=scan_ldapstring(src+res,max,&ilast->a))) + goto error; + res+=tmp; + } + } + } while (src+res<max); +// buffer_putsflush(buffer_2,"done with scan_ldapaddrequest!\n"); + return res; +error: + free_ldapaddrequest(a); + return 0; +} + +static void free_add(struct Addition * a) { + while (a) { + struct Addition * tmp = a->next; + free(a); + a = tmp; + } +} + +void free_ldapaddrequest(struct AddRequest * a) { + free_ldapadl(a->a.vals.next); + free_add(a->a.next); +} diff --git a/scan_ldapava.c b/scan_ldapava.c new file mode 100644 index 0000000..b9332c4 --- /dev/null +++ b/scan_ldapava.c @@ -0,0 +1,10 @@ +#include "ldap.h" + +size_t scan_ldapava(const char* src,const char* max,struct AttributeValueAssertion* ava) { + size_t res,tmp; + if (!(res=scan_ldapstring(src,max,&ava->desc))) goto error; + if (!(tmp=scan_ldapstring(src+res,max,&ava->value))) goto error; + return res+tmp; +error: + return 0; +} diff --git a/scan_ldapbindrequest.c b/scan_ldapbindrequest.c new file mode 100644 index 0000000..0ce7e12 --- /dev/null +++ b/scan_ldapbindrequest.c @@ -0,0 +1,17 @@ +#include "ldap.h" + +size_t scan_ldapbindrequest(const char* src,const char* max, + unsigned long* version,struct string* name, + unsigned long* method) { + size_t res,tmp; + if (!(res=scan_asn1INTEGER(src,max,(signed long*)version))) return 0; + if (!(tmp=scan_ldapstring(src+res,max,name))) return 0; + res+=tmp; + { + enum asn1_tagclass tc; + enum asn1_tagtype tt; + if (!(tmp=scan_asn1tag(src+res,max,&tc,&tt,method))) return 0; + if (tc!=PRIVATE || tt!=PRIMITIVE) return 0; + } + return res; +} diff --git a/scan_ldapbindresponse.c b/scan_ldapbindresponse.c new file mode 100644 index 0000000..1161ab1 --- /dev/null +++ b/scan_ldapbindresponse.c @@ -0,0 +1,23 @@ +#include "ldap.h" + +size_t scan_ldapbindresponse(const char* src,const char* max, + unsigned long* result,struct string* matcheddn, + struct string* errormessage,struct string* referral) { + size_t res,tmp; + if (!(res=scan_asn1ENUMERATED(src,max,result))) return 0; + if (!(tmp=scan_ldapstring(src+res,max,matcheddn))) return 0; + res+=tmp; + if (src+res<max) { + if (!(tmp=scan_ldapstring(src+res,max,errormessage))) return 0; + res+=tmp; + } else { + errormessage->s=0; errormessage->l=0; + } + if (src+res<max) { + if (!(tmp=scan_ldapstring(src+res,max,referral))) return res; + res+=tmp; + } else { + referral->s=0; referral->l=0; + } + return res; +} diff --git a/scan_ldapdeleterequest.c b/scan_ldapdeleterequest.c new file mode 100644 index 0000000..f2c8021 --- /dev/null +++ b/scan_ldapdeleterequest.c @@ -0,0 +1,9 @@ +#include "ldap.h" + +size_t scan_ldapdeleterequest(const char* src,const char* max, + struct string* s) { + if (src>=max) return 0; + s->l=max-src; + s->s=src; + return s->l; +} diff --git a/scan_ldapmessage.c b/scan_ldapmessage.c new file mode 100644 index 0000000..baa1173 --- /dev/null +++ b/scan_ldapmessage.c @@ -0,0 +1,21 @@ +#include "ldap.h" + +size_t scan_ldapmessage(const char* src,const char* max, + unsigned long* messageid,unsigned long* op,size_t* len) { + size_t res,tmp; + if (!(res=scan_asn1SEQUENCE(src,max,len))) goto error; + if (!(tmp=scan_asn1INTEGER(src+res,max,(long*)messageid))) goto error; + res+=tmp; + { + enum asn1_tagclass tc; + enum asn1_tagtype tt; + if (!(tmp=scan_asn1tag(src+res,max,&tc,&tt,op))) goto error; + if (tc!=APPLICATION) goto error; + res+=tmp; + if (!(tmp=scan_asn1length(src+res,max,len))) goto error; + res+=tmp; + } + return res; +error: + return 0; +} diff --git a/scan_ldapmodifyrequest.c b/scan_ldapmodifyrequest.c new file mode 100644 index 0000000..ba42e4f --- /dev/null +++ b/scan_ldapmodifyrequest.c @@ -0,0 +1,88 @@ +#include <stdlib.h> +#include "ldap.h" + +#if 0 + ModifyRequest ::= [APPLICATION 6] SEQUENCE { + object LDAPDN, + modification SEQUENCE OF SEQUENCE { + operation ENUMERATED { + add (0), + delete (1), + replace (2) }, + modification AttributeTypeAndValues } } + + AttributeTypeAndValues ::= SEQUENCE { + type AttributeDescription, + vals SET OF AttributeValue } +#endif + +size_t scan_ldapmodifyrequest(const char* src,const char* max,struct ModifyRequest* m) { + size_t res,tmp,oslen; /* outer sequence length */ + struct Modification* last=0; + m->m.next=0; + if (!(res=scan_ldapstring(src,max,&m->object))) goto error; + if (!(tmp=scan_asn1SEQUENCE(src+res,max,&oslen))) goto error; + res+=tmp; + if (src+res+oslen>max) goto error; + max=src+res+oslen; + if (src+res>=max) goto error; /* need at least one record */ + do { + size_t islen, etmp; + if (last) { + struct Modification* cur; + if (!(cur=malloc(sizeof(struct Modification)))) goto error; + last->next=cur; last=cur; + } else + last=&m->m; + last->next=0; + if (!(tmp=scan_asn1SEQUENCE(src+res,max,&islen))) goto error; + res+=tmp; + if (!(tmp=scan_asn1ENUMERATED(src+res,max,&etmp))) goto error; + if (etmp>2) goto error; last->operation=etmp; res+=tmp; + { + size_t iislen; /* urgh, _three_ levels of indirection */ + const char* imax; + if (!(tmp=scan_asn1SEQUENCE(src+res,max,&iislen))) goto error; + res+=tmp; + imax=src+res+iislen; + if (imax>max) goto error; + if (!(tmp=scan_ldapstring(src+res,imax,&last->AttributeDescription))) goto error; + res+=tmp; + { + size_t iiislen; /* waah, _four_ levels of indirection! It doesn't get more inefficient than this */ + const char* iimax; + struct AttributeDescriptionList** ilast=0; + if (!(tmp=scan_asn1SET(src+res,max,&iiislen))) goto error; + res+=tmp; + iimax=src+res+iiislen; + if (src+res+iiislen!=imax) goto error; + ilast=&last->vals; + while (src+res<iimax) { + if (!(*ilast=malloc(sizeof(struct AttributeDescriptionList)))) goto error; + if (!(tmp=scan_ldapstring(src+res,imax,&(*ilast)->a))) goto error; + (*ilast)->next=0; + ilast=&(*ilast)->next; + res+=tmp; + } + } + } + break; + } while (src+res<max); + return res; +error: + free_ldapmodifyrequest(m); + return 0; +} + +static void free_mod(struct Modification* m) { + while (m) { + struct Modification* tmp=m->next; + free(m); + m=tmp; + } +} + +void free_ldapmodifyrequest(struct ModifyRequest* m) { + free_ldapadl(m->m.vals); + free_mod(m->m.next); +} diff --git a/scan_ldapresult.c b/scan_ldapresult.c new file mode 100644 index 0000000..930f458 --- /dev/null +++ b/scan_ldapresult.c @@ -0,0 +1,15 @@ +#include "ldap.h" + +size_t scan_ldapresult(const char* src,const char* max,unsigned long* result, + struct string* matcheddn,struct string* errormessage, + struct string* referral) { + size_t res,tmp; + if (!(res=scan_asn1ENUMERATED(src,max,result))) return 0; + if (!(tmp=scan_ldapstring(src+res,max,matcheddn))) return 0; + res+=tmp; + if (!(tmp=scan_ldapstring(src+res,max,errormessage))) return 0; + res+=tmp; + if (src+res==max) { referral->l=0; referral->s=0; return res; } + if (!(tmp=scan_ldapstring(src+res,max,referral))) return 0; + return res+tmp; +} diff --git a/scan_ldapsearchfilter.c b/scan_ldapsearchfilter.c new file mode 100644 index 0000000..a6016fa --- /dev/null +++ b/scan_ldapsearchfilter.c @@ -0,0 +1,113 @@ +#include "ldap.h" +#include <stdlib.h> + +/* + Filter ::= CHOICE { + and [0] SET OF Filter, + or [1] SET OF Filter, + not [2] Filter, + equalityMatch [3] AttributeValueAssertion, + substrings [4] SubstringFilter, + greaterOrEqual [5] AttributeValueAssertion, + lessOrEqual [6] AttributeValueAssertion, + present [7] AttributeDescription, + approxMatch [8] AttributeValueAssertion, + extensibleMatch [9] MatchingRuleAssertion } + + SubstringFilter ::= SEQUENCE { + type AttributeDescription, + -- at least one must be present + substrings SEQUENCE OF CHOICE { + initial [0] LDAPString, + any [1] LDAPString, + final [2] LDAPString } } + + MatchingRuleAssertion ::= SEQUENCE { + matchingRule [1] MatchingRuleId OPTIONAL, + type [2] AttributeDescription OPTIONAL, + matchValue [3] AssertionValue, + dnAttributes [4] BOOLEAN DEFAULT FALSE } +*/ + +size_t scan_ldapsearchfilter(const char* src,const char* max,struct Filter** f) { + enum asn1_tagclass tc; + enum asn1_tagtype tt; + size_t tag,len,res,tmp; + const char* nmax; + *f=0; + if (!(res=scan_asn1tag(src,max,&tc,&tt,&tag))) goto error; + if (tc!=PRIVATE || (tt!=CONSTRUCTED && tag!=7) || tag>9) goto error; + if (!(tmp=scan_asn1length(src+res,max,&len))) goto error; + res+=tmp; + nmax=src+res+len; + if (nmax>max) goto error; + if (!(*f=malloc(sizeof(struct Filter)))) goto error; + (*f)->next=0; + (*f)->x=0; + (*f)->substrings=0; + switch ((*f)->type=tag) { + case 0: /* and [0] SET OF Filter, */ + case 1: /* or [1] SET OF Filter, */ + (*f)->x=0; + while (src+res<nmax) { + struct Filter* F=(*f)->x; + if (!(tmp=scan_ldapsearchfilter(src+res,nmax,&(*f)->x))) { + if (F) { /* OK, end of sequence */ + (*f)->x=F; + break; + } + (*f)->x=F; + goto error; + } + (*f)->x->next=F; + res+=tmp; + } + break; + case 2: /* not [2] Filter, */ + if (!(tmp=scan_ldapsearchfilter(src+res,nmax,&(*f)->x))) goto error; + if (tmp!=len) goto error; + res+=tmp; + break; + case 3: /* equalityMatch [3] AttributeValueAssertion, */ + case 5: /* greaterOrEqual [5] AttributeValueAssertion, */ + case 6: /* lessOrEqual [6] AttributeValueAssertion, */ + case 8: /* approxMatch [8] AttributeValueAssertion, */ + if (!(tmp=scan_ldapava(src+res,nmax,&(*f)->ava))) goto error; + res+=tmp; + break; + case 4: /* substrings [4] SubstringFilter, */ + { + size_t len2; + if (!(tmp=scan_ldapstring(src+res,nmax,&(*f)->ava.desc))) goto error; + res+=tmp; + if (!(tmp=scan_asn1SEQUENCE(src+res,nmax,&len2))) goto error; + res+=tmp; + if (src+res+len2!=nmax) goto error; + while (src+res<nmax) { + struct Substring* s=malloc(sizeof(struct Substring)); + unsigned long x; + enum asn1_tagtype tt; + enum asn1_tagclass tc; + if (!s) goto error; + if (!(tmp=scan_asn1string(src+res,nmax,&tc,&tt,&x,&s->s.s,&s->s.l))) { free(s); goto error; } + if (x>2) goto error; + s->substrtype=x; + res+=tmp; + s->next=(*f)->substrings; + (*f)->substrings=s; + } + break; + } + case 7: /* present [7] AttributeDescription, */ + (*f)->ava.desc.s=src+res; + (*f)->ava.desc.l=len; + res+=len; + break; + case 9: /* extensibleMatch [9] MatchingRuleAssertion } */ + goto error; + } + return res; +error: + free_ldapsearchfilter(*f); + return 0; +} diff --git a/scan_ldapsearchfilterstring.c b/scan_ldapsearchfilterstring.c new file mode 100644 index 0000000..5c478ba --- /dev/null +++ b/scan_ldapsearchfilterstring.c @@ -0,0 +1,100 @@ +#include <stdlib.h> +#include "ldap.h" +#include <str.h> + +size_t scan_ldapsearchfilterstring(const char* src,struct Filter** f) { + char* s=(char*)src; + if (!(*f=calloc(sizeof(struct Filter),1))) goto error; + if (s[0]=='*' && (s[1]==0 || s[1]=='(')) { + size_t i=scan_ldapsearchfilterstring("(objectClass=*)",f); + if (i) return 1; + } + if (*s!='(') goto error; + switch (*(++s)) { + case '&': ++s; (*f)->type=AND; +scan_filterlist: + { + struct Filter** n; + s+=scan_ldapsearchfilterstring(s,&(*f)->x); + n=&(*f)->x->next; + while (*s!=')') { + size_t l=scan_ldapsearchfilterstring(s,n); + if (!l) return 0; + s+=l; + n=&(*n)->next; + } + } + break; + case '|': ++s; (*f)->type=OR; + goto scan_filterlist; + break; + case '!': + (*f)->type=NOT; + ++s; + s+=scan_ldapsearchfilterstring(s,&(*f)->x); + break; + default: + (*f)->ava.desc.s=s; + (*f)->ava.desc.l=str_chr(s,'=')-1; + s+=(*f)->ava.desc.l+1; + switch (*(s-1)) { + case '~': (*f)->type=APPROX; break; + case '>': (*f)->type=GREATEQUAL; break; + case '<': (*f)->type=LESSEQUAL; break; + default: + ++(*f)->ava.desc.l; + if (*(++s)=='*') { + if (*(++s)==')') { + (*f)->type=PRESENT; + return s-src; + } + (*f)->type=SUBSTRING; +substring: + while (*s!=')') { + size_t i,j; + struct Substring* substring=malloc(sizeof(struct Substring)); + if (!substring) goto error; + substring->s.s=s; + i=str_chr(s,')'); + j=str_chr(s,'*'); + if (i>j) { + substring->substrtype=any; + s+=substring->s.l=j; + ++s; + } else { + substring->substrtype=suffix; + s+=substring->s.l=i; + } + substring->next=(*f)->substrings; + (*f)->substrings=substring; + if (*s==0) goto error; + } + } else { + size_t i,j; + i=str_chr(s,')'); + j=str_chr(s,'*'); + if (i>j) { + struct Substring* substring=malloc(sizeof(struct Substring)); + if (!substring) goto error; + (*f)->type=SUBSTRING; + substring->substrtype=prefix; + substring->s.s=s; + s+=substring->s.l=j; + ++s; + substring->next=(*f)->substrings; + (*f)->substrings=substring; + goto substring; + } else { + (*f)->type=EQUAL; + } + } + } + if (*s=='=') ++s; + (*f)->ava.value.s=s; + s+=(*f)->ava.value.l=str_chr(s,')'); + if (*s!=')') return 0; + } + return s-src+1; +error: + return 0; +} diff --git a/scan_ldapsearchrequest.c b/scan_ldapsearchrequest.c new file mode 100644 index 0000000..8b685bf --- /dev/null +++ b/scan_ldapsearchrequest.c @@ -0,0 +1,55 @@ +#include <stdlib.h> +#include "ldap.h" + +size_t scan_ldapsearchrequest(const char* src,const char* max, + struct SearchRequest* s) { + size_t res,tmp; + unsigned long etmp; + signed long ltmp; + s->attributes=0; + s->filter=0; + if (!(res=scan_ldapstring(src,max,&s->baseObject))) goto error; + if (!(tmp=scan_asn1ENUMERATED(src+res,max,&etmp))) goto error; + if (etmp>2) goto error; s->scope=etmp; res+=tmp; + if (!(tmp=scan_asn1ENUMERATED(src+res,max,&etmp))) goto error; + if (etmp>3) goto error; s->derefAliases=etmp; res+=tmp; + if (!(tmp=scan_asn1INTEGER(src+res,max,<mp)) || ltmp<0) goto error; + s->sizeLimit=(unsigned long)ltmp; + res+=tmp; + if (!(tmp=scan_asn1INTEGER(src+res,max,<mp)) || ltmp<0) goto error; + s->timeLimit=(unsigned long)ltmp; + res+=tmp; + if (!(tmp=scan_asn1BOOLEAN(src+res,max,&s->typesOnly))) goto error; + res+=tmp; + if (!(tmp=scan_ldapsearchfilter(src+res,max,&s->filter))) goto error; + res+=tmp; + /* now for the attributelist */ + if (!(tmp=scan_asn1SEQUENCE(src+res,max,&etmp))) goto error; + res+=tmp; + { + const char* nmax=src+res+etmp; +//#define nmax max + struct AttributeDescriptionList** a=&s->attributes; + if (nmax>max) goto error; + for (;;) { + if (src+res>nmax) goto error; + if (src+res==nmax) break; + if (!*a) *a=malloc(sizeof(struct AttributeDescriptionList)); + if (!*a) goto error; + (*a)->next=0; + if (!(tmp=scan_ldapstring(src+res,nmax,&(*a)->a))) goto error; + res+=tmp; + a=&(*a)->next; + } + return res; + } +error: + free_ldapsearchrequest(s); + return 0; +} + +void free_ldapsearchrequest(struct SearchRequest* s) { + if (s->attributes) + free_ldapadl(s->attributes); // ->next !? + free_ldapsearchfilter(s->filter); +} diff --git a/scan_ldapsearchresultentry.c b/scan_ldapsearchresultentry.c new file mode 100644 index 0000000..50e8654 --- /dev/null +++ b/scan_ldapsearchresultentry.c @@ -0,0 +1,42 @@ +#include <stdlib.h> +#include "ldap.h" + +size_t scan_ldapsearchresultentry(const char* src,const char* max,struct SearchResultEntry* sre) { + size_t res,tmp,oslen; /* outer sequence length */ + struct PartialAttributeList** a=&sre->attributes; + *a=0; + if (!(res=scan_ldapstring(src,max,&sre->objectName))) goto error; + if (!(tmp=scan_asn1SEQUENCE(src+res,max,&oslen))) goto error; + res+=tmp; + if (src+res+oslen>max) goto error; + max=src+res+oslen; /* we now may have a stronger limit */ + while (src+res<max) { + struct string s; + struct AttributeDescriptionList* x; + size_t islen; + const char* nmax; + if (!(tmp=scan_asn1SEQUENCE(src+res,max,&islen))) goto error; + res+=tmp; nmax=src+res+islen; if (nmax>max) goto error; + if (!(tmp=scan_ldapstring(src+res,nmax,&s))) goto error; + if (!(*a=malloc(sizeof(struct PartialAttributeList)))) goto error; + (*a)->next=0; (*a)->values=0; (*a)->type=s; + res+=tmp; + if (!(tmp=scan_asn1SET(src+res,max,&islen))) goto error; + res+=tmp; if (src+res+islen!=nmax) goto error; + while (src+res<nmax) { + if (!(tmp=scan_ldapstring(src+res,max,&s))) goto error; + if (!(x=malloc(sizeof(struct AttributeDescriptionList)))) goto error; + x->a=s; + x->next=(*a)->values; + (*a)->values=x; + res+=tmp; + } + a=&(*a)->next; + } + *a=0; + return res; +error: + freepal(sre->attributes); + return 0; +} + diff --git a/scan_ldapstring.c b/scan_ldapstring.c new file mode 100644 index 0000000..c6054ee --- /dev/null +++ b/scan_ldapstring.c @@ -0,0 +1,5 @@ +#include "ldap.h" + +size_t scan_ldapstring(const char* src,const char* max,struct string* s) { + return scan_asn1STRING(src,max,&s->s,&s->l); +} diff --git a/server.c b/server.c new file mode 100644 index 0000000..b303e67 --- /dev/null +++ b/server.c @@ -0,0 +1,353 @@ +#include "server.h" +#include "proxy.h" +#include "helper.h" +#include "epoll.h" +#include "tmpbuffer.h" +#include <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <socket.h> + +#define ADDRLEN 40 +#define BINDLEN 200 +#define PWLEN 40 +#define BASELEN 100 +#define ALIASLEN 40 + +#define AD_PORT 3268 +#define MSGID_BIND 1 + +typedef struct { + size_t aliasLen; + size_t baseLen; + char ip[4]; + time_t lastLookup; + char addr[ADDRLEN]; + char bind[BINDLEN]; + char password[PWLEN]; + char base[BASELEN]; + char alias[ALIASLEN]; + epoll_server_t con; +} server_t; + +#define MAX_SERVERS 10 +static server_t *servers = NULL; +static int serverCount = 0; + +static void server_init(); +static server_t *server_create(const char *server); +static void server_callback(void *data, int haveIn, int haveOut, int doCleanup); +static void server_flush(epoll_server_t * const server); +static BOOL server_ensureConnected(const int index); +static void server_ensureSendBuffer(epoll_server_t * const s, const size_t len); + +// Generate a message ID for request to AD +static inline uint32_t msgId() +{ + static uint32_t id = 1336; + if (++id < 2) id = 2; + return id; +} + +// Setting up server(s) + +void server_setBind(const char *server, const char *bind) +{ + server_t *entry = server_create(server); + if (entry == NULL) return; + if (snprintf(entry->bind, BINDLEN, "%s", bind) >= BINDLEN) printf("Warning: BindDN for %s is too long.\n", server); +} + +void server_setPassword(const char *server, const char *password) +{ + server_t *entry = server_create(server); + if (entry == NULL) return; + if (snprintf(entry->password, PWLEN, "%s", password) >= PWLEN) printf("Warning: BindPW for %s is too long.\n", server); +} + +void server_setBase(const char *server, const char *base) +{ + server_t *entry = server_create(server); + if (entry == NULL) return; + if (snprintf(entry->base, BASELEN, "%s", base) >= BASELEN) printf("Warning: SearchBase for %s is too long.\n", server); + entry->baseLen = normalize_dn(entry->base, entry->base, min(strlen(entry->base), BASELEN - 1)); + entry->base[entry->baseLen] = '\0'; +} + +void server_setAlias(const char *server, const char *alias) +{ + server_t *entry = server_create(server); + if (entry == NULL) return; + if (snprintf(entry->alias, ALIASLEN, "%s", alias) >= ALIASLEN) printf("Warning: SearchBase Alias for %s is too long.\n", server); + entry->aliasLen = normalize_dn(entry->alias, entry->alias, min(strlen(entry->alias), ALIASLEN - 1)); + entry->alias[entry->aliasLen] = '\0'; +} + +void server_initServers() +{ + int i; + printf("%d servers configured.\n", serverCount); + for (i = 0; i < serverCount; ++i) { + printf("%s:\n Bind: %s\n Base: %s\n Proxy Alias: %s\n", servers[i].addr, servers[i].bind, servers[i].base, servers[i].alias); + server_ensureConnected(i); + } +} + +// What the proxy calls + +int server_aliasToBase(struct string *in, struct string *out) +{ + int i; + char buffer[TMPLEN]; + const size_t searchLen = normalize_dn(buffer, in->s, min(in->l, TMPLEN - 1)); + buffer[searchLen] = '\0'; + // Now buffer contains the normalized wanted alias. Try to find a match in the server list + for (i = 0; i < serverCount; ++i) { + if (searchLen < servers[i].aliasLen) continue; + if (strcmp(servers[i].alias, buffer + (searchLen - servers[i].aliasLen)) == 0) { + // Found, handle + tmpbuffer_format(out, "%.*s%s", (int)(searchLen - servers[i].aliasLen), buffer, servers[i].base); + return i; + } + } + return -1; +} + +int server_baseToAlias(struct string *in, struct string *out) +{ + int i; + char buffer[TMPLEN]; + const size_t searchLen = normalize_dn(buffer, in->s, min(in->l, TMPLEN - 1)); + buffer[searchLen] = '\0'; + // Now buffer contains the normalized wanted base. Try to find a match in the server list + for (i = 0; i < serverCount; ++i) { + printf("Comparing %s (%s) to %s\n", buffer, buffer + (searchLen - servers[i].baseLen), servers[i].base); + if (searchLen < servers[i].baseLen) continue; + if (strcmp(servers[i].base, buffer + (searchLen - servers[i].baseLen)) == 0) { + // Found, handle + tmpbuffer_format(out, "%.*s%s", (int)(searchLen - servers[i].baseLen), buffer, servers[i].alias); + printf("Match, returning %s\n", out->s); + return i; + } + } + return -1; +} + +uint32_t server_searchRequest(int server, struct SearchRequest *req) +{ + if (!server_ensureConnected(server)) return 0; + const uint32_t msgid = msgId(); + const size_t bodyLen = fmt_ldapsearchrequest(NULL, req); + const size_t headerLen = fmt_ldapmessage(NULL, msgid, SearchRequest, bodyLen); + char buffer[bodyLen + 50]; + char *bufoff = buffer + 50; + fmt_ldapsearchrequest(bufoff, req); + fmt_ldapmessage(bufoff - headerLen, msgid, SearchRequest, bodyLen); + epoll_server_t * const s = &servers[server].con; + server_send(s, bufoff - headerLen, headerLen + bodyLen, FALSE); + return msgid; +} + +// +// Private stuff + +static void server_init() +{ + if (servers != NULL) return; + servers = calloc(MAX_SERVERS, sizeof(server_t)); +} + +static server_t *server_create(const char *server) +{ + int i; + server_init(); + for (i = 0; i < serverCount; ++i) { + if (strcmp(servers[i].addr, server) == 0) return &servers[i]; + } + if (serverCount >= MAX_SERVERS) { + printf("Cannot add server %s: Too many servers.\n", server); + return NULL; + } + snprintf(servers[serverCount].addr, ADDRLEN, "%s", server); + servers[serverCount].con.fd = -1; + return &servers[serverCount++]; +} + +static void server_free(epoll_server_t *server) +{ + server->bound = FALSE; + if (server->fd != -1) close(server->fd); + server->fd = -1; + server->sbPos = server->sbFill = 0; +} + +static void server_callback(void *data, int haveIn, int haveOut, int doCleanup) +{ + epoll_server_t *server = (epoll_server_t *)data; + if (doCleanup) { + server_free(server); + return; + } + if (haveIn) { + for (;;) { + if (server->rbPos >= MAXMSGLEN) { + printf("[AD->Proxy] Read buffer overflow. Disconnecting.\n"); + server_free(server); + return; + } + const size_t buflen = MAXMSGLEN - server->rbPos; + const ssize_t ret = read(server->fd, server->readBuffer + server->rbPos, buflen); + printf("AD read %d (err %d)\n", (int)ret, errno); + if (ret < 0 && errno == EINTR) continue; + if (ret < 0 && errno == EAGAIN) break; + if (ret <= 0) { + printf("AD gone while reading.\n"); + server_free(server); + return; + } + server->rbPos += ret; + // Request complete? + for (;;) { + size_t consumed, len; + consumed = scan_asn1SEQUENCE(server->readBuffer, server->readBuffer + server->rbPos, &len); + if (consumed == 0) break; // Length-Header not complete + len += consumed; + if (len > server->rbPos) break; // Body not complete + printf("[AD] Received complete reply...\n"); + if (proxy_fromServer(server, len) == -1) { + printf("Error parsing reply from AD.\n"); + server_free(server); + return; + } + // Shift remaining buffer contents + if (len == server->rbPos) { + server->rbPos = 0; + break; + } + memmove(server->readBuffer, server->readBuffer + len, server->rbPos - len); + server->rbPos -= len; + } + if ((ssize_t)buflen > ret) break; // Read less than buffer len, epoll will fire again + } + } + if (haveOut) server_flush(server); +} + +int server_send(epoll_server_t *server, const char *buffer, size_t len, const BOOL cork) +{ + if (server->sbFill == 0 && !cork) { + // Nothing in send buffer, fire away + const int ret = write(server->fd, buffer, len); + if (ret == 0 || (ret < 0 && errno != EINTR && errno != EAGAIN)) { + printf("Server gone when trying to send.\n"); + return -1; + } + server->lastActive = time(NULL); + if (ret == (int)len) return 0; + // Couldn't send everything, continue with buffering logic below + if (ret > 0) { + printf("[AD] Partial send (%d of %d)\n", ret, (int)len); + buffer += ret; + len -= (size_t)ret; + } + } + // Buffer... + server_ensureSendBuffer(server, len); + // Finally append to buffer + memcpy(server->sendBuffer + server->sbFill, buffer, len); + server->sbFill += len; + if (!cork) server_flush(server); + return 0; +} + +static void server_flush(epoll_server_t * const server) +{ + while (server->sbPos < server->sbFill) { + const int tosend = server->sbFill - server->sbPos; + const int ret = write(server->fd, server->sendBuffer + server->sbPos, tosend); + if (ret < 0 && errno == EINTR) continue; + if (ret < 0 && errno == EAGAIN) return; + if (ret <= 0) { + printf("Connection to AD Server failed while flushing (ret: %d, errno: %d)\n", ret, errno); + return; + } + server->lastActive = time(NULL); + server->sbPos += ret; + if (ret != tosend) return; + } + server->sbPos = server->sbFill = 0; +} + +static BOOL server_ensureConnected(const int index) +{ + server_t * const server = &servers[index]; + epoll_server_t * const con = &server->con; + if (con->fd != -1 && con->lastActive + 120 > time(NULL)) return TRUE; + if (con->fd != -1) close(con->fd); + con->bound = FALSE; + printf("Connecting to AD %s...\n", server->addr); + con->sbPos = con->sbFill = 0; + int sock; + if (server->lastLookup + 300 < time(NULL)) { + sock = helper_connect4(server->addr, AD_PORT, server->ip); + if (sock == -1) { + printf("Could not resolve/connect to AD server %s\n", server->addr); + return FALSE; + } + } else { + sock = socket_tcp4b(); + if (sock == -1) { + printf("Could not allocate socket for connection to AD\n"); + return FALSE; + } + if (socket_connect4(sock, server->ip, AD_PORT) == -1) { + printf("Could not connect to cached IP of %s\n", server->addr); + close(sock); + return FALSE; + } + } + printf("Connected, binding....\n"); + helper_nonblock(sock); + con->fd = sock; + con->callback = &server_callback; + if (ePoll_add(EPOLLIN | EPOLLOUT | EPOLLET, (epoll_item_t*)con) == -1) { + printf("epoll_add failed for ad server %s\n", server->addr); + close(con->fd); + con->fd = -1; + return FALSE; + } + // Now bind + const size_t bodyLen = fmt_ldapbindrequest(NULL, 3, server->bind, server->password); + const size_t headerLen = fmt_ldapmessage(NULL, MSGID_BIND, BindResponse, bodyLen); + char buffer[bodyLen + 50]; + char *bufoff = buffer + 50; + if (headerLen >= 50) { + printf("[AD] bind too long for %s\n", server->addr); + close(con->fd); + con->fd = -1; + return FALSE; + } + fmt_ldapbindrequest(bufoff, 3, server->bind, server->password); + fmt_ldapmessage(bufoff - headerLen, MSGID_BIND, BindRequest, bodyLen); + server_send(con, bufoff - headerLen, bodyLen + headerLen, FALSE); + return TRUE; +} + +static void server_ensureSendBuffer(epoll_server_t * const s, const size_t len) +{ + if (len > 1000000) bail("server_ensureSendBuffer: request too large!"); + if (s->sbLen - s->sbFill < len) { + if (s->sbPos != 0) { + memmove(s->sendBuffer, s->sendBuffer + s->sbPos, s->sbFill - s->sbPos); + s->sbFill -= s->sbPos; + s->sbPos = 0; + } + if (s->sbLen - s->sbFill < len) { + helper_realloc(&s->sendBuffer, &s->sbLen, s->sbLen + len + 1000, "server_ensureSendBuffer"); + } + } +} + diff --git a/server.h b/server.h new file mode 100644 index 0000000..1463f8a --- /dev/null +++ b/server.h @@ -0,0 +1,28 @@ +#ifndef _SERVER_H_ +#define _SERVER_H_ + +#include "types.h" + +struct string; +struct SearchRequest; + +void server_setBind(const char *server, const char *bind); + +void server_setPassword(const char *server, const char *password); + +void server_setBase(const char *server, const char *base); + +void server_setAlias(const char *server, const char *alias); + +void server_initServers(); + +int server_send(epoll_server_t *server, const char *buffer, size_t len, const BOOL cork); + +int server_aliasToBase(struct string *in, struct string *out); + +int server_baseToAlias(struct string *in, struct string *out); + +uint32_t server_searchRequest(int server, struct SearchRequest *req); + +#endif + diff --git a/tmpbuffer.c b/tmpbuffer.c new file mode 100644 index 0000000..4f2c632 --- /dev/null +++ b/tmpbuffer.c @@ -0,0 +1,31 @@ +#include "tmpbuffer.h" +#include "asn1.h" +#include <stdio.h> + +#define COUNT 40 + +static char buffer[TMPLEN * COUNT]; +static int pos = 0; + +inline char* tmpbuffer_get() +{ + pos = (pos + 1) % COUNT; + return buffer + TMPLEN * pos; +} + +void tmpbuffer_format(struct string *dest, const char *format, ...) +{ + va_list args; + va_start(args, format); + tmpbuffer_formatva(dest, format, args); + va_end(args); +} + +void tmpbuffer_formatva(struct string *dest, const char *format, va_list args) +{ + char *b = tmpbuffer_get(); + const int ret = vsnprintf(b, TMPLEN, format, args); + dest->l = (ret >= TMPLEN ? TMPLEN - 1 : ret); + dest->s = b; +} + diff --git a/tmpbuffer.h b/tmpbuffer.h new file mode 100644 index 0000000..d923e29 --- /dev/null +++ b/tmpbuffer.h @@ -0,0 +1,17 @@ +#ifndef _TMPBUFFER_H_ +#define _TMPBUFFER_H_ + +#include <stdarg.h> + +#define TMPLEN 512 + +struct string; + +char* tmpbuffer_get(); + +void tmpbuffer_format(struct string *dest, const char *format, ...); + +void tmpbuffer_formatva(struct string *dest, const char *format, va_list args); + +#endif + @@ -0,0 +1,52 @@ +#ifndef _TYPES_H_ +#define _TYPES_H_ + +#include <stddef.h> +#include <stdint.h> +#include <time.h> + +#define REQLEN 4000 +#define MAXMSGLEN 10000 + +#define BOOL uint8_t +#define TRUE 1 +#define FALSE 0 + +typedef struct { + void (*callback)(void *data, int haveIn, int haveOut, int doCleanup); + int fd; +} epoll_item_t; + +typedef struct { + void (*callback)(void *data, int haveIn, int haveOut, int doCleanup); + int fd; +} epoll_listen_t; + +typedef struct { + void (*callback)(void *data, int haveIn, int haveOut, int doCleanup); + int fd; + int padding; + // Send buffer (me to client) + size_t sbPos, sbFill, sbLen; + char *sendBuffer; // Dynamically allocated, might or might not get huge + // Recv buffer (client's request) + size_t rbPos; + char readBuffer[REQLEN]; // Static, queries > 4000 bytes simply not supported + BOOL bound; +} epoll_client_t; + +typedef struct { + void (*callback)(void *data, int haveIn, int haveOut, int doCleanup); + int fd; + // Send buffer (me to server) + size_t sbPos, sbFill, sbLen; + char *sendBuffer; // Dynamically allocated, might or might not get huge + // Recv buffer (server's response) + size_t rbPos; + char readBuffer[MAXMSGLEN]; + BOOL bound; + //unsigned long messageId; // ID of message currently being received + time_t lastActive; +} epoll_server_t; + +#endif |