support pipelining, requests > 8k
support modify requests on records in data (not the journal)
This commit is contained in:
6
Makefile
6
Makefile
@@ -15,7 +15,8 @@ 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
|
||||
fmt_asn1generic.o scan_asn1rawoid.o fmt_asn1bitstring.o asn1oid.o \
|
||||
scan_asn1SEQUENCE_nolengthcheck.o
|
||||
|
||||
ldap.a: scan_ldapmessage.o fmt_ldapmessage.o fmt_ldapbindrequest.o \
|
||||
scan_ldapbindrequest.o scan_ldapbindresponse.o scan_ldapresult.o \
|
||||
@@ -30,7 +31,8 @@ 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 \
|
||||
fmt_ldapmodifyrequest.o fmt_ldapaddrequest.o
|
||||
fmt_ldapmodifyrequest.o fmt_ldapaddrequest.o \
|
||||
scan_ldapmessage_nolengthcheck.o
|
||||
|
||||
ldif.a: ldif_parse.o ldap_match_mapped.o
|
||||
|
||||
|
||||
11
asn1.h
11
asn1.h
@@ -118,8 +118,14 @@ 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 */
|
||||
/* only return success if source buffer is large enough to hold length bytes */
|
||||
size_t scan_asn1length(const char* src,const char* max,size_t* length);
|
||||
|
||||
/* Same but does not check the source buffer is large enough to hold
|
||||
* length bytes. Useful to find out how many more bytes we need to read
|
||||
* from network */
|
||||
size_t scan_asn1length_nolengthcheck(const char* src,const char* max, size_t* length);
|
||||
|
||||
/* helper for scan_asn1INT, scan_asn1ENUMERATED and scan_asn1BOOLEAN */
|
||||
size_t scan_asn1int(const char* src,const char* max,
|
||||
enum asn1_tagclass* tc,enum asn1_tagtype* tt, unsigned long* tag,
|
||||
@@ -144,6 +150,11 @@ 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);
|
||||
/* note: these only parse the header. src + return value points to first element */
|
||||
size_t scan_asn1SEQUENCE(const char* src,const char* max,size_t* len);
|
||||
/* scan_asn1SEQUENCE will only return success if the header and the
|
||||
* whole contents fit into src..max; this function will only parse the
|
||||
* outer sequence header and return the number of bytes it say it wants.
|
||||
* For finding out how much more data you need to read from the socket. */
|
||||
size_t scan_asn1SEQUENCE_nolengthcheck(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.
|
||||
|
||||
4
ldap.h
4
ldap.h
@@ -164,6 +164,10 @@ 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_ldapmessage_nolengthcheck(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);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <inttypes.h>
|
||||
#include "asn1.h"
|
||||
|
||||
size_t scan_asn1length(const char* src,const char* max,size_t* value) {
|
||||
size_t scan_asn1length_nolengthcheck(const char* src,const char* max,size_t* value) {
|
||||
size_t len=max-src;
|
||||
if (len==0 || len>=-(uintptr_t)src) return 0;
|
||||
unsigned int i,c=*src;
|
||||
@@ -26,12 +26,21 @@ size_t scan_asn1length(const char* src,const char* max,size_t* value) {
|
||||
if (l<0x7f)
|
||||
return 0; /* not minimally encoded: 0x81 0x70 instead of 0x70 */
|
||||
}
|
||||
if (l>len-i)
|
||||
return 0; /* if the length would not fit into the buffer, return 0 */
|
||||
*value=l;
|
||||
return i;
|
||||
}
|
||||
|
||||
size_t scan_asn1length(const char* src,const char* max,size_t* value) {
|
||||
size_t tmp;
|
||||
size_t len=scan_asn1length_nolengthcheck(src,max,&tmp);
|
||||
if (len && (max-src-len >= tmp)) {
|
||||
*value=tmp;
|
||||
return len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef UNITTEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
8
scan_ldapmessage_nolengthcheck.c
Normal file
8
scan_ldapmessage_nolengthcheck.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include "ldap.h"
|
||||
|
||||
size_t scan_ldapmessage_nolengthcheck(const char* src,const char* max,
|
||||
unsigned long* messageid,unsigned long* op,size_t* len) {
|
||||
size_t res,tmp;
|
||||
if (!(res=scan_asn1SEQUENCE_nolengthcheck(src,max,len))) return 0;
|
||||
return res;
|
||||
}
|
||||
154
tinyldap.c
154
tinyldap.c
@@ -73,6 +73,12 @@ uint32 magic,attribute_count,record_count,indices_offset,size_of_string_table;
|
||||
* basic counts and offsets needed to calculate the positions of
|
||||
* the data structures in the file. */
|
||||
|
||||
static uint32* getrecptr(size_t recno) {
|
||||
if (recno>=record_count) return 0;
|
||||
uint32_t thisrec = uint32_read(map+indices_offset+4*recno);
|
||||
return (uint32*)(map+thisrec);
|
||||
}
|
||||
|
||||
|
||||
/* We do queries with indexes by evaluating all the filters (subexpressions) that can be
|
||||
* answered with an index, and then getting a bit vector, one bit for each record. */
|
||||
@@ -388,6 +394,7 @@ void map_datafile(const char* filename) {
|
||||
*/
|
||||
|
||||
#define BUFSIZE 8192
|
||||
#define MAXBUFSIZE 1024*1024
|
||||
|
||||
#if (debug != 0)
|
||||
/* debugging support functions, adapted from t2.c */
|
||||
@@ -1185,7 +1192,7 @@ add_attribute:
|
||||
long tmp;
|
||||
if (l<=HUGE_SIZE_FOR_SANITY_CHECKS) {
|
||||
buf=alloca(l+300); /* you never know ;) */
|
||||
if (verbose) {
|
||||
if (debug) {
|
||||
buffer_puts(buffer_2,"sre len ");
|
||||
buffer_putulong(buffer_2,l);
|
||||
buffer_putsflush(buffer_2,".\n");
|
||||
@@ -1483,6 +1490,30 @@ static int lookupdn(struct string* dn,size_t* index, struct hashnode** hn) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return fake hashnode for record from data file.
|
||||
* all the internal pointers point into the data file, free on the pointer is sufficient
|
||||
* to clean up everything */
|
||||
static struct hashnode* load_record_into_hashnode(size_t recno) {
|
||||
uint32* attr = getrecptr(recno);
|
||||
uint32 attrs;
|
||||
uint32 i;
|
||||
if (!attr) return 0;
|
||||
attrs = uint32_read((const char*)attr);
|
||||
struct hashnode* h = malloc(sizeof(struct hashnode)+attrs*sizeof(struct attribute2));
|
||||
if (!h) return 0;
|
||||
h->next=h->linear=0;
|
||||
h->hashval=0;
|
||||
h->dn=(unsigned char*)map+uint32_read((const char*)&(attr[2]));
|
||||
h->overwrite=1;
|
||||
h->a[0].a=(unsigned char*)"objectClass"; h->a[0].v=(unsigned char*)map+uint32_read((const char*)&(attr[3]));
|
||||
h->n=attrs-1; // dn is extra
|
||||
for (i=2; i<attrs; ++i) {
|
||||
h->a[i-1].a=(unsigned char*)map+uint32_read((const char*)&(attr[i*2]));
|
||||
h->a[i-1].v=(unsigned char*)map+uint32_read((const char*)&(attr[i*2+1]));
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static void normalize_string_dn(struct string* s) {
|
||||
/* OK this is a kludge. s->s is supposed to be read-only because it points into the
|
||||
* buffer where we read it into from the network.
|
||||
@@ -1545,21 +1576,105 @@ void reply_with_index(struct SearchRequest* sr,unsigned long* messageid,int out)
|
||||
*/
|
||||
static int handle(int in,int out) {
|
||||
size_t len;
|
||||
char buf[BUFSIZE];
|
||||
char stackbuf[BUFSIZE];
|
||||
size_t bufsize=BUFSIZE;
|
||||
char* buf=stackbuf;
|
||||
for (len=0;;) {
|
||||
int tmp=read(in,buf+len,BUFSIZE-len);
|
||||
int tmp;
|
||||
int res;
|
||||
unsigned long messageid,op;
|
||||
size_t Len;
|
||||
if (tmp==0) {
|
||||
close(in);
|
||||
if (in!=out) close(out);
|
||||
return 0;
|
||||
// if (BUFSIZE-len) { return 0; }
|
||||
}
|
||||
if (tmp<0) { write(2,"error!\n",7); return 1; }
|
||||
len+=tmp;
|
||||
res=scan_ldapmessage(buf,buf+len,&messageid,&op,&Len);
|
||||
if (res==0) {
|
||||
/* Maybe the message is larger than the buffer. Attempt to find out how large the
|
||||
* buffer should be so we can capture the whole message */
|
||||
if (len>0) {
|
||||
res=scan_ldapmessage_nolengthcheck(buf,buf+len,&messageid,&op,&Len);
|
||||
if (res) {
|
||||
/* we could parse the header and have a size. Now check if it is plausible. */
|
||||
|
||||
if (debug) {
|
||||
buffer_puts(buffer_2,"got partial message (");
|
||||
buffer_putulong(buffer_2,len);
|
||||
buffer_puts(buffer_2," of ");
|
||||
buffer_putulong(buffer_2,Len);
|
||||
buffer_puts(buffer_2," bytes). bufsize is ");
|
||||
buffer_putulong(buffer_2,bufsize);
|
||||
buffer_putnlflush(buffer_2);
|
||||
}
|
||||
|
||||
if (Len > MAXBUFSIZE-100)
|
||||
outofmemory:
|
||||
{
|
||||
/* Peer wants to send us more than MAXBUFSIZE in a message. Abort. */
|
||||
char outbuf[1024];
|
||||
size_t s=100;
|
||||
int response;
|
||||
switch (op) {
|
||||
case SearchRequest: response=SearchResultDone; break;
|
||||
case ModifyRequest: response=ModifyResponse; break;
|
||||
case AddRequest: response=AddResponse; break;
|
||||
case DelRequest: response=DelResponse; break;
|
||||
case ModifyDNRequest: response=ModifyDNResponse; break;
|
||||
case CompareRequest: response=CompareResponse; break;
|
||||
default: response=BindResponse;
|
||||
}
|
||||
size_t len=fmt_ldapresult(outbuf+s,sizeLimitExceeded,"","message too large","");
|
||||
size_t hlen=fmt_ldapmessage(0,messageid,response,len);
|
||||
fmt_ldapmessage(outbuf+s-hlen,messageid,response,len);
|
||||
write(out,outbuf+s-hlen,len+hlen);
|
||||
/* This is an attack. We don't continue talking to attackers. */
|
||||
/* Also we would have to wastefully read Len bytes here if we wanted to continue. */
|
||||
exit(3);
|
||||
}
|
||||
/* Peer wants to send more than BUFSIZE bytes, but less than MAXBUFSIZE. */
|
||||
bufsize=Len+100; // MAXBUFSIZE should be small enough that adding 100 won't overflow
|
||||
if (bufsize<100) goto outofmemory;
|
||||
char* newbuf;
|
||||
if (buf==stackbuf) {
|
||||
newbuf=malloc(bufsize);
|
||||
if (newbuf) byte_copy(newbuf,len,stackbuf);
|
||||
} else
|
||||
newbuf=realloc(buf,bufsize);
|
||||
if (!newbuf) {
|
||||
if (buf!=stackbuf) free(buf);
|
||||
goto outofmemory;
|
||||
}
|
||||
buf=newbuf;
|
||||
if (debug) {
|
||||
buffer_puts(buffer_2,"resized. bufsize now ");
|
||||
buffer_putulong(buffer_2,bufsize);
|
||||
buffer_putnlflush(buffer_2);
|
||||
}
|
||||
}
|
||||
}
|
||||
tmp=read(in,buf+len,bufsize-len);
|
||||
|
||||
if (debug) {
|
||||
buffer_puts(buffer_2,"read ");
|
||||
buffer_putlong(buffer_2,tmp);
|
||||
buffer_puts(buffer_2," bytes at ofs ");
|
||||
buffer_putulong(buffer_2,len);
|
||||
buffer_putnlflush(buffer_2);
|
||||
}
|
||||
|
||||
if (tmp==0) {
|
||||
close(in);
|
||||
if (in!=out) close(out);
|
||||
return 0;
|
||||
// if (BUFSIZE-len) { return 0; }
|
||||
}
|
||||
if (tmp<0) { write(2,"error!\n",7); return 1; }
|
||||
len+=tmp;
|
||||
if (debug) {
|
||||
buffer_puts(buffer_2,"len now ");
|
||||
buffer_putulong(buffer_2,len);
|
||||
buffer_putnlflush(buffer_2);
|
||||
}
|
||||
|
||||
continue;
|
||||
// res=scan_ldapmessage(buf,buf+len,&messageid,&op,&Len);
|
||||
}
|
||||
if (res>0) {
|
||||
if (verbose) {
|
||||
buffer_puts(buffer_2,"got message of length ");
|
||||
@@ -1663,7 +1778,7 @@ authfailure:
|
||||
size_t hlen=fmt_ldapmessage(0,messageid,BindResponse,len);
|
||||
fmt_ldapmessage(outbuf+s-hlen,messageid,BindResponse,len);
|
||||
write(out,outbuf+s-hlen,len+hlen);
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1807,7 +1922,16 @@ authfailure:
|
||||
if (err==success) {
|
||||
#if 1
|
||||
/* 3. apply modifications to record to get new record */
|
||||
if (!applymodreq(hn,&mr,&sre)) {
|
||||
struct hashnode* h;
|
||||
if (hn)
|
||||
h=hn;
|
||||
else
|
||||
h=load_record_into_hashnode(idx);
|
||||
if (!h) {
|
||||
err=operationsError; // can't happen
|
||||
goto modreqerror;
|
||||
}
|
||||
if (!applymodreq(h,&mr,&sre)) {
|
||||
/* 4. write record to journal */
|
||||
int fd=open(journalfilename,O_WRONLY|O_APPEND|O_CREAT,0600);
|
||||
if (fd==-1)
|
||||
@@ -1819,6 +1943,8 @@ authfailure:
|
||||
}
|
||||
} else
|
||||
err=operationsError;
|
||||
if (h != hn) free(h);
|
||||
modreqerror:
|
||||
free_ldapsearchresultentry(&sre);
|
||||
#else
|
||||
err=operationsError;
|
||||
@@ -1933,7 +2059,7 @@ authfailure:
|
||||
if (checkacl(0,0,acl_delete,&sre)!=1)
|
||||
err=insufficientAccessRights;
|
||||
if (err==success) {
|
||||
/* 2. check if there already is a record with this dn */
|
||||
/* 2. check if there is a record with this dn */
|
||||
struct hashnode* hn;
|
||||
size_t idx;
|
||||
switch (lookupdn(&s,&idx,&hn)) {
|
||||
|
||||
Reference in New Issue
Block a user