#define MAIN #include #include #include #include #include #include #include #include #include #include #include #include const char Any[]="*"; const char Self[]="self"; const char Dn[]="dn"; enum { acl_read = 1, acl_write = 2, acl_auth = 4, acl_delete = 8, acl_rendn = 16, }; struct assertion { const char* filterstring; struct Filter* f; struct assertion* sameas; }; struct acl { struct assertion subject,object; char* attrib; uint32 anum; char** attrs; unsigned short may,maynot; struct acl* next; }; static unsigned long lines; static stralloc x; void parseerror() { char buf[FMT_ULONG]; buf[fmt_ulong(buf,lines)]=0; die(1,"parse error in line ",buf); } int skipws(buffer* in) { char c; for (;;) { if (in->p < in->n && buffer_feed(in)<1) return 0; c=*buffer_peek(in); if (c=='\n') ++lines; if (c==' ' || c=='\n' || c=='\t') { buffer_getc(in,&c); continue; } else if (c=='#') { for (;;) { int r=buffer_getc(in,&c); if (r!=1) return r; if (c=='\n') { ++lines; break; } } } else return 1; } return 1; } int parseacldn(buffer* in,struct assertion* a) { int r,l; /* possible forms: * -> "dn", Any dn:*foo -> "dn", "*foo" */ byte_zero(a,sizeof(*a)); a->sameas=0; if ((r=skipws(in))!=1) return r; stralloc_zero(&x); l=0; for (;;) { char tmp; r=buffer_getc(in,&tmp); if (r!=1) return 0; if (!stralloc_append(&x,&tmp)) return 0; if (tmp=='(') ++l; if (tmp==')') { --l; if (l==0) break; } if (stralloc_equals(&x,"*")) { a->filterstring=Any; return 1; } if (stralloc_equals(&x,"self")) { a->filterstring=Self; return 1; } } if (x.len+1filterstring=(const char*)tmp; } if (scan_ldapsearchfilterstring(a->filterstring,&a->f) != x.len) { free_ldapsearchfilter(a->f); return 0; } return 1; } int parseaclattrib(buffer* in,struct acl* a) { /* possible forms: cn,sn mail * */ 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; a->anum=1; return 1; } } r=buffer_get_new_token_sa(in,&x," \t",2); if (r!=1) return r; stralloc_chop(&x); if (!stralloc_0(&x)) return -1; if (str_equal(x.s,"*")) { a->attrib=Any; a->anum=1; return 1; } if (!(a->attrib=strdup(x.s))) return -1; { unsigned int i,j; j=1; for (i=0; ianum=j; } return 1; } int parseaclpermissions(buffer* in,struct acl* a) { char c; int r; unsigned short* s; a->may=a->maynot=0; s=&a->may; for (;;) { r=buffer_getc(in,&c); if (r<1) return r; switch (c) { case '+': s=&a->may; break; case '-': s=&a->maynot; break; case 'r': *s|=acl_read; break; case 'w': *s|=acl_write; break; case 'a': *s|=acl_auth; break; case 'd': *s|=acl_delete; break; case 'R': *s|=acl_rendn; break; case ' ': case '\t': case '\n': break; case ';': return 1; default: parseerror(); } } } static int parseacl(buffer* in,struct acl* a) { int i,r; 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==0 && i==0) return 0; parseerror(); } if ((r=parseacldn(in,&a->subject))!=1) return r; if ((r=parseacldn(in,&a->object))!=1) return r; if ((r=parseaclattrib(in,a))!=1) return r; if ((r=parseaclpermissions(in,a))!=1) return r; a->next=0; return 1; } static void fold(struct assertion* a,struct assertion* b) { if (a->sameas || b->sameas) return; if (!strcmp(a->filterstring,b->filterstring)) b->sameas=a; } static void optimize(struct acl* a) { struct acl* b; for (; a; a=a->next) for (b=a; b; b=b->next) { fold(&a->subject,&b->subject); fold(&a->object,&b->object); fold(&a->subject,&a->object); fold(&a->subject,&b->object); fold(&b->subject,&a->object); fold(&b->subject,&b->object); } } static struct acl* root; int readacls(const char* filename) { buffer b; struct acl **next, a; int r; root=0; next=&root; if (buffer_mmapread(&b,filename)==-1) return -1; while ((r=parseacl(&b,&a))!=-1) { *next=malloc(sizeof(struct acl)); if (!*next) diesys(1,"malloc"); **next=a; next=&(*next)->next; if (r==0) break; } if (r==-1) parseerror(); buffer_close(&b); optimize(root); return 0; } int marshalfilter(stralloc* x,struct assertion* a) { if (a->filterstring==Self) return stralloc_catb(x,Self,5); else { char* tmp; 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"); exit(1); } return stralloc_catb(x,tmp,l); } } int marshal(char* map,unsigned long filelen) { unsigned long filters,acls,i,j,k; unsigned long filter_offset,acl_offset; struct acl* a; uint32* F,* A; uint32 attribute_count; uint32 attrtab; static stralloc x; stralloc_copys(&x,""); filters=acls=0; for (a=root; a; a=a->next) { ++acls; if (!a->subject.sameas) ++filters; if (!a->object.sameas) ++filters; } F=malloc(sizeof(*F)*(filters+1)); filter_offset=filelen+(filters+4)*sizeof(*F); /* 2 uints for index header, 1 uint filters_count, then filter_count+1 uint32 in F */ i=0; x.len=0; for (a=root; a; a=a->next) { if (!a->subject.sameas) { F[i]=x.len+filter_offset; ++i; if (!marshalfilter(&x,&a->subject)) { nomem: buffer_putmflush(buffer_2,"out of memory!\n"); exit(1); } } if (!a->object.sameas) { F[i]=x.len+filter_offset; ++i; if (!marshalfilter(&x,&a->object)) goto nomem; } } attribute_count=uint32_read(map+4); attrtab=5*4+uint32_read(map+16); /* here we need to have each attribute mentioned in any ACL * point to the proper offset in the data file. But what if an ACL * mentions an attribute that never occurs in any of the records? In * that case, we insert a small "string table" between the filters and * the ACLs. To make it possible to skip over it, the offset table of * the filters has one more element, which points to the start of the * ACLs. */ for (a=root; a; a=a->next) { unsigned int l=0; if (!(a->attrs=malloc(a->anum*sizeof(*a->attrs)))) goto nomem; a->attrs[l]=a->attrib; ++l; for (k=0; a->attrib[k]; ++k) if (a->attrib[k]==',') { a->attrib[k]=0; a->attrs[l]=a->attrib+k+1; } if (a->attrib!=Self && a->attrib!=Any) for (k=0; kanum; ++k) { int found=0; for (j=0; jattrs[k])) { a->attrs[k]=map+uint32_read(map+attrtab+j*4); found=1; break; } } if (!found) { /* warning: evil kludge ahead! We assume that the sum of the * lengths of the new attributes plus the ACLs is smaller than * the address where mmap mapped the file. */ char* tmp=a->attrs[k]; a->attrs[k]=map+filelen+ 2*4+ /* index_type and next */ (filters+2)*4+ /* filters_count plus (filter_count+1)+uint32 */ x.len; if (!stralloc_cats(&x,tmp)) goto nomem; } } } /* 32-bit align */ { unsigned int align=(-(x.len&3))&3; if (!stralloc_catb(&x,"\0\0\0",align)) goto nomem; } F[i]=x.len+filter_offset; /* now the ACLs */ A=malloc(sizeof(*A)*acls); if (!A) goto nomem; acl_offset=F[i]+(acls+1)*sizeof(*A); /* FIXME, TODO */ } #ifdef MAIN int main(int argc,char* argv[]) { unsigned long filelen; char* map=mmap_read(argc>1?argv[1]:"data",&filelen); if (filelen<5*4 || uint32_read(map)!=0xfefe1da9) { buffer_putmflush(buffer_2,"not a valid tinyldap data file!\n"); exit(0); } if (readacls("acls")==-1) die(1,"readacls failed"); // acl_offsets(map,filelen); marshal(map,filelen); return 0; } #endif