1 /* aclparse.c - routines to parse and check acl's */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2005 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
16 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
17 * All rights reserved.
19 * Redistribution and use in source and binary forms are permitted
20 * provided that this notice is preserved and that due credit is given
21 * to the University of Michigan at Ann Arbor. The name of the University
22 * may not be used to endorse or promote products derived from this
23 * software without specific prior written permission. This software
24 * is provided ``as is'' without express or implied warranty.
33 #include <ac/socket.h>
34 #include <ac/string.h>
35 #include <ac/unistd.h>
41 static char *style_strings[] = {
58 static void split(char *line, int splitchar, char **left, char **right);
59 static void access_append(Access **l, Access *a);
60 static void acl_usage(void) LDAP_GCCATTR((noreturn));
62 static void acl_regex_normalized_dn(const char *src, struct berval *pat);
65 static void print_acl(Backend *be, AccessControl *a);
66 static void print_access(Access *b);
69 static int check_scope( BackendDB *be, AccessControl *a );
73 slap_dynacl_config( const char *fname, int lineno, Access *b, const char *name, slap_style_t sty, const char *right )
75 slap_dynacl_t *da, *tmp;
78 for ( da = b->a_dynacl; da; da = da->da_next ) {
79 if ( strcasecmp( da->da_name, name ) == 0 ) {
81 "%s: line %d: dynacl \"%s\" already specified.\n",
82 fname, lineno, name );
87 da = slap_dynacl_get( name );
92 tmp = ch_malloc( sizeof( slap_dynacl_t ) );
95 if ( tmp->da_parse ) {
96 rc = ( *tmp->da_parse )( fname, lineno, sty, right, &tmp->da_private );
103 tmp->da_next = b->a_dynacl;
108 #endif /* SLAP_DYNACL */
111 regtest(const char *fname, int lineno, char *pat) {
127 for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
129 if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
146 if ( size >= (sizeof(buf) - 1) ) {
148 "%s: line %d: regular expression \"%s\" too large\n",
149 fname, lineno, pat );
153 if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
155 regerror(e, &re, error, sizeof(error));
157 "%s: line %d: regular expression \"%s\" bad because of %s\n",
158 fname, lineno, pat, error );
167 * Check if the pattern of an ACL, if any, matches the scope
168 * of the backend it is defined within.
170 #define ACL_SCOPE_UNKNOWN (-2)
171 #define ACL_SCOPE_ERR (-1)
172 #define ACL_SCOPE_OK (0)
173 #define ACL_SCOPE_PARTIAL (1)
174 #define ACL_SCOPE_WARN (2)
177 check_scope( BackendDB *be, AccessControl *a )
182 dn = be->be_nsuffix[0];
184 if ( BER_BVISEMPTY( &dn ) ) {
188 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
189 a->acl_dn_style != ACL_STYLE_REGEX )
191 slap_style_t style = a->acl_dn_style;
193 if ( style == ACL_STYLE_REGEX ) {
194 char dnbuf[SLAP_LDAPDN_MAXLEN + 2];
195 char rebuf[SLAP_LDAPDN_MAXLEN + 1];
200 /* add trailing '$' to database suffix to form
201 * a simple trial regex pattern "<suffix>$" */
202 AC_MEMCPY( dnbuf, be->be_nsuffix[0].bv_val,
203 be->be_nsuffix[0].bv_len );
204 dnbuf[be->be_nsuffix[0].bv_len] = '$';
205 dnbuf[be->be_nsuffix[0].bv_len + 1] = '\0';
207 if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) {
208 return ACL_SCOPE_WARN;
211 /* remove trailing ')$', if any, from original
213 rebuflen = a->acl_dn_pat.bv_len;
214 AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val, rebuflen + 1 );
215 if ( rebuf[rebuflen - 1] == '$' ) {
216 rebuf[--rebuflen] = '\0';
218 while ( rebuflen > be->be_nsuffix[0].bv_len && rebuf[rebuflen - 1] == ')' ) {
219 rebuf[--rebuflen] = '\0';
221 if ( rebuflen == be->be_nsuffix[0].bv_len ) {
226 /* not a clear indication of scoping error, though */
227 rc = regexec( &re, rebuf, 0, NULL, 0 )
228 ? ACL_SCOPE_WARN : ACL_SCOPE_OK;
235 patlen = a->acl_dn_pat.bv_len;
236 /* If backend suffix is longer than pattern,
237 * it is a potential mismatch (in the sense
238 * that a superior naming context could
240 if ( dn.bv_len > patlen ) {
241 /* base is blatantly wrong */
242 if ( style == ACL_STYLE_BASE ) return ACL_SCOPE_ERR;
244 /* a style of one can be wrong if there is
245 * more than one level between the suffix
247 if ( style == ACL_STYLE_ONE ) {
248 int rdnlen = -1, sep = 0;
251 if ( !DN_SEPARATOR( dn.bv_val[dn.bv_len - patlen - 1] )) {
252 return ACL_SCOPE_ERR;
257 rdnlen = dn_rdnlen( NULL, &dn );
258 if ( rdnlen != dn.bv_len - patlen - sep )
259 return ACL_SCOPE_ERR;
262 /* if the trailing part doesn't match,
263 * then it's an error */
264 if ( strcmp( a->acl_dn_pat.bv_val,
265 &dn.bv_val[dn.bv_len - patlen] ) != 0 )
267 return ACL_SCOPE_ERR;
270 return ACL_SCOPE_PARTIAL;
276 case ACL_STYLE_CHILDREN:
277 case ACL_STYLE_SUBTREE:
285 if ( dn.bv_len < patlen &&
286 !DN_SEPARATOR( a->acl_dn_pat.bv_val[patlen - dn.bv_len - 1] ))
288 return ACL_SCOPE_ERR;
291 if ( strcmp( &a->acl_dn_pat.bv_val[patlen - dn.bv_len], dn.bv_val )
294 return ACL_SCOPE_ERR;
300 return ACL_SCOPE_UNKNOWN;
312 char *left, *right, *style, *next;
320 for ( i = 1; i < argc; i++ ) {
321 /* to clause - select which entries are protected */
322 if ( strcasecmp( argv[i], "to" ) == 0 ) {
324 fprintf( stderr, "%s: line %d: "
325 "only one to clause allowed in access line\n",
329 a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
330 for ( ++i; i < argc; i++ ) {
331 if ( strcasecmp( argv[i], "by" ) == 0 ) {
336 if ( strcasecmp( argv[i], "*" ) == 0 ) {
337 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
338 a->acl_dn_style != ACL_STYLE_REGEX )
341 "%s: line %d: dn pattern"
342 " already specified in to clause.\n",
347 ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
351 split( argv[i], '=', &left, &right );
352 split( left, '.', &left, &style );
354 if ( right == NULL ) {
355 fprintf( stderr, "%s: line %d: "
356 "missing \"=\" in \"%s\" in to clause\n",
357 fname, lineno, left );
361 if ( strcasecmp( left, "dn" ) == 0 ) {
362 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
363 a->acl_dn_style != ACL_STYLE_REGEX )
366 "%s: line %d: dn pattern"
367 " already specified in to clause.\n",
372 if ( style == NULL || *style == '\0' ||
373 strcasecmp( style, "baseObject" ) == 0 ||
374 strcasecmp( style, "base" ) == 0 ||
375 strcasecmp( style, "exact" ) == 0 )
377 a->acl_dn_style = ACL_STYLE_BASE;
378 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
380 } else if ( strcasecmp( style, "oneLevel" ) == 0 ||
381 strcasecmp( style, "one" ) == 0 )
383 a->acl_dn_style = ACL_STYLE_ONE;
384 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
386 } else if ( strcasecmp( style, "subtree" ) == 0 ||
387 strcasecmp( style, "sub" ) == 0 )
389 if( *right == '\0' ) {
390 ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
393 a->acl_dn_style = ACL_STYLE_SUBTREE;
394 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
397 } else if ( strcasecmp( style, "children" ) == 0 ) {
398 a->acl_dn_style = ACL_STYLE_CHILDREN;
399 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
401 } else if ( strcasecmp( style, "regex" ) == 0 ) {
402 a->acl_dn_style = ACL_STYLE_REGEX;
404 if ( *right == '\0' ) {
405 /* empty regex should match empty DN */
406 a->acl_dn_style = ACL_STYLE_BASE;
407 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
409 } else if ( strcmp(right, "*") == 0
410 || strcmp(right, ".*") == 0
411 || strcmp(right, ".*$") == 0
412 || strcmp(right, "^.*") == 0
413 || strcmp(right, "^.*$") == 0
414 || strcmp(right, ".*$$") == 0
415 || strcmp(right, "^.*$$") == 0 )
417 ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat );
420 acl_regex_normalized_dn( right, &a->acl_dn_pat );
424 fprintf( stderr, "%s: line %d: "
425 "unknown dn style \"%s\" in to clause\n",
426 fname, lineno, style );
433 if ( strcasecmp( left, "filter" ) == 0 ) {
434 if ( (a->acl_filter = str2filter( right )) == NULL ) {
436 "%s: line %d: bad filter \"%s\" in to clause\n",
437 fname, lineno, right );
441 } else if ( strcasecmp( left, "attr" ) == 0
442 || strcasecmp( left, "attrs" ) == 0 ) {
443 a->acl_attrs = str2anlist( a->acl_attrs,
445 if ( a->acl_attrs == NULL ) {
447 "%s: line %d: unknown attr \"%s\" in to clause\n",
448 fname, lineno, right );
452 } else if ( strncasecmp( left, "val", 3 ) == 0 ) {
453 if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
455 "%s: line %d: attr val already specified in to clause.\n",
459 if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) )
462 "%s: line %d: attr val requires a single attribute.\n",
466 ber_str2bv( right, 0, 1, &a->acl_attrval );
467 if ( style && strcasecmp( style, "regex" ) == 0 ) {
468 int e = regcomp( &a->acl_attrval_re, a->acl_attrval.bv_val,
469 REG_EXTENDED | REG_ICASE | REG_NOSUB );
472 regerror( e, &a->acl_attrval_re, buf, sizeof(buf) );
473 fprintf( stderr, "%s: line %d: "
474 "regular expression \"%s\" bad because of %s\n",
475 fname, lineno, right, buf );
478 a->acl_attrval_style = ACL_STYLE_REGEX;
480 /* FIXME: if the attribute has DN syntax, we might
481 * allow one, subtree and children styles as well */
482 if ( !strcasecmp( style, "exact" ) ) {
483 a->acl_attrval_style = ACL_STYLE_BASE;
485 } else if ( a->acl_attrs[0].an_desc->ad_type->
486 sat_syntax == slap_schema.si_syn_distinguishedName )
488 if ( !strcasecmp( style, "baseObject" ) ||
489 !strcasecmp( style, "base" ) )
491 a->acl_attrval_style = ACL_STYLE_BASE;
492 } else if ( !strcasecmp( style, "onelevel" ) ||
493 !strcasecmp( style, "one" ) )
495 a->acl_attrval_style = ACL_STYLE_ONE;
496 } else if ( !strcasecmp( style, "subtree" ) ||
497 !strcasecmp( style, "sub" ) )
499 a->acl_attrval_style = ACL_STYLE_SUBTREE;
500 } else if ( !strcasecmp( style, "children" ) ) {
501 a->acl_attrval_style = ACL_STYLE_CHILDREN;
504 "%s: line %d: unknown val.<style> \"%s\" "
505 "for attributeType \"%s\" with DN syntax; "
507 fname, lineno, style,
508 a->acl_attrs[0].an_desc->ad_cname.bv_val );
509 a->acl_attrval_style = ACL_STYLE_BASE;
514 "%s: line %d: unknown val.<style> \"%s\" "
515 "for attributeType \"%s\"; using \"exact\"\n",
516 fname, lineno, style,
517 a->acl_attrs[0].an_desc->ad_cname.bv_val );
518 a->acl_attrval_style = ACL_STYLE_BASE;
524 "%s: line %d: expecting <what> got \"%s\"\n",
525 fname, lineno, left );
530 if ( !BER_BVISNULL( &a->acl_dn_pat ) &&
531 ber_bvccmp( &a->acl_dn_pat, '*' ) )
533 free( a->acl_dn_pat.bv_val );
534 BER_BVZERO( &a->acl_dn_pat );
537 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
538 a->acl_dn_style != ACL_STYLE_REGEX )
540 if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
542 rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
543 if ( rc != LDAP_SUCCESS ) {
545 "%s: line %d: bad DN \"%s\" in to DN clause\n",
546 fname, lineno, a->acl_dn_pat.bv_val );
549 free( a->acl_dn_pat.bv_val );
553 int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
554 REG_EXTENDED | REG_ICASE );
557 regerror( e, &a->acl_dn_re, buf, sizeof(buf) );
558 fprintf( stderr, "%s: line %d: "
559 "regular expression \"%s\" bad because of %s\n",
560 fname, lineno, right, buf );
566 /* by clause - select who has what access to entries */
567 } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
569 fprintf( stderr, "%s: line %d: "
570 "to clause required before by clause in access line\n",
576 * by clause consists of <who> and <access>
579 b = (Access *) ch_calloc( 1, sizeof(Access) );
581 ACL_INVALIDATE( b->a_access_mask );
585 "%s: line %d: premature eol: expecting <who>\n",
591 for ( ; i < argc; i++ ) {
592 slap_style_t sty = ACL_STYLE_REGEX;
593 char *style_modifier = NULL;
594 char *style_level = NULL;
598 split( argv[i], '=', &left, &right );
599 split( left, '.', &left, &style );
601 split( style, ',', &style, &style_modifier );
603 if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
604 split( style, '{', &style, &style_level );
605 if ( style_level != NULL ) {
606 char *p = strchr( style_level, '}' );
609 "%s: line %d: premature eol: "
610 "expecting closing '}' in \"level{n}\"\n",
613 } else if ( p == style_level ) {
615 "%s: line %d: empty level "
625 if ( style == NULL || *style == '\0' ||
626 strcasecmp( style, "exact" ) == 0 ||
627 strcasecmp( style, "baseObject" ) == 0 ||
628 strcasecmp( style, "base" ) == 0 )
630 sty = ACL_STYLE_BASE;
632 } else if ( strcasecmp( style, "onelevel" ) == 0 ||
633 strcasecmp( style, "one" ) == 0 )
637 } else if ( strcasecmp( style, "subtree" ) == 0 ||
638 strcasecmp( style, "sub" ) == 0 )
640 sty = ACL_STYLE_SUBTREE;
642 } else if ( strcasecmp( style, "children" ) == 0 ) {
643 sty = ACL_STYLE_CHILDREN;
645 } else if ( strcasecmp( style, "level" ) == 0 )
649 level = strtol( style_level, &next, 10 );
650 if ( next[0] != '\0' ) {
652 "%s: line %d: unable to parse level "
658 sty = ACL_STYLE_LEVEL;
660 } else if ( strcasecmp( style, "regex" ) == 0 ) {
661 sty = ACL_STYLE_REGEX;
663 } else if ( strcasecmp( style, "expand" ) == 0 ) {
664 sty = ACL_STYLE_EXPAND;
666 } else if ( strcasecmp( style, "ip" ) == 0 ) {
669 } else if ( strcasecmp( style, "path" ) == 0 ) {
670 sty = ACL_STYLE_PATH;
671 #ifndef LDAP_PF_LOCAL
672 fprintf( stderr, "%s: line %d: "
673 "path style modifier is useless without local\n",
675 #endif /* LDAP_PF_LOCAL */
679 "%s: line %d: unknown style \"%s\" in by clause\n",
680 fname, lineno, style );
684 if ( style_modifier &&
685 strcasecmp( style_modifier, "expand" ) == 0 )
688 case ACL_STYLE_REGEX:
689 fprintf( stderr, "%s: line %d: "
690 "\"regex\" style implies "
691 "\"expand\" modifier (ignored)\n",
695 case ACL_STYLE_EXPAND:
697 /* FIXME: now it's legal... */
698 fprintf( stderr, "%s: line %d: "
699 "\"expand\" style used "
700 "in conjunction with "
701 "\"expand\" modifier (ignored)\n",
707 /* we'll see later if it's pertinent */
713 /* expand in <who> needs regex in <what> */
714 if ( ( sty == ACL_STYLE_EXPAND || expand )
715 && a->acl_dn_style != ACL_STYLE_REGEX )
717 fprintf( stderr, "%s: line %d: "
718 "\"expand\" style or modifier used "
719 "in conjunction with "
720 "a non-regex <what> clause\n",
724 if ( strcasecmp( argv[i], "*" ) == 0 ) {
725 ber_str2bv( "*", STRLENOF( "*" ), 1, &bv );
726 sty = ACL_STYLE_REGEX;
728 } else if ( strcasecmp( argv[i], "anonymous" ) == 0 ) {
729 ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv);
730 sty = ACL_STYLE_ANONYMOUS;
732 } else if ( strcasecmp( argv[i], "users" ) == 0 ) {
733 ber_str2bv("users", STRLENOF( "users" ), 1, &bv);
734 sty = ACL_STYLE_USERS;
736 } else if ( strcasecmp( argv[i], "self" ) == 0 ) {
737 ber_str2bv("self", STRLENOF( "self" ), 1, &bv);
738 sty = ACL_STYLE_SELF;
740 } else if ( strcasecmp( left, "dn" ) == 0 ) {
741 if ( sty == ACL_STYLE_REGEX ) {
742 b->a_dn_style = ACL_STYLE_REGEX;
743 if ( right == NULL ) {
748 b->a_dn_style = ACL_STYLE_USERS;
750 } else if (*right == '\0' ) {
752 ber_str2bv("anonymous",
753 STRLENOF( "anonymous" ),
755 b->a_dn_style = ACL_STYLE_ANONYMOUS;
757 } else if ( strcmp( right, "*" ) == 0 ) {
759 /* any or users? users for now */
763 b->a_dn_style = ACL_STYLE_USERS;
765 } else if ( strcmp( right, ".+" ) == 0
766 || strcmp( right, "^.+" ) == 0
767 || strcmp( right, ".+$" ) == 0
768 || strcmp( right, "^.+$" ) == 0
769 || strcmp( right, ".+$$" ) == 0
770 || strcmp( right, "^.+$$" ) == 0 )
775 b->a_dn_style = ACL_STYLE_USERS;
777 } else if ( strcmp( right, ".*" ) == 0
778 || strcmp( right, "^.*" ) == 0
779 || strcmp( right, ".*$" ) == 0
780 || strcmp( right, "^.*$" ) == 0
781 || strcmp( right, ".*$$" ) == 0
782 || strcmp( right, "^.*$$" ) == 0 )
789 acl_regex_normalized_dn( right, &bv );
790 if ( !ber_bvccmp( &bv, '*' ) ) {
791 regtest( fname, lineno, bv.bv_val );
795 } else if ( right == NULL || *right == '\0' ) {
796 fprintf( stderr, "%s: line %d: "
797 "missing \"=\" in (or value after) \"%s\" "
799 fname, lineno, left );
803 ber_str2bv( right, 0, 1, &bv );
810 if ( !BER_BVISNULL( &bv ) ) {
811 if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
813 "%s: line %d: dn pattern already specified.\n",
818 if ( sty != ACL_STYLE_REGEX &&
819 sty != ACL_STYLE_ANONYMOUS &&
820 sty != ACL_STYLE_USERS &&
821 sty != ACL_STYLE_SELF &&
824 rc = dnNormalize(0, NULL, NULL,
825 &bv, &b->a_dn_pat, NULL);
826 if ( rc != LDAP_SUCCESS ) {
828 "%s: line %d: bad DN \"%s\" in by DN clause\n",
829 fname, lineno, bv.bv_val );
838 b->a_dn_expand = expand;
839 if ( sty == ACL_STYLE_SELF ) {
840 b->a_dn_self_level = level;
845 "%s: line %d: bad negative level \"%d\" "
847 fname, lineno, level );
849 } else if ( level == 1 ) {
851 "%s: line %d: \"onelevel\" should be used "
852 "instead of \"level{1}\" in by DN clause\n",
854 } else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
856 "%s: line %d: \"base\" should be used "
857 "instead of \"level{0}\" in by DN clause\n",
861 b->a_dn_level = level;
866 if ( strcasecmp( left, "dnattr" ) == 0 ) {
867 if ( right == NULL || right[0] == '\0' ) {
868 fprintf( stderr, "%s: line %d: "
869 "missing \"=\" in (or value after) \"%s\" "
871 fname, lineno, left );
875 if( b->a_dn_at != NULL ) {
877 "%s: line %d: dnattr already specified.\n",
882 rc = slap_str2ad( right, &b->a_dn_at, &text );
884 if( rc != LDAP_SUCCESS ) {
886 "%s: line %d: dnattr \"%s\": %s\n",
887 fname, lineno, right, text );
892 if( !is_at_syntax( b->a_dn_at->ad_type,
894 !is_at_syntax( b->a_dn_at->ad_type,
895 SLAPD_NAMEUID_SYNTAX ))
898 "%s: line %d: dnattr \"%s\": "
899 "inappropriate syntax: %s\n",
900 fname, lineno, right,
901 b->a_dn_at->ad_type->sat_syntax_oid );
905 if( b->a_dn_at->ad_type->sat_equality == NULL ) {
907 "%s: line %d: dnattr \"%s\": "
908 "inappropriate matching (no EQUALITY)\n",
909 fname, lineno, right );
916 if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) {
921 case ACL_STYLE_REGEX:
922 /* legacy, tolerated */
923 fprintf( stderr, "%s: line %d: "
924 "deprecated group style \"regex\"; "
925 "use \"expand\" instead\n",
926 fname, lineno, style );
927 sty = ACL_STYLE_EXPAND;
931 /* legal, traditional */
932 case ACL_STYLE_EXPAND:
933 /* legal, substring expansion; supersedes regex */
938 fprintf( stderr, "%s: line %d: "
939 "inappropriate style \"%s\" in by clause\n",
940 fname, lineno, style );
944 if ( right == NULL || right[0] == '\0' ) {
945 fprintf( stderr, "%s: line %d: "
946 "missing \"=\" in (or value after) \"%s\" "
948 fname, lineno, left );
952 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
954 "%s: line %d: group pattern already specified.\n",
959 /* format of string is
960 "group/objectClassValue/groupAttrName" */
961 if ( ( value = strchr(left, '/') ) != NULL ) {
963 if ( *value && ( name = strchr( value, '/' ) ) != NULL ) {
968 b->a_group_style = sty;
969 if ( sty == ACL_STYLE_EXPAND ) {
970 acl_regex_normalized_dn( right, &bv );
971 if ( !ber_bvccmp( &bv, '*' ) ) {
972 regtest( fname, lineno, bv.bv_val );
977 ber_str2bv( right, 0, 0, &bv );
978 rc = dnNormalize( 0, NULL, NULL, &bv,
979 &b->a_group_pat, NULL );
980 if ( rc != LDAP_SUCCESS ) {
982 "%s: line %d: bad DN \"%s\"\n",
983 fname, lineno, right );
988 if ( value && *value ) {
989 b->a_group_oc = oc_find( value );
992 if ( b->a_group_oc == NULL ) {
994 "%s: line %d: group objectclass "
996 fname, lineno, value );
1001 b->a_group_oc = oc_find(SLAPD_GROUP_CLASS);
1003 if( b->a_group_oc == NULL ) {
1005 "%s: line %d: group default objectclass "
1007 fname, lineno, SLAPD_GROUP_CLASS );
1012 if ( is_object_subclass( slap_schema.si_oc_referral,
1016 "%s: line %d: group objectclass \"%s\" "
1017 "is subclass of referral\n",
1018 fname, lineno, value );
1022 if ( is_object_subclass( slap_schema.si_oc_alias,
1026 "%s: line %d: group objectclass \"%s\" "
1027 "is subclass of alias\n",
1028 fname, lineno, value );
1032 if ( name && *name ) {
1033 rc = slap_str2ad( name, &b->a_group_at, &text );
1035 if( rc != LDAP_SUCCESS ) {
1037 "%s: line %d: group \"%s\": %s\n",
1038 fname, lineno, right, text );
1044 rc = slap_str2ad( SLAPD_GROUP_ATTR, &b->a_group_at, &text );
1046 if ( rc != LDAP_SUCCESS ) {
1048 "%s: line %d: group \"%s\": %s\n",
1049 fname, lineno, SLAPD_GROUP_ATTR, text );
1054 if ( !is_at_syntax( b->a_group_at->ad_type,
1055 SLAPD_DN_SYNTAX ) &&
1056 !is_at_syntax( b->a_group_at->ad_type,
1057 SLAPD_NAMEUID_SYNTAX ) &&
1058 !is_at_subtype( b->a_group_at->ad_type, slap_schema.si_ad_labeledURI->ad_type ) )
1061 "%s: line %d: group \"%s\": inappropriate syntax: %s\n",
1062 fname, lineno, right,
1063 b->a_group_at->ad_type->sat_syntax_oid );
1070 struct berval vals[2];
1072 ber_str2bv( b->a_group_oc->soc_oid, 0, 0, &vals[0] );
1073 BER_BVZERO( &vals[1] );
1075 rc = oc_check_allowed( b->a_group_at->ad_type,
1079 fprintf( stderr, "%s: line %d: "
1080 "group: \"%s\" not allowed by \"%s\"\n",
1082 b->a_group_at->ad_cname.bv_val,
1083 b->a_group_oc->soc_oid );
1090 if ( strcasecmp( left, "peername" ) == 0 ) {
1092 case ACL_STYLE_REGEX:
1093 case ACL_STYLE_BASE:
1094 /* legal, traditional */
1095 case ACL_STYLE_EXPAND:
1096 /* cheap replacement to regex for simple expansion */
1098 case ACL_STYLE_PATH:
1099 /* legal, peername specific */
1103 fprintf( stderr, "%s: line %d: "
1104 "inappropriate style \"%s\" in by clause\n",
1105 fname, lineno, style );
1109 if ( right == NULL || right[0] == '\0' ) {
1110 fprintf( stderr, "%s: line %d: "
1111 "missing \"=\" in (or value after) \"%s\" "
1113 fname, lineno, left );
1117 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
1118 fprintf( stderr, "%s: line %d: "
1119 "peername pattern already specified.\n",
1124 b->a_peername_style = sty;
1125 if ( sty == ACL_STYLE_REGEX ) {
1126 acl_regex_normalized_dn( right, &bv );
1127 if ( !ber_bvccmp( &bv, '*' ) ) {
1128 regtest( fname, lineno, bv.bv_val );
1130 b->a_peername_pat = bv;
1133 ber_str2bv( right, 0, 1, &b->a_peername_pat );
1135 if ( sty == ACL_STYLE_IP ) {
1140 split( right, '{', &addr, &port );
1141 split( addr, '%', &addr, &mask );
1143 b->a_peername_addr = inet_addr( addr );
1144 if ( b->a_peername_addr == (unsigned long)(-1) ) {
1145 /* illegal address */
1146 fprintf( stderr, "%s: line %d: "
1147 "illegal peername address \"%s\".\n",
1148 fname, lineno, addr );
1152 b->a_peername_mask = (unsigned long)(-1);
1153 if ( mask != NULL ) {
1154 b->a_peername_mask = inet_addr( mask );
1155 if ( b->a_peername_mask ==
1156 (unsigned long)(-1) )
1159 fprintf( stderr, "%s: line %d: "
1160 "illegal peername address mask "
1162 fname, lineno, mask );
1167 b->a_peername_port = -1;
1171 b->a_peername_port = strtol( port, &end, 10 );
1172 if ( end[0] != '}' ) {
1174 fprintf( stderr, "%s: line %d: "
1175 "illegal peername port specification "
1177 fname, lineno, port );
1186 if ( strcasecmp( left, "sockname" ) == 0 ) {
1188 case ACL_STYLE_REGEX:
1189 case ACL_STYLE_BASE:
1190 /* legal, traditional */
1191 case ACL_STYLE_EXPAND:
1192 /* cheap replacement to regex for simple expansion */
1197 fprintf( stderr, "%s: line %d: "
1198 "inappropriate style \"%s\" in by clause\n",
1199 fname, lineno, style );
1203 if ( right == NULL || right[0] == '\0' ) {
1204 fprintf( stderr, "%s: line %d: "
1205 "missing \"=\" in (or value after) \"%s\" "
1207 fname, lineno, left );
1211 if ( !BER_BVISNULL( &b->a_sockname_pat ) ) {
1212 fprintf( stderr, "%s: line %d: "
1213 "sockname pattern already specified.\n",
1218 b->a_sockname_style = sty;
1219 if ( sty == ACL_STYLE_REGEX ) {
1220 acl_regex_normalized_dn( right, &bv );
1221 if ( !ber_bvccmp( &bv, '*' ) ) {
1222 regtest( fname, lineno, bv.bv_val );
1224 b->a_sockname_pat = bv;
1227 ber_str2bv( right, 0, 1, &b->a_sockname_pat );
1232 if ( strcasecmp( left, "domain" ) == 0 ) {
1234 case ACL_STYLE_REGEX:
1235 case ACL_STYLE_BASE:
1236 case ACL_STYLE_SUBTREE:
1237 /* legal, traditional */
1240 case ACL_STYLE_EXPAND:
1241 /* tolerated: means exact,expand */
1245 "\"expand\" modifier "
1246 "with \"expand\" style\n",
1249 sty = ACL_STYLE_BASE;
1255 fprintf( stderr, "%s: line %d: "
1256 "inappropriate style \"%s\" in by clause\n",
1257 fname, lineno, style );
1261 if ( right == NULL || right[0] == '\0' ) {
1262 fprintf( stderr, "%s: line %d: "
1263 "missing \"=\" in (or value after) \"%s\" "
1265 fname, lineno, left );
1269 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
1271 "%s: line %d: domain pattern already specified.\n",
1276 b->a_domain_style = sty;
1277 b->a_domain_expand = expand;
1278 if ( sty == ACL_STYLE_REGEX ) {
1279 acl_regex_normalized_dn( right, &bv );
1280 if ( !ber_bvccmp( &bv, '*' ) ) {
1281 regtest( fname, lineno, bv.bv_val );
1283 b->a_domain_pat = bv;
1286 ber_str2bv( right, 0, 1, &b->a_domain_pat );
1291 if ( strcasecmp( left, "sockurl" ) == 0 ) {
1293 case ACL_STYLE_REGEX:
1294 case ACL_STYLE_BASE:
1295 /* legal, traditional */
1296 case ACL_STYLE_EXPAND:
1297 /* cheap replacement to regex for simple expansion */
1302 fprintf( stderr, "%s: line %d: "
1303 "inappropriate style \"%s\" in by clause\n",
1304 fname, lineno, style );
1308 if ( right == NULL || right[0] == '\0' ) {
1309 fprintf( stderr, "%s: line %d: "
1310 "missing \"=\" in (or value after) \"%s\" "
1312 fname, lineno, left );
1316 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
1318 "%s: line %d: sockurl pattern already specified.\n",
1323 b->a_sockurl_style = sty;
1324 if ( sty == ACL_STYLE_REGEX ) {
1325 acl_regex_normalized_dn( right, &bv );
1326 if ( !ber_bvccmp( &bv, '*' ) ) {
1327 regtest( fname, lineno, bv.bv_val );
1329 b->a_sockurl_pat = bv;
1332 ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
1337 if ( strcasecmp( left, "set" ) == 0 ) {
1340 case ACL_STYLE_REGEX:
1341 fprintf( stderr, "%s: line %d: "
1342 "deprecated set style "
1343 "\"regex\" in <by> clause; "
1344 "use \"expand\" instead\n",
1346 sty = ACL_STYLE_EXPAND;
1349 case ACL_STYLE_BASE:
1350 case ACL_STYLE_EXPAND:
1354 fprintf( stderr, "%s: line %d: "
1355 "inappropriate style \"%s\" in by clause\n",
1356 fname, lineno, style );
1360 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
1362 "%s: line %d: set attribute already specified.\n",
1367 if ( right == NULL || *right == '\0' ) {
1369 "%s: line %d: no set is defined\n",
1374 b->a_set_style = sty;
1375 ber_str2bv( right, 0, 1, &b->a_set_pat );
1384 if ( strcasecmp( left, "aci" ) == 0 ) {
1387 } else if ( strncasecmp( left, "dynacl/", STRLENOF( "dynacl/" ) ) == 0 ) {
1388 name = &left[ STRLENOF( "dynacl/" ) ];
1392 if ( slap_dynacl_config( fname, lineno, b, name, sty, right ) ) {
1393 fprintf( stderr, "%s: line %d: "
1394 "unable to configure dynacl \"%s\"\n",
1395 fname, lineno, name );
1402 #else /* ! SLAP_DYNACL */
1404 #ifdef SLAPD_ACI_ENABLED
1405 if ( strcasecmp( left, "aci" ) == 0 ) {
1406 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1407 fprintf( stderr, "%s: line %d: "
1408 "inappropriate style \"%s\" in by clause\n",
1409 fname, lineno, style );
1413 if( b->a_aci_at != NULL ) {
1415 "%s: line %d: aci attribute already specified.\n",
1420 if ( right != NULL && *right != '\0' ) {
1421 rc = slap_str2ad( right, &b->a_aci_at, &text );
1423 if( rc != LDAP_SUCCESS ) {
1425 "%s: line %d: aci \"%s\": %s\n",
1426 fname, lineno, right, text );
1431 b->a_aci_at = slap_schema.si_ad_aci;
1434 if( !is_at_syntax( b->a_aci_at->ad_type,
1437 fprintf( stderr, "%s: line %d: "
1438 "aci \"%s\": inappropriate syntax: %s\n",
1439 fname, lineno, right,
1440 b->a_aci_at->ad_type->sat_syntax_oid );
1446 #endif /* SLAPD_ACI_ENABLED */
1447 #endif /* ! SLAP_DYNACL */
1449 if ( strcasecmp( left, "ssf" ) == 0 ) {
1450 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1451 fprintf( stderr, "%s: line %d: "
1452 "inappropriate style \"%s\" in by clause\n",
1453 fname, lineno, style );
1457 if ( b->a_authz.sai_ssf ) {
1459 "%s: line %d: ssf attribute already specified.\n",
1464 if ( right == NULL || *right == '\0' ) {
1466 "%s: line %d: no ssf is defined\n",
1471 b->a_authz.sai_ssf = strtol( right, &next, 10 );
1472 if ( next == NULL || next[0] != '\0' ) {
1474 "%s: line %d: unable to parse ssf value (%s)\n",
1475 fname, lineno, right );
1479 if ( !b->a_authz.sai_ssf ) {
1481 "%s: line %d: invalid ssf value (%s)\n",
1482 fname, lineno, right );
1488 if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
1489 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1490 fprintf( stderr, "%s: line %d: "
1491 "inappropriate style \"%s\" in by clause\n",
1492 fname, lineno, style );
1496 if ( b->a_authz.sai_transport_ssf ) {
1497 fprintf( stderr, "%s: line %d: "
1498 "transport_ssf attribute already specified.\n",
1503 if ( right == NULL || *right == '\0' ) {
1505 "%s: line %d: no transport_ssf is defined\n",
1510 b->a_authz.sai_transport_ssf = strtol( right, &next, 10 );
1511 if ( next == NULL || next[0] != '\0' ) {
1512 fprintf( stderr, "%s: line %d: "
1513 "unable to parse transport_ssf value (%s)\n",
1514 fname, lineno, right );
1518 if ( !b->a_authz.sai_transport_ssf ) {
1520 "%s: line %d: invalid transport_ssf value (%s)\n",
1521 fname, lineno, right );
1527 if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
1528 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1529 fprintf( stderr, "%s: line %d: "
1530 "inappropriate style \"%s\" in by clause\n",
1531 fname, lineno, style );
1535 if ( b->a_authz.sai_tls_ssf ) {
1536 fprintf( stderr, "%s: line %d: "
1537 "tls_ssf attribute already specified.\n",
1542 if ( right == NULL || *right == '\0' ) {
1544 "%s: line %d: no tls_ssf is defined\n",
1549 b->a_authz.sai_tls_ssf = strtol( right, &next, 10 );
1550 if ( next == NULL || next[0] != '\0' ) {
1551 fprintf( stderr, "%s: line %d: "
1552 "unable to parse tls_ssf value (%s)\n",
1553 fname, lineno, right );
1557 if ( !b->a_authz.sai_tls_ssf ) {
1559 "%s: line %d: invalid tls_ssf value (%s)\n",
1560 fname, lineno, right );
1566 if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
1567 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1568 fprintf( stderr, "%s: line %d: "
1569 "inappropriate style \"%s\" in by clause\n",
1570 fname, lineno, style );
1574 if ( b->a_authz.sai_sasl_ssf ) {
1575 fprintf( stderr, "%s: line %d: "
1576 "sasl_ssf attribute already specified.\n",
1581 if ( right == NULL || *right == '\0' ) {
1583 "%s: line %d: no sasl_ssf is defined\n",
1588 b->a_authz.sai_sasl_ssf = strtol( right, &next, 10 );
1589 if ( next == NULL || next[0] != '\0' ) {
1590 fprintf( stderr, "%s: line %d: "
1591 "unable to parse sasl_ssf value (%s)\n",
1592 fname, lineno, right );
1596 if ( !b->a_authz.sai_sasl_ssf ) {
1598 "%s: line %d: invalid sasl_ssf value (%s)\n",
1599 fname, lineno, right );
1605 if ( right != NULL ) {
1612 if ( i == argc || ( strcasecmp( left, "stop" ) == 0 ) ) {
1613 /* out of arguments or plain stop */
1615 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1616 b->a_type = ACL_STOP;
1618 access_append( &a->acl_access, b );
1622 if ( strcasecmp( left, "continue" ) == 0 ) {
1623 /* plain continue */
1625 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1626 b->a_type = ACL_CONTINUE;
1628 access_append( &a->acl_access, b );
1632 if ( strcasecmp( left, "break" ) == 0 ) {
1633 /* plain continue */
1635 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1636 b->a_type = ACL_BREAK;
1638 access_append( &a->acl_access, b );
1642 if ( strcasecmp( left, "by" ) == 0 ) {
1643 /* we've gone too far */
1645 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1646 b->a_type = ACL_STOP;
1648 access_append( &a->acl_access, b );
1653 if ( strncasecmp( left, "self", 4 ) == 0 ) {
1655 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( &left[4] ) );
1658 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( left ) );
1661 if ( ACL_IS_INVALID( b->a_access_mask ) ) {
1663 "%s: line %d: expecting <access> got \"%s\"\n",
1664 fname, lineno, left );
1668 b->a_type = ACL_STOP;
1670 if ( ++i == argc ) {
1671 /* out of arguments or plain stop */
1672 access_append( &a->acl_access, b );
1676 if ( strcasecmp( argv[i], "continue" ) == 0 ) {
1677 /* plain continue */
1678 b->a_type = ACL_CONTINUE;
1680 } else if ( strcasecmp( argv[i], "break" ) == 0 ) {
1681 /* plain continue */
1682 b->a_type = ACL_BREAK;
1684 } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
1689 access_append( &a->acl_access, b );
1693 "%s: line %d: expecting \"to\" "
1694 "or \"by\" got \"%s\"\n",
1695 fname, lineno, argv[i] );
1700 /* if we have no real access clause, complain and do nothing */
1702 fprintf( stderr, "%s: line %d: "
1703 "warning: no access clause(s) specified in access line\n",
1708 if ( ldap_debug & LDAP_DEBUG_ACL ) {
1713 if ( a->acl_access == NULL ) {
1714 fprintf( stderr, "%s: line %d: "
1715 "warning: no by clause(s) specified in access line\n",
1720 if ( !BER_BVISNULL( &be->be_nsuffix[ 1 ] ) ) {
1721 fprintf( stderr, "%s: line %d: warning: "
1722 "scope checking only applies to single-valued "
1723 "suffix databases\n",
1725 /* go ahead, since checking is not authoritative */
1728 switch ( check_scope( be, a ) ) {
1729 case ACL_SCOPE_UNKNOWN:
1730 fprintf( stderr, "%s: line %d: warning: "
1731 "cannot assess the validity of the ACL scope within "
1732 "backend naming context\n",
1736 case ACL_SCOPE_WARN:
1737 fprintf( stderr, "%s: line %d: warning: "
1738 "ACL could be out of scope within backend naming context\n",
1742 case ACL_SCOPE_PARTIAL:
1743 fprintf( stderr, "%s: line %d: warning: "
1744 "ACL appears to be partially out of scope within "
1745 "backend naming context\n",
1750 fprintf( stderr, "%s: line %d: warning: "
1751 "ACL appears to be out of scope within "
1752 "backend naming context\n",
1759 acl_append( &be->be_acl, a );
1762 acl_append( &frontendDB->be_acl, a );
1768 accessmask2str( slap_mask_t mask, char *buf, int debug )
1773 assert( buf != NULL );
1775 if ( ACL_IS_INVALID( mask ) ) {
1781 if ( ACL_IS_LEVEL( mask ) ) {
1782 if ( ACL_LVL_IS_NONE(mask) ) {
1783 ptr = lutil_strcopy( ptr, "none" );
1785 } else if ( ACL_LVL_IS_DISCLOSE(mask) ) {
1786 ptr = lutil_strcopy( ptr, "disclose" );
1788 } else if ( ACL_LVL_IS_AUTH(mask) ) {
1789 ptr = lutil_strcopy( ptr, "auth" );
1791 } else if ( ACL_LVL_IS_COMPARE(mask) ) {
1792 ptr = lutil_strcopy( ptr, "compare" );
1794 } else if ( ACL_LVL_IS_SEARCH(mask) ) {
1795 ptr = lutil_strcopy( ptr, "search" );
1797 } else if ( ACL_LVL_IS_READ(mask) ) {
1798 ptr = lutil_strcopy( ptr, "read" );
1800 } else if ( ACL_LVL_IS_WRITE(mask) ) {
1801 ptr = lutil_strcopy( ptr, "write" );
1803 } else if ( ACL_LVL_IS_MANAGE(mask) ) {
1804 ptr = lutil_strcopy( ptr, "manage" );
1807 ptr = lutil_strcopy( ptr, "unknown" );
1817 if( ACL_IS_ADDITIVE( mask ) ) {
1820 } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
1827 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_MANAGE) ) {
1832 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
1837 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
1842 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
1847 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
1852 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
1857 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_DISCLOSE) ) {
1862 if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
1871 if ( ACL_IS_LEVEL( mask ) ) {
1881 str2accessmask( const char *str )
1885 if( !ASCII_ALPHA(str[0]) ) {
1888 if ( str[0] == '=' ) {
1891 } else if( str[0] == '+' ) {
1892 ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
1894 } else if( str[0] == '-' ) {
1895 ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
1898 ACL_INVALIDATE(mask);
1902 for( i=1; str[i] != '\0'; i++ ) {
1903 if( TOLOWER((unsigned char) str[i]) == 'm' ) {
1904 ACL_PRIV_SET(mask, ACL_PRIV_MANAGE);
1906 } else if( TOLOWER((unsigned char) str[i]) == 'w' ) {
1907 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
1909 } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
1910 ACL_PRIV_SET(mask, ACL_PRIV_READ);
1912 } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
1913 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
1915 } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
1916 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
1918 } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
1919 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
1921 } else if( TOLOWER((unsigned char) str[i]) == 'd' ) {
1922 ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
1924 } else if( str[i] != '0' ) {
1925 ACL_INVALIDATE(mask);
1933 if ( strcasecmp( str, "none" ) == 0 ) {
1934 ACL_LVL_ASSIGN_NONE(mask);
1936 } else if ( strcasecmp( str, "disclose" ) == 0 ) {
1937 ACL_LVL_ASSIGN_DISCLOSE(mask);
1939 } else if ( strcasecmp( str, "auth" ) == 0 ) {
1940 ACL_LVL_ASSIGN_AUTH(mask);
1942 } else if ( strcasecmp( str, "compare" ) == 0 ) {
1943 ACL_LVL_ASSIGN_COMPARE(mask);
1945 } else if ( strcasecmp( str, "search" ) == 0 ) {
1946 ACL_LVL_ASSIGN_SEARCH(mask);
1948 } else if ( strcasecmp( str, "read" ) == 0 ) {
1949 ACL_LVL_ASSIGN_READ(mask);
1951 } else if ( strcasecmp( str, "write" ) == 0 ) {
1952 ACL_LVL_ASSIGN_WRITE(mask);
1954 } else if ( strcasecmp( str, "manage" ) == 0 ) {
1955 ACL_LVL_ASSIGN_MANAGE(mask);
1958 ACL_INVALIDATE( mask );
1967 fprintf( stderr, "%s%s%s\n",
1968 "<access clause> ::= access to <what> "
1969 "[ by <who> <access> [ <control> ] ]+ \n"
1970 "<what> ::= * | [dn[.<dnstyle>]=<DN>] [filter=<filter>] [attrs=<attrlist>]\n"
1971 "<attrlist> ::= <attr> [val[.<attrstyle>]=<value>] | <attr> , <attrlist>\n"
1972 "<attr> ::= <attrname> | entry | children\n",
1973 "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
1974 "\t[dnattr=<attrname>]\n"
1975 "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
1976 "\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n"
1977 "\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n"
1978 #ifdef SLAPD_ACI_ENABLED
1979 "\t[aci=<attrname>]\n"
1981 "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n",
1982 "<style> ::= exact | regex | base(Object)\n"
1983 "<dnstyle> ::= base(Object) | one(level) | sub(tree) | children | "
1985 "<attrstyle> ::= exact | regex | base(Object) | one(level) | "
1986 "sub(tree) | children\n"
1987 "<peernamestyle> ::= exact | regex | ip | path\n"
1988 "<domainstyle> ::= exact | regex | base(Object) | sub(tree)\n"
1989 "<access> ::= [self]{<level>|<priv>}\n"
1990 "<level> ::= none|disclose|auth|compare|search|read|write|manage\n"
1991 "<priv> ::= {=|+|-}{0|d|x|c|s|r|w|m}+\n"
1992 "<control> ::= [ stop | continue | break ]\n"
1994 exit( EXIT_FAILURE );
1998 * Set pattern to a "normalized" DN from src.
1999 * At present it simply eats the (optional) space after
2000 * a RDN separator (,)
2001 * Eventually will evolve in a more complete normalization
2004 acl_regex_normalized_dn(
2006 struct berval *pattern )
2011 str = ch_strdup( src );
2012 len = strlen( src );
2014 for ( p = str; p && p[0]; p++ ) {
2016 if ( p[0] == '\\' && p[1] ) {
2018 * if escaping a hex pair we should
2019 * increment p twice; however, in that
2020 * case the second hex number does
2026 if ( p[0] == ',' && p[1] == ' ' ) {
2030 * too much space should be an error if we are pedantic
2032 for ( q = &p[2]; q[0] == ' '; q++ ) {
2035 AC_MEMCPY( p+1, q, len-(q-str)+1);
2038 pattern->bv_val = str;
2039 pattern->bv_len = p - str;
2052 if ( (*right = strchr( line, splitchar )) != NULL ) {
2053 *((*right)++) = '\0';
2058 access_append( Access **l, Access *a )
2060 for ( ; *l != NULL; l = &(*l)->a_next ) {
2068 acl_append( AccessControl **l, AccessControl *a )
2070 for ( ; *l != NULL; l = &(*l)->acl_next ) {
2078 access_free( Access *a )
2080 if ( !BER_BVISNULL( &a->a_dn_pat ) ) {
2081 free( a->a_dn_pat.bv_val );
2083 if ( !BER_BVISNULL( &a->a_peername_pat ) ) {
2084 free( a->a_peername_pat.bv_val );
2086 if ( !BER_BVISNULL( &a->a_sockname_pat ) ) {
2087 free( a->a_sockname_pat.bv_val );
2089 if ( !BER_BVISNULL( &a->a_domain_pat ) ) {
2090 free( a->a_domain_pat.bv_val );
2092 if ( !BER_BVISNULL( &a->a_sockurl_pat ) ) {
2093 free( a->a_sockurl_pat.bv_val );
2095 if ( !BER_BVISNULL( &a->a_set_pat ) ) {
2096 free( a->a_set_pat.bv_val );
2098 if ( !BER_BVISNULL( &a->a_group_pat ) ) {
2099 free( a->a_group_pat.bv_val );
2105 acl_free( AccessControl *a )
2110 if ( a->acl_filter ) {
2111 filter_free( a->acl_filter );
2113 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
2114 free ( a->acl_dn_pat.bv_val );
2116 if ( a->acl_attrs ) {
2117 for ( an = a->acl_attrs; !BER_BVISNULL( &an->an_name ); an++ ) {
2118 free( an->an_name.bv_val );
2120 free( a->acl_attrs );
2122 for ( ; a->acl_access; a->acl_access = n ) {
2123 n = a->acl_access->a_next;
2124 access_free( a->acl_access );
2129 /* Because backend_startup uses acl_append to tack on the global_acl to
2130 * the end of each backend's acl, we cannot just take one argument and
2131 * merrily free our way to the end of the list. backend_destroy calls us
2132 * with the be_acl in arg1, and global_acl in arg2 to give us a stopping
2133 * point. config_destroy calls us with global_acl in arg1 and NULL in
2134 * arg2, so we then proceed to polish off the global_acl.
2137 acl_destroy( AccessControl *a, AccessControl *end )
2141 for (; a && a!= end; a=n) {
2148 access2str( slap_access_t access )
2150 if ( access == ACL_NONE ) {
2153 } else if ( access == ACL_DISCLOSE ) {
2156 } else if ( access == ACL_AUTH ) {
2159 } else if ( access == ACL_COMPARE ) {
2162 } else if ( access == ACL_SEARCH ) {
2165 } else if ( access == ACL_READ ) {
2168 } else if ( access == ACL_WRITE ) {
2171 } else if ( access == ACL_MANAGE ) {
2180 str2access( const char *str )
2182 if ( strcasecmp( str, "none" ) == 0 ) {
2185 } else if ( strcasecmp( str, "disclose" ) == 0 ) {
2186 return ACL_DISCLOSE;
2188 } else if ( strcasecmp( str, "auth" ) == 0 ) {
2191 } else if ( strcasecmp( str, "compare" ) == 0 ) {
2194 } else if ( strcasecmp( str, "search" ) == 0 ) {
2197 } else if ( strcasecmp( str, "read" ) == 0 ) {
2200 } else if ( strcasecmp( str, "write" ) == 0 ) {
2203 } else if ( strcasecmp( str, "manage" ) == 0 ) {
2207 return( ACL_INVALID_ACCESS );
2210 #define ACLBUF_MAXLEN 8192
2212 static char aclbuf[ACLBUF_MAXLEN];
2215 access2text( Access *b, char *ptr )
2217 char maskbuf[ACCESSMASK_MAXLEN];
2219 ptr = lutil_strcopy( ptr, "\tby" );
2221 if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
2223 if ( ber_bvccmp( &b->a_dn_pat, '*' ) ||
2224 b->a_dn_style == ACL_STYLE_ANONYMOUS ||
2225 b->a_dn_style == ACL_STYLE_USERS ||
2226 b->a_dn_style == ACL_STYLE_SELF )
2228 ptr = lutil_strcopy( ptr, b->a_dn_pat.bv_val );
2229 if ( b->a_dn_style == ACL_STYLE_SELF && b->a_dn_self_level != 0 ) {
2230 int n = sprintf( ptr, ".level{%d}", b->a_dn_self_level );
2237 ptr = lutil_strcopy( ptr, "dn." );
2238 ptr = lutil_strcopy( ptr, style_strings[b->a_dn_style] );
2239 if ( b->a_dn_style == ACL_STYLE_LEVEL ) {
2240 int n = sprintf( ptr, "{%d}", b->a_dn_level );
2245 if ( b->a_dn_expand ) {
2246 ptr = lutil_strcopy( ptr, ",expand" );
2250 ptr = lutil_strcopy( ptr, b->a_dn_pat.bv_val );
2255 if ( b->a_dn_at != NULL ) {
2256 ptr = lutil_strcopy( ptr, " dnattr=" );
2257 ptr = lutil_strcopy( ptr, b->a_dn_at->ad_cname.bv_val );
2260 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
2261 ptr = lutil_strcopy( ptr, " group/" );
2262 ptr = lutil_strcopy( ptr, b->a_group_oc ?
2263 b->a_group_oc->soc_cname.bv_val : "groupOfNames" );
2265 ptr = lutil_strcopy( ptr, b->a_group_at ?
2266 b->a_group_at->ad_cname.bv_val : "member" );
2268 ptr = lutil_strcopy( ptr, style_strings[b->a_group_style] );
2271 ptr = lutil_strcopy( ptr, b->a_group_pat.bv_val );
2275 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
2276 ptr = lutil_strcopy( ptr, " peername=\"" );
2277 ptr = lutil_strcopy( ptr, b->a_peername_pat.bv_val );
2281 if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
2282 ptr = lutil_strcopy( ptr, " sockname=\"" );
2283 ptr = lutil_strcopy( ptr, b->a_sockname_pat.bv_val );
2287 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
2288 ptr = lutil_strcopy( ptr, " domain=" );
2289 ptr = lutil_strcopy( ptr, b->a_domain_pat.bv_val );
2292 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
2293 ptr = lutil_strcopy( ptr, " sockurl=\"" );
2294 ptr = lutil_strcopy( ptr, b->a_sockurl_pat.bv_val );
2298 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
2299 ptr = lutil_strcopy( ptr, " set=\"" );
2300 ptr = lutil_strcopy( ptr, b->a_set_pat.bv_val );
2305 if ( b->a_dynacl ) {
2308 for ( da = b->a_dynacl; da; da = da->da_next ) {
2309 if ( da->da_unparse ) {
2311 (void)( *da->da_unparse )( da->da_private, &bv );
2312 ptr = lutil_strcopy( ptr, bv.bv_val );
2313 ch_free( bv.bv_val );
2317 #else /* ! SLAP_DYNACL */
2318 #ifdef SLAPD_ACI_ENABLED
2319 if ( b->a_aci_at != NULL ) {
2320 ptr = lutil_strcopy( ptr, " aci=" );
2321 ptr = lutil_strcopy( ptr, b->a_aci_at->ad_cname.bv_val );
2324 #endif /* SLAP_DYNACL */
2326 /* Security Strength Factors */
2327 if ( b->a_authz.sai_ssf ) {
2328 ptr += sprintf( ptr, " ssf=%u",
2329 b->a_authz.sai_ssf );
2331 if ( b->a_authz.sai_transport_ssf ) {
2332 ptr += sprintf( ptr, " transport_ssf=%u",
2333 b->a_authz.sai_transport_ssf );
2335 if ( b->a_authz.sai_tls_ssf ) {
2336 ptr += sprintf( ptr, " tls_ssf=%u",
2337 b->a_authz.sai_tls_ssf );
2339 if ( b->a_authz.sai_sasl_ssf ) {
2340 ptr += sprintf( ptr, " sasl_ssf=%u",
2341 b->a_authz.sai_sasl_ssf );
2345 if ( b->a_dn_self ) ptr = lutil_strcopy( ptr, "self" );
2346 ptr = lutil_strcopy( ptr, accessmask2str( b->a_access_mask, maskbuf, 0 ));
2347 if ( !maskbuf[0] ) ptr--;
2349 if( b->a_type == ACL_BREAK ) {
2350 ptr = lutil_strcopy( ptr, " break" );
2352 } else if( b->a_type == ACL_CONTINUE ) {
2353 ptr = lutil_strcopy( ptr, " continue" );
2355 } else if( b->a_type != ACL_STOP ) {
2356 ptr = lutil_strcopy( ptr, " unknown-control" );
2358 if ( !maskbuf[0] ) ptr = lutil_strcopy( ptr, " stop" );
2366 acl_unparse( AccessControl *a, struct berval *bv )
2373 bv->bv_val = aclbuf;
2378 ptr = lutil_strcopy( ptr, "to" );
2379 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
2381 ptr = lutil_strcopy( ptr, " dn." );
2382 ptr = lutil_strcopy( ptr, style_strings[a->acl_dn_style] );
2385 ptr = lutil_strcopy( ptr, a->acl_dn_pat.bv_val );
2386 ptr = lutil_strcopy( ptr, "\"\n" );
2389 if ( a->acl_filter != NULL ) {
2390 struct berval bv = BER_BVNULL;
2393 filter2bv( a->acl_filter, &bv );
2394 ptr = lutil_strcopy( ptr, " filter=\"" );
2395 ptr = lutil_strcopy( ptr, bv.bv_val );
2398 ch_free( bv.bv_val );
2401 if ( a->acl_attrs != NULL ) {
2406 ptr = lutil_strcopy( ptr, " attrs=" );
2407 for ( an = a->acl_attrs; an && !BER_BVISNULL( &an->an_name ); an++ ) {
2408 if ( ! first ) *ptr++ = ',';
2410 *ptr++ = an->an_oc_exclude ? '!' : '@';
2411 ptr = lutil_strcopy( ptr, an->an_oc->soc_cname.bv_val );
2414 ptr = lutil_strcopy( ptr, an->an_name.bv_val );
2421 if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
2423 ptr = lutil_strcopy( ptr, " val." );
2424 ptr = lutil_strcopy( ptr, style_strings[a->acl_attrval_style] );
2427 ptr = lutil_strcopy( ptr, a->acl_attrval.bv_val );
2433 ptr = lutil_strcopy( ptr, " *\n" );
2436 for ( b = a->acl_access; b != NULL; b = b->a_next ) {
2437 ptr = access2text( b, ptr );
2440 bv->bv_len = ptr - bv->bv_val;
2446 print_acl( Backend *be, AccessControl *a )
2452 acl_unparse( a, &bv );
2453 fprintf( stderr, "%s ACL: access %s\n",
2454 be == NULL ? "Global" : "Backend", bv.bv_val );
2456 #endif /* LDAP_DEBUG */