From bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Sat, 15 Mar 2014 01:49:50 +0100 Subject: Lean and mean initial commit Not much functionality yet --- .gitignore | 5 + Makefile | 120 ++++++++++++ README | 16 ++ asn1.h | 232 ++++++++++++++++++++++ asn1oid.c | 100 ++++++++++ bstr.h | 12 ++ bstrfirst.c | 6 + bstrlen.c | 8 + bstrstart.c | 6 + client.c | 136 +++++++++++++ client.h | 10 + config/config.example | 6 + epoll.c | 43 ++++ epoll.h | 15 ++ fmt_asn1OID.c | 16 ++ fmt_asn1bitstring.c | 19 ++ fmt_asn1generic.c | 139 +++++++++++++ fmt_asn1int.c | 11 ++ fmt_asn1intpayload.c | 20 ++ fmt_asn1length.c | 25 +++ fmt_asn1sint.c | 11 ++ fmt_asn1sintpayload.c | 22 +++ fmt_asn1string.c | 10 + fmt_asn1tag.c | 15 ++ fmt_asn1tagint.c | 17 ++ fmt_asn1transparent.c | 10 + fmt_ldapadl.c | 32 +++ fmt_ldapava.c | 9 + fmt_ldapbindrequest.c | 18 ++ fmt_ldapdeleterequest.c | 8 + fmt_ldapmessage.c | 12 ++ fmt_ldappal.c | 23 +++ fmt_ldapresult.c | 20 ++ fmt_ldapsearchfilter.c | 70 +++++++ fmt_ldapsearchfilterstring.c | 69 +++++++ fmt_ldapsearchrequest.c | 21 ++ fmt_ldapsearchresultentry.c | 11 ++ fmt_ldapstring.c | 5 + free_ldapadl.c | 10 + free_ldappal.c | 11 ++ free_ldapsearchfilter.c | 22 +++ free_ldapsearchresultentry.c | 5 + freeava.c | 10 + freefilter.c | 16 ++ freepal.c | 15 ++ helper.c | 174 +++++++++++++++++ helper.h | 27 +++ ini.c | 166 ++++++++++++++++ ini.h | 66 +++++++ ldadp.c | 86 ++++++++ ldap.h | 221 +++++++++++++++++++++ ldap_match_sre.c | 166 ++++++++++++++++ matchcaseprefix.c | 20 ++ matchcasestring.c | 21 ++ matchprefix.c | 20 ++ matchstring.c | 21 ++ normalize_dn.c | 26 +++ proxy.c | 445 ++++++++++++++++++++++++++++++++++++++++++ proxy.h | 14 ++ scan_asn1BITSTRING.c | 27 +++ scan_asn1BOOLEAN.c | 16 ++ scan_asn1ENUMERATED.c | 15 ++ scan_asn1INTEGER.c | 12 ++ scan_asn1SEQUENCE.c | 14 ++ scan_asn1SET.c | 14 ++ scan_asn1STRING.c | 12 ++ scan_asn1generic.c | 270 +++++++++++++++++++++++++ scan_asn1int.c | 10 + scan_asn1length.c | 25 +++ scan_asn1oid.c | 21 ++ scan_asn1rawint.c | 17 ++ scan_asn1rawoid.c | 40 ++++ scan_asn1string.c | 12 ++ scan_asn1tag.c | 17 ++ scan_asn1tagint.c | 14 ++ scan_certificate.c | 338 ++++++++++++++++++++++++++++++++ scan_ldapaddrequest.c | 95 +++++++++ scan_ldapava.c | 10 + scan_ldapbindrequest.c | 17 ++ scan_ldapbindresponse.c | 23 +++ scan_ldapdeleterequest.c | 9 + scan_ldapmessage.c | 21 ++ scan_ldapmodifyrequest.c | 88 +++++++++ scan_ldapresult.c | 15 ++ scan_ldapsearchfilter.c | 113 +++++++++++ scan_ldapsearchfilterstring.c | 100 ++++++++++ scan_ldapsearchrequest.c | 55 ++++++ scan_ldapsearchresultentry.c | 42 ++++ scan_ldapstring.c | 5 + server.c | 353 +++++++++++++++++++++++++++++++++ server.h | 28 +++ tmpbuffer.c | 31 +++ tmpbuffer.h | 17 ++ types.h | 52 +++++ 94 files changed, 4838 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README create mode 100644 asn1.h create mode 100644 asn1oid.c create mode 100644 bstr.h create mode 100644 bstrfirst.c create mode 100644 bstrlen.c create mode 100644 bstrstart.c create mode 100644 client.c create mode 100644 client.h create mode 100644 config/config.example create mode 100644 epoll.c create mode 100644 epoll.h create mode 100644 fmt_asn1OID.c create mode 100644 fmt_asn1bitstring.c create mode 100644 fmt_asn1generic.c create mode 100644 fmt_asn1int.c create mode 100644 fmt_asn1intpayload.c create mode 100644 fmt_asn1length.c create mode 100644 fmt_asn1sint.c create mode 100644 fmt_asn1sintpayload.c create mode 100644 fmt_asn1string.c create mode 100644 fmt_asn1tag.c create mode 100644 fmt_asn1tagint.c create mode 100644 fmt_asn1transparent.c create mode 100644 fmt_ldapadl.c create mode 100644 fmt_ldapava.c create mode 100644 fmt_ldapbindrequest.c create mode 100644 fmt_ldapdeleterequest.c create mode 100644 fmt_ldapmessage.c create mode 100644 fmt_ldappal.c create mode 100644 fmt_ldapresult.c create mode 100644 fmt_ldapsearchfilter.c create mode 100644 fmt_ldapsearchfilterstring.c create mode 100644 fmt_ldapsearchrequest.c create mode 100644 fmt_ldapsearchresultentry.c create mode 100644 fmt_ldapstring.c create mode 100644 free_ldapadl.c create mode 100644 free_ldappal.c create mode 100644 free_ldapsearchfilter.c create mode 100644 free_ldapsearchresultentry.c create mode 100644 freeava.c create mode 100644 freefilter.c create mode 100644 freepal.c create mode 100644 helper.c create mode 100644 helper.h create mode 100644 ini.c create mode 100644 ini.h create mode 100644 ldadp.c create mode 100644 ldap.h create mode 100644 ldap_match_sre.c create mode 100644 matchcaseprefix.c create mode 100644 matchcasestring.c create mode 100644 matchprefix.c create mode 100644 matchstring.c create mode 100644 normalize_dn.c create mode 100644 proxy.c create mode 100644 proxy.h create mode 100644 scan_asn1BITSTRING.c create mode 100644 scan_asn1BOOLEAN.c create mode 100644 scan_asn1ENUMERATED.c create mode 100644 scan_asn1INTEGER.c create mode 100644 scan_asn1SEQUENCE.c create mode 100644 scan_asn1SET.c create mode 100644 scan_asn1STRING.c create mode 100644 scan_asn1generic.c create mode 100644 scan_asn1int.c create mode 100644 scan_asn1length.c create mode 100644 scan_asn1oid.c create mode 100644 scan_asn1rawint.c create mode 100644 scan_asn1rawoid.c create mode 100644 scan_asn1string.c create mode 100644 scan_asn1tag.c create mode 100644 scan_asn1tagint.c create mode 100644 scan_certificate.c create mode 100644 scan_ldapaddrequest.c create mode 100644 scan_ldapava.c create mode 100644 scan_ldapbindrequest.c create mode 100644 scan_ldapbindresponse.c create mode 100644 scan_ldapdeleterequest.c create mode 100644 scan_ldapmessage.c create mode 100644 scan_ldapmodifyrequest.c create mode 100644 scan_ldapresult.c create mode 100644 scan_ldapsearchfilter.c create mode 100644 scan_ldapsearchfilterstring.c create mode 100644 scan_ldapsearchrequest.c create mode 100644 scan_ldapsearchresultentry.c create mode 100644 scan_ldapstring.c create mode 100644 server.c create mode 100644 server.h create mode 100644 tmpbuffer.c create mode 100644 tmpbuffer.h create mode 100644 types.h 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 $@ + diff --git a/README b/README new file mode 100644 index 0000000..da62664 --- /dev/null +++ b/README @@ -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/ + diff --git a/asn1.h b/asn1.h new file mode 100644 index 0000000..78351b7 --- /dev/null +++ b/asn1.h @@ -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 + +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 +#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 + +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 +#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 +#include +#include +#include +#include + +#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 + diff --git a/epoll.c b/epoll.c new file mode 100644 index 0000000..576fb0e --- /dev/null +++ b/epoll.c @@ -0,0 +1,43 @@ +#include "epoll.h" +#include + +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); +} + diff --git a/epoll.h b/epoll.h new file mode 100644 index 0000000..e3e5dd0 --- /dev/null +++ b/epoll.h @@ -0,0 +1,15 @@ +#ifndef _EPOLL_H_ +#define _EPOLL_H_ + +#include "types.h" +#include + +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(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 +#include +#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>(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>(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>(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>(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 +#include "ldap.h" +#include +#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 +#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 + +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 +#include "ldap.h" +#include + +/* + 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 +#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 +#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 +#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 +#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 +#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 + +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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + diff --git a/ini.c b/ini.c new file mode 100644 index 0000000..df9dbc6 --- /dev/null +++ b/ini.c @@ -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 +#include +#include + +#include "ini.h" + +#if !INI_USE_STACK +#include +#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; +} + diff --git a/ini.h b/ini.h new file mode 100644 index 0000000..06f1123 --- /dev/null +++ b/ini.h @@ -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 + +/* 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__ */ diff --git a/ldadp.c b/ldadp.c new file mode 100644 index 0000000..8afb3bd --- /dev/null +++ b/ldadp.c @@ -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 +#include +#include +#include +#include + +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); +} + diff --git a/ldap.h b/ldap.h new file mode 100644 index 0000000..2270f56 --- /dev/null +++ b/ldap.h @@ -0,0 +1,221 @@ +#ifndef _LDAP_H +#define _LDAP_H + +#include +#include +#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 +#include "ldap.h" +#include "byte.h" +#include "case.h" +#include + +static int matchcasestr(struct string* a,struct string* b) { + unsigned long l=a->l; + unsigned long r; + if (b->ll; + 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->ll; + 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->ls.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; iobjectName.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 +#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->ll; + 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->ll; + 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 (l1s[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->ll; + 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->ll; + 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 (l1s[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 +#include + +/* "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; +} diff --git a/proxy.c b/proxy.c new file mode 100644 index 0000000..c627238 --- /dev/null +++ b/proxy.c @@ -0,0 +1,445 @@ +#include "proxy.h" +#include "client.h" +#include "server.h" +#include "helper.h" +#include "tmpbuffer.h" +#include "ldap.h" +#include +#include +#include +#include +#include + +#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; +} + diff --git a/proxy.h b/proxy.h new file mode 100644 index 0000000..032ebe4 --- /dev/null +++ b/proxy.h @@ -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 +#include +#include +#include +#include "asn1.h" +#include + +size_t scan_asn1generic(const char* src,const char* max,const char* fmt,...) { + size_t curlen,seqlen; + const char* maxstack[100]; + size_t curmax=0; + va_list args; + int optional=0; + unsigned long* application=NULL; + unsigned long tag; + enum asn1_tagclass tc; + enum asn1_tagtype tt; + unsigned int wantedtag; + unsigned long* desttag=NULL; + const char* orig=src; + va_start(args,fmt); + maxstack[0]=max; + while (*fmt) { + switch (*fmt) { + case '?': // ? = rest is optional (until end of sequence) + optional=1; + break; + case 'i': // i = INTEGER + { + long* dest=va_arg(args,long*); + *dest=0; + curlen=scan_asn1int(src,maxstack[curmax],&tc,&tt,&tag,dest); + if (application) { + if (tc!=APPLICATION) return 0; + *application=tag; + } else { + if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=INTEGER) + return 0; + } + if (!curlen) { if (optional) break; else return 0; } + src+=curlen; + application=NULL; + break; + } + case 'I': // I = INTEGER, but for bignum integers; writes to an array of size_t, first one contains number of digits after it + { + size_t* dest=va_arg(args,size_t*); + size_t len,tmp,tlen,j,t; + if (!(len=scan_asn1tag(src,maxstack[curmax],&tc,&tt,&tag))) return 0; + if (!(tmp=scan_asn1length(src+len,maxstack[curmax],&tlen))) return 0; + len+=tmp; + j=0; t=1; + src+=len; + /* asn.1 sends n bytes, most significant first. + * we want m digits, most significant first. + * if n is not a multiple of sizeof(digit) then we need to + * insert a few 0 bytes in the first word + */ + while (tlen) { + j=(j<<8)+(unsigned char)(*src); + ++src; + --tlen; + if ((tlen%sizeof(j))==0 && (j || t>1)) { + dest[t]=j; + j=0; + ++t; + } + } + if (j) dest[t++]=j; + dest[0]=t-1; + break; + } + case 'b': + wantedtag=BIT_STRING; goto stringmain; + case 'u': + wantedtag=UTCTIME; goto stringmain; + case 'p': + wantedtag=PrintableString; goto stringmain; + case 'a': + wantedtag=IA5String; goto stringmain; + case 's': + wantedtag=OCTET_STRING; goto stringmain; +stringmain: + { + struct string* dest; + struct string temp; + time_t* desttime=NULL; + size_t i; + if (wantedtag==UTCTIME) { + dest=&temp; + desttime=va_arg(args,time_t*); + } else + dest=va_arg(args,struct string*); + dest->l=0; + dest->s=0; + curlen=scan_asn1string(src,maxstack[curmax],&tc,&tt,&tag,&dest->s,&dest->l); + if (!curlen) { if (optional) break; else return 0; } + if (application) { + if (tc!=APPLICATION) return 0; + *application=tag; + } else { + if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=wantedtag) + return 0; + } + if (wantedtag==BIT_STRING) { // additional checks for bit strings + if (dest->l==0 || // length can't be 0 because the format starts with 1 octet that contains the number of unused bits in the last octet + ((unsigned char)(dest->s[0])>7) || // it's the number of unused bits in an octet, must be [0..7] + (dest->l==1 && dest->s[0])) return 0; // if there is no last octet, there can't be any unused bits in there + dest->l=(dest->l-1)*8-dest->s[0]; + dest->s+=1; + } else if (wantedtag==PrintableString) { + for (i=0; il; ++i) // RFC 2252 section 4.1 production p + if (!isalnum(dest->s[i]) + && dest->s[i]!='"' + && dest->s[i]!='(' + && dest->s[i]!=')' + && dest->s[i]!='+' + && dest->s[i]!=',' + && dest->s[i]!='-' + && dest->s[i]!='.' + && dest->s[i]!='/' + && dest->s[i]!=':' + && dest->s[i]!='?' + && dest->s[i]!=' ') return 0; + } else if (wantedtag==IA5String) { + for (i=0; il; ++i) // IA5String is an ASCII string, which means 0 <= s[i] <= 127 + if ((unsigned char)(dest->s[i]) > 127) return 0; + } else if (wantedtag==UTCTIME) { + size_t j; + struct tm t; + memset(&t,0,sizeof(t)); + /* + YYMMDDhhmmZ + YYMMDDhhmm+hh'mm' + YYMMDDhhmm-hh'mm' + YYMMDDhhmmssZ + YYMMDDhhmmss+hh'mm' + YYMMDDhhmmss-hh'mm' + */ + if (dest->l<11 || dest->l>17) return 0; + j=(dest->s[0]-'0')*10+dest->s[1]-'0'; + t.tm_year=j+(j<70)*100; + + for (i=0; i<10; ++i) + if (!isdigit(dest->s[i])) return 0; + j=(dest->s[2]-'0')*10+dest->s[3]-'0'; // is the month plausible? + if (j<1 || j>12) return 0; + t.tm_mon=j-1; + j=(dest->s[4]-'0')*10+dest->s[5]-'0'; // is the day plausible? + if (j<1 || j>31) return 0; + t.tm_mday=j; + j=(dest->s[6]-'0')*10+dest->s[7]-'0'; // is the hour plausible? + if (j>23) return 0; + t.tm_hour=j; + j=(dest->s[8]-'0')*10+dest->s[9]-'0'; // is the minutes plausible? + if (j>59) return 0; + t.tm_min=j; + i=10; + if (isdigit(dest->s[10])) { + i+=2; + j=(dest->s[10]-'0')*10+dest->s[11]-'0'; // is the seconds plausible? + if (j>59) return 0; + t.tm_sec=j; + } + *desttime=mktime(&t); + if (dest->s[i]=='+' || dest->s[i]=='-') { + size_t j; + if (dest->l!=15) return 0; + for (j=i; js[j])) return 0; + j=(dest->s[i]-'0')*10+dest->s[i+1]-'0'; // is the offset minutes plausible? + if (j>59) return 0; + if (dest->s[i]=='+') + *desttime+=j*60; + else + *desttime-=j*60; + j=(dest->s[i+2]-'0')*10+dest->s[i+3]-'0'; // is the offset seconds plausible? + if (j>59) return 0; + if (dest->s[i]=='+') + *desttime+=j; + else + *desttime-=j; + } else if (dest->s[i]!='Z') return 0; + } + src+=curlen; + application=NULL; + break; + } + case 'o': // o == OID + { + struct string* dest=va_arg(args,struct string*); + curlen=scan_asn1tag(src,maxstack[curmax],&tc,&tt,&tag); + if (!curlen) { if (optional) break; else return 0; } + if (application) { + if (tc!=APPLICATION) return 0; + *application=tag; + } else { + if (tc!=UNIVERSAL || tt!=PRIMITIVE || tag!=OBJECT_IDENTIFIER) + return 0; + } + src+=curlen; + curlen=scan_asn1length(src,maxstack[curmax],&seqlen); + if (!curlen) return 0; + src+=curlen; + dest->s=src; + dest->l=seqlen; + src+=seqlen; + application=NULL; + break; + } + case '*': // next tag class is APPLICATION instead of UNIVERSAL; write tag to unsigned long* + { + application=va_arg(args,unsigned long*); + break; + } + case 'c': // c = context specific; PRIVATE CONSTRUCTED 0, close with '}' + desttag=va_arg(args,unsigned long*); + // fall through + case '[': // [ = SET + case '{': // { = SEQUENCE + { + curlen=scan_asn1tag(src,maxstack[curmax],&tc,&tt,&tag); + if (!curlen) { if (optional) break; else return 0; } + if (application) { + if (tc!=APPLICATION || tt!=CONSTRUCTED) return 0; + *application=tag; + } else { + if (*fmt=='c') { + if (tc!=PRIVATE || tt!=CONSTRUCTED) + return 0; + *desttag=tag; + } else { + if (tc!=UNIVERSAL || tt!=CONSTRUCTED || tag!=(*fmt=='{'?SEQUENCE_OF:SET_OF)) + return 0; + } + } + src+=curlen; + curlen=scan_asn1length(src,maxstack[curmax],&seqlen); + if (!curlen) return 0; + if (curmax>99) return 0; + maxstack[++curmax]=src+curlen+seqlen; + src+=curlen; + application=NULL; + break; + } + case '!': // save current src and max-src into struct string* + // useful for optional parts or CHOICEs + { + struct string* dest=va_arg(args,struct string*); + dest->s=src; + dest->l=maxstack[curmax]-src; + break; + } + case ']': // ] = end of SET + case '}': // } = end of SEQUENCE + { + optional=0; + if (curmax==0) return 0; + src=maxstack[curmax]; + --curmax; + break; + } + default: + return 0; + } + ++fmt; + } + va_end(args); + return src-orig; +} 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 +#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; imax) 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 && cural) /* 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 +#include +#include "asn1.h" +#include +#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'~')) { + 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>((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 + +#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 +#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+resnext=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+resnext; + 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+ress=0; errormessage->l=0; + } + if (src+ress=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 +#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+resa))) goto error; + (*ilast)->next=0; + ilast=&(*ilast)->next; + res+=tmp; + } + } + } + break; + } while (src+resnext; + 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 + +/* + 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+resx; + 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+ress.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 +#include "ldap.h" +#include + +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 +#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 +#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+resmax) 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+resa=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 +#include +#include +#include +#include +#include +#include + +#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 + +#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 + +#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 + diff --git a/types.h b/types.h new file mode 100644 index 0000000..058d3df --- /dev/null +++ b/types.h @@ -0,0 +1,52 @@ +#ifndef _TYPES_H_ +#define _TYPES_H_ + +#include +#include +#include + +#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 -- cgit v1.2.3-55-g7522