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