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-2004 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[] = { "regex",
42 "base", "one", "subtree", "children", NULL };
44 static void split(char *line, int splitchar, char **left, char **right);
45 static void access_append(Access **l, Access *a);
46 static void acl_usage(void) LDAP_GCCATTR((noreturn));
48 static void acl_regex_normalized_dn(const char *src, struct berval *pat);
51 static void print_acl(Backend *be, AccessControl *a);
52 static void print_access(Access *b);
56 regtest(const char *fname, int lineno, char *pat) {
72 for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
74 if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
91 if ( size >= (sizeof(buf)-1) ) {
93 "%s: line %d: regular expression \"%s\" too large\n",
98 if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
100 regerror(e, &re, error, sizeof(error));
102 "%s: line %d: regular expression \"%s\" bad because of %s\n",
103 fname, lineno, pat, error );
119 char *left, *right, *style;
127 for ( i = 1; i < argc; i++ ) {
128 /* to clause - select which entries are protected */
129 if ( strcasecmp( argv[i], "to" ) == 0 ) {
131 fprintf( stderr, "%s: line %d: "
132 "only one to clause allowed in access line\n",
136 a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
137 for ( ++i; i < argc; i++ ) {
138 if ( strcasecmp( argv[i], "by" ) == 0 ) {
143 if ( strcasecmp( argv[i], "*" ) == 0 ) {
144 if( a->acl_dn_pat.bv_len ||
145 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
148 "%s: line %d: dn pattern"
149 " already specified in to clause.\n",
154 a->acl_dn_pat.bv_val = ch_strdup( "*" );
155 a->acl_dn_pat.bv_len = 1;
159 split( argv[i], '=', &left, &right );
160 split( left, '.', &left, &style );
162 if ( right == NULL ) {
163 fprintf( stderr, "%s: line %d: "
164 "missing \"=\" in \"%s\" in to clause\n",
165 fname, lineno, left );
169 if ( strcasecmp( left, "dn" ) == 0 ) {
170 if( a->acl_dn_pat.bv_len != 0 ||
171 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
174 "%s: line %d: dn pattern"
175 " already specified in to clause.\n",
180 if ( style == NULL || *style == '\0' ||
181 ( strcasecmp( style, "base" ) == 0 ) ||
182 ( strcasecmp( style, "exact" ) == 0 ))
184 a->acl_dn_style = ACL_STYLE_BASE;
185 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
187 } else if ( strcasecmp( style, "onelevel" ) == 0
188 || strcasecmp( style, "one" ) == 0 ) {
189 a->acl_dn_style = ACL_STYLE_ONE;
190 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
192 } else if ( strcasecmp( style, "subtree" ) == 0
193 || strcasecmp( style, "sub" ) == 0 )
195 if( *right == '\0' ) {
196 a->acl_dn_pat.bv_val = ch_strdup( "*" );
197 a->acl_dn_pat.bv_len = 1;
200 a->acl_dn_style = ACL_STYLE_SUBTREE;
201 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
204 } else if ( strcasecmp( style, "children" ) == 0 ) {
205 a->acl_dn_style = ACL_STYLE_CHILDREN;
206 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
208 } else if ( strcasecmp( style, "regex" ) == 0 ) {
209 a->acl_dn_style = ACL_STYLE_REGEX;
211 if ( *right == '\0' ) {
212 /* empty regex should match empty DN */
213 a->acl_dn_style = ACL_STYLE_BASE;
214 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
216 } else if ( strcmp(right, "*") == 0
217 || strcmp(right, ".*") == 0
218 || strcmp(right, ".*$") == 0
219 || strcmp(right, "^.*") == 0
220 || strcmp(right, "^.*$") == 0
221 || strcmp(right, ".*$$") == 0
222 || strcmp(right, "^.*$$") == 0 )
224 a->acl_dn_pat.bv_val = ch_strdup( "*" );
225 a->acl_dn_pat.bv_len = sizeof("*")-1;
228 acl_regex_normalized_dn( right, &a->acl_dn_pat );
232 fprintf( stderr, "%s: line %d: "
233 "unknown dn style \"%s\" in to clause\n",
234 fname, lineno, style );
241 if ( strcasecmp( left, "filter" ) == 0 ) {
242 if ( (a->acl_filter = str2filter( right )) == NULL ) {
244 "%s: line %d: bad filter \"%s\" in to clause\n",
245 fname, lineno, right );
249 } else if ( strcasecmp( left, "attr" ) == 0
250 || strcasecmp( left, "attrs" ) == 0 ) {
251 a->acl_attrs = str2anlist( a->acl_attrs,
253 if ( a->acl_attrs == NULL ) {
255 "%s: line %d: unknown attr \"%s\" in to clause\n",
256 fname, lineno, right );
260 } else if ( strncasecmp( left, "val", 3 ) == 0 ) {
261 if ( a->acl_attrval.bv_len ) {
263 "%s: line %d: attr val already specified in to clause.\n",
267 if ( a->acl_attrs == NULL || a->acl_attrs[1].an_name.bv_val ) {
269 "%s: line %d: attr val requires a single attribute.\n",
273 ber_str2bv( right, 0, 1, &a->acl_attrval );
274 if ( style && strcasecmp( style, "regex" ) == 0 ) {
275 int e = regcomp( &a->acl_attrval_re, a->acl_attrval.bv_val,
276 REG_EXTENDED | REG_ICASE | REG_NOSUB );
279 regerror( e, &a->acl_attrval_re, buf, sizeof(buf) );
280 fprintf( stderr, "%s: line %d: "
281 "regular expression \"%s\" bad because of %s\n",
282 fname, lineno, right, buf );
285 a->acl_attrval_style = ACL_STYLE_REGEX;
287 /* FIXME: if the attribute has DN syntax,
288 * we might allow one, subtree and children styles as well */
289 if ( !strcasecmp( style, "exact" ) ) {
290 a->acl_attrval_style = ACL_STYLE_BASE;
292 } else if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) {
293 if ( !strcasecmp( style, "base" ) ) {
294 a->acl_attrval_style = ACL_STYLE_BASE;
295 } else if ( !strcasecmp( style, "onelevel" ) || !strcasecmp( style, "one" ) ) {
296 a->acl_attrval_style = ACL_STYLE_ONE;
297 } else if ( !strcasecmp( style, "subtree" ) || !strcasecmp( style, "sub" ) ) {
298 a->acl_attrval_style = ACL_STYLE_SUBTREE;
299 } else if ( !strcasecmp( style, "children" ) ) {
300 a->acl_attrval_style = ACL_STYLE_CHILDREN;
303 "%s: line %d: unknown val.<style> \"%s\" "
304 "for attributeType \"%s\" with DN syntax; using \"base\"\n",
305 fname, lineno, style,
306 a->acl_attrs[0].an_desc->ad_cname.bv_val );
307 a->acl_attrval_style = ACL_STYLE_BASE;
312 "%s: line %d: unknown val.<style> \"%s\" "
313 "for attributeType \"%s\"; using \"exact\"\n",
314 fname, lineno, style,
315 a->acl_attrs[0].an_desc->ad_cname.bv_val );
316 a->acl_attrval_style = ACL_STYLE_BASE;
322 "%s: line %d: expecting <what> got \"%s\"\n",
323 fname, lineno, left );
328 if ( a->acl_dn_pat.bv_len != 0 &&
329 strcmp(a->acl_dn_pat.bv_val, "*") == 0 )
331 free( a->acl_dn_pat.bv_val );
332 a->acl_dn_pat.bv_val = NULL;
333 a->acl_dn_pat.bv_len = 0;
336 if( a->acl_dn_pat.bv_len != 0 ||
337 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
339 if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
341 rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
342 if ( rc != LDAP_SUCCESS ) {
344 "%s: line %d: bad DN \"%s\" in to DN clause\n",
345 fname, lineno, a->acl_dn_pat.bv_val );
348 free( a->acl_dn_pat.bv_val );
351 int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
352 REG_EXTENDED | REG_ICASE );
355 regerror( e, &a->acl_dn_re, buf, sizeof(buf) );
356 fprintf( stderr, "%s: line %d: "
357 "regular expression \"%s\" bad because of %s\n",
358 fname, lineno, right, buf );
364 /* by clause - select who has what access to entries */
365 } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
367 fprintf( stderr, "%s: line %d: "
368 "to clause required before by clause in access line\n",
374 * by clause consists of <who> and <access>
377 b = (Access *) ch_calloc( 1, sizeof(Access) );
379 ACL_INVALIDATE( b->a_access_mask );
383 "%s: line %d: premature eol: expecting <who>\n",
389 for ( ; i < argc; i++ ) {
390 slap_style_t sty = ACL_STYLE_REGEX;
391 char *style_modifier = NULL;
394 split( argv[i], '=', &left, &right );
395 split( left, '.', &left, &style );
397 split( style, ',', &style, &style_modifier);
400 if ( style == NULL || *style == '\0' ||
401 strcasecmp( style, "exact" ) == 0 ||
402 strcasecmp( style, "base" ) == 0 )
404 sty = ACL_STYLE_BASE;
406 } else if ( strcasecmp( style, "onelevel" ) == 0 ||
407 strcasecmp( style, "one" ) == 0 ) {
410 } else if ( strcasecmp( style, "subtree" ) == 0 ||
411 strcasecmp( style, "sub" ) == 0 )
413 sty = ACL_STYLE_SUBTREE;
415 } else if ( strcasecmp( style, "children" ) == 0 ) {
416 sty = ACL_STYLE_CHILDREN;
418 } else if ( strcasecmp( style, "regex" ) == 0 ) {
419 sty = ACL_STYLE_REGEX;
423 "%s: line %d: unknown style \"%s\" in by clause\n",
424 fname, lineno, style );
428 if ( style_modifier &&
429 strcasecmp( style_modifier, "expand" ) == 0 )
434 if ( strcasecmp( argv[i], "*" ) == 0 ) {
435 bv.bv_val = ch_strdup( "*" );
437 sty = ACL_STYLE_REGEX;
439 } else if ( strcasecmp( argv[i], "anonymous" ) == 0 ) {
440 ber_str2bv("anonymous", sizeof("anonymous")-1, 1, &bv);
441 sty = ACL_STYLE_REGEX;
443 } else if ( strcasecmp( argv[i], "self" ) == 0 ) {
444 ber_str2bv("self", sizeof("self")-1, 1, &bv);
445 sty = ACL_STYLE_REGEX;
447 } else if ( strcasecmp( argv[i], "users" ) == 0 ) {
448 ber_str2bv("users", sizeof("users")-1, 1, &bv);
449 sty = ACL_STYLE_REGEX;
451 } else if ( strcasecmp( left, "dn" ) == 0 ) {
452 if ( sty == ACL_STYLE_REGEX ) {
453 b->a_dn_style = ACL_STYLE_REGEX;
454 if( right == NULL ) {
459 } else if (*right == '\0' ) {
461 ber_str2bv("anonymous",
462 sizeof("anonymous")-1,
464 } else if ( strcmp( right, "*" ) == 0 ) {
466 /* any or users? users for now */
470 } else if ( strcmp( right, ".+" ) == 0
471 || strcmp( right, "^.+" ) == 0
472 || strcmp( right, ".+$" ) == 0
473 || strcmp( right, "^.+$" ) == 0
474 || strcmp( right, ".+$$" ) == 0
475 || strcmp( right, "^.+$$" ) == 0 )
480 } else if ( strcmp( right, ".*" ) == 0
481 || strcmp( right, "^.*" ) == 0
482 || strcmp( right, ".*$" ) == 0
483 || strcmp( right, "^.*$" ) == 0
484 || strcmp( right, ".*$$" ) == 0
485 || strcmp( right, "^.*$$" ) == 0 )
492 acl_regex_normalized_dn( right, &bv );
493 if ( !ber_bvccmp( &bv, '*' ) ) {
494 regtest(fname, lineno, bv.bv_val);
497 } else if ( right == NULL || *right == '\0' ) {
498 fprintf( stderr, "%s: line %d: "
499 "missing \"=\" in (or value after) \"%s\" "
501 fname, lineno, left );
505 ber_str2bv( right, 0, 1, &bv );
512 if( bv.bv_val != NULL ) {
513 if( b->a_dn_pat.bv_len != 0 ) {
515 "%s: line %d: dn pattern already specified.\n",
520 if ( sty != ACL_STYLE_REGEX && expand == 0 ) {
521 rc = dnNormalize(0, NULL, NULL,
522 &bv, &b->a_dn_pat, NULL);
523 if ( rc != LDAP_SUCCESS ) {
525 "%s: line %d: bad DN \"%s\" in by DN clause\n",
526 fname, lineno, bv.bv_val );
534 b->a_dn_expand = expand;
538 if ( strcasecmp( left, "dnattr" ) == 0 ) {
539 if ( right == NULL || right[ 0 ] == '\0' ) {
541 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
542 fname, lineno, left );
546 if( b->a_dn_at != NULL ) {
548 "%s: line %d: dnattr already specified.\n",
553 rc = slap_str2ad( right, &b->a_dn_at, &text );
555 if( rc != LDAP_SUCCESS ) {
557 "%s: line %d: dnattr \"%s\": %s\n",
558 fname, lineno, right, text );
563 if( !is_at_syntax( b->a_dn_at->ad_type,
565 !is_at_syntax( b->a_dn_at->ad_type,
566 SLAPD_NAMEUID_SYNTAX ))
569 "%s: line %d: dnattr \"%s\": "
570 "inappropriate syntax: %s\n",
571 fname, lineno, right,
572 b->a_dn_at->ad_type->sat_syntax_oid );
576 if( b->a_dn_at->ad_type->sat_equality == NULL ) {
578 "%s: line %d: dnattr \"%s\": "
579 "inappropriate matching (no EQUALITY)\n",
580 fname, lineno, right );
587 if ( strncasecmp( left, "group", sizeof("group")-1 ) == 0 ) {
591 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
592 fprintf( stderr, "%s: line %d: "
593 "inappropriate style \"%s\" in by clause\n",
594 fname, lineno, style );
598 if ( right == NULL || right[ 0 ] == '\0' ) {
599 fprintf( stderr, "%s: line %d: "
600 "missing \"=\" in (or value after) \"%s\" "
602 fname, lineno, left );
606 if( b->a_group_pat.bv_len ) {
608 "%s: line %d: group pattern already specified.\n",
613 /* format of string is
614 "group/objectClassValue/groupAttrName" */
615 if ((value = strchr(left, '/')) != NULL) {
617 if (*value && (name = strchr(value, '/')) != NULL) {
622 b->a_group_style = sty;
623 if (sty == ACL_STYLE_REGEX) {
624 acl_regex_normalized_dn( right, &bv );
625 if ( !ber_bvccmp( &bv, '*' ) ) {
626 regtest(fname, lineno, bv.bv_val);
630 ber_str2bv( right, 0, 0, &bv );
631 rc = dnNormalize( 0, NULL, NULL, &bv,
632 &b->a_group_pat, NULL );
633 if ( rc != LDAP_SUCCESS ) {
635 "%s: line %d: bad DN \"%s\"\n",
636 fname, lineno, right );
641 if (value && *value) {
642 b->a_group_oc = oc_find( value );
645 if( b->a_group_oc == NULL ) {
647 "%s: line %d: group objectclass "
649 fname, lineno, value );
653 b->a_group_oc = oc_find(SLAPD_GROUP_CLASS);
655 if( b->a_group_oc == NULL ) {
657 "%s: line %d: group default objectclass "
659 fname, lineno, SLAPD_GROUP_CLASS );
664 if( is_object_subclass( slap_schema.si_oc_referral,
668 "%s: line %d: group objectclass \"%s\" "
669 "is subclass of referral\n",
670 fname, lineno, value );
674 if( is_object_subclass( slap_schema.si_oc_alias,
678 "%s: line %d: group objectclass \"%s\" "
679 "is subclass of alias\n",
680 fname, lineno, value );
685 rc = slap_str2ad( name, &b->a_group_at, &text );
687 if( rc != LDAP_SUCCESS ) {
689 "%s: line %d: group \"%s\": %s\n",
690 fname, lineno, right, text );
695 rc = slap_str2ad( SLAPD_GROUP_ATTR, &b->a_group_at, &text );
697 if( rc != LDAP_SUCCESS ) {
699 "%s: line %d: group \"%s\": %s\n",
700 fname, lineno, SLAPD_GROUP_ATTR, text );
705 if( !is_at_syntax( b->a_group_at->ad_type,
707 !is_at_syntax( b->a_group_at->ad_type,
708 SLAPD_NAMEUID_SYNTAX ) &&
709 !is_at_subtype( b->a_group_at->ad_type, slap_schema.si_ad_labeledURI->ad_type ))
712 "%s: line %d: group \"%s\": inappropriate syntax: %s\n",
713 fname, lineno, right,
714 b->a_group_at->ad_type->sat_syntax_oid );
721 struct berval vals[2];
723 vals[0].bv_val = b->a_group_oc->soc_oid;
724 vals[0].bv_len = strlen(vals[0].bv_val);
725 vals[1].bv_val = NULL;
728 rc = oc_check_allowed( b->a_group_at->ad_type,
732 fprintf( stderr, "%s: line %d: "
733 "group: \"%s\" not allowed by \"%s\"\n",
735 b->a_group_at->ad_cname.bv_val,
736 b->a_group_oc->soc_oid );
743 if ( strcasecmp( left, "peername" ) == 0 ) {
744 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
745 fprintf( stderr, "%s: line %d: "
746 "inappropriate style \"%s\" in by clause\n",
747 fname, lineno, style );
751 if ( right == NULL || right[ 0 ] == '\0' ) {
752 fprintf( stderr, "%s: line %d: "
753 "missing \"=\" in (or value after) \"%s\" "
755 fname, lineno, left );
759 if( b->a_peername_pat.bv_len ) {
760 fprintf( stderr, "%s: line %d: "
761 "peername pattern already specified.\n",
766 b->a_peername_style = sty;
767 if (sty == ACL_STYLE_REGEX) {
768 acl_regex_normalized_dn( right, &bv );
769 if ( !ber_bvccmp( &bv, '*' ) ) {
770 regtest(fname, lineno, bv.bv_val);
772 b->a_peername_pat = bv;
774 ber_str2bv( right, 0, 1, &b->a_peername_pat );
779 if ( strcasecmp( left, "sockname" ) == 0 ) {
780 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
781 fprintf( stderr, "%s: line %d: "
782 "inappropriate style \"%s\" in by clause\n",
783 fname, lineno, style );
787 if ( right == NULL || right[ 0 ] == '\0' ) {
788 fprintf( stderr, "%s: line %d: "
789 "missing \"=\" in (or value after) \"%s\" "
791 fname, lineno, left );
795 if( b->a_sockname_pat.bv_len ) {
796 fprintf( stderr, "%s: line %d: "
797 "sockname pattern already specified.\n",
802 b->a_sockname_style = sty;
803 if (sty == ACL_STYLE_REGEX) {
804 acl_regex_normalized_dn( right, &bv );
805 if ( !ber_bvccmp( &bv, '*' ) ) {
806 regtest(fname, lineno, bv.bv_val);
808 b->a_sockname_pat = bv;
810 ber_str2bv( right, 0, 1, &b->a_sockname_pat );
815 if ( strcasecmp( left, "domain" ) == 0 ) {
817 case ACL_STYLE_REGEX:
819 case ACL_STYLE_SUBTREE:
824 "%s: line %d: inappropriate style \"%s\" in by clause\n",
825 fname, lineno, style );
829 if ( right == NULL || right[ 0 ] == '\0' ) {
831 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
832 fname, lineno, left );
836 if( b->a_domain_pat.bv_len ) {
838 "%s: line %d: domain pattern already specified.\n",
843 b->a_domain_style = sty;
844 b->a_domain_expand = expand;
845 if (sty == ACL_STYLE_REGEX) {
846 acl_regex_normalized_dn( right, &bv );
847 if ( !ber_bvccmp( &bv, '*' ) ) {
848 regtest(fname, lineno, bv.bv_val);
850 b->a_domain_pat = bv;
852 ber_str2bv( right, 0, 1, &b->a_domain_pat );
857 if ( strcasecmp( left, "sockurl" ) == 0 ) {
858 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
860 "%s: line %d: inappropriate style \"%s\" in by clause\n",
861 fname, lineno, style );
865 if ( right == NULL || right[ 0 ] == '\0' ) {
867 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
868 fname, lineno, left );
872 if( b->a_sockurl_pat.bv_len ) {
874 "%s: line %d: sockurl pattern already specified.\n",
879 b->a_sockurl_style = sty;
880 if (sty == ACL_STYLE_REGEX) {
881 acl_regex_normalized_dn( right, &bv );
882 if ( !ber_bvccmp( &bv, '*' ) ) {
883 regtest(fname, lineno, bv.bv_val);
885 b->a_sockurl_pat = bv;
887 ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
892 if ( strcasecmp( left, "set" ) == 0 ) {
893 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
895 "%s: line %d: inappropriate style \"%s\" in by clause\n",
896 fname, lineno, style );
900 if( b->a_set_pat.bv_len != 0 ) {
902 "%s: line %d: set attribute already specified.\n",
907 if ( right == NULL || *right == '\0' ) {
909 "%s: line %d: no set is defined\n",
914 b->a_set_style = sty;
915 ber_str2bv( right, 0, 1, &b->a_set_pat );
920 #ifdef SLAPD_ACI_ENABLED
921 if ( strcasecmp( left, "aci" ) == 0 ) {
922 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
924 "%s: line %d: inappropriate style \"%s\" in by clause\n",
925 fname, lineno, style );
929 if( b->a_aci_at != NULL ) {
931 "%s: line %d: aci attribute already specified.\n",
936 if ( right != NULL && *right != '\0' ) {
937 rc = slap_str2ad( right, &b->a_aci_at, &text );
939 if( rc != LDAP_SUCCESS ) {
941 "%s: line %d: aci \"%s\": %s\n",
942 fname, lineno, right, text );
947 b->a_aci_at = slap_schema.si_ad_aci;
950 if( !is_at_syntax( b->a_aci_at->ad_type,
954 "%s: line %d: aci \"%s\": inappropriate syntax: %s\n",
955 fname, lineno, right,
956 b->a_aci_at->ad_type->sat_syntax_oid );
962 #endif /* SLAPD_ACI_ENABLED */
964 if ( strcasecmp( left, "ssf" ) == 0 ) {
965 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
967 "%s: line %d: inappropriate style \"%s\" in by clause\n",
968 fname, lineno, style );
972 if( b->a_authz.sai_ssf ) {
974 "%s: line %d: ssf attribute already specified.\n",
979 if ( right == NULL || *right == '\0' ) {
981 "%s: line %d: no ssf is defined\n",
986 b->a_authz.sai_ssf = atoi( right );
988 if( !b->a_authz.sai_ssf ) {
990 "%s: line %d: invalid ssf value (%s)\n",
991 fname, lineno, right );
997 if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
998 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1000 "%s: line %d: inappropriate style \"%s\" in by clause\n",
1001 fname, lineno, style );
1005 if( b->a_authz.sai_transport_ssf ) {
1007 "%s: line %d: transport_ssf attribute already specified.\n",
1012 if ( right == NULL || *right == '\0' ) {
1014 "%s: line %d: no transport_ssf is defined\n",
1019 b->a_authz.sai_transport_ssf = atoi( right );
1021 if( !b->a_authz.sai_transport_ssf ) {
1023 "%s: line %d: invalid transport_ssf value (%s)\n",
1024 fname, lineno, right );
1030 if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
1031 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1033 "%s: line %d: inappropriate style \"%s\" in by clause\n",
1034 fname, lineno, style );
1038 if( b->a_authz.sai_tls_ssf ) {
1040 "%s: line %d: tls_ssf attribute already specified.\n",
1045 if ( right == NULL || *right == '\0' ) {
1047 "%s: line %d: no tls_ssf is defined\n",
1052 b->a_authz.sai_tls_ssf = atoi( right );
1054 if( !b->a_authz.sai_tls_ssf ) {
1056 "%s: line %d: invalid tls_ssf value (%s)\n",
1057 fname, lineno, right );
1063 if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
1064 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1066 "%s: line %d: inappropriate style \"%s\" in by clause\n",
1067 fname, lineno, style );
1071 if( b->a_authz.sai_sasl_ssf ) {
1073 "%s: line %d: sasl_ssf attribute already specified.\n",
1078 if ( right == NULL || *right == '\0' ) {
1080 "%s: line %d: no sasl_ssf is defined\n",
1085 b->a_authz.sai_sasl_ssf = atoi( right );
1087 if( !b->a_authz.sai_sasl_ssf ) {
1089 "%s: line %d: invalid sasl_ssf value (%s)\n",
1090 fname, lineno, right );
1096 if( right != NULL ) {
1103 if( i == argc || ( strcasecmp( left, "stop" ) == 0 )) {
1104 /* out of arguments or plain stop */
1106 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1107 b->a_type = ACL_STOP;
1109 access_append( &a->acl_access, b );
1113 if( strcasecmp( left, "continue" ) == 0 ) {
1114 /* plain continue */
1116 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1117 b->a_type = ACL_CONTINUE;
1119 access_append( &a->acl_access, b );
1123 if( strcasecmp( left, "break" ) == 0 ) {
1124 /* plain continue */
1126 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1127 b->a_type = ACL_BREAK;
1129 access_append( &a->acl_access, b );
1133 if ( strcasecmp( left, "by" ) == 0 ) {
1134 /* we've gone too far */
1136 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1137 b->a_type = ACL_STOP;
1139 access_append( &a->acl_access, b );
1144 if( strncasecmp( left, "self", 4 ) == 0 ) {
1146 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( &left[4] ) );
1149 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( left ) );
1152 if( ACL_IS_INVALID( b->a_access_mask ) ) {
1154 "%s: line %d: expecting <access> got \"%s\"\n",
1155 fname, lineno, left );
1159 b->a_type = ACL_STOP;
1162 /* out of arguments or plain stop */
1163 access_append( &a->acl_access, b );
1167 if( strcasecmp( argv[i], "continue" ) == 0 ) {
1168 /* plain continue */
1169 b->a_type = ACL_CONTINUE;
1171 } else if( strcasecmp( argv[i], "break" ) == 0 ) {
1172 /* plain continue */
1173 b->a_type = ACL_BREAK;
1175 } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
1180 access_append( &a->acl_access, b );
1184 "%s: line %d: expecting \"to\" or \"by\" got \"%s\"\n",
1185 fname, lineno, argv[i] );
1190 /* if we have no real access clause, complain and do nothing */
1193 "%s: line %d: warning: no access clause(s) specified in access line\n",
1198 if (ldap_debug & LDAP_DEBUG_ACL)
1202 if ( a->acl_access == NULL ) {
1204 "%s: line %d: warning: no by clause(s) specified in access line\n",
1209 acl_append( &be->be_acl, a );
1211 acl_append( &global_acl, a );
1217 accessmask2str( slap_mask_t mask, char *buf )
1222 assert( buf != NULL );
1224 if ( ACL_IS_INVALID( mask ) ) {
1230 if ( ACL_IS_LEVEL( mask ) ) {
1231 if ( ACL_LVL_IS_NONE(mask) ) {
1232 ptr = lutil_strcopy( ptr, "none" );
1234 } else if ( ACL_LVL_IS_AUTH(mask) ) {
1235 ptr = lutil_strcopy( ptr, "auth" );
1237 } else if ( ACL_LVL_IS_COMPARE(mask) ) {
1238 ptr = lutil_strcopy( ptr, "compare" );
1240 } else if ( ACL_LVL_IS_SEARCH(mask) ) {
1241 ptr = lutil_strcopy( ptr, "search" );
1243 } else if ( ACL_LVL_IS_READ(mask) ) {
1244 ptr = lutil_strcopy( ptr, "read" );
1246 } else if ( ACL_LVL_IS_WRITE(mask) ) {
1247 ptr = lutil_strcopy( ptr, "write" );
1249 ptr = lutil_strcopy( ptr, "unknown" );
1255 if( ACL_IS_ADDITIVE( mask ) ) {
1258 } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
1265 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
1270 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
1275 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
1280 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
1285 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
1290 if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
1299 if ( ACL_IS_LEVEL( mask ) ) {
1309 str2accessmask( const char *str )
1313 if( !ASCII_ALPHA(str[0]) ) {
1316 if ( str[0] == '=' ) {
1319 } else if( str[0] == '+' ) {
1320 ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
1322 } else if( str[0] == '-' ) {
1323 ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
1326 ACL_INVALIDATE(mask);
1330 for( i=1; str[i] != '\0'; i++ ) {
1331 if( TOLOWER((unsigned char) str[i]) == 'w' ) {
1332 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
1334 } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
1335 ACL_PRIV_SET(mask, ACL_PRIV_READ);
1337 } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
1338 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
1340 } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
1341 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
1343 } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
1344 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
1346 } else if( str[i] != '0' ) {
1347 ACL_INVALIDATE(mask);
1355 if ( strcasecmp( str, "none" ) == 0 ) {
1356 ACL_LVL_ASSIGN_NONE(mask);
1358 } else if ( strcasecmp( str, "auth" ) == 0 ) {
1359 ACL_LVL_ASSIGN_AUTH(mask);
1361 } else if ( strcasecmp( str, "compare" ) == 0 ) {
1362 ACL_LVL_ASSIGN_COMPARE(mask);
1364 } else if ( strcasecmp( str, "search" ) == 0 ) {
1365 ACL_LVL_ASSIGN_SEARCH(mask);
1367 } else if ( strcasecmp( str, "read" ) == 0 ) {
1368 ACL_LVL_ASSIGN_READ(mask);
1370 } else if ( strcasecmp( str, "write" ) == 0 ) {
1371 ACL_LVL_ASSIGN_WRITE(mask);
1374 ACL_INVALIDATE( mask );
1383 fprintf( stderr, "%s%s\n",
1384 "<access clause> ::= access to <what> "
1385 "[ by <who> <access> [ <control> ] ]+ \n"
1386 "<what> ::= * | [dn[.<dnstyle>]=<DN>] [filter=<filter>] [attrs=<attrlist>]\n"
1387 "<attrlist> ::= <attr> [val[.<style>]=<value>] | <attr> , <attrlist>\n"
1388 "<attr> ::= <attrname> | entry | children\n"
1389 "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
1390 "\t[dnattr=<attrname>]\n"
1391 "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
1392 "\t[peername[.<style>]=<peer>] [sockname[.<style>]=<name>]\n",
1393 "\t[domain[.<style>]=<domain>] [sockurl[.<style>]=<url>]\n"
1394 #ifdef SLAPD_ACI_ENABLED
1395 "\t[aci=<attrname>]\n"
1397 "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
1398 "<dnstyle> ::= base | exact | one | subtree | children | regex\n"
1399 "<style> ::= regex | base | exact\n"
1400 "<access> ::= [self]{<level>|<priv>}\n"
1401 "<level> ::= none | auth | compare | search | read | write\n"
1402 "<priv> ::= {=|+|-}{w|r|s|c|x|0}+\n"
1403 "<control> ::= [ stop | continue | break ]\n"
1405 exit( EXIT_FAILURE );
1409 * Set pattern to a "normalized" DN from src.
1410 * At present it simply eats the (optional) space after
1411 * a RDN separator (,)
1412 * Eventually will evolve in a more complete normalization
1415 acl_regex_normalized_dn(
1417 struct berval *pattern
1423 str = ch_strdup( src );
1424 len = strlen( src );
1426 for ( p = str; p && p[ 0 ]; p++ ) {
1428 if ( p[ 0 ] == '\\' && p[ 1 ] ) {
1430 * if escaping a hex pair we should
1431 * increment p twice; however, in that
1432 * case the second hex number does
1438 if ( p[ 0 ] == ',' ) {
1439 if ( p[ 1 ] == ' ' ) {
1443 * too much space should be
1444 * an error if we are pedantic
1446 for ( q = &p[ 2 ]; q[ 0 ] == ' '; q++ ) {
1449 AC_MEMCPY( p+1, q, len-(q-str)+1);
1453 pattern->bv_val = str;
1454 pattern->bv_len = p-str;
1468 if ( (*right = strchr( line, splitchar )) != NULL ) {
1469 *((*right)++) = '\0';
1474 access_append( Access **l, Access *a )
1476 for ( ; *l != NULL; l = &(*l)->a_next )
1483 acl_append( AccessControl **l, AccessControl *a )
1485 for ( ; *l != NULL; l = &(*l)->acl_next )
1492 access_free( Access *a )
1494 if ( a->a_dn_pat.bv_val )
1495 free ( a->a_dn_pat.bv_val );
1496 if ( a->a_peername_pat.bv_val )
1497 free ( a->a_peername_pat.bv_val );
1498 if ( a->a_sockname_pat.bv_val )
1499 free ( a->a_sockname_pat.bv_val );
1500 if ( a->a_domain_pat.bv_val )
1501 free ( a->a_domain_pat.bv_val );
1502 if ( a->a_sockurl_pat.bv_val )
1503 free ( a->a_sockurl_pat.bv_val );
1504 if ( a->a_set_pat.bv_len )
1505 free ( a->a_set_pat.bv_val );
1506 if ( a->a_group_pat.bv_len )
1507 free ( a->a_group_pat.bv_val );
1512 acl_free( AccessControl *a )
1517 if ( a->acl_filter )
1518 filter_free( a->acl_filter );
1519 if ( a->acl_dn_pat.bv_len )
1520 free ( a->acl_dn_pat.bv_val );
1521 if ( a->acl_attrs ) {
1522 for ( an = a->acl_attrs; an->an_name.bv_val; an++ ) {
1523 free( an->an_name.bv_val );
1525 free( a->acl_attrs );
1527 for (; a->acl_access; a->acl_access = n) {
1528 n = a->acl_access->a_next;
1529 access_free( a->acl_access );
1534 /* Because backend_startup uses acl_append to tack on the global_acl to
1535 * the end of each backend's acl, we cannot just take one argument and
1536 * merrily free our way to the end of the list. backend_destroy calls us
1537 * with the be_acl in arg1, and global_acl in arg2 to give us a stopping
1538 * point. config_destroy calls us with global_acl in arg1 and NULL in
1539 * arg2, so we then proceed to polish off the global_acl.
1542 acl_destroy( AccessControl *a, AccessControl *end )
1546 for (; a && a!= end; a=n) {
1553 access2str( slap_access_t access )
1555 if ( access == ACL_NONE ) {
1558 } else if ( access == ACL_AUTH ) {
1561 } else if ( access == ACL_COMPARE ) {
1564 } else if ( access == ACL_SEARCH ) {
1567 } else if ( access == ACL_READ ) {
1570 } else if ( access == ACL_WRITE ) {
1578 str2access( const char *str )
1580 if ( strcasecmp( str, "none" ) == 0 ) {
1583 } else if ( strcasecmp( str, "auth" ) == 0 ) {
1586 } else if ( strcasecmp( str, "compare" ) == 0 ) {
1589 } else if ( strcasecmp( str, "search" ) == 0 ) {
1592 } else if ( strcasecmp( str, "read" ) == 0 ) {
1595 } else if ( strcasecmp( str, "write" ) == 0 ) {
1599 return( ACL_INVALID_ACCESS );
1605 print_access( Access *b )
1607 char maskbuf[ACCESSMASK_MAXLEN];
1609 fprintf( stderr, "\tby" );
1611 if ( b->a_dn_pat.bv_len != 0 ) {
1612 if( strcmp(b->a_dn_pat.bv_val, "*") == 0
1613 || strcmp(b->a_dn_pat.bv_val, "users") == 0
1614 || strcmp(b->a_dn_pat.bv_val, "anonymous") == 0
1615 || strcmp(b->a_dn_pat.bv_val, "self") == 0 )
1617 fprintf( stderr, " %s", b->a_dn_pat.bv_val );
1620 fprintf( stderr, " dn.%s=\"%s\"",
1621 style_strings[b->a_dn_style], b->a_dn_pat.bv_val );
1625 if ( b->a_dn_at != NULL ) {
1626 fprintf( stderr, " dnattr=%s", b->a_dn_at->ad_cname.bv_val );
1629 if ( b->a_group_pat.bv_len ) {
1630 fprintf( stderr, " group/%s/%s.%s=\"%s\"",
1631 b->a_group_oc ? b->a_group_oc->soc_cname.bv_val : "groupOfNames",
1632 b->a_group_at ? b->a_group_at->ad_cname.bv_val : "member",
1633 style_strings[b->a_group_style],
1634 b->a_group_pat.bv_val );
1637 if ( b->a_peername_pat.bv_len != 0 ) {
1638 fprintf( stderr, " peername=\"%s\"", b->a_peername_pat.bv_val );
1641 if ( b->a_sockname_pat.bv_len != 0 ) {
1642 fprintf( stderr, " sockname=\"%s\"", b->a_sockname_pat.bv_val );
1645 if ( b->a_domain_pat.bv_len != 0 ) {
1646 fprintf( stderr, " domain=%s", b->a_domain_pat.bv_val );
1649 if ( b->a_sockurl_pat.bv_len != 0 ) {
1650 fprintf( stderr, " sockurl=\"%s\"", b->a_sockurl_pat.bv_val );
1653 #ifdef SLAPD_ACI_ENABLED
1654 if ( b->a_aci_at != NULL ) {
1655 fprintf( stderr, " aci=%s", b->a_aci_at->ad_cname.bv_val );
1659 /* Security Strength Factors */
1660 if ( b->a_authz.sai_ssf ) {
1661 fprintf( stderr, " ssf=%u",
1662 b->a_authz.sai_ssf );
1664 if ( b->a_authz.sai_transport_ssf ) {
1665 fprintf( stderr, " transport_ssf=%u",
1666 b->a_authz.sai_transport_ssf );
1668 if ( b->a_authz.sai_tls_ssf ) {
1669 fprintf( stderr, " tls_ssf=%u",
1670 b->a_authz.sai_tls_ssf );
1672 if ( b->a_authz.sai_sasl_ssf ) {
1673 fprintf( stderr, " sasl_ssf=%u",
1674 b->a_authz.sai_sasl_ssf );
1677 fprintf( stderr, " %s%s",
1678 b->a_dn_self ? "self" : "",
1679 accessmask2str( b->a_access_mask, maskbuf ) );
1681 if( b->a_type == ACL_BREAK ) {
1682 fprintf( stderr, " break" );
1684 } else if( b->a_type == ACL_CONTINUE ) {
1685 fprintf( stderr, " continue" );
1687 } else if( b->a_type != ACL_STOP ) {
1688 fprintf( stderr, " unknown-control" );
1691 fprintf( stderr, "\n" );
1696 print_acl( Backend *be, AccessControl *a )
1701 fprintf( stderr, "%s ACL: access to",
1702 be == NULL ? "Global" : "Backend" );
1704 if ( a->acl_dn_pat.bv_len != 0 ) {
1706 fprintf( stderr, " dn.%s=\"%s\"\n",
1707 style_strings[a->acl_dn_style], a->acl_dn_pat.bv_val );
1710 if ( a->acl_filter != NULL ) {
1711 struct berval bv = { 0, NULL };
1713 filter2bv( a->acl_filter, &bv );
1714 fprintf( stderr, " filter=%s\n", bv.bv_val );
1715 ch_free( bv.bv_val );
1718 if ( a->acl_attrs != NULL ) {
1723 fprintf( stderr, " attrs=" );
1724 for ( an = a->acl_attrs; an && an->an_name.bv_val; an++ ) {
1726 fprintf( stderr, "," );
1729 fputc( an->an_oc_exclude ? '!' : '@', stderr);
1731 fputs( an->an_name.bv_val, stderr );
1734 fprintf( stderr, "\n" );
1737 if ( a->acl_attrval.bv_len != 0 ) {
1739 fprintf( stderr, " val.%s=\"%s\"\n",
1740 style_strings[a->acl_attrval_style], a->acl_attrval.bv_val );
1745 fprintf( stderr, " *\n" );
1748 for ( b = a->acl_access; b != NULL; b = b->a_next ) {
1752 fprintf( stderr, "\n" );
1755 #endif /* LDAP_DEBUG */