implement modifyrequest

rewrite bindrequest handling (reuse lookupdn)
update acl filter cache when bind succeeds
handle authenticating against a record in the journal
This commit is contained in:
leitner
2007-11-01 00:43:29 +00:00
parent b941b8b4f4
commit fcf6ed51f2
9 changed files with 257 additions and 99 deletions

View File

@@ -22,3 +22,4 @@ t6
acl
acls
dumpacls
journal

9
FORMAT
View File

@@ -96,12 +96,3 @@ The syntax of the list should be:
uint32_t attributes[]; /* offsets of attribute names in stringtab,
terminated by 0. Empty list means: all */
Typische ACL:
access to dn="ou=(Fraktion-[^,]+),ou=Fraktionen,o=bundestag,c=de" attr=userPassword
by self write
by anonymous auth
by group="cn=Gruppe A,ou=Administration,o=bundestag,c=de" write
by group="cn=$1,ou=Administration,o=bundestag,c=de" write
by * none

15
acl.c
View File

@@ -143,7 +143,8 @@ int parseaclattrib(buffer* in,struct acl* a) {
a->anum=1;
return 1;
}
if (!(a->attrib=strdup(x.s))) return -1;
if (!(a->attrib=malloc(x.len))) return -1;
memcpy(a->attrib,x.s,x.len);
{
unsigned int i,j;
j=1;
@@ -182,7 +183,7 @@ static int parseacl(buffer* in,struct acl* a) {
char c;
if ((r=skipws(in))!=1) return r;
for (i=0; i<3; ++i)
if ((r=buffer_getc(in,&c))!=1 && c!="acl"[i]) {
if ((r=buffer_getc(in,&c))!=1 || c!="acl"[i]) {
if (r==0 && i==0) return 0;
parseerror();
}
@@ -242,7 +243,8 @@ int readacls(const char* filename) {
while ((r=parseacl(&b,&a))==1) {
*next=malloc(sizeof(struct acl));
if (!*next) diesys(1,"malloc");
**next=a;
byte_copy(*next,sizeof(a),&a);
// **next=a;
next=&(*next)->next;
if (r==0) break;
}
@@ -264,7 +266,7 @@ int marshalfilter(stralloc* x,struct assertion* a) {
unsigned long l=fmt_ldapsearchfilter(0,a->f);
tmp=alloca(l+10); // you never know
if (fmt_ldapsearchfilter(tmp,a->f)!=l) {
buffer_putmflush(buffer_2,"internal error!\n");
buffer_putsflush(buffer_2,"internal error!\n");
exit(1);
}
return stralloc_catb(x,tmp,l);
@@ -316,7 +318,7 @@ int marshal(char* map,size_t filelen,const char* filename) {
++i;
if (!marshalfilter(&x,&a->subject)) {
nomem:
buffer_putmflush(buffer_2,"out of memory!\n");
buffer_putsflush(buffer_2,"out of memory!\n");
exit(1);
}
// printf("marshalled \"%s\" to %ld\n",a->subject.filterstring,F[i-1]);
@@ -342,6 +344,7 @@ nomem:
for (a=root; a; a=a->next) {
unsigned int l=0;
// printf("a->anum = %lu\nsizeof(*a->attrs) = %lu\n",a->anum,sizeof(*a->attrs));
if (!(a->attrs=malloc(a->anum*sizeof(*a->attrs))))
goto nomem;
a->attrs[l]=a->attrib; ++l;
@@ -489,7 +492,7 @@ int main(int argc,char* argv[]) {
char* map=mmap_read(filename,&filelen);
if (filelen<5*4 || uint32_read(map)!=0xfefe1da9) {
buffer_putmflush(buffer_2,"not a valid tinyldap data file!\n");
buffer_putsflush(buffer_2,"not a valid tinyldap data file!\n");
exit(0);
}

2
acls
View File

@@ -5,6 +5,6 @@ acl * * userPassword -r;
# but everyone can authenticate using it
acl * self * +a;
# admins at fefe.de can write in their tree
acl (dn=*ou=admin,o=fefe,c=de) (dn=*,o=fefe,c=de) * +rwdR;
acl (dn=*ou=admin,d=fefe,c=de) (dn=*,d=fefe,c=de) * +rwdR;
# everyone can read everything else
acl * * * +r;

1
auth.c
View File

@@ -11,6 +11,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <alloca.h>
#include <string.h>
#include "ldap.h"
#include "auth.h"
#include "str.h"

2
ldap.h
View File

@@ -74,7 +74,7 @@ struct SearchResultEntry {
struct Modification {
enum { Add=0, Delete=1, Replace=2 } operation;
struct string AttributeDescription; /* ? */
struct AttributeDescriptionList vals;
struct AttributeDescriptionList* vals;
struct Modification* next;
};

View File

@@ -8,6 +8,7 @@
#include <sys/mman.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <string.h>
#include "buffer.h"
#include "ldif.h"
#include "mduptab.h"

View File

@@ -52,19 +52,17 @@ size_t scan_ldapmodifyrequest(const char* src,const char* max,struct ModifyReque
{
size_t iiislen; /* waah, _four_ levels of indirection! It doesn't get more inefficient than this */
const char* iimax;
struct AttributeDescriptionList* ilast=0;
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) {
struct AttributeDescriptionList* x;
if (!(x=malloc(sizeof(struct AttributeDescriptionList)))) goto error;
x->next=ilast; ilast=x;
} else
ilast=&last->vals;
if (!(tmp=scan_ldapstring(src+res,imax,&ilast->a))) goto error;
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;
}
}
@@ -86,6 +84,6 @@ static void free_mod(struct Modification* m) {
}
void free_ldapmodifyrequest(struct ModifyRequest* m) {
free_ldapadl(m->m.vals.next);
free_ldapadl(m->m.vals);
free_mod(m->m.next);
}

View File

@@ -184,6 +184,7 @@ struct Filter** Filters;
char Self[]="self";
char Any[]="*";
uint32 authenticated_as;
char* authenticated_as_str;
struct acl {
uint32 subject,object; /* index of filter for subject,object */
@@ -849,9 +850,12 @@ static int checkacl(uint32 recofs,uint32 attrofs,unsigned long operation,struct
int match=0;
if (Filters[Acls[j]->object]==(struct Filter*)Any)
match=1;
else if (Filters[Acls[j]->object]==(struct Filter*)Self)
match=(recofs==authenticated_as);
else if (recofs)
else if (Filters[Acls[j]->object]==(struct Filter*)Self) {
if (authenticated_as==0 && authenticated_as_str)
match=!strcmp(map+uint32_read(map+recofs+8),authenticated_as_str);
else
match=(recofs==authenticated_as);
} else if (recofs)
match=ldap_matchfilter_mapped(recofs,Filters[Acls[j]->object]);
else if (sre)
match=ldap_matchfilter_sre(sre,Filters[Acls[j]->object]);
@@ -895,7 +899,7 @@ static int checkacl_hn(struct hashnode* hn,const unsigned char* attr,unsigned lo
if (Filters[Acls[j]->object]==(struct Filter*)Any)
match=1;
else if (Filters[Acls[j]->object]==(struct Filter*)Self)
match=dn && !strcmp((char*)hn->dn,map+authenticated_as);
match=dn && !strcmp((char*)hn->dn,authenticated_as_str);
else if (dn)
match=ldap_matchfilter_hn(hn,Filters[Acls[j]->object]);
else
@@ -1105,6 +1109,22 @@ static int copyadl(struct AttributeDescriptionList** dest,struct AttributeDescri
return 0;
}
/* semi-deep copy an attribute description list */
static int copyadl2(struct AttributeDescriptionList** dest,struct AttributeDescriptionList* src) {
*dest=0;
while (src) {
if (!(*dest=malloc(sizeof(*src)))) return -1;
byte_zero(*dest,sizeof(*src));
(*dest)->a=src->a;
(*dest)->attrofs=src->attrofs;
dest=&(*dest)->next;
src=src->next;
}
return 0;
}
#if 0
/* deep copy a partial attribute list */
@@ -1148,6 +1168,97 @@ static int addreq2sre(struct SearchResultEntry* sre,struct AddRequest* ar) {
return 0;
}
/* small helper for modreq2sre */
static int mr2sreh1(struct PartialAttributeList** dest,struct Modification* src) {
*dest=0;
while (src) {
if (!(*dest=malloc(sizeof(**dest)))) return -1;
byte_zero(*dest,sizeof(**dest));
(*dest)->type=src->AttributeDescription;
if (copyadl2(&(*dest)->values,src->vals)) return -1;
dest=&(*dest)->next;
src=src->next;
}
return 0;
}
/* We need two versions for the modify request. The first one just creates a stupid
* SearchResultEntry out of just the changed attributes, which is then only used for ACL
* matching. The second version merges in the existing record to form the modified
* record. This is the first version for ACL checking. */
static int modreq2sre(struct SearchResultEntry* sre,struct ModifyRequest* mr) {
byte_zero(sre,sizeof(*sre));
sre->objectName=mr->object;
if (!(sre->attributes=malloc(sizeof(*sre->attributes))) ||
mr2sreh1(&sre->attributes,&mr->m)) {
free_ldapsearchresultentry(sre);
return -1;
}
return 0;
}
static int applymodreq(struct hashnode* hn,struct ModifyRequest* mr,struct SearchResultEntry* sre) {
struct PartialAttributeList** l;
struct Modification* m;
size_t i;
sre->objectName.l=strlen((char*)hn->dn);
sre->objectName.s=(char*)hn->dn;
sre->attributes=0;
l=&(sre->attributes);
/* go through all the attributes in the hash node and apply the modifications */
for (i=0; i<hn->n; ++i) {
enum { Keep, Drop } todo=Keep;
for (m=&mr->m; m; m=m->next) {
if (!matchstring(&m->AttributeDescription,(char*)hn->a[i].a)) {
/* same attribute */
if (m->operation==Add)
continue;
else if (m->operation==Delete) {
/* if it's delete, we need to check the value list */
struct AttributeDescriptionList* adl=m->vals;
if (!adl)
todo=Drop; /* if the list is empty, drop all */
else
for (adl=m->vals; adl; adl=adl->next) {
if (!matchstring(&adl->a,(char*)hn->a[i].v)) {
todo=Drop;
break;
}
}
} else
todo=Drop;
}
if (todo==Drop) break;
}
if (todo==Keep) {
*l=malloc(sizeof(**l));
if (!*l) return -1;
(*l)->next=0;
(*l)->type.s=bstrfirst((char*)hn->a[i].a);
(*l)->type.l=bstrlen((char*)hn->a[i].a);
if (!((*l)->values=malloc(sizeof(*(*l)->values)))) return -1;
(*l)->values->a.s=bstrfirst((char*)hn->a[i].v);
(*l)->values->a.l=bstrlen((char*)hn->a[i].v);
(*l)->values->attrofs=0;
(*l)->values->next=0;
l=&(*l)->next;
}
}
/* then add all the "replace" or "add" attributes */
for (m=&mr->m; m; m=m->next) {
if ((m->operation==Add || m->operation==Replace) && m->vals) {
*l=malloc(sizeof(**l));
if (!*l) return -1;
(*l)->next=0;
(*l)->type.s=m->AttributeDescription.s;
(*l)->type.l=m->AttributeDescription.l;
if (copyadl2(&(*l)->values,m->vals)==-1) return -1;
l=&(*l)->next;
}
}
return 0;
}
/* write a search result entry to a file */
static int writesretofd(int fd,struct SearchResultEntry* sre) {
/* we have no locking, but we open using O_APPEND, so the OS synchronizes for us as long
@@ -1204,6 +1315,10 @@ static struct hashnode** dn_in_journal2(const char* dn,size_t dnlen);
static int lookupdn(struct string* dn,size_t* index, struct hashnode** hn) {
struct Filter f;
struct hashnode** tmphn;
if (dn->l<1 || !dn->s) {
buffer_putsflush(buffer_2,"lookupdn called for NULL dn!\n");
return -1;
}
if ((tmphn=dn_in_journal2(dn->s,dn->l)) && *tmphn) {
*hn=*tmphn;
*index=-1;
@@ -1213,20 +1328,18 @@ static int lookupdn(struct string* dn,size_t* index, struct hashnode** hn) {
f.type=EQUAL;
f.ava.desc.l=2; f.ava.desc.s="dn";
f.ava.value=*dn;
f.next=0;
f.next=f.x=0;
fixup(&f);
if (!indexable(&f)) {
buffer_putsflush(buffer_2,"no index for dn, lookup failed!\n");
return -1;
} else {
struct bitfield result;
size_t i,done;
size_t i;
result.bits=alloca(record_set_length*sizeof(unsigned long));
useindex(&f,&result);
done=0;
if (result.first>result.last)
return 0;
done=0;
assert(result.last<=record_count);
for (i=result.first; i<=result.last; ) {
if (!result.bits[i/(8*sizeof(long))]) {
@@ -1300,17 +1413,64 @@ int handle(int in,int out) {
buffer_putsflush(buffer_2,".\n");
}
if (name.l) {
struct Filter f;
struct string password;
f.type=EQUAL;
scan_ldapstring(buf+res+tmp,buf+res+len,&password);
f.ava.desc.l=2; f.ava.desc.s="dn";
f.ava.value=name;
f.next=0;
fixup(&f);
size_t idx;
struct hashnode* hn;
int err=success;
if (!indexable(&f)) {
buffer_putsflush(buffer_2,"no index for dn, bind failed!\n");
scan_ldapstring(buf+res+tmp,buf+res+len,&password);
switch (lookupdn(&name,&idx,&hn)) {
case -1: err=operationsError; break;
case 1: break;
case 0: err=noSuchObject; break;
default: err=operationsError;
}
if (err!=success)
goto authfailure;
else {
char* c=0;
uint32 authdn;
char* authdn_str=0;
if (idx==(size_t)-1) { // found in journal
size_t i;
for (i=0; i<hn->n; ++i)
if (!strcmp((char*)hn->a[i].a,"userPassword")) {
c=(char*)hn->a[i].v;
authdn=0;
authdn_str=(char*)hn->dn;
break;
}
} else { // found in db
uint32 j;
uint32_unpack(map+indices_offset+4*idx,&j);
uint32_unpack(map+j+8,&authdn);
authdn_str=map+authdn;
if (!(j=ldap_find_attr_value(j,userPassword_ofs))) {
buffer_putsflush(buffer_2,"no userPassword attribute found, bind failed!\n");
goto authfailure;
}
c=map+j;
}
if (check_password(c,&password)) {
authenticated_as=authdn;
authenticated_as_str=authdn_str;
if (acls) {
size_t i;
for (i=0; i<filters; ++i)
acl_ec_subjects[i]=(Filters[i]==(struct Filter*)Any);
for (i=0; i<acls; ++i) {
size_t j=Acls[i]->subject;
if (!acl_ec_subjects[j]) {
if (authdn==0) // authenticated against hashnode
acl_ec_subjects[j]=ldap_matchfilter_hn(hn,Filters[j]);
else // authenticated against mapped db
acl_ec_subjects[j]=ldap_matchfilter_mapped(authdn,Filters[j]);
}
}
}
} else
authfailure:
{
char outbuf[1024];
@@ -1321,56 +1481,8 @@ authfailure:
write(out,outbuf+s-hlen,len+hlen);
continue;
}
} else {
struct bitfield result;
size_t i,done;
result.bits=alloca(record_set_length*sizeof(unsigned long));
useindex(&f,&result);
done=0;
if (result.first>result.last) {
buffer_putsflush(buffer_2,"no matching dn found, bind failed!\n");
goto authfailure;
}
done=0;
assert(result.last<=record_count);
for (i=result.first; i<=result.last; ) {
if (!result.bits[i/(8*sizeof(long))]) {
i+=8*sizeof(long);
continue;
}
for (; i<=result.last; ++i) {
if (isset(&result,i)) {
uint32 j,authdn;
const char* c;
uint32_unpack(map+indices_offset+4*i,&j);
uint32_unpack(map+j+8,&authdn);
if (!(j=ldap_find_attr_value(j,userPassword_ofs))) {
buffer_putsflush(buffer_2,"no userPassword attribute found, bind failed!\n");
goto authfailure;
}
c=map+j;
#if 0
buffer_puts(buffer_2,"compare ");
buffer_puts(buffer_2,c);
buffer_puts(buffer_2," with ");
buffer_put(buffer_2,f.ava.value.s,f.ava.value.l);
buffer_putsflush(buffer_2,".\n");
#endif
if (check_password(c,&password)) {
done=1;
authenticated_as=authdn;
goto found;
}
}
}
}
if (!done) {
buffer_putsflush(buffer_2,"wrong password, bind failed!\n");
goto authfailure;
}
}
}
found:
{
char outbuf[1024];
int s=100;
@@ -1490,9 +1602,10 @@ found:
case ModifyRequest:
{
struct ModifyRequest mr;
int tmp;
int tmp,err=success;
buffer_putsflush(buffer_2,"modifyrequest!\n");
if ((tmp=scan_ldapmodifyrequest(buf+res,buf+res+len,&mr))) {
struct SearchResultEntry sre;
buffer_puts(buffer_1,"modify request: dn \"");
buffer_put(buffer_1,mr.object.s,mr.object.l);
buffer_putsflush(buffer_1,"\"\n");
@@ -1504,7 +1617,7 @@ found:
buffer_put(buffer_1,mr.m.AttributeDescription.s,mr.m.AttributeDescription.l);
buffer_puts(buffer_1,"\n");
{
struct AttributeDescriptionList* x=&mr.m.vals;
struct AttributeDescriptionList* x=mr.m.vals;
do {
buffer_puts(buffer_1," -> \"");
buffer_put(buffer_1,x->a.s,x->a.l);
@@ -1513,12 +1626,60 @@ found:
} while (x);
}
/* TODO: do something with the modify request ;-) */
free_ldapmodifyrequest(&mr);
} else {
buffer_putsflush(buffer_2,"couldn't parse modify request!\n");
exit(1);
if (acls) {
/* convert modifyrequest to searchresultentry */
modreq2sre(&sre,&mr);
/* 1. check ACLs */
if (checkacl(0,0,acl_write,&sre)!=1)
err=insufficientAccessRights;
free_ldapsearchresultentry(&sre);
}
if (err==success) {
/* 2. check if there already is a record with this dn */
struct hashnode* hn;
size_t idx;
switch (lookupdn(&mr.object,&idx,&hn)) {
case -1: err=operationsError; break;
case 1: break;
case 0: err=noSuchObject; break;
default: err=operationsError;
}
if (err==success) {
#if 1
/* 3. apply modifications to record to get new record */
if (!applymodreq(hn,&mr,&sre)) {
/* 4. write record to "data.upd" */
int fd=open("journal",O_WRONLY|O_APPEND|O_CREAT,0600);
if (fd==-1)
err=operationsError;
else {
if (writesretofd(fd,&sre)==-1)
err=operationsError;
close(fd);
}
} else
err=operationsError;
free_ldapsearchresultentry(&sre);
#else
err=operationsError;
#endif
}
}
} else
err=protocolError;
{
char outbuf[1024];
int s=100;
int len=fmt_ldapresult(outbuf+s,err,"","","");
int hlen=fmt_ldapmessage(0,messageid,AddResponse,len);
fmt_ldapmessage(outbuf+s-hlen,messageid,AddResponse,len);
write(out,outbuf+s-hlen,len+hlen);
}
free_ldapmodifyrequest(&mr);
}
break;
case AbandonRequest:
buffer_putsflush(buffer_2,"AbandonRequest!\n");
/* do nothing */
@@ -1632,7 +1793,7 @@ unsigned long hash2(const unsigned char* c) {
h ^= *c;
++c;
}
return h;
return (uint32)h;
}
#define HASHTABSIZE 8191
@@ -1689,7 +1850,8 @@ static struct hashnode** dn_in_journal(unsigned char* dn) {
static struct hashnode** dn_in_journal2(const char* dn,size_t dnlen) {
unsigned long hashval;
struct hashnode** hn;
hashval=hash2((const unsigned char*)dn);
hashval=hash((const unsigned char*)dn,dnlen);
// printf("lookup: \"%.*s\" -> %lu\n",dnlen,dn,hashval);
hn=hashtab+(hashval%HASHTABSIZE);
while (*hn) {
if ((*hn)->hashval==hashval) {
@@ -1710,6 +1872,7 @@ int parse_callback(struct ldaprec* l) {
struct hashnode** hn;
if (l->dn==(uint32)-1) return -1;
hashval=hash2((unsigned char*)stringtable.root+l->dn);
// printf("journal: \"%s\" -> %lu\n",stringtable.root+l->dn,hashval);
hn=hashtab+(hashval%HASHTABSIZE);
while (*hn) {
if ((*hn)->hashval==hashval) {