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[] = {
53 static void split(char *line, int splitchar, char **left, char **right);
54 static void access_append(Access **l, Access *a);
55 static void acl_usage(void) LDAP_GCCATTR((noreturn));
57 static void acl_regex_normalized_dn(const char *src, struct berval *pat);
60 static void print_acl(Backend *be, AccessControl *a);
61 static void print_access(Access *b);
65 regtest(const char *fname, int lineno, char *pat) {
81 for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
83 if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
100 if ( size >= (sizeof(buf)-1) ) {
102 "%s: line %d: regular expression \"%s\" too large\n",
103 fname, lineno, pat );
107 if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
109 regerror(e, &re, error, sizeof(error));
111 "%s: line %d: regular expression \"%s\" bad because of %s\n",
112 fname, lineno, pat, error );
128 char *left, *right, *style;
136 for ( i = 1; i < argc; i++ ) {
137 /* to clause - select which entries are protected */
138 if ( strcasecmp( argv[i], "to" ) == 0 ) {
140 fprintf( stderr, "%s: line %d: "
141 "only one to clause allowed in access line\n",
145 a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
146 for ( ++i; i < argc; i++ ) {
147 if ( strcasecmp( argv[i], "by" ) == 0 ) {
152 if ( strcasecmp( argv[i], "*" ) == 0 ) {
153 if( a->acl_dn_pat.bv_len ||
154 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
157 "%s: line %d: dn pattern"
158 " already specified in to clause.\n",
163 a->acl_dn_pat.bv_val = ch_strdup( "*" );
164 a->acl_dn_pat.bv_len = 1;
168 split( argv[i], '=', &left, &right );
169 split( left, '.', &left, &style );
171 if ( right == NULL ) {
172 fprintf( stderr, "%s: line %d: "
173 "missing \"=\" in \"%s\" in to clause\n",
174 fname, lineno, left );
178 if ( strcasecmp( left, "dn" ) == 0 ) {
179 if( a->acl_dn_pat.bv_len != 0 ||
180 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
183 "%s: line %d: dn pattern"
184 " already specified in to clause.\n",
189 if ( style == NULL || *style == '\0' ||
190 ( strcasecmp( style, "base" ) == 0 ) ||
191 ( strcasecmp( style, "exact" ) == 0 ))
193 a->acl_dn_style = ACL_STYLE_BASE;
194 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
196 } else if ( strcasecmp( style, "onelevel" ) == 0
197 || strcasecmp( style, "one" ) == 0 ) {
198 a->acl_dn_style = ACL_STYLE_ONE;
199 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
201 } else if ( strcasecmp( style, "subtree" ) == 0
202 || strcasecmp( style, "sub" ) == 0 )
204 if( *right == '\0' ) {
205 a->acl_dn_pat.bv_val = ch_strdup( "*" );
206 a->acl_dn_pat.bv_len = 1;
209 a->acl_dn_style = ACL_STYLE_SUBTREE;
210 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
213 } else if ( strcasecmp( style, "children" ) == 0 ) {
214 a->acl_dn_style = ACL_STYLE_CHILDREN;
215 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
217 } else if ( strcasecmp( style, "regex" ) == 0 ) {
218 a->acl_dn_style = ACL_STYLE_REGEX;
220 if ( *right == '\0' ) {
221 /* empty regex should match empty DN */
222 a->acl_dn_style = ACL_STYLE_BASE;
223 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
225 } else if ( strcmp(right, "*") == 0
226 || strcmp(right, ".*") == 0
227 || strcmp(right, ".*$") == 0
228 || strcmp(right, "^.*") == 0
229 || strcmp(right, "^.*$") == 0
230 || strcmp(right, ".*$$") == 0
231 || strcmp(right, "^.*$$") == 0 )
233 a->acl_dn_pat.bv_val = ch_strdup( "*" );
234 a->acl_dn_pat.bv_len = sizeof("*")-1;
237 acl_regex_normalized_dn( right, &a->acl_dn_pat );
241 fprintf( stderr, "%s: line %d: "
242 "unknown dn style \"%s\" in to clause\n",
243 fname, lineno, style );
250 if ( strcasecmp( left, "filter" ) == 0 ) {
251 if ( (a->acl_filter = str2filter( right )) == NULL ) {
253 "%s: line %d: bad filter \"%s\" in to clause\n",
254 fname, lineno, right );
258 } else if ( strcasecmp( left, "attr" ) == 0
259 || strcasecmp( left, "attrs" ) == 0 ) {
260 a->acl_attrs = str2anlist( a->acl_attrs,
262 if ( a->acl_attrs == NULL ) {
264 "%s: line %d: unknown attr \"%s\" in to clause\n",
265 fname, lineno, right );
269 } else if ( strncasecmp( left, "val", 3 ) == 0 ) {
270 if ( a->acl_attrval.bv_len ) {
272 "%s: line %d: attr val already specified in to clause.\n",
276 if ( a->acl_attrs == NULL || a->acl_attrs[1].an_name.bv_val ) {
278 "%s: line %d: attr val requires a single attribute.\n",
282 ber_str2bv( right, 0, 1, &a->acl_attrval );
283 if ( style && strcasecmp( style, "regex" ) == 0 ) {
284 int e = regcomp( &a->acl_attrval_re, a->acl_attrval.bv_val,
285 REG_EXTENDED | REG_ICASE | REG_NOSUB );
288 regerror( e, &a->acl_attrval_re, buf, sizeof(buf) );
289 fprintf( stderr, "%s: line %d: "
290 "regular expression \"%s\" bad because of %s\n",
291 fname, lineno, right, buf );
294 a->acl_attrval_style = ACL_STYLE_REGEX;
296 /* FIXME: if the attribute has DN syntax,
297 * we might allow one, subtree and children styles as well */
298 if ( !strcasecmp( style, "exact" ) ) {
299 a->acl_attrval_style = ACL_STYLE_BASE;
301 } else if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) {
302 if ( !strcasecmp( style, "base" ) ) {
303 a->acl_attrval_style = ACL_STYLE_BASE;
304 } else if ( !strcasecmp( style, "onelevel" ) || !strcasecmp( style, "one" ) ) {
305 a->acl_attrval_style = ACL_STYLE_ONE;
306 } else if ( !strcasecmp( style, "subtree" ) || !strcasecmp( style, "sub" ) ) {
307 a->acl_attrval_style = ACL_STYLE_SUBTREE;
308 } else if ( !strcasecmp( style, "children" ) ) {
309 a->acl_attrval_style = ACL_STYLE_CHILDREN;
312 "%s: line %d: unknown val.<style> \"%s\" "
313 "for attributeType \"%s\" with DN syntax; using \"base\"\n",
314 fname, lineno, style,
315 a->acl_attrs[0].an_desc->ad_cname.bv_val );
316 a->acl_attrval_style = ACL_STYLE_BASE;
321 "%s: line %d: unknown val.<style> \"%s\" "
322 "for attributeType \"%s\"; using \"exact\"\n",
323 fname, lineno, style,
324 a->acl_attrs[0].an_desc->ad_cname.bv_val );
325 a->acl_attrval_style = ACL_STYLE_BASE;
331 "%s: line %d: expecting <what> got \"%s\"\n",
332 fname, lineno, left );
337 if ( a->acl_dn_pat.bv_len != 0 &&
338 strcmp(a->acl_dn_pat.bv_val, "*") == 0 )
340 free( a->acl_dn_pat.bv_val );
341 a->acl_dn_pat.bv_val = NULL;
342 a->acl_dn_pat.bv_len = 0;
345 if( a->acl_dn_pat.bv_len != 0 ||
346 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
348 if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
350 rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
351 if ( rc != LDAP_SUCCESS ) {
353 "%s: line %d: bad DN \"%s\" in to DN clause\n",
354 fname, lineno, a->acl_dn_pat.bv_val );
357 free( a->acl_dn_pat.bv_val );
360 int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
361 REG_EXTENDED | REG_ICASE );
364 regerror( e, &a->acl_dn_re, buf, sizeof(buf) );
365 fprintf( stderr, "%s: line %d: "
366 "regular expression \"%s\" bad because of %s\n",
367 fname, lineno, right, buf );
373 /* by clause - select who has what access to entries */
374 } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
376 fprintf( stderr, "%s: line %d: "
377 "to clause required before by clause in access line\n",
383 * by clause consists of <who> and <access>
386 b = (Access *) ch_calloc( 1, sizeof(Access) );
388 ACL_INVALIDATE( b->a_access_mask );
392 "%s: line %d: premature eol: expecting <who>\n",
398 for ( ; i < argc; i++ ) {
399 slap_style_t sty = ACL_STYLE_REGEX;
400 char *style_modifier = NULL;
403 split( argv[i], '=', &left, &right );
404 split( left, '.', &left, &style );
406 split( style, ',', &style, &style_modifier);
409 if ( style == NULL || *style == '\0' ||
410 strcasecmp( style, "exact" ) == 0 ||
411 strcasecmp( style, "base" ) == 0 )
413 sty = ACL_STYLE_BASE;
415 } else if ( strcasecmp( style, "onelevel" ) == 0 ||
416 strcasecmp( style, "one" ) == 0 ) {
419 } else if ( strcasecmp( style, "subtree" ) == 0 ||
420 strcasecmp( style, "sub" ) == 0 )
422 sty = ACL_STYLE_SUBTREE;
424 } else if ( strcasecmp( style, "children" ) == 0 ) {
425 sty = ACL_STYLE_CHILDREN;
427 } else if ( strcasecmp( style, "regex" ) == 0 ) {
428 sty = ACL_STYLE_REGEX;
430 } else if ( strcasecmp( style, "ip" ) == 0 ) {
433 } else if ( strcasecmp( style, "path" ) == 0 ) {
434 sty = ACL_STYLE_PATH;
435 #ifndef LDAP_PF_LOCAL
436 fprintf( stderr, "%s: line %d: "
437 "path style modifier is useless without local\n",
439 #endif /* LDAP_PF_LOCAL */
443 "%s: line %d: unknown style \"%s\" in by clause\n",
444 fname, lineno, style );
448 if ( style_modifier &&
449 strcasecmp( style_modifier, "expand" ) == 0 )
454 if ( strcasecmp( argv[i], "*" ) == 0 ) {
455 bv.bv_val = ch_strdup( "*" );
457 sty = ACL_STYLE_REGEX;
459 } else if ( strcasecmp( argv[i], "anonymous" ) == 0 ) {
460 ber_str2bv("anonymous", sizeof("anonymous")-1, 1, &bv);
461 sty = ACL_STYLE_REGEX;
463 } else if ( strcasecmp( argv[i], "self" ) == 0 ) {
464 ber_str2bv("self", sizeof("self")-1, 1, &bv);
465 sty = ACL_STYLE_REGEX;
467 } else if ( strcasecmp( argv[i], "users" ) == 0 ) {
468 ber_str2bv("users", sizeof("users")-1, 1, &bv);
469 sty = ACL_STYLE_REGEX;
471 } else if ( strcasecmp( left, "dn" ) == 0 ) {
472 if ( sty == ACL_STYLE_REGEX ) {
473 b->a_dn_style = ACL_STYLE_REGEX;
474 if( right == NULL ) {
479 } else if (*right == '\0' ) {
481 ber_str2bv("anonymous",
482 sizeof("anonymous")-1,
484 } else if ( strcmp( right, "*" ) == 0 ) {
486 /* any or users? users for now */
490 } else if ( strcmp( right, ".+" ) == 0
491 || strcmp( right, "^.+" ) == 0
492 || strcmp( right, ".+$" ) == 0
493 || strcmp( right, "^.+$" ) == 0
494 || strcmp( right, ".+$$" ) == 0
495 || strcmp( right, "^.+$$" ) == 0 )
500 } else if ( strcmp( right, ".*" ) == 0
501 || strcmp( right, "^.*" ) == 0
502 || strcmp( right, ".*$" ) == 0
503 || strcmp( right, "^.*$" ) == 0
504 || strcmp( right, ".*$$" ) == 0
505 || strcmp( right, "^.*$$" ) == 0 )
512 acl_regex_normalized_dn( right, &bv );
513 if ( !ber_bvccmp( &bv, '*' ) ) {
514 regtest(fname, lineno, bv.bv_val);
517 } else if ( right == NULL || *right == '\0' ) {
518 fprintf( stderr, "%s: line %d: "
519 "missing \"=\" in (or value after) \"%s\" "
521 fname, lineno, left );
525 ber_str2bv( right, 0, 1, &bv );
532 if( bv.bv_val != NULL ) {
533 if( b->a_dn_pat.bv_len != 0 ) {
535 "%s: line %d: dn pattern already specified.\n",
540 if ( sty != ACL_STYLE_REGEX && expand == 0 ) {
541 rc = dnNormalize(0, NULL, NULL,
542 &bv, &b->a_dn_pat, NULL);
543 if ( rc != LDAP_SUCCESS ) {
545 "%s: line %d: bad DN \"%s\" in by DN clause\n",
546 fname, lineno, bv.bv_val );
554 b->a_dn_expand = expand;
558 if ( strcasecmp( left, "dnattr" ) == 0 ) {
559 if ( right == NULL || right[ 0 ] == '\0' ) {
561 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
562 fname, lineno, left );
566 if( b->a_dn_at != NULL ) {
568 "%s: line %d: dnattr already specified.\n",
573 rc = slap_str2ad( right, &b->a_dn_at, &text );
575 if( rc != LDAP_SUCCESS ) {
577 "%s: line %d: dnattr \"%s\": %s\n",
578 fname, lineno, right, text );
583 if( !is_at_syntax( b->a_dn_at->ad_type,
585 !is_at_syntax( b->a_dn_at->ad_type,
586 SLAPD_NAMEUID_SYNTAX ))
589 "%s: line %d: dnattr \"%s\": "
590 "inappropriate syntax: %s\n",
591 fname, lineno, right,
592 b->a_dn_at->ad_type->sat_syntax_oid );
596 if( b->a_dn_at->ad_type->sat_equality == NULL ) {
598 "%s: line %d: dnattr \"%s\": "
599 "inappropriate matching (no EQUALITY)\n",
600 fname, lineno, right );
607 if ( strncasecmp( left, "group", sizeof("group")-1 ) == 0 ) {
611 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
612 fprintf( stderr, "%s: line %d: "
613 "inappropriate style \"%s\" in by clause\n",
614 fname, lineno, style );
618 if ( right == NULL || right[ 0 ] == '\0' ) {
619 fprintf( stderr, "%s: line %d: "
620 "missing \"=\" in (or value after) \"%s\" "
622 fname, lineno, left );
626 if( b->a_group_pat.bv_len ) {
628 "%s: line %d: group pattern already specified.\n",
633 /* format of string is
634 "group/objectClassValue/groupAttrName" */
635 if ((value = strchr(left, '/')) != NULL) {
637 if (*value && (name = strchr(value, '/')) != NULL) {
642 b->a_group_style = sty;
643 if (sty == ACL_STYLE_REGEX) {
644 acl_regex_normalized_dn( right, &bv );
645 if ( !ber_bvccmp( &bv, '*' ) ) {
646 regtest(fname, lineno, bv.bv_val);
650 ber_str2bv( right, 0, 0, &bv );
651 rc = dnNormalize( 0, NULL, NULL, &bv,
652 &b->a_group_pat, NULL );
653 if ( rc != LDAP_SUCCESS ) {
655 "%s: line %d: bad DN \"%s\"\n",
656 fname, lineno, right );
661 if (value && *value) {
662 b->a_group_oc = oc_find( value );
665 if( b->a_group_oc == NULL ) {
667 "%s: line %d: group objectclass "
669 fname, lineno, value );
673 b->a_group_oc = oc_find(SLAPD_GROUP_CLASS);
675 if( b->a_group_oc == NULL ) {
677 "%s: line %d: group default objectclass "
679 fname, lineno, SLAPD_GROUP_CLASS );
684 if( is_object_subclass( slap_schema.si_oc_referral,
688 "%s: line %d: group objectclass \"%s\" "
689 "is subclass of referral\n",
690 fname, lineno, value );
694 if( is_object_subclass( slap_schema.si_oc_alias,
698 "%s: line %d: group objectclass \"%s\" "
699 "is subclass of alias\n",
700 fname, lineno, value );
705 rc = slap_str2ad( name, &b->a_group_at, &text );
707 if( rc != LDAP_SUCCESS ) {
709 "%s: line %d: group \"%s\": %s\n",
710 fname, lineno, right, text );
715 rc = slap_str2ad( SLAPD_GROUP_ATTR, &b->a_group_at, &text );
717 if( rc != LDAP_SUCCESS ) {
719 "%s: line %d: group \"%s\": %s\n",
720 fname, lineno, SLAPD_GROUP_ATTR, text );
725 if( !is_at_syntax( b->a_group_at->ad_type,
727 !is_at_syntax( b->a_group_at->ad_type,
728 SLAPD_NAMEUID_SYNTAX ) &&
729 !is_at_subtype( b->a_group_at->ad_type, slap_schema.si_ad_labeledURI->ad_type ))
732 "%s: line %d: group \"%s\": inappropriate syntax: %s\n",
733 fname, lineno, right,
734 b->a_group_at->ad_type->sat_syntax_oid );
741 struct berval vals[2];
743 vals[0].bv_val = b->a_group_oc->soc_oid;
744 vals[0].bv_len = strlen(vals[0].bv_val);
745 vals[1].bv_val = NULL;
748 rc = oc_check_allowed( b->a_group_at->ad_type,
752 fprintf( stderr, "%s: line %d: "
753 "group: \"%s\" not allowed by \"%s\"\n",
755 b->a_group_at->ad_cname.bv_val,
756 b->a_group_oc->soc_oid );
763 if ( strcasecmp( left, "peername" ) == 0 ) {
765 case ACL_STYLE_REGEX:
772 fprintf( stderr, "%s: line %d: "
773 "inappropriate style \"%s\" in by clause\n",
774 fname, lineno, style );
778 if ( right == NULL || right[ 0 ] == '\0' ) {
779 fprintf( stderr, "%s: line %d: "
780 "missing \"=\" in (or value after) \"%s\" "
782 fname, lineno, left );
786 if( b->a_peername_pat.bv_len ) {
787 fprintf( stderr, "%s: line %d: "
788 "peername pattern already specified.\n",
793 b->a_peername_style = sty;
794 if (sty == ACL_STYLE_REGEX) {
795 acl_regex_normalized_dn( right, &bv );
796 if ( !ber_bvccmp( &bv, '*' ) ) {
797 regtest(fname, lineno, bv.bv_val);
799 b->a_peername_pat = bv;
802 ber_str2bv( right, 0, 1, &b->a_peername_pat );
804 if ( sty == ACL_STYLE_IP ) {
809 split( right, '{', &addr, &port );
810 split( addr, '%', &addr, &mask );
812 b->a_peername_addr = inet_addr( addr );
813 if ( b->a_peername_addr == (unsigned long)(-1)) {
814 /* illegal address */
815 fprintf( stderr, "%s: line %d: "
816 "illegal peername address \"%s\".\n",
817 fname, lineno, addr );
821 b->a_peername_mask = (unsigned long)(-1);
822 if ( mask != NULL ) {
823 b->a_peername_mask = inet_addr( mask );
824 if ( b->a_peername_mask == (unsigned long)(-1)) {
826 fprintf( stderr, "%s: line %d: "
827 "illegal peername address mask \"%s\".\n",
828 fname, lineno, mask );
833 b->a_peername_port = -1;
837 b->a_peername_port = strtol( port, &end, 10 );
838 if ( end[ 0 ] != '}' ) {
840 fprintf( stderr, "%s: line %d: "
841 "illegal peername port specification \"{%s}\".\n",
842 fname, lineno, port );
851 if ( strcasecmp( left, "sockname" ) == 0 ) {
852 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
853 fprintf( stderr, "%s: line %d: "
854 "inappropriate style \"%s\" in by clause\n",
855 fname, lineno, style );
859 if ( right == NULL || right[ 0 ] == '\0' ) {
860 fprintf( stderr, "%s: line %d: "
861 "missing \"=\" in (or value after) \"%s\" "
863 fname, lineno, left );
867 if( b->a_sockname_pat.bv_len ) {
868 fprintf( stderr, "%s: line %d: "
869 "sockname pattern already specified.\n",
874 b->a_sockname_style = sty;
875 if (sty == ACL_STYLE_REGEX) {
876 acl_regex_normalized_dn( right, &bv );
877 if ( !ber_bvccmp( &bv, '*' ) ) {
878 regtest(fname, lineno, bv.bv_val);
880 b->a_sockname_pat = bv;
882 ber_str2bv( right, 0, 1, &b->a_sockname_pat );
887 if ( strcasecmp( left, "domain" ) == 0 ) {
889 case ACL_STYLE_REGEX:
891 case ACL_STYLE_SUBTREE:
896 "%s: line %d: inappropriate style \"%s\" in by clause\n",
897 fname, lineno, style );
901 if ( right == NULL || right[ 0 ] == '\0' ) {
903 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
904 fname, lineno, left );
908 if( b->a_domain_pat.bv_len ) {
910 "%s: line %d: domain pattern already specified.\n",
915 b->a_domain_style = sty;
916 b->a_domain_expand = expand;
917 if (sty == ACL_STYLE_REGEX) {
918 acl_regex_normalized_dn( right, &bv );
919 if ( !ber_bvccmp( &bv, '*' ) ) {
920 regtest(fname, lineno, bv.bv_val);
922 b->a_domain_pat = bv;
924 ber_str2bv( right, 0, 1, &b->a_domain_pat );
929 if ( strcasecmp( left, "sockurl" ) == 0 ) {
930 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
932 "%s: line %d: inappropriate style \"%s\" in by clause\n",
933 fname, lineno, style );
937 if ( right == NULL || right[ 0 ] == '\0' ) {
939 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
940 fname, lineno, left );
944 if( b->a_sockurl_pat.bv_len ) {
946 "%s: line %d: sockurl pattern already specified.\n",
951 b->a_sockurl_style = sty;
952 if (sty == ACL_STYLE_REGEX) {
953 acl_regex_normalized_dn( right, &bv );
954 if ( !ber_bvccmp( &bv, '*' ) ) {
955 regtest(fname, lineno, bv.bv_val);
957 b->a_sockurl_pat = bv;
959 ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
964 if ( strcasecmp( left, "set" ) == 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_set_pat.bv_len != 0 ) {
974 "%s: line %d: set attribute already specified.\n",
979 if ( right == NULL || *right == '\0' ) {
981 "%s: line %d: no set is defined\n",
986 b->a_set_style = sty;
987 ber_str2bv( right, 0, 1, &b->a_set_pat );
992 #ifdef SLAPD_ACI_ENABLED
993 if ( strcasecmp( left, "aci" ) == 0 ) {
994 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
996 "%s: line %d: inappropriate style \"%s\" in by clause\n",
997 fname, lineno, style );
1001 if( b->a_aci_at != NULL ) {
1003 "%s: line %d: aci attribute already specified.\n",
1008 if ( right != NULL && *right != '\0' ) {
1009 rc = slap_str2ad( right, &b->a_aci_at, &text );
1011 if( rc != LDAP_SUCCESS ) {
1013 "%s: line %d: aci \"%s\": %s\n",
1014 fname, lineno, right, text );
1019 b->a_aci_at = slap_schema.si_ad_aci;
1022 if( !is_at_syntax( b->a_aci_at->ad_type,
1026 "%s: line %d: aci \"%s\": inappropriate syntax: %s\n",
1027 fname, lineno, right,
1028 b->a_aci_at->ad_type->sat_syntax_oid );
1034 #endif /* SLAPD_ACI_ENABLED */
1036 if ( strcasecmp( left, "ssf" ) == 0 ) {
1037 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1039 "%s: line %d: inappropriate style \"%s\" in by clause\n",
1040 fname, lineno, style );
1044 if( b->a_authz.sai_ssf ) {
1046 "%s: line %d: ssf attribute already specified.\n",
1051 if ( right == NULL || *right == '\0' ) {
1053 "%s: line %d: no ssf is defined\n",
1058 b->a_authz.sai_ssf = atoi( right );
1060 if( !b->a_authz.sai_ssf ) {
1062 "%s: line %d: invalid ssf value (%s)\n",
1063 fname, lineno, right );
1069 if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
1070 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1072 "%s: line %d: inappropriate style \"%s\" in by clause\n",
1073 fname, lineno, style );
1077 if( b->a_authz.sai_transport_ssf ) {
1079 "%s: line %d: transport_ssf attribute already specified.\n",
1084 if ( right == NULL || *right == '\0' ) {
1086 "%s: line %d: no transport_ssf is defined\n",
1091 b->a_authz.sai_transport_ssf = atoi( right );
1093 if( !b->a_authz.sai_transport_ssf ) {
1095 "%s: line %d: invalid transport_ssf value (%s)\n",
1096 fname, lineno, right );
1102 if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
1103 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1105 "%s: line %d: inappropriate style \"%s\" in by clause\n",
1106 fname, lineno, style );
1110 if( b->a_authz.sai_tls_ssf ) {
1112 "%s: line %d: tls_ssf attribute already specified.\n",
1117 if ( right == NULL || *right == '\0' ) {
1119 "%s: line %d: no tls_ssf is defined\n",
1124 b->a_authz.sai_tls_ssf = atoi( right );
1126 if( !b->a_authz.sai_tls_ssf ) {
1128 "%s: line %d: invalid tls_ssf value (%s)\n",
1129 fname, lineno, right );
1135 if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
1136 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1138 "%s: line %d: inappropriate style \"%s\" in by clause\n",
1139 fname, lineno, style );
1143 if( b->a_authz.sai_sasl_ssf ) {
1145 "%s: line %d: sasl_ssf attribute already specified.\n",
1150 if ( right == NULL || *right == '\0' ) {
1152 "%s: line %d: no sasl_ssf is defined\n",
1157 b->a_authz.sai_sasl_ssf = atoi( right );
1159 if( !b->a_authz.sai_sasl_ssf ) {
1161 "%s: line %d: invalid sasl_ssf value (%s)\n",
1162 fname, lineno, right );
1168 if( right != NULL ) {
1175 if( i == argc || ( strcasecmp( left, "stop" ) == 0 )) {
1176 /* out of arguments or plain stop */
1178 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1179 b->a_type = ACL_STOP;
1181 access_append( &a->acl_access, b );
1185 if( strcasecmp( left, "continue" ) == 0 ) {
1186 /* plain continue */
1188 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1189 b->a_type = ACL_CONTINUE;
1191 access_append( &a->acl_access, b );
1195 if( strcasecmp( left, "break" ) == 0 ) {
1196 /* plain continue */
1198 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1199 b->a_type = ACL_BREAK;
1201 access_append( &a->acl_access, b );
1205 if ( strcasecmp( left, "by" ) == 0 ) {
1206 /* we've gone too far */
1208 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1209 b->a_type = ACL_STOP;
1211 access_append( &a->acl_access, b );
1216 if( strncasecmp( left, "self", 4 ) == 0 ) {
1218 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( &left[4] ) );
1221 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( left ) );
1224 if( ACL_IS_INVALID( b->a_access_mask ) ) {
1226 "%s: line %d: expecting <access> got \"%s\"\n",
1227 fname, lineno, left );
1231 b->a_type = ACL_STOP;
1234 /* out of arguments or plain stop */
1235 access_append( &a->acl_access, b );
1239 if( strcasecmp( argv[i], "continue" ) == 0 ) {
1240 /* plain continue */
1241 b->a_type = ACL_CONTINUE;
1243 } else if( strcasecmp( argv[i], "break" ) == 0 ) {
1244 /* plain continue */
1245 b->a_type = ACL_BREAK;
1247 } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
1252 access_append( &a->acl_access, b );
1256 "%s: line %d: expecting \"to\" or \"by\" got \"%s\"\n",
1257 fname, lineno, argv[i] );
1262 /* if we have no real access clause, complain and do nothing */
1265 "%s: line %d: warning: no access clause(s) specified in access line\n",
1270 if (ldap_debug & LDAP_DEBUG_ACL)
1274 if ( a->acl_access == NULL ) {
1276 "%s: line %d: warning: no by clause(s) specified in access line\n",
1281 acl_append( &be->be_acl, a );
1283 acl_append( &global_acl, a );
1289 accessmask2str( slap_mask_t mask, char *buf )
1294 assert( buf != NULL );
1296 if ( ACL_IS_INVALID( mask ) ) {
1302 if ( ACL_IS_LEVEL( mask ) ) {
1303 if ( ACL_LVL_IS_NONE(mask) ) {
1304 ptr = lutil_strcopy( ptr, "none" );
1306 } else if ( ACL_LVL_IS_AUTH(mask) ) {
1307 ptr = lutil_strcopy( ptr, "auth" );
1309 } else if ( ACL_LVL_IS_COMPARE(mask) ) {
1310 ptr = lutil_strcopy( ptr, "compare" );
1312 } else if ( ACL_LVL_IS_SEARCH(mask) ) {
1313 ptr = lutil_strcopy( ptr, "search" );
1315 } else if ( ACL_LVL_IS_READ(mask) ) {
1316 ptr = lutil_strcopy( ptr, "read" );
1318 } else if ( ACL_LVL_IS_WRITE(mask) ) {
1319 ptr = lutil_strcopy( ptr, "write" );
1321 ptr = lutil_strcopy( ptr, "unknown" );
1327 if( ACL_IS_ADDITIVE( mask ) ) {
1330 } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
1337 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
1342 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
1347 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
1352 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
1357 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
1362 if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
1371 if ( ACL_IS_LEVEL( mask ) ) {
1381 str2accessmask( const char *str )
1385 if( !ASCII_ALPHA(str[0]) ) {
1388 if ( str[0] == '=' ) {
1391 } else if( str[0] == '+' ) {
1392 ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
1394 } else if( str[0] == '-' ) {
1395 ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
1398 ACL_INVALIDATE(mask);
1402 for( i=1; str[i] != '\0'; i++ ) {
1403 if( TOLOWER((unsigned char) str[i]) == 'w' ) {
1404 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
1406 } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
1407 ACL_PRIV_SET(mask, ACL_PRIV_READ);
1409 } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
1410 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
1412 } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
1413 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
1415 } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
1416 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
1418 } else if( str[i] != '0' ) {
1419 ACL_INVALIDATE(mask);
1427 if ( strcasecmp( str, "none" ) == 0 ) {
1428 ACL_LVL_ASSIGN_NONE(mask);
1430 } else if ( strcasecmp( str, "auth" ) == 0 ) {
1431 ACL_LVL_ASSIGN_AUTH(mask);
1433 } else if ( strcasecmp( str, "compare" ) == 0 ) {
1434 ACL_LVL_ASSIGN_COMPARE(mask);
1436 } else if ( strcasecmp( str, "search" ) == 0 ) {
1437 ACL_LVL_ASSIGN_SEARCH(mask);
1439 } else if ( strcasecmp( str, "read" ) == 0 ) {
1440 ACL_LVL_ASSIGN_READ(mask);
1442 } else if ( strcasecmp( str, "write" ) == 0 ) {
1443 ACL_LVL_ASSIGN_WRITE(mask);
1446 ACL_INVALIDATE( mask );
1455 fprintf( stderr, "%s%s\n",
1456 "<access clause> ::= access to <what> "
1457 "[ by <who> <access> [ <control> ] ]+ \n"
1458 "<what> ::= * | [dn[.<dnstyle>]=<DN>] [filter=<filter>] [attrs=<attrlist>]\n"
1459 "<attrlist> ::= <attr> [val[.<style>]=<value>] | <attr> , <attrlist>\n"
1460 "<attr> ::= <attrname> | entry | children\n"
1461 "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
1462 "\t[dnattr=<attrname>]\n"
1463 "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
1464 "\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n",
1465 "\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n"
1466 #ifdef SLAPD_ACI_ENABLED
1467 "\t[aci=<attrname>]\n"
1469 "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
1470 "<dnstyle> ::= base | exact | one(level) | sub(tree) | children | regex\n"
1471 "<style> ::= regex | base | exact\n"
1472 "<peernamestyle> ::= regex | exact | ip | path\n"
1473 "<domainstyle> ::= regex | base | exact | sub(tree)\n"
1474 "<access> ::= [self]{<level>|<priv>}\n"
1475 "<level> ::= none | auth | compare | search | read | write\n"
1476 "<priv> ::= {=|+|-}{w|r|s|c|x|0}+\n"
1477 "<control> ::= [ stop | continue | break ]\n"
1479 exit( EXIT_FAILURE );
1483 * Set pattern to a "normalized" DN from src.
1484 * At present it simply eats the (optional) space after
1485 * a RDN separator (,)
1486 * Eventually will evolve in a more complete normalization
1489 acl_regex_normalized_dn(
1491 struct berval *pattern
1497 str = ch_strdup( src );
1498 len = strlen( src );
1500 for ( p = str; p && p[ 0 ]; p++ ) {
1502 if ( p[ 0 ] == '\\' && p[ 1 ] ) {
1504 * if escaping a hex pair we should
1505 * increment p twice; however, in that
1506 * case the second hex number does
1512 if ( p[ 0 ] == ',' ) {
1513 if ( p[ 1 ] == ' ' ) {
1517 * too much space should be
1518 * an error if we are pedantic
1520 for ( q = &p[ 2 ]; q[ 0 ] == ' '; q++ ) {
1523 AC_MEMCPY( p+1, q, len-(q-str)+1);
1527 pattern->bv_val = str;
1528 pattern->bv_len = p-str;
1542 if ( (*right = strchr( line, splitchar )) != NULL ) {
1543 *((*right)++) = '\0';
1548 access_append( Access **l, Access *a )
1550 for ( ; *l != NULL; l = &(*l)->a_next )
1557 acl_append( AccessControl **l, AccessControl *a )
1559 for ( ; *l != NULL; l = &(*l)->acl_next )
1566 access_free( Access *a )
1568 if ( a->a_dn_pat.bv_val )
1569 free ( a->a_dn_pat.bv_val );
1570 if ( a->a_peername_pat.bv_val )
1571 free ( a->a_peername_pat.bv_val );
1572 if ( a->a_sockname_pat.bv_val )
1573 free ( a->a_sockname_pat.bv_val );
1574 if ( a->a_domain_pat.bv_val )
1575 free ( a->a_domain_pat.bv_val );
1576 if ( a->a_sockurl_pat.bv_val )
1577 free ( a->a_sockurl_pat.bv_val );
1578 if ( a->a_set_pat.bv_len )
1579 free ( a->a_set_pat.bv_val );
1580 if ( a->a_group_pat.bv_len )
1581 free ( a->a_group_pat.bv_val );
1586 acl_free( AccessControl *a )
1591 if ( a->acl_filter )
1592 filter_free( a->acl_filter );
1593 if ( a->acl_dn_pat.bv_len )
1594 free ( a->acl_dn_pat.bv_val );
1595 if ( a->acl_attrs ) {
1596 for ( an = a->acl_attrs; an->an_name.bv_val; an++ ) {
1597 free( an->an_name.bv_val );
1599 free( a->acl_attrs );
1601 for (; a->acl_access; a->acl_access = n) {
1602 n = a->acl_access->a_next;
1603 access_free( a->acl_access );
1608 /* Because backend_startup uses acl_append to tack on the global_acl to
1609 * the end of each backend's acl, we cannot just take one argument and
1610 * merrily free our way to the end of the list. backend_destroy calls us
1611 * with the be_acl in arg1, and global_acl in arg2 to give us a stopping
1612 * point. config_destroy calls us with global_acl in arg1 and NULL in
1613 * arg2, so we then proceed to polish off the global_acl.
1616 acl_destroy( AccessControl *a, AccessControl *end )
1620 for (; a && a!= end; a=n) {
1627 access2str( slap_access_t access )
1629 if ( access == ACL_NONE ) {
1632 } else if ( access == ACL_AUTH ) {
1635 } else if ( access == ACL_COMPARE ) {
1638 } else if ( access == ACL_SEARCH ) {
1641 } else if ( access == ACL_READ ) {
1644 } else if ( access == ACL_WRITE ) {
1652 str2access( const char *str )
1654 if ( strcasecmp( str, "none" ) == 0 ) {
1657 } else if ( strcasecmp( str, "auth" ) == 0 ) {
1660 } else if ( strcasecmp( str, "compare" ) == 0 ) {
1663 } else if ( strcasecmp( str, "search" ) == 0 ) {
1666 } else if ( strcasecmp( str, "read" ) == 0 ) {
1669 } else if ( strcasecmp( str, "write" ) == 0 ) {
1673 return( ACL_INVALID_ACCESS );
1679 print_access( Access *b )
1681 char maskbuf[ACCESSMASK_MAXLEN];
1683 fprintf( stderr, "\tby" );
1685 if ( b->a_dn_pat.bv_len != 0 ) {
1686 if( strcmp(b->a_dn_pat.bv_val, "*") == 0
1687 || strcmp(b->a_dn_pat.bv_val, "users") == 0
1688 || strcmp(b->a_dn_pat.bv_val, "anonymous") == 0
1689 || strcmp(b->a_dn_pat.bv_val, "self") == 0 )
1691 fprintf( stderr, " %s", b->a_dn_pat.bv_val );
1694 fprintf( stderr, " dn.%s=\"%s\"",
1695 style_strings[b->a_dn_style], b->a_dn_pat.bv_val );
1699 if ( b->a_dn_at != NULL ) {
1700 fprintf( stderr, " dnattr=%s", b->a_dn_at->ad_cname.bv_val );
1703 if ( b->a_group_pat.bv_len ) {
1704 fprintf( stderr, " group/%s/%s.%s=\"%s\"",
1705 b->a_group_oc ? b->a_group_oc->soc_cname.bv_val : "groupOfNames",
1706 b->a_group_at ? b->a_group_at->ad_cname.bv_val : "member",
1707 style_strings[b->a_group_style],
1708 b->a_group_pat.bv_val );
1711 if ( b->a_peername_pat.bv_len != 0 ) {
1712 fprintf( stderr, " peername=\"%s\"", b->a_peername_pat.bv_val );
1715 if ( b->a_sockname_pat.bv_len != 0 ) {
1716 fprintf( stderr, " sockname=\"%s\"", b->a_sockname_pat.bv_val );
1719 if ( b->a_domain_pat.bv_len != 0 ) {
1720 fprintf( stderr, " domain=%s", b->a_domain_pat.bv_val );
1723 if ( b->a_sockurl_pat.bv_len != 0 ) {
1724 fprintf( stderr, " sockurl=\"%s\"", b->a_sockurl_pat.bv_val );
1727 if ( b->a_set_pat.bv_len != 0 ) {
1728 fprintf( stderr, " set=\"%s\"", b->a_set_pat.bv_val );
1731 #ifdef SLAPD_ACI_ENABLED
1732 if ( b->a_aci_at != NULL ) {
1733 fprintf( stderr, " aci=%s", b->a_aci_at->ad_cname.bv_val );
1737 /* Security Strength Factors */
1738 if ( b->a_authz.sai_ssf ) {
1739 fprintf( stderr, " ssf=%u",
1740 b->a_authz.sai_ssf );
1742 if ( b->a_authz.sai_transport_ssf ) {
1743 fprintf( stderr, " transport_ssf=%u",
1744 b->a_authz.sai_transport_ssf );
1746 if ( b->a_authz.sai_tls_ssf ) {
1747 fprintf( stderr, " tls_ssf=%u",
1748 b->a_authz.sai_tls_ssf );
1750 if ( b->a_authz.sai_sasl_ssf ) {
1751 fprintf( stderr, " sasl_ssf=%u",
1752 b->a_authz.sai_sasl_ssf );
1755 fprintf( stderr, " %s%s",
1756 b->a_dn_self ? "self" : "",
1757 accessmask2str( b->a_access_mask, maskbuf ) );
1759 if( b->a_type == ACL_BREAK ) {
1760 fprintf( stderr, " break" );
1762 } else if( b->a_type == ACL_CONTINUE ) {
1763 fprintf( stderr, " continue" );
1765 } else if( b->a_type != ACL_STOP ) {
1766 fprintf( stderr, " unknown-control" );
1769 fprintf( stderr, "\n" );
1774 print_acl( Backend *be, AccessControl *a )
1779 fprintf( stderr, "%s ACL: access to",
1780 be == NULL ? "Global" : "Backend" );
1782 if ( a->acl_dn_pat.bv_len != 0 ) {
1784 fprintf( stderr, " dn.%s=\"%s\"\n",
1785 style_strings[a->acl_dn_style], a->acl_dn_pat.bv_val );
1788 if ( a->acl_filter != NULL ) {
1789 struct berval bv = { 0, NULL };
1791 filter2bv( a->acl_filter, &bv );
1792 fprintf( stderr, " filter=%s\n", bv.bv_val );
1793 ch_free( bv.bv_val );
1796 if ( a->acl_attrs != NULL ) {
1801 fprintf( stderr, " attrs=" );
1802 for ( an = a->acl_attrs; an && an->an_name.bv_val; an++ ) {
1804 fprintf( stderr, "," );
1807 fputc( an->an_oc_exclude ? '!' : '@', stderr);
1809 fputs( an->an_name.bv_val, stderr );
1812 fprintf( stderr, "\n" );
1815 if ( a->acl_attrval.bv_len != 0 ) {
1817 fprintf( stderr, " val.%s=\"%s\"\n",
1818 style_strings[a->acl_attrval_style], a->acl_attrval.bv_val );
1823 fprintf( stderr, " *\n" );
1826 for ( b = a->acl_access; b != NULL; b = b->a_next ) {
1830 fprintf( stderr, "\n" );
1833 #endif /* LDAP_DEBUG */