Files
mars-tinyldap/acl.c
2005-07-08 23:13:17 +00:00

367 lines
8.2 KiB
C

#define MAIN
#include <buffer.h>
#include <stralloc.h>
#include <str.h>
#include <uint32.h>
#include <string.h>
#include <errmsg.h>
#include <fmt.h>
#include <byte.h>
#include <mmap.h>
#include <case.h>
#include <ldap.h>
#include <uint32.h>
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+1<x.len) return 0; /* catch integer overflow */
{
char* tmp=malloc(x.len+1);
byte_copy(tmp,x.len,x.s);
tmp[x.len]=0;
a->filterstring=(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; i<x.len; ++i)
if (x.s[i]==',') ++j;
a->anum=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; k<a->anum; ++k) {
int found=0;
for (j=0; j<attribute_count; ++j) {
if (!strcmp(map+uint32_read(map+attrtab+j*4),a->attrs[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