/* aclparse.c - routines to parse and check acl's */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * Copyright 1998-2004 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted only as authorized by the OpenLDAP * Public License. * * A copy of this license is available in the file LICENSE in the * top-level directory of the distribution or, alternatively, at * . */ /* Portions Copyright (c) 1995 Regents of the University of Michigan. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and that due credit is given * to the University of Michigan at Ann Arbor. The name of the University * may not be used to endorse or promote products derived from this * software without specific prior written permission. This software * is provided ``as is'' without express or implied warranty. */ #include "portable.h" #include #include #include #include #include #include #include "slap.h" #include "lber_pvt.h" #include "lutil.h" static char *style_strings[] = { "regex", "base", "one", "subtree", "children", NULL }; static void split(char *line, int splitchar, char **left, char **right); static void access_append(Access **l, Access *a); static void acl_usage(void) LDAP_GCCATTR((noreturn)); static void acl_regex_normalized_dn(const char *src, struct berval *pat); #ifdef LDAP_DEBUG static void print_acl(Backend *be, AccessControl *a); static void print_access(Access *b); #endif static void regtest(const char *fname, int lineno, char *pat) { int e; regex_t re; char buf[512]; unsigned size; char *sp; char *dp; int flag; sp = pat; dp = buf; size = 0; buf[0] = '\0'; for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) { if (flag) { if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) { *dp++ = *sp; size++; } flag = 0; } else { if (*sp == '$') { flag = 1; } else { *dp++ = *sp; size++; } } } *dp = '\0'; if ( size >= (sizeof(buf)-1) ) { fprintf( stderr, "%s: line %d: regular expression \"%s\" too large\n", fname, lineno, pat ); acl_usage(); } if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) { char error[512]; regerror(e, &re, error, sizeof(error)); fprintf( stderr, "%s: line %d: regular expression \"%s\" bad because of %s\n", fname, lineno, pat, error ); acl_usage(); } regfree(&re); } void parse_acl( Backend *be, const char *fname, int lineno, int argc, char **argv ) { int i; char *left, *right, *style; struct berval bv; AccessControl *a; Access *b; int rc; const char *text; a = NULL; for ( i = 1; i < argc; i++ ) { /* to clause - select which entries are protected */ if ( strcasecmp( argv[i], "to" ) == 0 ) { if ( a != NULL ) { fprintf( stderr, "%s: line %d: " "only one to clause allowed in access line\n", fname, lineno ); acl_usage(); } a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) ); for ( ++i; i < argc; i++ ) { if ( strcasecmp( argv[i], "by" ) == 0 ) { i--; break; } if ( strcasecmp( argv[i], "*" ) == 0 ) { if( a->acl_dn_pat.bv_len || ( a->acl_dn_style != ACL_STYLE_REGEX ) ) { fprintf( stderr, "%s: line %d: dn pattern" " already specified in to clause.\n", fname, lineno ); acl_usage(); } a->acl_dn_pat.bv_val = ch_strdup( "*" ); a->acl_dn_pat.bv_len = 1; continue; } split( argv[i], '=', &left, &right ); split( left, '.', &left, &style ); if ( right == NULL ) { fprintf( stderr, "%s: line %d: " "missing \"=\" in \"%s\" in to clause\n", fname, lineno, left ); acl_usage(); } if ( strcasecmp( left, "dn" ) == 0 ) { if( a->acl_dn_pat.bv_len != 0 || ( a->acl_dn_style != ACL_STYLE_REGEX ) ) { fprintf( stderr, "%s: line %d: dn pattern" " already specified in to clause.\n", fname, lineno ); acl_usage(); } if ( style == NULL || *style == '\0' || ( strcasecmp( style, "base" ) == 0 ) || ( strcasecmp( style, "exact" ) == 0 )) { a->acl_dn_style = ACL_STYLE_BASE; ber_str2bv( right, 0, 1, &a->acl_dn_pat ); } else if ( strcasecmp( style, "onelevel" ) == 0 || strcasecmp( style, "one" ) == 0 ) { a->acl_dn_style = ACL_STYLE_ONE; ber_str2bv( right, 0, 1, &a->acl_dn_pat ); } else if ( strcasecmp( style, "subtree" ) == 0 || strcasecmp( style, "sub" ) == 0 ) { if( *right == '\0' ) { a->acl_dn_pat.bv_val = ch_strdup( "*" ); a->acl_dn_pat.bv_len = 1; } else { a->acl_dn_style = ACL_STYLE_SUBTREE; ber_str2bv( right, 0, 1, &a->acl_dn_pat ); } } else if ( strcasecmp( style, "children" ) == 0 ) { a->acl_dn_style = ACL_STYLE_CHILDREN; ber_str2bv( right, 0, 1, &a->acl_dn_pat ); } else if ( strcasecmp( style, "regex" ) == 0 ) { a->acl_dn_style = ACL_STYLE_REGEX; if ( *right == '\0' ) { /* empty regex should match empty DN */ a->acl_dn_style = ACL_STYLE_BASE; ber_str2bv( right, 0, 1, &a->acl_dn_pat ); } else if ( strcmp(right, "*") == 0 || strcmp(right, ".*") == 0 || strcmp(right, ".*$") == 0 || strcmp(right, "^.*") == 0 || strcmp(right, "^.*$") == 0 || strcmp(right, ".*$$") == 0 || strcmp(right, "^.*$$") == 0 ) { a->acl_dn_pat.bv_val = ch_strdup( "*" ); a->acl_dn_pat.bv_len = sizeof("*")-1; } else { acl_regex_normalized_dn( right, &a->acl_dn_pat ); } } else { fprintf( stderr, "%s: line %d: " "unknown dn style \"%s\" in to clause\n", fname, lineno, style ); acl_usage(); } continue; } if ( strcasecmp( left, "filter" ) == 0 ) { if ( (a->acl_filter = str2filter( right )) == NULL ) { fprintf( stderr, "%s: line %d: bad filter \"%s\" in to clause\n", fname, lineno, right ); acl_usage(); } } else if ( strcasecmp( left, "attr" ) == 0 || strcasecmp( left, "attrs" ) == 0 ) { a->acl_attrs = str2anlist( a->acl_attrs, right, "," ); if ( a->acl_attrs == NULL ) { fprintf( stderr, "%s: line %d: unknown attr \"%s\" in to clause\n", fname, lineno, right ); acl_usage(); } } else if ( strncasecmp( left, "val", 3 ) == 0 ) { if ( a->acl_attrval.bv_len ) { fprintf( stderr, "%s: line %d: attr val already specified in to clause.\n", fname, lineno ); acl_usage(); } if ( a->acl_attrs == NULL || a->acl_attrs[1].an_name.bv_val ) { fprintf( stderr, "%s: line %d: attr val requires a single attribute.\n", fname, lineno ); acl_usage(); } ber_str2bv( right, 0, 1, &a->acl_attrval ); if ( style && strcasecmp( style, "regex" ) == 0 ) { int e = regcomp( &a->acl_attrval_re, a->acl_attrval.bv_val, REG_EXTENDED | REG_ICASE | REG_NOSUB ); if ( e ) { char buf[512]; regerror( e, &a->acl_attrval_re, buf, sizeof(buf) ); fprintf( stderr, "%s: line %d: " "regular expression \"%s\" bad because of %s\n", fname, lineno, right, buf ); acl_usage(); } a->acl_attrval_style = ACL_STYLE_REGEX; } else { /* FIXME: if the attribute has DN syntax, * we might allow one, subtree and children styles as well */ if ( !strcasecmp( style, "exact" ) ) { a->acl_attrval_style = ACL_STYLE_BASE; } else if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) { if ( !strcasecmp( style, "base" ) ) { a->acl_attrval_style = ACL_STYLE_BASE; } else if ( !strcasecmp( style, "onelevel" ) || !strcasecmp( style, "one" ) ) { a->acl_attrval_style = ACL_STYLE_ONE; } else if ( !strcasecmp( style, "subtree" ) || !strcasecmp( style, "sub" ) ) { a->acl_attrval_style = ACL_STYLE_SUBTREE; } else if ( !strcasecmp( style, "children" ) ) { a->acl_attrval_style = ACL_STYLE_CHILDREN; } else { fprintf( stderr, "%s: line %d: unknown val.