diff --git a/acl.c b/acl.c index d03781e..a7885b9 100644 --- a/acl.c +++ b/acl.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include const char Any[]="*"; const char Self[]="self"; @@ -46,7 +48,8 @@ struct assertion { struct acl { struct assertion login,target; const char* attrib; - short may,maynot; + uint32 anum; + unsigned short may,maynot; struct acl* next; }; @@ -72,7 +75,7 @@ int skipws(buffer* in) { for (;;) { int r=buffer_getc(in,&c); if (r!=1) return r; - if (c=='\n') break; + if (c=='\n') { ++lines; break; } } } else return 1; } @@ -90,7 +93,11 @@ int parseacldn(buffer* in,struct assertion* a) { do { r=buffer_get_token_sa(in,&x," \t",2); if (r!=1) return r; - } while (!x.len || x.s[x.len-1]!='\\'); + if (x.len>0 && x.s[x.len-1]=='\\') { + x.s[--x.len]=' '; + continue; + } + } while (x.len==0); stralloc_chop(&x); if (!stralloc_0(&x)) return -1; r=byte_chr(x.s,x.len,':'); @@ -129,6 +136,16 @@ int parseaclattrib(buffer* in,struct acl* a) { int r; a->attrib=0; if ((r=skipws(in))!=1) return r; + + if (in->p < in->n && buffer_feed(in)<1) return 0; + { + char c=*buffer_peek(in); + if (c=='+' || c=='-') { + a->attrib=Any; + return 1; + } + } + r=buffer_get_new_token_sa(in,&x," \t",2); if (r!=1) return r; stralloc_chop(&x); @@ -143,7 +160,7 @@ int parseaclattrib(buffer* in,struct acl* a) { int parseaclpermissions(buffer* in,struct acl* a) { char c; int r; - short* s; + unsigned short* s; a->may=a->maynot=0; s=&a->may; for (;;) { r=buffer_getc(in,&c); @@ -168,7 +185,10 @@ static int parseacl(buffer* in,struct acl* a) { char c; if ((r=skipws(in))!=1) return r; for (i=0; i<3; ++i) - if (buffer_getc(in,&c)!=1 && c!="acl"[i]) parseerror(); + if ((r=buffer_getc(in,&c))!=1 && c!="acl"[i]) { + if (r==0 && i==0) return 0; + parseerror(); + } if ((r=parseacldn(in,&a->login))!=1) return r; if ((r=parseacldn(in,&a->target))!=1) return r; if ((r=parseaclattrib(in,a))!=1) return r; @@ -197,14 +217,14 @@ static void optimize(struct acl* a) { } } -int main() { +static struct acl* root; + +int readacls(const char* filename) { buffer b; - struct acl* root,**next, a; + struct acl **next, a; int r; root=0; next=&root; - - if (buffer_mmapread(&b,"acls")==-1) diesys(1,"buffer_mmapread"); - + if (buffer_mmapread(&b,filename)==-1) return -1; while ((r=parseacl(&b,&a))!=-1) { *next=malloc(sizeof(struct acl)); if (!*next) diesys(1,"malloc"); @@ -216,5 +236,141 @@ int main() { buffer_close(&b); optimize(root); + return 0; } + +/* given a DN a (logged in as DN b), we need to quickly find out what + * kind of permissions we have for an attribute c. To make this extra + * quick, I'm only comparing DNs if the rule + * a) grants or denies the permission I'm interested in (bit test) + * b) actually says something about the attribute I'm interested in. + * To make b) cheap, I'll not actually compare attribute strings, but + * I'll compare the offset of the attribute name in the mmapped file. + * The * attribute is represented as 0. An attribute that is not found + * in the mmapped file is represented as -1. */ + +static uint32 acl_map(char* map,char* x,const char* s,unsigned int attribute_count) { + unsigned int i; + for (i=0; imaplen-2) { carp("invalid offset in attribute table"); return; } /* can't happen */ + if (case_equals(map+j,"dn")) + dn_ofs=j; + else if (case_equals(map+j,"objectClass")) + oc_ofs=j; + } + + for (a=root; a; a=a->next) { + a->anum=(uint32)-1; + if (a->attrib==Any) + a->anum=0; + else + a->anum=acl_map(map,maplen,x,a->attrib,attribute_count); + + if (a->login.attr==Dn) a->login.where=dn_ofs; + else if (case_equals(a->login.attr,"objectClass")) a->login.where=oc_ofs; + else a->login.where=acl_map(map,x,a->login.attr,attribute_count); + + if (a->target.attr==Dn) a->target.where=dn_ofs; + else if (case_equals(a->target.attr,"objectClass")) a->target.where=oc_ofs; + else a->target.where=acl_map(map,x,a->target.attr,attribute_count); + +#if 0 + if (a->anum==-1) + printf("no offset found for %s\n",a->attrib); + if (a->login.where==-1) + printf("no offset found for %s\n",a->login.attr); + if (a->target.where==-1) + printf("no offset found for %s\n",a->target.attr); +#endif + } +} + +extern uint32 dn_ofs,objectClass_ofs; + +int acl_allowed(char* map,unsigned long maplen,const char* logindn,const char* targetdn,uint32 attr,unsigned short wanted) { + struct acl* a; + struct assertion* l; + for (a=root; a; a=a->next) { + if (((a->may|a->maynot)&wanted) && /* acl applies to action we want to do */ + (!a->anum || a->anum==attr)) { /* acl applies to this attribute */ + + l=&a->login; if (l->sameas) l=l->sameas; + + /* first, see if logindn matches */ + if (logindn==0) { + /* special case: anonymous bind. + * Do not match if login assertion is not "Any" */ + if (l->attr != Any) continue; + } else if (logindn>=map && logindn<=map+maplen) { + uint32 n=uint32_read(logindn); + uint32 v=0; + if (l->attr != Any) { + v=ldap_find_attr_value(logindn,l->where); + if (v==0) continue; /* attribute not there, no match */ + if (l->what != Any) { + if (l->what[0]=='*') { + /* suffix match */ + } else if (l->what[strlen(l->what)-1]=='*') { + /* prefix match */ + } else { + /* direct match */ + /* TODO XXX FIXME */ + } + } + } + } else + die(1,"unhandled case: logindn outside map"); + } + +#if 0 +struct assertion { + const char* attr; + uint32 where; + const char* what; + struct assertion* sameas; +}; +#endif + + } +} + +#if 0 +void dumpacls() { + struct acl* a; + for (a=root; a; a=a->next) { + printf("\n--=[ record at %p ]=--\n",a); + printf("%s=%s %s=%s %s\n",a->login.attr,a->login.what,a->target.attr,a->target.what,a->attrib); + } +} +#endif + +#ifdef MAIN +int main() { + unsigned long filelen; + char* map=mmap_read("data",&filelen); + + if (readacls("acls")==-1) die(1,"readacls failed"); +// dumpacls(); + acl_offsets(map,filelen); + return 0; +} +#endif diff --git a/acls b/acls index cfabc35..ac3d0d0 100644 --- a/acls +++ b/acls @@ -1,8 +1,8 @@ # root@fefe.de can do everything -acl dn:cn=root,o=fefe,c=de * +rwdR; +acl dn:cn=root,o=fefe,c=de * * +rwdR; # noone can read userPassword acl * * userPassword -r; # but everyone can authenticate using it -acl * self +a; +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,o=fefe,c=de dn:*,o=fefe,c=de * +rwdR; diff --git a/ldap_match_mapped.c b/ldap_match_mapped.c index 7e627c8..2b50871 100644 --- a/ldap_match_mapped.c +++ b/ldap_match_mapped.c @@ -66,6 +66,17 @@ uint32 ldap_find_attr_value(uint32 ofs,uint32 attrofs) { return 0; } +static inline int matchint(struct Filter* f,const char* t) { + int r; + if (f->attrflag&1) + r=matchcasestring(&f->ava.value,t); + else + r=matchstring(&f->ava.value,t); + if (f->type==EQUAL) return (r!=0); + if (f->type==LESSEQUAL) return (r>0); + return (r<0); +} + /* return non-zero if the record matches the search filter */ int ldap_matchfilter_mapped(uint32 ofs,struct Filter* f) { struct Filter* y=f->x; @@ -88,36 +99,26 @@ int ldap_matchfilter_mapped(uint32 ofs,struct Filter* f) { case PRESENT: return ldap_match_present(ofs,f->attrofs); case EQUAL: + case LESSEQUAL: + case GREATEQUAL: { uint32 i,j,k; uint32_unpack(map+ofs,&j); // if (!matchstring(&f->ava.desc,"dn")) { if (f->attrofs==dn_ofs) { uint32_unpack(map+ofs+8,&k); - if (f->attrflag&1) { - if (!matchcasestring(&f->ava.value,map+k)) return 1; - } else { - if (!matchstring(&f->ava.value,map+k)) return 1; - } + return matchint(f,map+k); // } else if (!matchstring(&f->ava.desc,"objectName")) { } else if (f->attrofs==objectClass_ofs) { uint32_unpack(map+ofs+12,&k); - if (f->attrflag&1) { - if (!matchcasestring(&f->ava.value,map+k)) return 1; - } else { - if (!matchstring(&f->ava.value,map+k)) return 1; - } + if (matchint(f,map+k)) return 1; } for (i=2; iava.desc,map+k)) { if (f->attrofs==k) { uint32_unpack(map+ofs+i*8+4,&k); - if (f->attrflag&1) { - if (!matchcasestring(&f->ava.value,map+k)) return 1; - } else { - if (!matchstring(&f->ava.value,map+k)) return 1; - } + if (matchint(f,map+k)) return 1; } } return 0; @@ -131,6 +132,7 @@ int ldap_matchfilter_mapped(uint32 ofs,struct Filter* f) { if (f->attrofs==dn_ofs) { uint32_unpack(map+ofs+8,&k); if (substringmatch(f->substrings,map+k,f->attrflag&1)) return 1; + return 0; // } else if (matchstring(&f->ava.desc,"objectName")) { } else if (f->attrofs==objectClass_ofs) { uint32_unpack(map+ofs+12,&k); diff --git a/parse.c b/parse.c index e31056a..d3a1d05 100644 --- a/parse.c +++ b/parse.c @@ -13,6 +13,7 @@ #include "uint32.h" #include "byte.h" #include "fmt.h" +#include "errmsg.h" /* these are defined in ldif_parse.c. * We extern them here so we can initialize them. @@ -189,6 +190,9 @@ derrout2: mstorage_add(&stringtable,dummy,5*4); } +// if ((mduptab_adds(&attributes,"*"))<0) +// die(1,"out of memory"); + ldif_parse(argc<2?"exp.ldif":argv[1]); if (!first) { buffer_putsflush(buffer_2,"usage: parse [src-ldif-filename] [dest-bin-filename]\n"); diff --git a/tinyldap.c b/tinyldap.c index 8882344..895df30 100644 --- a/tinyldap.c +++ b/tinyldap.c @@ -40,7 +40,7 @@ uint32 magic,attribute_count,record_count,indices_offset,size_of_string_table; uint32 record_set_length; /* some pre-looked-up attribute offsets to speed up ldap_match_mapped */ -uint32 dn_ofs,objectClass_ofs,userPassword_ofs; +uint32 dn_ofs,objectClass_ofs,userPassword_ofs,any_ofs; #define BUFSIZE 8192 @@ -879,16 +879,18 @@ int main() { { char* x=map+5*4+size_of_string_table; unsigned int i; - dn_ofs=objectClass_ofs=userPassword_ofs=0; + dn_ofs=objectClass_ofs=userPassword_ofs=any_ofs=0; for (i=0; i