support pipelining, requests > 8k

support modify requests on records in data (not the journal)
This commit is contained in:
leitner
2023-01-18 12:18:01 +00:00
parent 336118b774
commit 187eda5860
6 changed files with 179 additions and 19 deletions

View File

@@ -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
View File

@@ -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
View File

@@ -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);

View File

@@ -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>

View 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;
}

View File

@@ -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)) {