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 const char style_base[] = "base";
42 char *style_strings[] = {
59 static void split(char *line, int splitchar, char **left, char **right);
60 static void access_append(Access **l, Access *a);
61 static void acl_usage(void) LDAP_GCCATTR((noreturn));
63 static void acl_regex_normalized_dn(const char *src, struct berval *pat);
66 static void print_acl(Backend *be, AccessControl *a);
69 static int check_scope( BackendDB *be, AccessControl *a );
82 slap_dynacl_t *da, *tmp;
85 for ( da = b->a_dynacl; da; da = da->da_next ) {
86 if ( strcasecmp( da->da_name, name ) == 0 ) {
88 "%s: line %d: dynacl \"%s\" already specified.\n",
89 fname, lineno, name );
94 da = slap_dynacl_get( name );
99 tmp = ch_malloc( sizeof( slap_dynacl_t ) );
102 if ( tmp->da_parse ) {
103 rc = ( *tmp->da_parse )( fname, lineno, opts, sty, right, &tmp->da_private );
110 tmp->da_next = b->a_dynacl;
115 #endif /* SLAP_DYNACL */
118 regtest(const char *fname, int lineno, char *pat) {
134 for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
136 if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
153 if ( size >= (sizeof(buf) - 1) ) {
155 "%s: line %d: regular expression \"%s\" too large\n",
156 fname, lineno, pat );
160 if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
162 regerror(e, &re, error, sizeof(error));
164 "%s: line %d: regular expression \"%s\" bad because of %s\n",
165 fname, lineno, pat, error );
174 * Check if the pattern of an ACL, if any, matches the scope
175 * of the backend it is defined within.
177 #define ACL_SCOPE_UNKNOWN (-2)
178 #define ACL_SCOPE_ERR (-1)
179 #define ACL_SCOPE_OK (0)
180 #define ACL_SCOPE_PARTIAL (1)
181 #define ACL_SCOPE_WARN (2)
184 check_scope( BackendDB *be, AccessControl *a )
189 dn = be->be_nsuffix[0];
191 if ( BER_BVISEMPTY( &dn ) ) {
195 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
196 a->acl_dn_style != ACL_STYLE_REGEX )
198 slap_style_t style = a->acl_dn_style;
200 if ( style == ACL_STYLE_REGEX ) {
201 char dnbuf[SLAP_LDAPDN_MAXLEN + 2];
202 char rebuf[SLAP_LDAPDN_MAXLEN + 1];
207 /* add trailing '$' to database suffix to form
208 * a simple trial regex pattern "<suffix>$" */
209 AC_MEMCPY( dnbuf, be->be_nsuffix[0].bv_val,
210 be->be_nsuffix[0].bv_len );
211 dnbuf[be->be_nsuffix[0].bv_len] = '$';
212 dnbuf[be->be_nsuffix[0].bv_len + 1] = '\0';
214 if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) {
215 return ACL_SCOPE_WARN;
218 /* remove trailing ')$', if any, from original
220 rebuflen = a->acl_dn_pat.bv_len;
221 AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val, rebuflen + 1 );
222 if ( rebuf[rebuflen - 1] == '$' ) {
223 rebuf[--rebuflen] = '\0';
225 while ( rebuflen > be->be_nsuffix[0].bv_len && rebuf[rebuflen - 1] == ')' ) {
226 rebuf[--rebuflen] = '\0';
228 if ( rebuflen == be->be_nsuffix[0].bv_len ) {
233 /* not a clear indication of scoping error, though */
234 rc = regexec( &re, rebuf, 0, NULL, 0 )
235 ? ACL_SCOPE_WARN : ACL_SCOPE_OK;
242 patlen = a->acl_dn_pat.bv_len;
243 /* If backend suffix is longer than pattern,
244 * it is a potential mismatch (in the sense
245 * that a superior naming context could
247 if ( dn.bv_len > patlen ) {
248 /* base is blatantly wrong */
249 if ( style == ACL_STYLE_BASE ) return ACL_SCOPE_ERR;
251 /* a style of one can be wrong if there is
252 * more than one level between the suffix
254 if ( style == ACL_STYLE_ONE ) {
255 int rdnlen = -1, sep = 0;
258 if ( !DN_SEPARATOR( dn.bv_val[dn.bv_len - patlen - 1] )) {
259 return ACL_SCOPE_ERR;
264 rdnlen = dn_rdnlen( NULL, &dn );
265 if ( rdnlen != dn.bv_len - patlen - sep )
266 return ACL_SCOPE_ERR;
269 /* if the trailing part doesn't match,
270 * then it's an error */
271 if ( strcmp( a->acl_dn_pat.bv_val,
272 &dn.bv_val[dn.bv_len - patlen] ) != 0 )
274 return ACL_SCOPE_ERR;
277 return ACL_SCOPE_PARTIAL;
283 case ACL_STYLE_CHILDREN:
284 case ACL_STYLE_SUBTREE:
292 if ( dn.bv_len < patlen &&
293 !DN_SEPARATOR( a->acl_dn_pat.bv_val[patlen - dn.bv_len - 1] ))
295 return ACL_SCOPE_ERR;
298 if ( strcmp( &a->acl_dn_pat.bv_val[patlen - dn.bv_len], dn.bv_val )
301 return ACL_SCOPE_ERR;
307 return ACL_SCOPE_UNKNOWN;
320 char *left, *right, *style, *next;
328 for ( i = 1; i < argc; i++ ) {
329 /* to clause - select which entries are protected */
330 if ( strcasecmp( argv[i], "to" ) == 0 ) {
332 fprintf( stderr, "%s: line %d: "
333 "only one to clause allowed in access line\n",
337 a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
338 for ( ++i; i < argc; i++ ) {
339 if ( strcasecmp( argv[i], "by" ) == 0 ) {
344 if ( strcasecmp( argv[i], "*" ) == 0 ) {
345 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
346 a->acl_dn_style != ACL_STYLE_REGEX )
349 "%s: line %d: dn pattern"
350 " already specified in to clause.\n",
355 ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
359 split( argv[i], '=', &left, &right );
360 split( left, '.', &left, &style );
362 if ( right == NULL ) {
363 fprintf( stderr, "%s: line %d: "
364 "missing \"=\" in \"%s\" in to clause\n",
365 fname, lineno, left );
369 if ( strcasecmp( left, "dn" ) == 0 ) {
370 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
371 a->acl_dn_style != ACL_STYLE_REGEX )
374 "%s: line %d: dn pattern"
375 " already specified in to clause.\n",
380 if ( style == NULL || *style == '\0' ||
381 strcasecmp( style, "baseObject" ) == 0 ||
382 strcasecmp( style, "base" ) == 0 ||
383 strcasecmp( style, "exact" ) == 0 )
385 a->acl_dn_style = ACL_STYLE_BASE;
386 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
388 } else if ( strcasecmp( style, "oneLevel" ) == 0 ||
389 strcasecmp( style, "one" ) == 0 )
391 a->acl_dn_style = ACL_STYLE_ONE;
392 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
394 } else if ( strcasecmp( style, "subtree" ) == 0 ||
395 strcasecmp( style, "sub" ) == 0 )
397 if( *right == '\0' ) {
398 ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
401 a->acl_dn_style = ACL_STYLE_SUBTREE;
402 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
405 } else if ( strcasecmp( style, "children" ) == 0 ) {
406 a->acl_dn_style = ACL_STYLE_CHILDREN;
407 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
409 } else if ( strcasecmp( style, "regex" ) == 0 ) {
410 a->acl_dn_style = ACL_STYLE_REGEX;
412 if ( *right == '\0' ) {
413 /* empty regex should match empty DN */
414 a->acl_dn_style = ACL_STYLE_BASE;
415 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
417 } else if ( strcmp(right, "*") == 0
418 || strcmp(right, ".*") == 0
419 || strcmp(right, ".*$") == 0
420 || strcmp(right, "^.*") == 0
421 || strcmp(right, "^.*$") == 0
422 || strcmp(right, ".*$$") == 0
423 || strcmp(right, "^.*$$") == 0 )
425 ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat );
428 acl_regex_normalized_dn( right, &a->acl_dn_pat );
432 fprintf( stderr, "%s: line %d: "
433 "unknown dn style \"%s\" in to clause\n",
434 fname, lineno, style );
441 if ( strcasecmp( left, "filter" ) == 0 ) {
442 if ( (a->acl_filter = str2filter( right )) == NULL ) {
444 "%s: line %d: bad filter \"%s\" in to clause\n",
445 fname, lineno, right );
449 } else if ( strcasecmp( left, "attr" ) == 0 /* TOLERATED */
450 || strcasecmp( left, "attrs" ) == 0 ) /* DOCUMENTED */
452 a->acl_attrs = str2anlist( a->acl_attrs,
454 if ( a->acl_attrs == NULL ) {
456 "%s: line %d: unknown attr \"%s\" in to clause\n",
457 fname, lineno, right );
461 } else if ( strncasecmp( left, "val", 3 ) == 0 ) {
464 if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
466 "%s: line %d: attr val already specified in to clause.\n",
470 if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) )
473 "%s: line %d: attr val requires a single attribute.\n",
478 ber_str2bv( right, 0, 1, &a->acl_attrval );
479 a->acl_attrval_style = ACL_STYLE_BASE;
481 mr = strchr( left, '/' );
486 a->acl_attrval_mr = mr_find( mr );
487 if ( a->acl_attrval_mr == NULL ) {
488 fprintf( stderr, "%s: line %d: "
489 "invalid matching rule \"%s\".\n",
494 if( !mr_usable_with_at( a->acl_attrval_mr, a->acl_attrs[ 0 ].an_desc->ad_type ) )
496 fprintf( stderr, "%s: line %d: "
497 "matching rule \"%s\" use "
498 "with attr \"%s\" not appropriate.\n",
500 a->acl_attrs[ 0 ].an_name.bv_val );
505 if ( style != NULL ) {
506 if ( strcasecmp( style, "regex" ) == 0 ) {
507 int e = regcomp( &a->acl_attrval_re, a->acl_attrval.bv_val,
508 REG_EXTENDED | REG_ICASE | REG_NOSUB );
511 regerror( e, &a->acl_attrval_re, buf, sizeof(buf) );
512 fprintf( stderr, "%s: line %d: "
513 "regular expression \"%s\" bad because of %s\n",
514 fname, lineno, right, buf );
517 a->acl_attrval_style = ACL_STYLE_REGEX;
520 /* FIXME: if the attribute has DN syntax, we might
521 * allow one, subtree and children styles as well */
522 if ( !strcasecmp( style, "base" ) ||
523 !strcasecmp( style, "exact" ) ) {
524 a->acl_attrval_style = ACL_STYLE_BASE;
526 } else if ( a->acl_attrs[0].an_desc->ad_type->
527 sat_syntax == slap_schema.si_syn_distinguishedName )
531 if ( !strcasecmp( style, "baseObject" ) ||
532 !strcasecmp( style, "base" ) )
534 a->acl_attrval_style = ACL_STYLE_BASE;
535 } else if ( !strcasecmp( style, "onelevel" ) ||
536 !strcasecmp( style, "one" ) )
538 a->acl_attrval_style = ACL_STYLE_ONE;
539 } else if ( !strcasecmp( style, "subtree" ) ||
540 !strcasecmp( style, "sub" ) )
542 a->acl_attrval_style = ACL_STYLE_SUBTREE;
543 } else if ( !strcasecmp( style, "children" ) ) {
544 a->acl_attrval_style = ACL_STYLE_CHILDREN;
547 "%s: line %d: unknown val.<style> \"%s\" "
548 "for attributeType \"%s\" with DN syntax; "
550 fname, lineno, style,
551 a->acl_attrs[0].an_desc->ad_cname.bv_val );
552 a->acl_attrval_style = ACL_STYLE_BASE;
556 rc = dnNormalize( 0, NULL, NULL, &bv, &a->acl_attrval, NULL );
557 if ( rc != LDAP_SUCCESS ) {
559 "%s: line %d: unable to normalize DN \"%s\" "
560 "for attributeType \"%s\" (%d).\n",
561 fname, lineno, bv.bv_val,
562 a->acl_attrs[0].an_desc->ad_cname.bv_val, rc );
565 ber_memfree( bv.bv_val );
569 "%s: line %d: unknown val.<style> \"%s\" "
570 "for attributeType \"%s\"; using \"exact\"\n",
571 fname, lineno, style,
572 a->acl_attrs[0].an_desc->ad_cname.bv_val );
573 a->acl_attrval_style = ACL_STYLE_BASE;
578 /* Check for appropriate matching rule */
579 if ( a->acl_attrval_style != ACL_STYLE_REGEX ) {
580 if ( a->acl_attrval_mr == NULL ) {
581 a->acl_attrval_mr = a->acl_attrs[ 0 ].an_desc->ad_type->sat_equality;
584 if ( a->acl_attrval_mr == NULL ) {
585 fprintf( stderr, "%s: line %d: "
586 "attr \"%s\" must have an EQUALITY matching rule.\n",
587 fname, lineno, a->acl_attrs[ 0 ].an_name.bv_val );
594 "%s: line %d: expecting <what> got \"%s\"\n",
595 fname, lineno, left );
600 if ( !BER_BVISNULL( &a->acl_dn_pat ) &&
601 ber_bvccmp( &a->acl_dn_pat, '*' ) )
603 free( a->acl_dn_pat.bv_val );
604 BER_BVZERO( &a->acl_dn_pat );
605 a->acl_dn_style = ACL_STYLE_REGEX;
608 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
609 a->acl_dn_style != ACL_STYLE_REGEX )
611 if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
613 rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
614 if ( rc != LDAP_SUCCESS ) {
616 "%s: line %d: bad DN \"%s\" in to DN clause\n",
617 fname, lineno, a->acl_dn_pat.bv_val );
620 free( a->acl_dn_pat.bv_val );
624 int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
625 REG_EXTENDED | REG_ICASE );
628 regerror( e, &a->acl_dn_re, buf, sizeof(buf) );
629 fprintf( stderr, "%s: line %d: "
630 "regular expression \"%s\" bad because of %s\n",
631 fname, lineno, right, buf );
637 /* by clause - select who has what access to entries */
638 } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
640 fprintf( stderr, "%s: line %d: "
641 "to clause required before by clause in access line\n",
647 * by clause consists of <who> and <access>
650 b = (Access *) ch_calloc( 1, sizeof(Access) );
652 ACL_INVALIDATE( b->a_access_mask );
656 "%s: line %d: premature eol: expecting <who>\n",
662 for ( ; i < argc; i++ ) {
663 slap_style_t sty = ACL_STYLE_REGEX;
664 char *style_modifier = NULL;
665 char *style_level = NULL;
668 slap_dn_access *bdn = &b->a_dn;
671 split( argv[i], '=', &left, &right );
672 split( left, '.', &left, &style );
674 split( style, ',', &style, &style_modifier );
676 if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
677 split( style, '{', &style, &style_level );
678 if ( style_level != NULL ) {
679 char *p = strchr( style_level, '}' );
682 "%s: line %d: premature eol: "
683 "expecting closing '}' in \"level{n}\"\n",
686 } else if ( p == style_level ) {
688 "%s: line %d: empty level "
698 if ( style == NULL || *style == '\0' ||
699 strcasecmp( style, "exact" ) == 0 ||
700 strcasecmp( style, "baseObject" ) == 0 ||
701 strcasecmp( style, "base" ) == 0 )
703 sty = ACL_STYLE_BASE;
705 } else if ( strcasecmp( style, "onelevel" ) == 0 ||
706 strcasecmp( style, "one" ) == 0 )
710 } else if ( strcasecmp( style, "subtree" ) == 0 ||
711 strcasecmp( style, "sub" ) == 0 )
713 sty = ACL_STYLE_SUBTREE;
715 } else if ( strcasecmp( style, "children" ) == 0 ) {
716 sty = ACL_STYLE_CHILDREN;
718 } else if ( strcasecmp( style, "level" ) == 0 )
722 level = strtol( style_level, &next, 10 );
723 if ( next[0] != '\0' ) {
725 "%s: line %d: unable to parse level "
731 sty = ACL_STYLE_LEVEL;
733 } else if ( strcasecmp( style, "regex" ) == 0 ) {
734 sty = ACL_STYLE_REGEX;
736 } else if ( strcasecmp( style, "expand" ) == 0 ) {
737 sty = ACL_STYLE_EXPAND;
739 } else if ( strcasecmp( style, "ip" ) == 0 ) {
742 } else if ( strcasecmp( style, "path" ) == 0 ) {
743 sty = ACL_STYLE_PATH;
744 #ifndef LDAP_PF_LOCAL
745 fprintf( stderr, "%s: line %d: "
746 "path style modifier is useless without local\n",
748 #endif /* LDAP_PF_LOCAL */
752 "%s: line %d: unknown style \"%s\" in by clause\n",
753 fname, lineno, style );
757 if ( style_modifier &&
758 strcasecmp( style_modifier, "expand" ) == 0 )
761 case ACL_STYLE_REGEX:
762 fprintf( stderr, "%s: line %d: "
763 "\"regex\" style implies "
764 "\"expand\" modifier"
765 SLAPD_CONF_UNKNOWN_IGNORED ".\n",
767 #ifdef SLAPD_CONF_UNKNOWN_BAILOUT
769 #endif /* SLAPD_CONF_UNKNOWN_BAILOUT */
772 case ACL_STYLE_EXPAND:
774 /* FIXME: now it's legal... */
775 fprintf( stderr, "%s: line %d: "
776 "\"expand\" style used "
777 "in conjunction with "
778 "\"expand\" modifier"
779 SLAPD_CONF_UNKNOWN_IGNORED ".\n",
781 #ifdef SLAPD_CONF_UNKNOWN_BAILOUT
783 #endif /* SLAPD_CONF_UNKNOWN_BAILOUT */
788 /* we'll see later if it's pertinent */
794 /* expand in <who> needs regex in <what> */
795 if ( ( sty == ACL_STYLE_EXPAND || expand )
796 && a->acl_dn_style != ACL_STYLE_REGEX )
798 fprintf( stderr, "%s: line %d: "
799 "\"expand\" style or modifier used "
800 "in conjunction with "
801 "a non-regex <what> clause\n",
805 if ( strncasecmp( left, "real", STRLENOF( "real" ) ) == 0 ) {
808 left += STRLENOF( "real" );
811 if ( strcasecmp( left, "*" ) == 0 ) {
816 ber_str2bv( "*", STRLENOF( "*" ), 1, &bv );
817 sty = ACL_STYLE_REGEX;
819 } else if ( strcasecmp( left, "anonymous" ) == 0 ) {
820 ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv);
821 sty = ACL_STYLE_ANONYMOUS;
823 } else if ( strcasecmp( left, "users" ) == 0 ) {
824 ber_str2bv("users", STRLENOF( "users" ), 1, &bv);
825 sty = ACL_STYLE_USERS;
827 } else if ( strcasecmp( left, "self" ) == 0 ) {
828 ber_str2bv("self", STRLENOF( "self" ), 1, &bv);
829 sty = ACL_STYLE_SELF;
831 } else if ( strcasecmp( left, "dn" ) == 0 ) {
832 if ( sty == ACL_STYLE_REGEX ) {
833 bdn->a_style = ACL_STYLE_REGEX;
834 if ( right == NULL ) {
839 bdn->a_style = ACL_STYLE_USERS;
841 } else if (*right == '\0' ) {
843 ber_str2bv("anonymous",
844 STRLENOF( "anonymous" ),
846 bdn->a_style = ACL_STYLE_ANONYMOUS;
848 } else if ( strcmp( right, "*" ) == 0 ) {
850 /* any or users? users for now */
854 bdn->a_style = ACL_STYLE_USERS;
856 } else if ( strcmp( right, ".+" ) == 0
857 || strcmp( right, "^.+" ) == 0
858 || strcmp( right, ".+$" ) == 0
859 || strcmp( right, "^.+$" ) == 0
860 || strcmp( right, ".+$$" ) == 0
861 || strcmp( right, "^.+$$" ) == 0 )
866 bdn->a_style = ACL_STYLE_USERS;
868 } else if ( strcmp( right, ".*" ) == 0
869 || strcmp( right, "^.*" ) == 0
870 || strcmp( right, ".*$" ) == 0
871 || strcmp( right, "^.*$" ) == 0
872 || strcmp( right, ".*$$" ) == 0
873 || strcmp( right, "^.*$$" ) == 0 )
880 acl_regex_normalized_dn( right, &bv );
881 if ( !ber_bvccmp( &bv, '*' ) ) {
882 regtest( fname, lineno, bv.bv_val );
886 } else if ( right == NULL || *right == '\0' ) {
887 fprintf( stderr, "%s: line %d: "
888 "missing \"=\" in (or value after) \"%s\" "
890 fname, lineno, left );
894 ber_str2bv( right, 0, 1, &bv );
901 if ( !BER_BVISNULL( &bv ) ) {
902 if ( !BER_BVISEMPTY( &bdn->a_pat ) ) {
904 "%s: line %d: dn pattern already specified.\n",
909 if ( sty != ACL_STYLE_REGEX &&
910 sty != ACL_STYLE_ANONYMOUS &&
911 sty != ACL_STYLE_USERS &&
912 sty != ACL_STYLE_SELF &&
915 rc = dnNormalize(0, NULL, NULL,
916 &bv, &bdn->a_pat, NULL);
917 if ( rc != LDAP_SUCCESS ) {
919 "%s: line %d: bad DN \"%s\" in by DN clause\n",
920 fname, lineno, bv.bv_val );
933 for ( exp = strchr( bdn->a_pat.bv_val, '$' );
934 exp && exp - bdn->a_pat.bv_val < bdn->a_pat.bv_len;
935 exp = strchr( exp, '$' ) )
937 if ( isdigit( exp[ 1 ] ) ) {
944 bdn->a_expand = expand;
948 "%s: line %d: \"expand\" used "
949 "with no expansions in \"pattern\""
950 SLAPD_CONF_UNKNOWN_IGNORED ".\n",
952 #ifdef SLAPD_CONF_UNKNOWN_BAILOUT
954 #endif /* SLAPD_CONF_UNKNOWN_BAILOUT */
957 if ( sty == ACL_STYLE_SELF ) {
958 bdn->a_self_level = level;
963 "%s: line %d: bad negative level \"%d\" "
965 fname, lineno, level );
967 } else if ( level == 1 ) {
969 "%s: line %d: \"onelevel\" should be used "
970 "instead of \"level{1}\" in by DN clause\n",
972 } else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
974 "%s: line %d: \"base\" should be used "
975 "instead of \"level{0}\" in by DN clause\n",
979 bdn->a_level = level;
984 if ( strcasecmp( left, "dnattr" ) == 0 ) {
985 if ( right == NULL || right[0] == '\0' ) {
986 fprintf( stderr, "%s: line %d: "
987 "missing \"=\" in (or value after) \"%s\" "
989 fname, lineno, left );
993 if( bdn->a_at != NULL ) {
995 "%s: line %d: dnattr already specified.\n",
1000 rc = slap_str2ad( right, &bdn->a_at, &text );
1002 if( rc != LDAP_SUCCESS ) {
1004 "%s: line %d: dnattr \"%s\": %s\n",
1005 fname, lineno, right, text );
1010 if( !is_at_syntax( bdn->a_at->ad_type,
1011 SLAPD_DN_SYNTAX ) &&
1012 !is_at_syntax( bdn->a_at->ad_type,
1013 SLAPD_NAMEUID_SYNTAX ))
1016 "%s: line %d: dnattr \"%s\": "
1017 "inappropriate syntax: %s\n",
1018 fname, lineno, right,
1019 bdn->a_at->ad_type->sat_syntax_oid );
1023 if( bdn->a_at->ad_type->sat_equality == NULL ) {
1025 "%s: line %d: dnattr \"%s\": "
1026 "inappropriate matching (no EQUALITY)\n",
1027 fname, lineno, right );
1034 if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) {
1039 case ACL_STYLE_REGEX:
1040 /* legacy, tolerated */
1041 fprintf( stderr, "%s: line %d: "
1042 "deprecated group style \"regex\"; "
1043 "use \"expand\" instead\n",
1045 sty = ACL_STYLE_EXPAND;
1048 case ACL_STYLE_BASE:
1049 /* legal, traditional */
1050 case ACL_STYLE_EXPAND:
1051 /* legal, substring expansion; supersedes regex */
1056 fprintf( stderr, "%s: line %d: "
1057 "inappropriate style \"%s\" in by clause\n",
1058 fname, lineno, style );
1062 if ( right == NULL || right[0] == '\0' ) {
1063 fprintf( stderr, "%s: line %d: "
1064 "missing \"=\" in (or value after) \"%s\" "
1066 fname, lineno, left );
1070 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
1072 "%s: line %d: group pattern already specified.\n",
1077 /* format of string is
1078 "group/objectClassValue/groupAttrName" */
1079 if ( ( value = strchr(left, '/') ) != NULL ) {
1081 if ( *value && ( name = strchr( value, '/' ) ) != NULL ) {
1086 b->a_group_style = sty;
1087 if ( sty == ACL_STYLE_EXPAND ) {
1088 acl_regex_normalized_dn( right, &bv );
1089 if ( !ber_bvccmp( &bv, '*' ) ) {
1090 regtest( fname, lineno, bv.bv_val );
1092 b->a_group_pat = bv;
1095 ber_str2bv( right, 0, 0, &bv );
1096 rc = dnNormalize( 0, NULL, NULL, &bv,
1097 &b->a_group_pat, NULL );
1098 if ( rc != LDAP_SUCCESS ) {
1100 "%s: line %d: bad DN \"%s\"\n",
1101 fname, lineno, right );
1106 if ( value && *value ) {
1107 b->a_group_oc = oc_find( value );
1110 if ( b->a_group_oc == NULL ) {
1112 "%s: line %d: group objectclass "
1114 fname, lineno, value );
1119 b->a_group_oc = oc_find( SLAPD_GROUP_CLASS );
1121 if( b->a_group_oc == NULL ) {
1123 "%s: line %d: group default objectclass "
1125 fname, lineno, SLAPD_GROUP_CLASS );
1130 if ( is_object_subclass( slap_schema.si_oc_referral,
1134 "%s: line %d: group objectclass \"%s\" "
1135 "is subclass of referral\n",
1136 fname, lineno, value );
1140 if ( is_object_subclass( slap_schema.si_oc_alias,
1144 "%s: line %d: group objectclass \"%s\" "
1145 "is subclass of alias\n",
1146 fname, lineno, value );
1150 if ( name && *name ) {
1151 rc = slap_str2ad( name, &b->a_group_at, &text );
1153 if( rc != LDAP_SUCCESS ) {
1155 "%s: line %d: group \"%s\": %s\n",
1156 fname, lineno, right, text );
1162 rc = slap_str2ad( SLAPD_GROUP_ATTR, &b->a_group_at, &text );
1164 if ( rc != LDAP_SUCCESS ) {
1166 "%s: line %d: group \"%s\": %s\n",
1167 fname, lineno, SLAPD_GROUP_ATTR, text );
1172 if ( !is_at_syntax( b->a_group_at->ad_type,
1173 SLAPD_DN_SYNTAX ) &&
1174 !is_at_syntax( b->a_group_at->ad_type,
1175 SLAPD_NAMEUID_SYNTAX ) &&
1176 !is_at_subtype( b->a_group_at->ad_type, slap_schema.si_ad_labeledURI->ad_type ) )
1179 "%s: line %d: group \"%s\": inappropriate syntax: %s\n",
1180 fname, lineno, right,
1181 b->a_group_at->ad_type->sat_syntax_oid );
1188 struct berval vals[2];
1190 ber_str2bv( b->a_group_oc->soc_oid, 0, 0, &vals[0] );
1191 BER_BVZERO( &vals[1] );
1193 rc = oc_check_allowed( b->a_group_at->ad_type,
1197 fprintf( stderr, "%s: line %d: "
1198 "group: \"%s\" not allowed by \"%s\"\n",
1200 b->a_group_at->ad_cname.bv_val,
1201 b->a_group_oc->soc_oid );
1208 if ( strcasecmp( left, "peername" ) == 0 ) {
1210 case ACL_STYLE_REGEX:
1211 case ACL_STYLE_BASE:
1212 /* legal, traditional */
1213 case ACL_STYLE_EXPAND:
1214 /* cheap replacement to regex for simple expansion */
1216 case ACL_STYLE_PATH:
1217 /* legal, peername specific */
1221 fprintf( stderr, "%s: line %d: "
1222 "inappropriate style \"%s\" in by clause\n",
1223 fname, lineno, style );
1227 if ( right == NULL || right[0] == '\0' ) {
1228 fprintf( stderr, "%s: line %d: "
1229 "missing \"=\" in (or value after) \"%s\" "
1231 fname, lineno, left );
1235 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
1236 fprintf( stderr, "%s: line %d: "
1237 "peername pattern already specified.\n",
1242 b->a_peername_style = sty;
1243 if ( sty == ACL_STYLE_REGEX ) {
1244 acl_regex_normalized_dn( right, &bv );
1245 if ( !ber_bvccmp( &bv, '*' ) ) {
1246 regtest( fname, lineno, bv.bv_val );
1248 b->a_peername_pat = bv;
1251 ber_str2bv( right, 0, 1, &b->a_peername_pat );
1253 if ( sty == ACL_STYLE_IP ) {
1258 split( right, '{', &addr, &port );
1259 split( addr, '%', &addr, &mask );
1261 b->a_peername_addr = inet_addr( addr );
1262 if ( b->a_peername_addr == (unsigned long)(-1) ) {
1263 /* illegal address */
1264 fprintf( stderr, "%s: line %d: "
1265 "illegal peername address \"%s\".\n",
1266 fname, lineno, addr );
1270 b->a_peername_mask = (unsigned long)(-1);
1271 if ( mask != NULL ) {
1272 b->a_peername_mask = inet_addr( mask );
1273 if ( b->a_peername_mask ==
1274 (unsigned long)(-1) )
1277 fprintf( stderr, "%s: line %d: "
1278 "illegal peername address mask "
1280 fname, lineno, mask );
1285 b->a_peername_port = -1;
1289 b->a_peername_port = strtol( port, &end, 10 );
1290 if ( end[0] != '}' ) {
1292 fprintf( stderr, "%s: line %d: "
1293 "illegal peername port specification "
1295 fname, lineno, port );
1304 if ( strcasecmp( left, "sockname" ) == 0 ) {
1306 case ACL_STYLE_REGEX:
1307 case ACL_STYLE_BASE:
1308 /* legal, traditional */
1309 case ACL_STYLE_EXPAND:
1310 /* cheap replacement to regex for simple expansion */
1315 fprintf( stderr, "%s: line %d: "
1316 "inappropriate style \"%s\" in by clause\n",
1317 fname, lineno, style );
1321 if ( right == NULL || right[0] == '\0' ) {
1322 fprintf( stderr, "%s: line %d: "
1323 "missing \"=\" in (or value after) \"%s\" "
1325 fname, lineno, left );
1329 if ( !BER_BVISNULL( &b->a_sockname_pat ) ) {
1330 fprintf( stderr, "%s: line %d: "
1331 "sockname pattern already specified.\n",
1336 b->a_sockname_style = sty;
1337 if ( sty == ACL_STYLE_REGEX ) {
1338 acl_regex_normalized_dn( right, &bv );
1339 if ( !ber_bvccmp( &bv, '*' ) ) {
1340 regtest( fname, lineno, bv.bv_val );
1342 b->a_sockname_pat = bv;
1345 ber_str2bv( right, 0, 1, &b->a_sockname_pat );
1350 if ( strcasecmp( left, "domain" ) == 0 ) {
1352 case ACL_STYLE_REGEX:
1353 case ACL_STYLE_BASE:
1354 case ACL_STYLE_SUBTREE:
1355 /* legal, traditional */
1358 case ACL_STYLE_EXPAND:
1359 /* tolerated: means exact,expand */
1363 "\"expand\" modifier "
1364 "with \"expand\" style\n",
1367 sty = ACL_STYLE_BASE;
1373 fprintf( stderr, "%s: line %d: "
1374 "inappropriate style \"%s\" in by clause\n",
1375 fname, lineno, style );
1379 if ( right == NULL || right[0] == '\0' ) {
1380 fprintf( stderr, "%s: line %d: "
1381 "missing \"=\" in (or value after) \"%s\" "
1383 fname, lineno, left );
1387 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
1389 "%s: line %d: domain pattern already specified.\n",
1394 b->a_domain_style = sty;
1395 b->a_domain_expand = expand;
1396 if ( sty == ACL_STYLE_REGEX ) {
1397 acl_regex_normalized_dn( right, &bv );
1398 if ( !ber_bvccmp( &bv, '*' ) ) {
1399 regtest( fname, lineno, bv.bv_val );
1401 b->a_domain_pat = bv;
1404 ber_str2bv( right, 0, 1, &b->a_domain_pat );
1409 if ( strcasecmp( left, "sockurl" ) == 0 ) {
1411 case ACL_STYLE_REGEX:
1412 case ACL_STYLE_BASE:
1413 /* legal, traditional */
1414 case ACL_STYLE_EXPAND:
1415 /* cheap replacement to regex for simple expansion */
1420 fprintf( stderr, "%s: line %d: "
1421 "inappropriate style \"%s\" in by clause\n",
1422 fname, lineno, style );
1426 if ( right == NULL || right[0] == '\0' ) {
1427 fprintf( stderr, "%s: line %d: "
1428 "missing \"=\" in (or value after) \"%s\" "
1430 fname, lineno, left );
1434 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
1436 "%s: line %d: sockurl pattern already specified.\n",
1441 b->a_sockurl_style = sty;
1442 if ( sty == ACL_STYLE_REGEX ) {
1443 acl_regex_normalized_dn( right, &bv );
1444 if ( !ber_bvccmp( &bv, '*' ) ) {
1445 regtest( fname, lineno, bv.bv_val );
1447 b->a_sockurl_pat = bv;
1450 ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
1455 if ( strcasecmp( left, "set" ) == 0 ) {
1458 case ACL_STYLE_REGEX:
1459 fprintf( stderr, "%s: line %d: "
1460 "deprecated set style "
1461 "\"regex\" in <by> clause; "
1462 "use \"expand\" instead\n",
1464 sty = ACL_STYLE_EXPAND;
1467 case ACL_STYLE_BASE:
1468 case ACL_STYLE_EXPAND:
1472 fprintf( stderr, "%s: line %d: "
1473 "inappropriate style \"%s\" in by clause\n",
1474 fname, lineno, style );
1478 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
1480 "%s: line %d: set attribute already specified.\n",
1485 if ( right == NULL || *right == '\0' ) {
1487 "%s: line %d: no set is defined\n",
1492 b->a_set_style = sty;
1493 ber_str2bv( right, 0, 1, &b->a_set_pat );
1503 if ( strcasecmp( left, "aci" ) == 0 ) {
1506 } else if ( strncasecmp( left, "dynacl/", STRLENOF( "dynacl/" ) ) == 0 ) {
1507 name = &left[ STRLENOF( "dynacl/" ) ];
1508 opts = strchr( name, '/' );
1516 if ( slap_dynacl_config( fname, lineno, b, name, opts, sty, right ) ) {
1517 fprintf( stderr, "%s: line %d: "
1518 "unable to configure dynacl \"%s\"\n",
1519 fname, lineno, name );
1526 #else /* ! SLAP_DYNACL */
1528 #ifdef SLAPD_ACI_ENABLED
1529 if ( strcasecmp( left, "aci" ) == 0 ) {
1530 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1531 fprintf( stderr, "%s: line %d: "
1532 "inappropriate style \"%s\" in by clause\n",
1533 fname, lineno, style );
1537 if( b->a_aci_at != NULL ) {
1539 "%s: line %d: aci attribute already specified.\n",
1544 if ( right != NULL && *right != '\0' ) {
1545 rc = slap_str2ad( right, &b->a_aci_at, &text );
1547 if( rc != LDAP_SUCCESS ) {
1549 "%s: line %d: aci \"%s\": %s\n",
1550 fname, lineno, right, text );
1555 b->a_aci_at = slap_ad_aci;
1558 if( !is_at_syntax( b->a_aci_at->ad_type,
1561 fprintf( stderr, "%s: line %d: "
1562 "aci \"%s\": inappropriate syntax: %s\n",
1563 fname, lineno, right,
1564 b->a_aci_at->ad_type->sat_syntax_oid );
1570 #endif /* SLAPD_ACI_ENABLED */
1571 #endif /* ! SLAP_DYNACL */
1573 if ( strcasecmp( left, "ssf" ) == 0 ) {
1574 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1575 fprintf( stderr, "%s: line %d: "
1576 "inappropriate style \"%s\" in by clause\n",
1577 fname, lineno, style );
1581 if ( b->a_authz.sai_ssf ) {
1583 "%s: line %d: ssf attribute already specified.\n",
1588 if ( right == NULL || *right == '\0' ) {
1590 "%s: line %d: no ssf is defined\n",
1595 b->a_authz.sai_ssf = strtol( right, &next, 10 );
1596 if ( next == NULL || next[0] != '\0' ) {
1598 "%s: line %d: unable to parse ssf value (%s)\n",
1599 fname, lineno, right );
1603 if ( !b->a_authz.sai_ssf ) {
1605 "%s: line %d: invalid ssf value (%s)\n",
1606 fname, lineno, right );
1612 if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
1613 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1614 fprintf( stderr, "%s: line %d: "
1615 "inappropriate style \"%s\" in by clause\n",
1616 fname, lineno, style );
1620 if ( b->a_authz.sai_transport_ssf ) {
1621 fprintf( stderr, "%s: line %d: "
1622 "transport_ssf attribute already specified.\n",
1627 if ( right == NULL || *right == '\0' ) {
1629 "%s: line %d: no transport_ssf is defined\n",
1634 b->a_authz.sai_transport_ssf = strtol( right, &next, 10 );
1635 if ( next == NULL || next[0] != '\0' ) {
1636 fprintf( stderr, "%s: line %d: "
1637 "unable to parse transport_ssf value (%s)\n",
1638 fname, lineno, right );
1642 if ( !b->a_authz.sai_transport_ssf ) {
1644 "%s: line %d: invalid transport_ssf value (%s)\n",
1645 fname, lineno, right );
1651 if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
1652 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1653 fprintf( stderr, "%s: line %d: "
1654 "inappropriate style \"%s\" in by clause\n",
1655 fname, lineno, style );
1659 if ( b->a_authz.sai_tls_ssf ) {
1660 fprintf( stderr, "%s: line %d: "
1661 "tls_ssf attribute already specified.\n",
1666 if ( right == NULL || *right == '\0' ) {
1668 "%s: line %d: no tls_ssf is defined\n",
1673 b->a_authz.sai_tls_ssf = strtol( right, &next, 10 );
1674 if ( next == NULL || next[0] != '\0' ) {
1675 fprintf( stderr, "%s: line %d: "
1676 "unable to parse tls_ssf value (%s)\n",
1677 fname, lineno, right );
1681 if ( !b->a_authz.sai_tls_ssf ) {
1683 "%s: line %d: invalid tls_ssf value (%s)\n",
1684 fname, lineno, right );
1690 if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
1691 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1692 fprintf( stderr, "%s: line %d: "
1693 "inappropriate style \"%s\" in by clause\n",
1694 fname, lineno, style );
1698 if ( b->a_authz.sai_sasl_ssf ) {
1699 fprintf( stderr, "%s: line %d: "
1700 "sasl_ssf attribute already specified.\n",
1705 if ( right == NULL || *right == '\0' ) {
1707 "%s: line %d: no sasl_ssf is defined\n",
1712 b->a_authz.sai_sasl_ssf = strtol( right, &next, 10 );
1713 if ( next == NULL || next[0] != '\0' ) {
1714 fprintf( stderr, "%s: line %d: "
1715 "unable to parse sasl_ssf value (%s)\n",
1716 fname, lineno, right );
1720 if ( !b->a_authz.sai_sasl_ssf ) {
1722 "%s: line %d: invalid sasl_ssf value (%s)\n",
1723 fname, lineno, right );
1729 if ( right != NULL ) {
1736 if ( i == argc || ( strcasecmp( left, "stop" ) == 0 ) ) {
1737 /* out of arguments or plain stop */
1739 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1740 b->a_type = ACL_STOP;
1742 access_append( &a->acl_access, b );
1746 if ( strcasecmp( left, "continue" ) == 0 ) {
1747 /* plain continue */
1749 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1750 b->a_type = ACL_CONTINUE;
1752 access_append( &a->acl_access, b );
1756 if ( strcasecmp( left, "break" ) == 0 ) {
1757 /* plain continue */
1759 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1760 b->a_type = ACL_BREAK;
1762 access_append( &a->acl_access, b );
1766 if ( strcasecmp( left, "by" ) == 0 ) {
1767 /* we've gone too far */
1769 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1770 b->a_type = ACL_STOP;
1772 access_append( &a->acl_access, b );
1777 if ( strncasecmp( left, "self", STRLENOF( "self" ) ) == 0 ) {
1779 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( &left[ STRLENOF( "self" ) ] ) );
1781 } else if ( strncasecmp( left, "realself", STRLENOF( "realself" ) ) == 0 ) {
1782 b->a_realdn_self = 1;
1783 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( &left[ STRLENOF( "realself" ) ] ) );
1786 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( left ) );
1789 if ( ACL_IS_INVALID( b->a_access_mask ) ) {
1791 "%s: line %d: expecting <access> got \"%s\"\n",
1792 fname, lineno, left );
1796 b->a_type = ACL_STOP;
1798 if ( ++i == argc ) {
1799 /* out of arguments or plain stop */
1800 access_append( &a->acl_access, b );
1804 if ( strcasecmp( argv[i], "continue" ) == 0 ) {
1805 /* plain continue */
1806 b->a_type = ACL_CONTINUE;
1808 } else if ( strcasecmp( argv[i], "break" ) == 0 ) {
1809 /* plain continue */
1810 b->a_type = ACL_BREAK;
1812 } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
1817 access_append( &a->acl_access, b );
1821 "%s: line %d: expecting \"to\" "
1822 "or \"by\" got \"%s\"\n",
1823 fname, lineno, argv[i] );
1828 /* if we have no real access clause, complain and do nothing */
1830 fprintf( stderr, "%s: line %d: "
1831 "warning: no access clause(s) specified in access line\n",
1836 if ( ldap_debug & LDAP_DEBUG_ACL ) {
1841 if ( a->acl_access == NULL ) {
1842 fprintf( stderr, "%s: line %d: "
1843 "warning: no by clause(s) specified in access line\n",
1848 if ( !BER_BVISNULL( &be->be_nsuffix[ 1 ] ) ) {
1849 fprintf( stderr, "%s: line %d: warning: "
1850 "scope checking only applies to single-valued "
1851 "suffix databases\n",
1853 /* go ahead, since checking is not authoritative */
1856 switch ( check_scope( be, a ) ) {
1857 case ACL_SCOPE_UNKNOWN:
1858 fprintf( stderr, "%s: line %d: warning: "
1859 "cannot assess the validity of the ACL scope within "
1860 "backend naming context\n",
1864 case ACL_SCOPE_WARN:
1865 fprintf( stderr, "%s: line %d: warning: "
1866 "ACL could be out of scope within backend naming context\n",
1870 case ACL_SCOPE_PARTIAL:
1871 fprintf( stderr, "%s: line %d: warning: "
1872 "ACL appears to be partially out of scope within "
1873 "backend naming context\n",
1878 fprintf( stderr, "%s: line %d: warning: "
1879 "ACL appears to be out of scope within "
1880 "backend naming context\n",
1887 acl_append( &be->be_acl, a, pos );
1890 acl_append( &frontendDB->be_acl, a, pos );
1896 accessmask2str( slap_mask_t mask, char *buf, int debug )
1901 assert( buf != NULL );
1903 if ( ACL_IS_INVALID( mask ) ) {
1909 if ( ACL_IS_LEVEL( mask ) ) {
1910 if ( ACL_LVL_IS_NONE(mask) ) {
1911 ptr = lutil_strcopy( ptr, "none" );
1913 } else if ( ACL_LVL_IS_DISCLOSE(mask) ) {
1914 ptr = lutil_strcopy( ptr, "disclose" );
1916 } else if ( ACL_LVL_IS_AUTH(mask) ) {
1917 ptr = lutil_strcopy( ptr, "auth" );
1919 } else if ( ACL_LVL_IS_COMPARE(mask) ) {
1920 ptr = lutil_strcopy( ptr, "compare" );
1922 } else if ( ACL_LVL_IS_SEARCH(mask) ) {
1923 ptr = lutil_strcopy( ptr, "search" );
1925 } else if ( ACL_LVL_IS_READ(mask) ) {
1926 ptr = lutil_strcopy( ptr, "read" );
1928 } else if ( ACL_LVL_IS_WRITE(mask) ) {
1929 ptr = lutil_strcopy( ptr, "write" );
1931 } else if ( ACL_LVL_IS_WADD(mask) ) {
1932 ptr = lutil_strcopy( ptr, "add" );
1934 } else if ( ACL_LVL_IS_WDEL(mask) ) {
1935 ptr = lutil_strcopy( ptr, "delete" );
1937 } else if ( ACL_LVL_IS_MANAGE(mask) ) {
1938 ptr = lutil_strcopy( ptr, "manage" );
1941 ptr = lutil_strcopy( ptr, "unknown" );
1951 if( ACL_IS_ADDITIVE( mask ) ) {
1954 } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
1961 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_MANAGE) ) {
1966 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
1970 } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WADD) ) {
1974 } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WDEL) ) {
1979 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
1984 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
1989 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
1994 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
1999 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_DISCLOSE) ) {
2004 if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
2013 if ( ACL_IS_LEVEL( mask ) ) {
2023 str2accessmask( const char *str )
2027 if( !ASCII_ALPHA(str[0]) ) {
2030 if ( str[0] == '=' ) {
2033 } else if( str[0] == '+' ) {
2034 ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
2036 } else if( str[0] == '-' ) {
2037 ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
2040 ACL_INVALIDATE(mask);
2044 for( i=1; str[i] != '\0'; i++ ) {
2045 if( TOLOWER((unsigned char) str[i]) == 'm' ) {
2046 ACL_PRIV_SET(mask, ACL_PRIV_MANAGE);
2048 } else if( TOLOWER((unsigned char) str[i]) == 'w' ) {
2049 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
2051 } else if( TOLOWER((unsigned char) str[i]) == 'a' ) {
2052 ACL_PRIV_SET(mask, ACL_PRIV_WADD);
2054 } else if( TOLOWER((unsigned char) str[i]) == 'z' ) {
2055 ACL_PRIV_SET(mask, ACL_PRIV_WDEL);
2057 } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
2058 ACL_PRIV_SET(mask, ACL_PRIV_READ);
2060 } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
2061 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
2063 } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
2064 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
2066 } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
2067 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
2069 } else if( TOLOWER((unsigned char) str[i]) == 'd' ) {
2070 ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
2072 } else if( str[i] != '0' ) {
2073 ACL_INVALIDATE(mask);
2081 if ( strcasecmp( str, "none" ) == 0 ) {
2082 ACL_LVL_ASSIGN_NONE(mask);
2084 } else if ( strcasecmp( str, "disclose" ) == 0 ) {
2085 ACL_LVL_ASSIGN_DISCLOSE(mask);
2087 } else if ( strcasecmp( str, "auth" ) == 0 ) {
2088 ACL_LVL_ASSIGN_AUTH(mask);
2090 } else if ( strcasecmp( str, "compare" ) == 0 ) {
2091 ACL_LVL_ASSIGN_COMPARE(mask);
2093 } else if ( strcasecmp( str, "search" ) == 0 ) {
2094 ACL_LVL_ASSIGN_SEARCH(mask);
2096 } else if ( strcasecmp( str, "read" ) == 0 ) {
2097 ACL_LVL_ASSIGN_READ(mask);
2099 } else if ( strcasecmp( str, "add" ) == 0 ) {
2100 ACL_LVL_ASSIGN_WADD(mask);
2102 } else if ( strcasecmp( str, "delete" ) == 0 ) {
2103 ACL_LVL_ASSIGN_WDEL(mask);
2105 } else if ( strcasecmp( str, "write" ) == 0 ) {
2106 ACL_LVL_ASSIGN_WRITE(mask);
2108 } else if ( strcasecmp( str, "manage" ) == 0 ) {
2109 ACL_LVL_ASSIGN_MANAGE(mask);
2112 ACL_INVALIDATE( mask );
2121 fprintf( stderr, "%s%s%s\n",
2122 "<access clause> ::= access to <what> "
2123 "[ by <who> <access> [ <control> ] ]+ \n"
2124 "<what> ::= * | [dn[.<dnstyle>]=<DN>] [filter=<filter>] [attrs=<attrlist>]\n"
2125 "<attrlist> ::= <attr> [val[/matchingRule][.<attrstyle>]=<value>] | <attr> , <attrlist>\n"
2126 "<attr> ::= <attrname> | entry | children\n",
2127 "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
2128 "\t[ realanonymous | realusers | realself | realdn[.<dnstyle>]=<DN> ]\n"
2129 "\t[dnattr=<attrname>]\n"
2130 "\t[realdnattr=<attrname>]\n"
2131 "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
2132 "\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n"
2133 "\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n"
2135 "\t[dynacl/<name>[/<options>][.<dynstyle>][=<pattern>]]\n"
2136 #else /* ! SLAP_DYNACL */
2137 #ifdef SLAPD_ACI_ENABLED
2138 "\t[aci[=<attrname>]]\n"
2139 #endif /* SLAPD_ACI_ENABLED */
2140 #endif /* ! SLAP_DYNACL */
2141 "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n",
2142 "<style> ::= exact | regex | base(Object)\n"
2143 "<dnstyle> ::= base(Object) | one(level) | sub(tree) | children | "
2145 "<attrstyle> ::= exact | regex | base(Object) | one(level) | "
2146 "sub(tree) | children\n"
2147 "<peernamestyle> ::= exact | regex | ip | path\n"
2148 "<domainstyle> ::= exact | regex | base(Object) | sub(tree)\n"
2149 "<access> ::= [[real]self]{<level>|<priv>}\n"
2150 "<level> ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage\n"
2151 "<priv> ::= {=|+|-}{0|d|x|c|s|r|{w|a|z}|m}+\n"
2152 "<control> ::= [ stop | continue | break ]\n"
2154 #ifdef SLAPD_ACI_ENABLED
2156 "\t<name>=ACI\t<pattern>=<attrname>\n"
2157 #endif /* SLAPD_ACI_ENABLED */
2158 #endif /* ! SLAP_DYNACL */
2160 exit( EXIT_FAILURE );
2164 * Set pattern to a "normalized" DN from src.
2165 * At present it simply eats the (optional) space after
2166 * a RDN separator (,)
2167 * Eventually will evolve in a more complete normalization
2170 acl_regex_normalized_dn(
2172 struct berval *pattern )
2177 str = ch_strdup( src );
2178 len = strlen( src );
2180 for ( p = str; p && p[0]; p++ ) {
2182 if ( p[0] == '\\' && p[1] ) {
2184 * if escaping a hex pair we should
2185 * increment p twice; however, in that
2186 * case the second hex number does
2192 if ( p[0] == ',' && p[1] == ' ' ) {
2196 * too much space should be an error if we are pedantic
2198 for ( q = &p[2]; q[0] == ' '; q++ ) {
2201 AC_MEMCPY( p+1, q, len-(q-str)+1);
2204 pattern->bv_val = str;
2205 pattern->bv_len = p - str;
2218 if ( (*right = strchr( line, splitchar )) != NULL ) {
2219 *((*right)++) = '\0';
2224 access_append( Access **l, Access *a )
2226 for ( ; *l != NULL; l = &(*l)->a_next ) {
2234 acl_append( AccessControl **l, AccessControl *a, int pos )
2238 for (i=0 ; i != pos && *l != NULL; l = &(*l)->acl_next, i++ ) {
2247 access_free( Access *a )
2249 if ( !BER_BVISNULL( &a->a_dn_pat ) ) {
2250 free( a->a_dn_pat.bv_val );
2252 if ( !BER_BVISNULL( &a->a_realdn_pat ) ) {
2253 free( a->a_realdn_pat.bv_val );
2255 if ( !BER_BVISNULL( &a->a_peername_pat ) ) {
2256 free( a->a_peername_pat.bv_val );
2258 if ( !BER_BVISNULL( &a->a_sockname_pat ) ) {
2259 free( a->a_sockname_pat.bv_val );
2261 if ( !BER_BVISNULL( &a->a_domain_pat ) ) {
2262 free( a->a_domain_pat.bv_val );
2264 if ( !BER_BVISNULL( &a->a_sockurl_pat ) ) {
2265 free( a->a_sockurl_pat.bv_val );
2267 if ( !BER_BVISNULL( &a->a_set_pat ) ) {
2268 free( a->a_set_pat.bv_val );
2270 if ( !BER_BVISNULL( &a->a_group_pat ) ) {
2271 free( a->a_group_pat.bv_val );
2274 if ( a->a_dynacl != NULL ) {
2276 for ( da = a->a_dynacl; da; ) {
2277 slap_dynacl_t *tmp = da;
2281 if ( tmp->da_destroy ) {
2282 tmp->da_destroy( tmp->da_private );
2288 #endif /* SLAP_DYNACL */
2293 acl_free( AccessControl *a )
2298 if ( a->acl_filter ) {
2299 filter_free( a->acl_filter );
2301 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
2302 if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
2303 regfree( &a->acl_dn_re );
2305 free ( a->acl_dn_pat.bv_val );
2307 if ( a->acl_attrs ) {
2308 for ( an = a->acl_attrs; !BER_BVISNULL( &an->an_name ); an++ ) {
2309 free( an->an_name.bv_val );
2311 free( a->acl_attrs );
2313 for ( ; a->acl_access; a->acl_access = n ) {
2314 n = a->acl_access->a_next;
2315 access_free( a->acl_access );
2320 /* Because backend_startup uses acl_append to tack on the global_acl to
2321 * the end of each backend's acl, we cannot just take one argument and
2322 * merrily free our way to the end of the list. backend_destroy calls us
2323 * with the be_acl in arg1, and global_acl in arg2 to give us a stopping
2324 * point. config_destroy calls us with global_acl in arg1 and NULL in
2325 * arg2, so we then proceed to polish off the global_acl.
2328 acl_destroy( AccessControl *a, AccessControl *end )
2332 for ( ; a && a != end; a = n ) {
2339 access2str( slap_access_t access )
2341 if ( access == ACL_NONE ) {
2344 } else if ( access == ACL_DISCLOSE ) {
2347 } else if ( access == ACL_AUTH ) {
2350 } else if ( access == ACL_COMPARE ) {
2353 } else if ( access == ACL_SEARCH ) {
2356 } else if ( access == ACL_READ ) {
2359 } else if ( access == ACL_WRITE ) {
2362 } else if ( access == ACL_WADD ) {
2365 } else if ( access == ACL_WDEL ) {
2368 } else if ( access == ACL_MANAGE ) {
2377 str2access( const char *str )
2379 if ( strcasecmp( str, "none" ) == 0 ) {
2382 } else if ( strcasecmp( str, "disclose" ) == 0 ) {
2383 #ifndef SLAP_ACL_HONOR_DISCLOSE
2384 fprintf( stderr, "str2access: warning, "
2385 "\"disclose\" privilege disabled.\n" );
2386 #endif /* SLAP_ACL_HONOR_DISCLOSE */
2387 return ACL_DISCLOSE;
2389 } else if ( strcasecmp( str, "auth" ) == 0 ) {
2392 } else if ( strcasecmp( str, "compare" ) == 0 ) {
2395 } else if ( strcasecmp( str, "search" ) == 0 ) {
2398 } else if ( strcasecmp( str, "read" ) == 0 ) {
2401 } else if ( strcasecmp( str, "write" ) == 0 ) {
2404 } else if ( strcasecmp( str, "add" ) == 0 ) {
2407 } else if ( strcasecmp( str, "delete" ) == 0 ) {
2410 } else if ( strcasecmp( str, "manage" ) == 0 ) {
2414 return( ACL_INVALID_ACCESS );
2417 #define ACLBUF_MAXLEN 8192
2419 static char aclbuf[ACLBUF_MAXLEN];
2422 dnaccess2text( slap_dn_access *bdn, char *ptr, int is_realdn )
2427 ptr = lutil_strcopy( ptr, "real" );
2430 if ( ber_bvccmp( &bdn->a_pat, '*' ) ||
2431 bdn->a_style == ACL_STYLE_ANONYMOUS ||
2432 bdn->a_style == ACL_STYLE_USERS ||
2433 bdn->a_style == ACL_STYLE_SELF )
2436 assert( ! ber_bvccmp( &bdn->a_pat, '*' ) );
2439 ptr = lutil_strcopy( ptr, bdn->a_pat.bv_val );
2440 if ( bdn->a_style == ACL_STYLE_SELF && bdn->a_self_level != 0 ) {
2441 int n = sprintf( ptr, ".level{%d}", bdn->a_self_level );
2448 ptr = lutil_strcopy( ptr, "dn." );
2449 if ( bdn->a_style == ACL_STYLE_BASE )
2450 ptr = lutil_strcopy( ptr, style_base );
2452 ptr = lutil_strcopy( ptr, style_strings[bdn->a_style] );
2453 if ( bdn->a_style == ACL_STYLE_LEVEL ) {
2454 int n = sprintf( ptr, "{%d}", bdn->a_level );
2459 if ( bdn->a_expand ) {
2460 ptr = lutil_strcopy( ptr, ",expand" );
2464 ptr = lutil_strcopy( ptr, bdn->a_pat.bv_val );
2471 access2text( Access *b, char *ptr )
2473 char maskbuf[ACCESSMASK_MAXLEN];
2475 ptr = lutil_strcopy( ptr, "\tby" );
2477 if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
2478 ptr = dnaccess2text( &b->a_dn, ptr, 0 );
2481 ptr = lutil_strcopy( ptr, " dnattr=" );
2482 ptr = lutil_strcopy( ptr, b->a_dn_at->ad_cname.bv_val );
2485 if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
2486 ptr = dnaccess2text( &b->a_realdn, ptr, 1 );
2488 if ( b->a_realdn_at ) {
2489 ptr = lutil_strcopy( ptr, " realdnattr=" );
2490 ptr = lutil_strcopy( ptr, b->a_realdn_at->ad_cname.bv_val );
2493 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
2494 ptr = lutil_strcopy( ptr, " group/" );
2495 ptr = lutil_strcopy( ptr, b->a_group_oc ?
2496 b->a_group_oc->soc_cname.bv_val : SLAPD_GROUP_CLASS );
2498 ptr = lutil_strcopy( ptr, b->a_group_at ?
2499 b->a_group_at->ad_cname.bv_val : SLAPD_GROUP_ATTR );
2501 ptr = lutil_strcopy( ptr, style_strings[b->a_group_style] );
2504 ptr = lutil_strcopy( ptr, b->a_group_pat.bv_val );
2508 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
2509 ptr = lutil_strcopy( ptr, " peername" );
2511 ptr = lutil_strcopy( ptr, style_strings[b->a_peername_style] );
2514 ptr = lutil_strcopy( ptr, b->a_peername_pat.bv_val );
2518 if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
2519 ptr = lutil_strcopy( ptr, " sockname" );
2521 ptr = lutil_strcopy( ptr, style_strings[b->a_sockname_style] );
2524 ptr = lutil_strcopy( ptr, b->a_sockname_pat.bv_val );
2528 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
2529 ptr = lutil_strcopy( ptr, " domain" );
2531 ptr = lutil_strcopy( ptr, style_strings[b->a_domain_style] );
2532 if ( b->a_domain_expand ) {
2533 ptr = lutil_strcopy( ptr, ",expand" );
2536 ptr = lutil_strcopy( ptr, b->a_domain_pat.bv_val );
2539 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
2540 ptr = lutil_strcopy( ptr, " sockurl" );
2542 ptr = lutil_strcopy( ptr, style_strings[b->a_sockurl_style] );
2545 ptr = lutil_strcopy( ptr, b->a_sockurl_pat.bv_val );
2549 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
2550 ptr = lutil_strcopy( ptr, " set" );
2552 ptr = lutil_strcopy( ptr, style_strings[b->a_set_style] );
2555 ptr = lutil_strcopy( ptr, b->a_set_pat.bv_val );
2560 if ( b->a_dynacl ) {
2563 for ( da = b->a_dynacl; da; da = da->da_next ) {
2564 if ( da->da_unparse ) {
2565 struct berval bv = BER_BVNULL;
2566 (void)( *da->da_unparse )( da->da_private, &bv );
2567 assert( !BER_BVISNULL( &bv ) );
2568 ptr = lutil_strcopy( ptr, bv.bv_val );
2569 ch_free( bv.bv_val );
2573 #else /* ! SLAP_DYNACL */
2574 #ifdef SLAPD_ACI_ENABLED
2575 if ( b->a_aci_at != NULL ) {
2576 ptr = lutil_strcopy( ptr, " aci=" );
2577 ptr = lutil_strcopy( ptr, b->a_aci_at->ad_cname.bv_val );
2580 #endif /* SLAP_DYNACL */
2582 /* Security Strength Factors */
2583 if ( b->a_authz.sai_ssf ) {
2584 ptr += sprintf( ptr, " ssf=%u",
2585 b->a_authz.sai_ssf );
2587 if ( b->a_authz.sai_transport_ssf ) {
2588 ptr += sprintf( ptr, " transport_ssf=%u",
2589 b->a_authz.sai_transport_ssf );
2591 if ( b->a_authz.sai_tls_ssf ) {
2592 ptr += sprintf( ptr, " tls_ssf=%u",
2593 b->a_authz.sai_tls_ssf );
2595 if ( b->a_authz.sai_sasl_ssf ) {
2596 ptr += sprintf( ptr, " sasl_ssf=%u",
2597 b->a_authz.sai_sasl_ssf );
2601 if ( b->a_dn_self ) {
2602 ptr = lutil_strcopy( ptr, "self" );
2603 } else if ( b->a_realdn_self ) {
2604 ptr = lutil_strcopy( ptr, "realself" );
2606 ptr = lutil_strcopy( ptr, accessmask2str( b->a_access_mask, maskbuf, 0 ));
2607 if ( !maskbuf[0] ) ptr--;
2609 if( b->a_type == ACL_BREAK ) {
2610 ptr = lutil_strcopy( ptr, " break" );
2612 } else if( b->a_type == ACL_CONTINUE ) {
2613 ptr = lutil_strcopy( ptr, " continue" );
2615 } else if( b->a_type != ACL_STOP ) {
2616 ptr = lutil_strcopy( ptr, " unknown-control" );
2618 if ( !maskbuf[0] ) ptr = lutil_strcopy( ptr, " stop" );
2626 acl_unparse( AccessControl *a, struct berval *bv )
2632 bv->bv_val = aclbuf;
2637 ptr = lutil_strcopy( ptr, "to" );
2638 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
2640 ptr = lutil_strcopy( ptr, " dn." );
2641 if ( a->acl_dn_style == ACL_STYLE_BASE )
2642 ptr = lutil_strcopy( ptr, style_base );
2644 ptr = lutil_strcopy( ptr, style_strings[a->acl_dn_style] );
2647 ptr = lutil_strcopy( ptr, a->acl_dn_pat.bv_val );
2648 ptr = lutil_strcopy( ptr, "\"\n" );
2651 if ( a->acl_filter != NULL ) {
2652 struct berval bv = BER_BVNULL;
2655 filter2bv( a->acl_filter, &bv );
2656 ptr = lutil_strcopy( ptr, " filter=\"" );
2657 ptr = lutil_strcopy( ptr, bv.bv_val );
2660 ch_free( bv.bv_val );
2663 if ( a->acl_attrs != NULL ) {
2668 ptr = lutil_strcopy( ptr, " attrs=" );
2669 for ( an = a->acl_attrs; an && !BER_BVISNULL( &an->an_name ); an++ ) {
2670 if ( ! first ) *ptr++ = ',';
2672 *ptr++ = an->an_oc_exclude ? '!' : '@';
2673 ptr = lutil_strcopy( ptr, an->an_oc->soc_cname.bv_val );
2676 ptr = lutil_strcopy( ptr, an->an_name.bv_val );
2683 if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
2685 ptr = lutil_strcopy( ptr, " val." );
2686 if ( a->acl_attrval_style == ACL_STYLE_BASE &&
2687 a->acl_attrs[0].an_desc->ad_type->sat_syntax ==
2688 slap_schema.si_syn_distinguishedName )
2689 ptr = lutil_strcopy( ptr, style_base );
2691 ptr = lutil_strcopy( ptr, style_strings[a->acl_attrval_style] );
2694 ptr = lutil_strcopy( ptr, a->acl_attrval.bv_val );
2700 ptr = lutil_strcopy( ptr, " *\n" );
2703 for ( b = a->acl_access; b != NULL; b = b->a_next ) {
2704 ptr = access2text( b, ptr );
2707 bv->bv_len = ptr - bv->bv_val;
2713 print_acl( Backend *be, AccessControl *a )
2717 acl_unparse( a, &bv );
2718 fprintf( stderr, "%s ACL: access %s\n",
2719 be == NULL ? "Global" : "Backend", bv.bv_val );
2721 #endif /* LDAP_DEBUG */