Files
mars-tinyldap/acl.c

221 lines
4.6 KiB
C

/*
tinyldap acl syntax:
acl login-in-as-dn target-dn attrib
e.g.
# root@fefe.de can do everything
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;
# admins at fefe.de can write in their tree
acl dn:*ou=admin,o=fefe,c=de dn:*,o=fefe,c=de +rwdR;
*/
#include <buffer.h>
#include <stralloc.h>
#include <str.h>
#include <uint32.h>
#include <string.h>
#include <errmsg.h>
#include <fmt.h>
#include <byte.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* attr;
uint32 where;
const char* what;
struct assertion* sameas;
};
struct acl {
struct assertion login,target;
const char* attrib;
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') break;
}
} else return 1;
}
return 1;
}
int parseacldn(buffer* in,struct assertion* a) {
int r;
/* possible forms:
* -> "dn", Any
dn:*foo -> "dn", "*foo" */
a->sameas=0;
if ((r=skipws(in))!=1) return r;
stralloc_zero(&x);
do {
r=buffer_get_token_sa(in,&x," \t",2);
if (r!=1) return r;
} while (!x.len || x.s[x.len-1]!='\\');
stralloc_chop(&x);
if (!stralloc_0(&x)) return -1;
r=byte_chr(x.s,x.len,':');
if (x.s[r]==':') {
x.s[r]=0;
if (str_equal(x.s,"dn")) {
a->attr=Dn;
a->what=strdup(x.s+r+1);
if (!a->what) return -1;
} else {
a->attr=malloc(x.len);
if (!a->attr) return -1;
byte_copy((char*)a->attr,x.len,x.s);
a->what=a->attr+r+1;
}
} else {
a->attr=Dn;
if (str_equal(x.s,"*"))
a->what=Any;
else if (str_equal(x.s,"self"))
a->what=Self;
else {
a->what=strdup(x.s);
if (!a->what) return -1;
}
}
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;
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;
return 1;
}
return ((a->attrib=strdup(x.s))?1:-1);
}
int parseaclpermissions(buffer* in,struct acl* a) {
char c;
int r;
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 (buffer_getc(in,&c)!=1 && c!="acl"[i]) 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;
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 (a->attr==b->attr || str_equal(a->attr,b->attr))
if (a->what==b->what || str_equal(a->what,b->what))
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->login,&b->login);
fold(&a->target,&b->target);
fold(&a->login,&a->target);
fold(&a->login,&b->target);
fold(&b->login,&a->target);
fold(&b->login,&b->target);
}
}
int main() {
buffer b;
struct acl* root,**next, a;
int r;
root=0; next=&root;
if (buffer_mmapread(&b,"acls")==-1) diesys(1,"buffer_mmapread");
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;
}