#define _FILE_OFFSET_BITS 64 #define MAIN #include "ldap.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include const char Any[]="*"; const char Self[]="self"; const char Dn[]="dn"; uint32 any_ofs; #include "acl.h" struct assertion { const char* filterstring; struct Filter* f; uint32 idx; 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=(char*)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=(char*)Any; a->anum=1; return 1; } if (!(a->attrib=malloc(x.len))) return -1; memcpy(a->attrib,x.s,x.len); { 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_add; 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==b) return; #if 0 printf("fold \"%s\" \"%s\"\n",a->filterstring,b->filterstring); #endif if (b->sameas || a->sameas) return; if (!strcmp(a->filterstring,b->filterstring)) { a->sameas=b; #if 0 printf(" -> folded!\n"); #endif } } static void optimize(struct acl* a) { struct acl* origa=a; struct acl* b; for (; a; a=a->next) for (b=origa; b!=a; b=b->next) { fold(&a->subject,&b->subject); fold(&a->object,&b->object); fold(&a->subject,&b->object); fold(&b->subject,&a->object); fold(&b->subject,&b->object); fold(&a->subject,&a->object); } #if 0 for (a=origa; a; a=a->next) { if (a->subject.sameas && a->subject.sameas->sameas) puts("ARGH 1!"); if (a->object.sameas && a->object.sameas->sameas) puts("ARGH 2!"); } #endif } 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"); byte_copy(*next,sizeof(a),&a); // **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); if (a->filterstring==Any) return stralloc_catb(x,Any,2); 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_putsflush(buffer_2,"internal error!\n"); exit(1); } return stralloc_catb(x,tmp,l); } } int marshal(const char* map,size_t filelen,const char* filename) { size_t filters,acls,i,j,k; size_t filter_offset; //,acl_offset; struct acl* a; uint32* F,* A; uint32 attribute_count; uint32 attrtab; static stralloc x,y; int fd=open_append(filename); if (fd==-1) diesys(1,"could not open file `",filename,"' for writing"); stralloc_copys(&x,""); stralloc_copys(&y,""); filters=acls=0; for (a=root; a; a=a->next) { ++acls; if (!a->subject.sameas) { a->subject.idx=filters; ++filters; } if (!a->object.sameas) { a->object.idx=filters; ++filters; } } buffer_putulong(buffer_1,acls); buffer_puts(buffer_1," ACLs with "); buffer_putulong(buffer_1,filters); buffer_putsflush(buffer_1," filters.\n"); if (acls==0) { buffer_putsflush(buffer_1,"No ACLs defined. We are done here.\n"); exit(0); } F=malloc(sizeof(*F)*(filters+1)); if (!F) goto nomem; 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_putsflush(buffer_2,"out of memory!\n"); exit(1); } // printf("marshalled \"%s\" to %ld\n",a->subject.filterstring,F[i-1]); } if (!a->object.sameas) { F[i]=x.len+filter_offset; ++i; if (!marshalfilter(&x,&a->object)) goto nomem; // printf("marshalled \"%s\" to %ld\n",a->object.filterstring,F[i-1]); } } attribute_count=uint32_read(map+4); attrtab=5*4+uint32_read(map+16); if (attribute_count == 0) { buffer_putsflush(buffer_2,"malformed data file (attribute_count zero!?)\n"); exit(1); } /* 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. */ { int anyused=0; 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=calloc(a->anum,sizeof(*a->attrs)))) goto nomem; a->attrs[l]=a->attrib; ++l; if (a->attrib!=Any) { for (k=0; a->attrib[k]; ++k) if (a->attrib[k]==',') { a->attrib[k]=0; a->attrs[l]=a->attrib+k+1; } #ifndef __dietlibc__ assert(l==a->anum); // this is for the benefit of clang's static analyzer #endif for (k=0; kanum; ++k) { int found=0; for (j=0; jattrs[k])) { a->attrs[k]=(char*)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]; // buffer_putmflush(buffer_1,"adding attribute ",a->attrs[k],"\n"); a->attrs[k]=(char*)map+filelen+ 2*4+ /* index_type and next */ (filters+2)*4+ /* filters_count plus (filter_count+1)*uint32 */ x.len; if (!stralloc_catb(&x,tmp,strlen(tmp)+1)) goto nomem; } } } else anyused=1; } if (anyused) { any_ofs=filelen+2*4+(filters+2)*4+x.len; // printf("filelen is %d, filters is %d, x.len is %d -> anyofs is %d\n",filelen,filters,x.len,any_ofs); if (!stralloc_catb(&x,Any,2)) 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); i=0; for (a=root; a; a=a->next) { char tmp[4]; unsigned int j; A[i]=y.len; ++i; if (!stralloc_readyplus(&y,12)) goto nomem; if (a->subject.sameas) uint32_pack(tmp,a->subject.sameas->idx); else uint32_pack(tmp,a->subject.idx); stralloc_catb(&y,tmp,4); if (a->object.sameas) uint32_pack(tmp,a->object.sameas->idx); else uint32_pack(tmp,a->object.idx); stralloc_catb(&y,tmp,4); uint16_pack(tmp,a->may); stralloc_catb(&y,tmp,2); uint16_pack(tmp,a->maynot); stralloc_catb(&y,tmp,2); if (a->attrib==Any) { uint32_pack(tmp,any_ofs); if (!stralloc_catb(&y,tmp,4)) goto nomem; } else { for (j=0; janum; ++j) { if (a->attrs[j]==Any) buffer_putmflush(buffer_1,a->attrs[j],"\n"); uint32_pack(tmp,a->attrs[j]-map); if (!stralloc_catb(&y,tmp,4)) goto nomem; } } uint32_pack(tmp,0); if (!stralloc_catb(&y,tmp,4)) goto nomem; } /* 32-bit align */ { unsigned int align=(-(y.len&3))&3; if (!stralloc_catb(&y,"\0\0\0",align)) goto nomem; } { char tmp[8]; unsigned long i; uint32 fixup; /* write index header: * uint32 index_type (2 in this case); * uint32 offset_of_next_header; */ uint32_pack(tmp,2); uint32_pack(tmp+4,filelen+ 8+ /* index header */ 4+ /* uint32 filters_count; */ 4*(filters+1)+ /* uint32 offsets_to_filters_in_scan_ldapsearchfilter_format[filter_count+1]; */ x.len+ /* marshalled filters */ 4+ /* uint32 acl_count */ 4*acls+ /* uint32 offsets_to_acls[acl_count]; */ y.len); /* marshalled acls */ if (write(fd,tmp,8)!=8) { shortwrite: free(A); free(F); ftruncate(fd,filelen); close(fd); diesys(1,"short write"); } /* uint32 filter_count */ uint32_pack(tmp,filters); if (write(fd,tmp,4)!=4) goto shortwrite; /* uint32 offsets_to_filters_in_scan_ldapsearchfilter_format[filter_count+1]; */ for (i=0; i1?argv[1]:"data"; const char* map=mmap_read(filename,&filelen); if (!map) { buffer_putmflush(buffer_2,"Could not open ",filename,"\n"); return 0; } if (filelen<5*4 || uint32_read(map)!=0xfefe1da9) { buffer_putsflush(buffer_2,"not a valid tinyldap data file!\n"); return 0; } if (readacls("acls")==-1) die(1,"readacls failed"); // acl_offsets(map,filelen); marshal(map,filelen,filename); return 0; } #endif