1 /* aclparse.c - routines to parse and check acl's */
4 * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
5 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
14 #include <ac/socket.h>
15 #include <ac/string.h>
16 #include <ac/unistd.h>
22 static void split(char *line, int splitchar, char **left, char **right);
23 static void access_append(Access **l, Access *a);
24 static void acl_usage(void) LDAP_GCCATTR((noreturn));
26 static void acl_regex_normalized_dn(const char *src, struct berval *pat);
29 static void print_acl(Backend *be, AccessControl *a);
30 static void print_access(Access *b);
34 regtest(const char *fname, int lineno, char *pat) {
50 for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
52 if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
69 if ( size >= (sizeof(buf)-1) ) {
71 "%s: line %d: regular expression \"%s\" too large\n",
76 if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
78 regerror(e, &re, error, sizeof(error));
80 "%s: line %d: regular expression \"%s\" bad because of %s\n",
81 fname, lineno, pat, error );
97 char *left, *right, *style;
105 for ( i = 1; i < argc; i++ ) {
106 /* to clause - select which entries are protected */
107 if ( strcasecmp( argv[i], "to" ) == 0 ) {
110 "%s: line %d: only one to clause allowed in access line\n",
114 a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
115 for ( ++i; i < argc; i++ ) {
116 if ( strcasecmp( argv[i], "by" ) == 0 ) {
121 if ( strcasecmp( argv[i], "*" ) == 0 ) {
122 if( a->acl_dn_pat.bv_len ||
123 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
126 "%s: line %d: dn pattern"
127 " already specified in to clause.\n",
132 a->acl_dn_pat.bv_val = ch_strdup( "*" );
133 a->acl_dn_pat.bv_len = 1;
137 split( argv[i], '=', &left, &right );
138 split( left, '.', &left, &style );
140 if ( right == NULL ) {
142 "%s: line %d: missing \"=\" in \"%s\" in to clause\n",
143 fname, lineno, left );
147 if ( strcasecmp( left, "dn" ) == 0 ) {
148 if( a->acl_dn_pat.bv_len != 0 ||
149 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
152 "%s: line %d: dn pattern"
153 " already specified in to clause.\n",
158 if ( style == NULL || *style == '\0'
159 || strcasecmp( style, "regex" ) == 0 )
161 a->acl_dn_style = ACL_STYLE_REGEX;
163 if ( *right == '\0' ) {
164 /* empty regex should match empty DN */
165 a->acl_dn_style = ACL_STYLE_BASE;
166 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
168 } else if ( strcmp(right, "*") == 0
169 || strcmp(right, ".*") == 0
170 || strcmp(right, ".*$") == 0
171 || strcmp(right, "^.*") == 0
172 || strcmp(right, "^.*$") == 0
173 || strcmp(right, ".*$$") == 0
174 || strcmp(right, "^.*$$") == 0 )
176 a->acl_dn_pat.bv_val = ch_strdup( "*" );
177 a->acl_dn_pat.bv_len = sizeof("*")-1;
180 acl_regex_normalized_dn( right, &a->acl_dn_pat );
182 } else if ( strcasecmp( style, "base" ) == 0
183 || strcasecmp( style, "exact" ) == 0 )
185 a->acl_dn_style = ACL_STYLE_BASE;
186 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
187 } else if ( strcasecmp( style, "one" ) == 0 ) {
188 a->acl_dn_style = ACL_STYLE_ONE;
189 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
190 } else if ( strcasecmp( style, "subtree" ) == 0 || strcasecmp( style, "sub" ) == 0 ) {
191 a->acl_dn_style = ACL_STYLE_SUBTREE;
192 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
193 } else if ( strcasecmp( style, "children" ) == 0 ) {
194 a->acl_dn_style = ACL_STYLE_CHILDREN;
195 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
198 "%s: line %d: unknown dn style \"%s\" in to clause\n",
199 fname, lineno, style );
206 if ( strcasecmp( left, "filter" ) == 0 ) {
207 if ( (a->acl_filter = str2filter( right )) == NULL ) {
209 "%s: line %d: bad filter \"%s\" in to clause\n",
210 fname, lineno, right );
214 } else if ( strncasecmp( left, "attr", 4 ) == 0 ) {
215 a->acl_attrs = str2anlist( a->acl_attrs,
217 if ( a->acl_attrs == NULL ) {
219 "%s: line %d: unknown attr \"%s\" in to clause\n",
220 fname, lineno, right );
225 "%s: line %d: expecting <what> got \"%s\"\n",
226 fname, lineno, left );
231 if ( a->acl_dn_pat.bv_len != 0 &&
232 strcmp(a->acl_dn_pat.bv_val, "*") == 0 )
234 free( a->acl_dn_pat.bv_val );
235 a->acl_dn_pat.bv_val = NULL;
236 a->acl_dn_pat.bv_len = 0;
239 if( a->acl_dn_pat.bv_len != 0 ||
240 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
242 if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
244 rc = dnNormalize2( NULL, &a->acl_dn_pat, &bv);
245 if ( rc != LDAP_SUCCESS ) {
247 "%s: line %d: bad DN \"%s\"\n",
248 fname, lineno, a->acl_dn_pat.bv_val );
251 free( a->acl_dn_pat.bv_val );
254 int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
255 REG_EXTENDED | REG_ICASE );
258 regerror( e, &a->acl_dn_re, buf, sizeof(buf) );
259 fprintf( stderr, "%s: line %d: "
260 "regular expression \"%s\" bad because of %s\n",
261 fname, lineno, right, buf );
267 /* by clause - select who has what access to entries */
268 } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
271 "%s: line %d: to clause required before by clause in access line\n",
277 * by clause consists of <who> and <access>
280 b = (Access *) ch_calloc( 1, sizeof(Access) );
282 ACL_INVALIDATE( b->a_access_mask );
286 "%s: line %d: premature eol: expecting <who>\n",
292 for ( ; i < argc; i++ ) {
293 slap_style_t sty = ACL_STYLE_REGEX;
294 char *style_modifier = NULL;
297 split( argv[i], '=', &left, &right );
298 split( left, '.', &left, &style );
300 split( style, ',', &style, &style_modifier);
302 if ( style == NULL || *style == '\0'
303 || strcasecmp( style, "regex" ) == 0 )
305 sty = ACL_STYLE_REGEX;
306 } else if ( strcasecmp( style, "exact" ) == 0 ||
307 strcasecmp( style, "base" ) == 0 )
309 sty = ACL_STYLE_BASE;
310 } else if ( strcasecmp( style, "one" ) == 0 ) {
312 } else if ( strcasecmp( style, "subtree" ) == 0 || strcasecmp( style, "sub" ) == 0 ) {
313 sty = ACL_STYLE_SUBTREE;
314 } else if ( strcasecmp( style, "children" ) == 0 ) {
315 sty = ACL_STYLE_CHILDREN;
318 "%s: line %d: unknown style \"%s\" in by clause\n",
319 fname, lineno, style );
323 if ( style_modifier && strcasecmp( style_modifier, "expand" ) == 0 ) {
327 if ( strcasecmp( argv[i], "*" ) == 0 ) {
328 bv.bv_val = ch_strdup( "*" );
331 } else if ( strcasecmp( argv[i], "anonymous" ) == 0 ) {
332 ber_str2bv("anonymous",
333 sizeof("anonymous")-1,
336 } else if ( strcasecmp( argv[i], "self" ) == 0 ) {
341 } else if ( strcasecmp( argv[i], "users" ) == 0 ) {
346 } else if ( strcasecmp( left, "dn" ) == 0 ) {
347 if ( sty == ACL_STYLE_REGEX ) {
348 b->a_dn_style = ACL_STYLE_REGEX;
349 if( right == NULL ) {
354 } else if (*right == '\0' ) {
356 ber_str2bv("anonymous",
357 sizeof("anonymous")-1,
359 } else if ( strcmp( right, "*" ) == 0 ) {
361 /* any or users? users for now */
365 } else if ( strcmp( right, ".+" ) == 0
366 || strcmp( right, "^.+" ) == 0
367 || strcmp( right, ".+$" ) == 0
368 || strcmp( right, "^.+$" ) == 0
369 || strcmp( right, ".+$$" ) == 0
370 || strcmp( right, "^.+$$" ) == 0 )
375 } else if ( strcmp( right, ".*" ) == 0
376 || strcmp( right, "^.*" ) == 0
377 || strcmp( right, ".*$" ) == 0
378 || strcmp( right, "^.*$" ) == 0
379 || strcmp( right, ".*$$" ) == 0
380 || strcmp( right, "^.*$$" ) == 0 )
387 acl_regex_normalized_dn( right, &bv );
388 if ( !ber_bvccmp( &bv, '*' ) ) {
389 regtest(fname, lineno, bv.bv_val);
392 } else if ( right == NULL || *right == '\0' ) {
394 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
395 fname, lineno, left );
399 ber_str2bv( right, 0, 1, &bv );
406 if( bv.bv_val != NULL ) {
407 if( b->a_dn_pat.bv_len != 0 ) {
409 "%s: line %d: dn pattern already specified.\n",
414 if ( sty != ACL_STYLE_REGEX && expand == 0 ) {
415 rc = dnNormalize2(NULL, &bv, &b->a_dn_pat);
416 if ( rc != LDAP_SUCCESS ) {
418 "%s: line %d: bad DN \"%s\"\n",
419 fname, lineno, bv.bv_val );
427 b->a_dn_expand = expand;
431 if ( strcasecmp( left, "dnattr" ) == 0 ) {
432 if ( right == NULL || right[ 0 ] == '\0' ) {
434 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
435 fname, lineno, left );
439 if( b->a_dn_at != NULL ) {
441 "%s: line %d: dnattr already specified.\n",
446 rc = slap_str2ad( right, &b->a_dn_at, &text );
448 if( rc != LDAP_SUCCESS ) {
450 "%s: line %d: dnattr \"%s\": %s\n",
451 fname, lineno, right, text );
456 if( !is_at_syntax( b->a_dn_at->ad_type,
458 !is_at_syntax( b->a_dn_at->ad_type,
459 SLAPD_NAMEUID_SYNTAX ))
462 "%s: line %d: dnattr \"%s\": "
463 "inappropriate syntax: %s\n",
464 fname, lineno, right,
465 b->a_dn_at->ad_type->sat_syntax_oid );
469 if( b->a_dn_at->ad_type->sat_equality == NULL )
472 "%s: line %d: dnattr \"%s\": "
473 "inappropriate matching (no EQUALITY)\n",
474 fname, lineno, right );
481 if ( strncasecmp( left, "group", sizeof("group")-1 ) == 0 ) {
485 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
487 "%s: line %d: inappropriate style \"%s\" in by clause\n",
488 fname, lineno, style );
492 if ( right == NULL || right[ 0 ] == '\0' ) {
494 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
495 fname, lineno, left );
499 if( b->a_group_pat.bv_len ) {
501 "%s: line %d: group pattern already specified.\n",
506 /* format of string is "group/objectClassValue/groupAttrName" */
507 if ((value = strchr(left, '/')) != NULL) {
510 && (name = strchr(value, '/')) != NULL)
516 b->a_group_style = sty;
517 if (sty == ACL_STYLE_REGEX) {
518 acl_regex_normalized_dn( right, &bv );
519 if ( !ber_bvccmp( &bv, '*' ) ) {
520 regtest(fname, lineno, bv.bv_val);
524 ber_str2bv( right, 0, 0, &bv );
525 rc = dnNormalize2( NULL, &bv, &b->a_group_pat );
526 if ( rc != LDAP_SUCCESS ) {
528 "%s: line %d: bad DN \"%s\"\n",
529 fname, lineno, right );
534 if (value && *value) {
535 b->a_group_oc = oc_find( value );
538 if( b->a_group_oc == NULL ) {
540 "%s: line %d: group objectclass "
542 fname, lineno, value );
546 b->a_group_oc = oc_find(SLAPD_GROUP_CLASS);
548 if( b->a_group_oc == NULL ) {
550 "%s: line %d: group default objectclass "
552 fname, lineno, SLAPD_GROUP_CLASS );
557 if( is_object_subclass( slap_schema.si_oc_referral,
561 "%s: line %d: group objectclass \"%s\" "
562 "is subclass of referral\n",
563 fname, lineno, value );
567 if( is_object_subclass( slap_schema.si_oc_alias,
571 "%s: line %d: group objectclass \"%s\" "
572 "is subclass of alias\n",
573 fname, lineno, value );
578 rc = slap_str2ad( name, &b->a_group_at, &text );
580 if( rc != LDAP_SUCCESS ) {
582 "%s: line %d: group \"%s\": %s\n",
583 fname, lineno, right, text );
588 rc = slap_str2ad( SLAPD_GROUP_ATTR, &b->a_group_at, &text );
590 if( rc != LDAP_SUCCESS ) {
592 "%s: line %d: group \"%s\": %s\n",
593 fname, lineno, SLAPD_GROUP_ATTR, text );
598 if( !is_at_syntax( b->a_group_at->ad_type,
600 !is_at_syntax( b->a_group_at->ad_type,
601 SLAPD_NAMEUID_SYNTAX ) )
604 "%s: line %d: group \"%s\": inappropriate syntax: %s\n",
605 fname, lineno, right,
606 b->a_group_at->ad_type->sat_syntax_oid );
613 struct berval vals[2];
615 vals[0].bv_val = b->a_group_oc->soc_oid;
616 vals[0].bv_len = strlen(vals[0].bv_val);
617 vals[1].bv_val = NULL;
620 rc = oc_check_allowed( b->a_group_at->ad_type, vals, NULL );
624 "%s: line %d: group: \"%s\" not allowed by \"%s\"\n",
626 b->a_group_at->ad_cname.bv_val,
627 b->a_group_oc->soc_oid );
634 if ( strcasecmp( left, "peername" ) == 0 ) {
635 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
637 "%s: line %d: inappropriate style \"%s\" in by clause\n",
638 fname, lineno, style );
642 if ( right == NULL || right[ 0 ] == '\0' ) {
644 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
645 fname, lineno, left );
649 if( b->a_peername_pat.bv_len ) {
651 "%s: line %d: peername pattern already specified.\n",
656 b->a_peername_style = sty;
657 if (sty == ACL_STYLE_REGEX) {
658 acl_regex_normalized_dn( right, &bv );
659 if ( !ber_bvccmp( &bv, '*' ) ) {
660 regtest(fname, lineno, bv.bv_val);
662 b->a_peername_pat = bv;
664 ber_str2bv( right, 0, 1, &b->a_peername_pat );
669 if ( strcasecmp( left, "sockname" ) == 0 ) {
670 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
672 "%s: line %d: inappropriate style \"%s\" in by clause\n",
673 fname, lineno, style );
677 if ( right == NULL || right[ 0 ] == '\0' ) {
679 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
680 fname, lineno, left );
684 if( b->a_sockname_pat.bv_len ) {
686 "%s: line %d: sockname pattern already specified.\n",
691 b->a_sockname_style = sty;
692 if (sty == ACL_STYLE_REGEX) {
693 acl_regex_normalized_dn( right, &bv );
694 if ( !ber_bvccmp( &bv, '*' ) ) {
695 regtest(fname, lineno, bv.bv_val);
697 b->a_sockname_pat = bv;
699 ber_str2bv( right, 0, 1, &b->a_sockname_pat );
704 if ( strcasecmp( left, "domain" ) == 0 ) {
706 case ACL_STYLE_REGEX:
708 case ACL_STYLE_SUBTREE:
713 "%s: line %d: inappropriate style \"%s\" in by clause\n",
714 fname, lineno, style );
718 if ( right == NULL || right[ 0 ] == '\0' ) {
720 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
721 fname, lineno, left );
725 if( b->a_domain_pat.bv_len ) {
727 "%s: line %d: domain pattern already specified.\n",
732 b->a_domain_style = sty;
733 b->a_domain_expand = expand;
734 if (sty == ACL_STYLE_REGEX) {
735 acl_regex_normalized_dn( right, &bv );
736 if ( !ber_bvccmp( &bv, '*' ) ) {
737 regtest(fname, lineno, bv.bv_val);
739 b->a_domain_pat = bv;
741 ber_str2bv( right, 0, 1, &b->a_domain_pat );
746 if ( strcasecmp( left, "sockurl" ) == 0 ) {
747 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
749 "%s: line %d: inappropriate style \"%s\" in by clause\n",
750 fname, lineno, style );
754 if ( right == NULL || right[ 0 ] == '\0' ) {
756 "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
757 fname, lineno, left );
761 if( b->a_sockurl_pat.bv_len ) {
763 "%s: line %d: sockurl pattern already specified.\n",
768 b->a_sockurl_style = sty;
769 if (sty == ACL_STYLE_REGEX) {
770 acl_regex_normalized_dn( right, &bv );
771 if ( !ber_bvccmp( &bv, '*' ) ) {
772 regtest(fname, lineno, bv.bv_val);
774 b->a_sockurl_pat = bv;
776 ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
781 if ( strcasecmp( left, "set" ) == 0 ) {
782 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
784 "%s: line %d: inappropriate style \"%s\" in by clause\n",
785 fname, lineno, style );
789 if( b->a_set_pat.bv_len != 0 ) {
791 "%s: line %d: set attribute already specified.\n",
796 if ( right == NULL || *right == '\0' ) {
798 "%s: line %d: no set is defined\n",
803 b->a_set_style = sty;
804 ber_str2bv( right, 0, 1, &b->a_set_pat );
809 #ifdef SLAPD_ACI_ENABLED
810 if ( strcasecmp( left, "aci" ) == 0 ) {
811 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
813 "%s: line %d: inappropriate style \"%s\" in by clause\n",
814 fname, lineno, style );
818 if( b->a_aci_at != NULL ) {
820 "%s: line %d: aci attribute already specified.\n",
825 if ( right != NULL && *right != '\0' ) {
826 rc = slap_str2ad( right, &b->a_aci_at, &text );
828 if( rc != LDAP_SUCCESS ) {
830 "%s: line %d: aci \"%s\": %s\n",
831 fname, lineno, right, text );
836 b->a_aci_at = slap_schema.si_ad_aci;
839 if( !is_at_syntax( b->a_aci_at->ad_type,
843 "%s: line %d: aci \"%s\": inappropriate syntax: %s\n",
844 fname, lineno, right,
845 b->a_aci_at->ad_type->sat_syntax_oid );
851 #endif /* SLAPD_ACI_ENABLED */
853 if ( strcasecmp( left, "ssf" ) == 0 ) {
854 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
856 "%s: line %d: inappropriate style \"%s\" in by clause\n",
857 fname, lineno, style );
861 if( b->a_authz.sai_ssf ) {
863 "%s: line %d: ssf attribute already specified.\n",
868 if ( right == NULL || *right == '\0' ) {
870 "%s: line %d: no ssf is defined\n",
875 b->a_authz.sai_ssf = atoi( right );
877 if( !b->a_authz.sai_ssf ) {
879 "%s: line %d: invalid ssf value (%s)\n",
880 fname, lineno, right );
886 if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
887 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
889 "%s: line %d: inappropriate style \"%s\" in by clause\n",
890 fname, lineno, style );
894 if( b->a_authz.sai_transport_ssf ) {
896 "%s: line %d: transport_ssf attribute already specified.\n",
901 if ( right == NULL || *right == '\0' ) {
903 "%s: line %d: no transport_ssf is defined\n",
908 b->a_authz.sai_transport_ssf = atoi( right );
910 if( !b->a_authz.sai_transport_ssf ) {
912 "%s: line %d: invalid transport_ssf value (%s)\n",
913 fname, lineno, right );
919 if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
920 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
922 "%s: line %d: inappropriate style \"%s\" in by clause\n",
923 fname, lineno, style );
927 if( b->a_authz.sai_tls_ssf ) {
929 "%s: line %d: tls_ssf attribute already specified.\n",
934 if ( right == NULL || *right == '\0' ) {
936 "%s: line %d: no tls_ssf is defined\n",
941 b->a_authz.sai_tls_ssf = atoi( right );
943 if( !b->a_authz.sai_tls_ssf ) {
945 "%s: line %d: invalid tls_ssf value (%s)\n",
946 fname, lineno, right );
952 if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
953 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
955 "%s: line %d: inappropriate style \"%s\" in by clause\n",
956 fname, lineno, style );
960 if( b->a_authz.sai_sasl_ssf ) {
962 "%s: line %d: sasl_ssf attribute already specified.\n",
967 if ( right == NULL || *right == '\0' ) {
969 "%s: line %d: no sasl_ssf is defined\n",
974 b->a_authz.sai_sasl_ssf = atoi( right );
976 if( !b->a_authz.sai_sasl_ssf ) {
978 "%s: line %d: invalid sasl_ssf value (%s)\n",
979 fname, lineno, right );
985 if( right != NULL ) {
992 if( i == argc || ( strcasecmp( left, "stop" ) == 0 )) {
993 /* out of arguments or plain stop */
995 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
996 b->a_type = ACL_STOP;
998 access_append( &a->acl_access, b );
1002 if( strcasecmp( left, "continue" ) == 0 ) {
1003 /* plain continue */
1005 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1006 b->a_type = ACL_CONTINUE;
1008 access_append( &a->acl_access, b );
1012 if( strcasecmp( left, "break" ) == 0 ) {
1013 /* plain continue */
1015 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1016 b->a_type = ACL_BREAK;
1018 access_append( &a->acl_access, b );
1022 if ( strcasecmp( left, "by" ) == 0 ) {
1023 /* we've gone too far */
1025 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1026 b->a_type = ACL_STOP;
1028 access_append( &a->acl_access, b );
1033 if( strncasecmp( left, "self", 4 ) == 0 ) {
1035 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( &left[4] ) );
1038 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( left ) );
1041 if( ACL_IS_INVALID( b->a_access_mask ) ) {
1043 "%s: line %d: expecting <access> got \"%s\"\n",
1044 fname, lineno, left );
1048 b->a_type = ACL_STOP;
1051 /* out of arguments or plain stop */
1052 access_append( &a->acl_access, b );
1056 if( strcasecmp( argv[i], "continue" ) == 0 ) {
1057 /* plain continue */
1058 b->a_type = ACL_CONTINUE;
1060 } else if( strcasecmp( argv[i], "break" ) == 0 ) {
1061 /* plain continue */
1062 b->a_type = ACL_BREAK;
1064 } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
1069 access_append( &a->acl_access, b );
1073 "%s: line %d: expecting \"to\" or \"by\" got \"%s\"\n",
1074 fname, lineno, argv[i] );
1079 /* if we have no real access clause, complain and do nothing */
1082 "%s: line %d: warning: no access clause(s) specified in access line\n",
1087 if (ldap_debug & LDAP_DEBUG_ACL)
1091 if ( a->acl_access == NULL ) {
1093 "%s: line %d: warning: no by clause(s) specified in access line\n",
1098 acl_append( &be->be_acl, a );
1100 acl_append( &global_acl, a );
1106 accessmask2str( slap_mask_t mask, char *buf )
1111 assert( buf != NULL );
1113 if ( ACL_IS_INVALID( mask ) ) {
1119 if ( ACL_IS_LEVEL( mask ) ) {
1120 if ( ACL_LVL_IS_NONE(mask) ) {
1121 ptr = lutil_strcopy( ptr, "none" );
1123 } else if ( ACL_LVL_IS_AUTH(mask) ) {
1124 ptr = lutil_strcopy( ptr, "auth" );
1126 } else if ( ACL_LVL_IS_COMPARE(mask) ) {
1127 ptr = lutil_strcopy( ptr, "compare" );
1129 } else if ( ACL_LVL_IS_SEARCH(mask) ) {
1130 ptr = lutil_strcopy( ptr, "search" );
1132 } else if ( ACL_LVL_IS_READ(mask) ) {
1133 ptr = lutil_strcopy( ptr, "read" );
1135 } else if ( ACL_LVL_IS_WRITE(mask) ) {
1136 ptr = lutil_strcopy( ptr, "write" );
1138 ptr = lutil_strcopy( ptr, "unknown" );
1144 if( ACL_IS_ADDITIVE( mask ) ) {
1147 } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
1154 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
1159 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
1164 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
1169 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
1174 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
1179 if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
1188 if ( ACL_IS_LEVEL( mask ) ) {
1198 str2accessmask( const char *str )
1202 if( !ASCII_ALPHA(str[0]) ) {
1205 if ( str[0] == '=' ) {
1208 } else if( str[0] == '+' ) {
1209 ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
1211 } else if( str[0] == '-' ) {
1212 ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
1215 ACL_INVALIDATE(mask);
1219 for( i=1; str[i] != '\0'; i++ ) {
1220 if( TOLOWER((unsigned char) str[i]) == 'w' ) {
1221 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
1223 } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
1224 ACL_PRIV_SET(mask, ACL_PRIV_READ);
1226 } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
1227 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
1229 } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
1230 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
1232 } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
1233 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
1235 } else if( str[i] != '0' ) {
1236 ACL_INVALIDATE(mask);
1244 if ( strcasecmp( str, "none" ) == 0 ) {
1245 ACL_LVL_ASSIGN_NONE(mask);
1247 } else if ( strcasecmp( str, "auth" ) == 0 ) {
1248 ACL_LVL_ASSIGN_AUTH(mask);
1250 } else if ( strcasecmp( str, "compare" ) == 0 ) {
1251 ACL_LVL_ASSIGN_COMPARE(mask);
1253 } else if ( strcasecmp( str, "search" ) == 0 ) {
1254 ACL_LVL_ASSIGN_SEARCH(mask);
1256 } else if ( strcasecmp( str, "read" ) == 0 ) {
1257 ACL_LVL_ASSIGN_READ(mask);
1259 } else if ( strcasecmp( str, "write" ) == 0 ) {
1260 ACL_LVL_ASSIGN_WRITE(mask);
1263 ACL_INVALIDATE( mask );
1272 fprintf( stderr, "\n"
1273 "<access clause> ::= access to <what> "
1274 "[ by <who> <access> [ <control> ] ]+ \n"
1275 "<what> ::= * | [dn[.<dnstyle>]=<regex>] [filter=<ldapfilter>] [attrs=<attrlist>]\n"
1276 "<attrlist> ::= <attr> | <attr> , <attrlist>\n"
1277 "<attr> ::= <attrname> | entry | children\n"
1278 "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<regex> ]\n"
1279 "\t[dnattr=<attrname>]\n"
1280 "\t[group[/<objectclass>[/<attrname>]][.<style>]=<regex>]\n"
1281 "\t[peername[.<style>]=<regex>] [sockname[.<style>]=<regex>]\n"
1282 "\t[domain[.<style>]=<regex>] [sockurl[.<style>]=<regex>]\n"
1283 #ifdef SLAPD_ACI_ENABLED
1284 "\t[aci=<attrname>]\n"
1286 "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
1287 "<dnstyle> ::= regex | base | exact (alias of base) | one | subtree | children\n"
1288 "<style> ::= regex | base | exact (alias of base)\n"
1289 "<groupflags> ::= R\n"
1290 "<access> ::= [self]{<level>|<priv>}\n"
1291 "<level> ::= none | auth | compare | search | read | write\n"
1292 "<priv> ::= {=|+|-}{w|r|s|c|x}+\n"
1293 "<control> ::= [ stop | continue | break ]\n"
1295 exit( EXIT_FAILURE );
1299 * Set pattern to a "normalized" DN from src.
1300 * At present it simply eats the (optional) space after
1301 * a RDN separator (,)
1302 * Eventually will evolve in a more complete normalization
1305 acl_regex_normalized_dn(
1307 struct berval *pattern
1313 str = ch_strdup( src );
1314 len = strlen( src );
1316 for ( p = str; p && p[ 0 ]; p++ ) {
1318 if ( p[ 0 ] == '\\' && p[ 1 ] ) {
1320 * if escaping a hex pair we should
1321 * increment p twice; however, in that
1322 * case the second hex number does
1328 if ( p[ 0 ] == ',' ) {
1329 if ( p[ 1 ] == ' ' ) {
1333 * too much space should be
1334 * an error if we are pedantic
1336 for ( q = &p[ 2 ]; q[ 0 ] == ' '; q++ ) {
1339 AC_MEMCPY( p+1, q, len-(q-str)+1);
1343 pattern->bv_val = str;
1344 pattern->bv_len = p-str;
1358 if ( (*right = strchr( line, splitchar )) != NULL ) {
1359 *((*right)++) = '\0';
1364 access_append( Access **l, Access *a )
1366 for ( ; *l != NULL; l = &(*l)->a_next )
1373 acl_append( AccessControl **l, AccessControl *a )
1375 for ( ; *l != NULL; l = &(*l)->acl_next )
1382 access_free( Access *a )
1384 if ( a->a_dn_pat.bv_val )
1385 free ( a->a_dn_pat.bv_val );
1386 if ( a->a_peername_pat.bv_val )
1387 free ( a->a_peername_pat.bv_val );
1388 if ( a->a_sockname_pat.bv_val )
1389 free ( a->a_sockname_pat.bv_val );
1390 if ( a->a_domain_pat.bv_val )
1391 free ( a->a_domain_pat.bv_val );
1392 if ( a->a_sockurl_pat.bv_val )
1393 free ( a->a_sockurl_pat.bv_val );
1394 if ( a->a_set_pat.bv_len )
1395 free ( a->a_set_pat.bv_val );
1396 if ( a->a_group_pat.bv_len )
1397 free ( a->a_group_pat.bv_val );
1402 acl_free( AccessControl *a )
1407 if ( a->acl_filter )
1408 filter_free( a->acl_filter );
1409 if ( a->acl_dn_pat.bv_len )
1410 free ( a->acl_dn_pat.bv_val );
1411 if ( a->acl_attrs ) {
1412 for ( an = a->acl_attrs; an->an_name.bv_val; an++ ) {
1413 free( an->an_name.bv_val );
1415 free( a->acl_attrs );
1417 for (; a->acl_access; a->acl_access = n) {
1418 n = a->acl_access->a_next;
1419 access_free( a->acl_access );
1424 /* Because backend_startup uses acl_append to tack on the global_acl to
1425 * the end of each backend's acl, we cannot just take one argument and
1426 * merrily free our way to the end of the list. backend_destroy calls us
1427 * with the be_acl in arg1, and global_acl in arg2 to give us a stopping
1428 * point. config_destroy calls us with global_acl in arg1 and NULL in
1429 * arg2, so we then proceed to polish off the global_acl.
1432 acl_destroy( AccessControl *a, AccessControl *end )
1436 for (; a && a!= end; a=n) {
1443 access2str( slap_access_t access )
1445 if ( access == ACL_NONE ) {
1448 } else if ( access == ACL_AUTH ) {
1451 } else if ( access == ACL_COMPARE ) {
1454 } else if ( access == ACL_SEARCH ) {
1457 } else if ( access == ACL_READ ) {
1460 } else if ( access == ACL_WRITE ) {
1468 str2access( const char *str )
1470 if ( strcasecmp( str, "none" ) == 0 ) {
1473 } else if ( strcasecmp( str, "auth" ) == 0 ) {
1476 } else if ( strcasecmp( str, "compare" ) == 0 ) {
1479 } else if ( strcasecmp( str, "search" ) == 0 ) {
1482 } else if ( strcasecmp( str, "read" ) == 0 ) {
1485 } else if ( strcasecmp( str, "write" ) == 0 ) {
1489 return( ACL_INVALID_ACCESS );
1494 static char *style_strings[5] = {
1504 print_access( Access *b )
1506 char maskbuf[ACCESSMASK_MAXLEN];
1508 fprintf( stderr, "\tby" );
1510 if ( b->a_dn_pat.bv_len != 0 ) {
1511 if( strcmp(b->a_dn_pat.bv_val, "*") == 0
1512 || strcmp(b->a_dn_pat.bv_val, "users") == 0
1513 || strcmp(b->a_dn_pat.bv_val, "anonymous") == 0
1514 || strcmp(b->a_dn_pat.bv_val, "self") == 0 )
1516 fprintf( stderr, " %s", b->a_dn_pat.bv_val );
1519 fprintf( stderr, " dn.%s=%s",
1520 style_strings[b->a_dn_style], b->a_dn_pat.bv_val );
1524 if ( b->a_dn_at != NULL ) {
1525 fprintf( stderr, " dnattr=%s", b->a_dn_at->ad_cname.bv_val );
1528 if ( b->a_group_pat.bv_len ) {
1529 fprintf( stderr, " group=%s", b->a_group_pat.bv_val );
1531 if ( b->a_group_oc ) {
1532 fprintf( stderr, " objectClass: %s",
1533 b->a_group_oc->soc_oclass.oc_oid );
1535 if ( b->a_group_at ) {
1536 fprintf( stderr, " attributeType: %s", b->a_group_at->ad_cname.bv_val );
1541 if ( b->a_peername_pat.bv_len != 0 ) {
1542 fprintf( stderr, " peername=%s", b->a_peername_pat.bv_val );
1545 if ( b->a_sockname_pat.bv_len != 0 ) {
1546 fprintf( stderr, " sockname=%s", b->a_sockname_pat.bv_val );
1549 if ( b->a_domain_pat.bv_len != 0 ) {
1550 fprintf( stderr, " domain=%s", b->a_domain_pat.bv_val );
1553 if ( b->a_sockurl_pat.bv_len != 0 ) {
1554 fprintf( stderr, " sockurl=%s", b->a_sockurl_pat.bv_val );
1557 if ( b->a_set_pat.bv_len != 0 ) {
1558 fprintf( stderr, " set=\"%s\"", b->a_set_pat.bv_val );
1561 #ifdef SLAPD_ACI_ENABLED
1562 if ( b->a_aci_at != NULL ) {
1563 fprintf( stderr, " aci=%s", b->a_aci_at->ad_cname.bv_val );
1567 /* Security Strength Factors */
1568 if ( b->a_authz.sai_ssf ) {
1569 fprintf( stderr, " ssf=%u",
1570 b->a_authz.sai_ssf );
1572 if ( b->a_authz.sai_transport_ssf ) {
1573 fprintf( stderr, " transport_ssf=%u",
1574 b->a_authz.sai_transport_ssf );
1576 if ( b->a_authz.sai_tls_ssf ) {
1577 fprintf( stderr, " tls_ssf=%u",
1578 b->a_authz.sai_tls_ssf );
1580 if ( b->a_authz.sai_sasl_ssf ) {
1581 fprintf( stderr, " sasl_ssf=%u",
1582 b->a_authz.sai_sasl_ssf );
1585 fprintf( stderr, " %s%s",
1586 b->a_dn_self ? "self" : "",
1587 accessmask2str( b->a_access_mask, maskbuf ) );
1589 if( b->a_type == ACL_BREAK ) {
1590 fprintf( stderr, " break" );
1592 } else if( b->a_type == ACL_CONTINUE ) {
1593 fprintf( stderr, " continue" );
1595 } else if( b->a_type != ACL_STOP ) {
1596 fprintf( stderr, " unknown-control" );
1599 fprintf( stderr, "\n" );
1604 print_acl( Backend *be, AccessControl *a )
1609 fprintf( stderr, "%s ACL: access to",
1610 be == NULL ? "Global" : "Backend" );
1612 if ( a->acl_dn_pat.bv_len != 0 ) {
1614 fprintf( stderr, " dn.%s=%s\n",
1615 style_strings[a->acl_dn_style], a->acl_dn_pat.bv_val );
1618 if ( a->acl_filter != NULL ) {
1619 struct berval bv = { 0, NULL };
1621 filter2bv( a->acl_filter, &bv );
1622 fprintf( stderr, " filter=%s\n", bv.bv_val );
1623 ch_free( bv.bv_val );
1626 if ( a->acl_attrs != NULL ) {
1631 fprintf( stderr, " attrs=" );
1632 for ( an = a->acl_attrs; an && an->an_name.bv_val; an++ ) {
1634 fprintf( stderr, "," );
1636 fputs( an->an_name.bv_val, stderr );
1639 fprintf( stderr, "\n" );
1643 fprintf( stderr, " *\n" );
1646 for ( b = a->acl_access; b != NULL; b = b->a_next ) {
1650 fprintf( stderr, "\n" );
1653 #endif /* LDAP_DEBUG */