]> git.sur5r.net Git - openldap/blob - servers/slapd/aclparse.c
Add comments
[openldap] / servers / slapd / aclparse.c
1 /* aclparse.c - routines to parse and check acl's */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/ctype.h>
13 #include <ac/regex.h>
14 #include <ac/socket.h>
15 #include <ac/string.h>
16 #include <ac/unistd.h>
17
18 #include "slap.h"
19
20 static void             split(char *line, int splitchar, char **left, char **right);
21 static void             access_append(Access **l, Access *a);
22 static void             acl_usage(void) LDAP_GCCATTR((noreturn));
23
24 static char             *acl_regex_normalized_dn(const char *pattern);
25
26 #ifdef LDAP_DEBUG
27 static void             print_acl(Backend *be, AccessControl *a);
28 static void             print_access(Access *b);
29 #endif
30
31 static int
32 regtest(const char *fname, int lineno, char *pat) {
33         int e;
34         regex_t re;
35
36         char buf[512];
37         unsigned size;
38
39         char *sp;
40         char *dp;
41         int  flag;
42
43         sp = pat;
44         dp = buf;
45         size = 0;
46         buf[0] = '\0';
47
48         for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
49                 if (flag) {
50                         if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
51                                 *dp++ = *sp;
52                                 size++;
53                         }
54                         flag = 0;
55
56                 } else {
57                         if (*sp == '$') {
58                                 flag = 1;
59                         } else {
60                                 *dp++ = *sp;
61                                 size++;
62                         }
63                 }
64         }
65
66         *dp = '\0';
67         if ( size >= (sizeof(buf)-1) ) {
68                 fprintf( stderr,
69                         "%s: line %d: regular expression \"%s\" too large\n",
70                         fname, lineno, pat );
71                 acl_usage();
72         }
73
74         if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
75                 char error[512];
76                 regerror(e, &re, error, sizeof(error));
77                 fprintf( stderr,
78                         "%s: line %d: regular expression \"%s\" bad because of %s\n",
79                         fname, lineno, pat, error );
80                 acl_usage();
81                 return(0);
82         }
83         regfree(&re);
84         return(1);
85 }
86
87 void
88 parse_acl(
89     Backend     *be,
90     const char  *fname,
91     int         lineno,
92     int         argc,
93     char        **argv
94 )
95 {
96         int             i;
97         char            *left, *right, *style;
98         AccessControl   *a;
99         Access  *b;
100         int rc;
101         const char *text;
102
103         a = NULL;
104         for ( i = 1; i < argc; i++ ) {
105                 /* to clause - select which entries are protected */
106                 if ( strcasecmp( argv[i], "to" ) == 0 ) {
107                         if ( a != NULL ) {
108                                 fprintf( stderr,
109                 "%s: line %d: only one to clause allowed in access line\n",
110                                     fname, lineno );
111                                 acl_usage();
112                         }
113                         a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
114                         a->acl_filter = NULL;
115                         a->acl_dn_pat = NULL;
116                         a->acl_attrs  = NULL;
117                         a->acl_access = NULL;
118                         a->acl_next   = NULL;
119                         for ( ++i; i < argc; i++ ) {
120                                 if ( strcasecmp( argv[i], "by" ) == 0 ) {
121                                         i--;
122                                         break;
123                                 }
124
125                                 if ( strcasecmp( argv[i], "*" ) == 0 ) {
126                                         if( a->acl_dn_pat != NULL ) {
127                                                 fprintf( stderr,
128                                                         "%s: line %d: dn pattern"
129                                                         " already specified in to clause.\n",
130                                                         fname, lineno );
131                                                 acl_usage();
132                                         }
133
134                                         a->acl_dn_pat = ch_strdup( "*" );
135                                         continue;
136                                 }
137
138                                 split( argv[i], '=', &left, &right );
139                                 split( left, '.', &left, &style );
140
141                                 if ( right == NULL ) {
142                                         fprintf( stderr,
143         "%s: line %d: missing \"=\" in \"%s\" in to clause\n",
144                                             fname, lineno, left );
145                                         acl_usage();
146                                 }
147
148                                 if ( strcasecmp( left, "dn" ) == 0 ) {
149                                         if( a->acl_dn_pat != NULL ) {
150                                                 fprintf( stderr,
151                                                         "%s: line %d: dn pattern"
152                                                         " already specified in to clause.\n",
153                                                         fname, lineno );
154                                                 acl_usage();
155                                         }
156
157                                         if ( style == NULL || *style == '\0'
158                                                 || strcasecmp( style, "regex" ) == 0 )
159                                         {
160                                                 a->acl_dn_style = ACL_STYLE_REGEX;
161                                                 if ( strcmp(right, "*") == 0 
162                                                         || strcmp(right, ".*") == 0 
163                                                         || strcmp(right, ".*$") == 0 
164                                                         || strcmp(right, "^.*") == 0 
165                                                         || strcmp(right, "^.*$$") == 0
166                                                         || strcmp(right, ".*$$") == 0 
167                                                         || strcmp(right, "^.*$$") == 0 )
168                                                 {
169                                                         a->acl_dn_pat = ch_strdup( "*" );
170
171                                                 } else {
172                                                         a->acl_dn_pat = acl_regex_normalized_dn( right );
173                                                 }
174                                         } else if ( strcasecmp( style, "base" ) == 0 ) {
175                                                 a->acl_dn_style = ACL_STYLE_BASE;
176                                                 a->acl_dn_pat = ch_strdup( right );
177
178                                         } else if ( strcasecmp( style, "one" ) == 0 ) {
179                                                 a->acl_dn_style = ACL_STYLE_ONE;
180                                                 a->acl_dn_pat = ch_strdup( right );
181
182                                         } else if ( strcasecmp( style, "subtree" ) == 0 ) {
183                                                 a->acl_dn_style = ACL_STYLE_SUBTREE;
184                                                 a->acl_dn_pat = ch_strdup( right );
185
186                                         } else if ( strcasecmp( style, "children" ) == 0 ) {
187                                                 a->acl_dn_style = ACL_STYLE_CHILDREN;
188                                                 a->acl_dn_pat = ch_strdup( right );
189
190                                         } else {
191                                                 fprintf( stderr,
192         "%s: line %d: unknown dn style \"%s\" in to clause\n",
193                                                     fname, lineno, style );
194                                                 acl_usage();
195                                         }
196
197                                         continue;
198                                 }
199
200                                 if ( strcasecmp( left, "filter" ) == 0 ) {
201                                         if ( (a->acl_filter = str2filter(
202                                             right )) == NULL ) {
203                                                 fprintf( stderr,
204                                 "%s: line %d: bad filter \"%s\" in to clause\n",
205                                                     fname, lineno, right );
206                                                 acl_usage();
207                                         }
208
209                                 } else if ( strncasecmp( left, "attr", 4 ) == 0 ) {
210                                         char    **alist;
211
212                                         alist = str2charray( right, "," );
213                                         charray_merge( &a->acl_attrs, alist );
214                                         charray_free( alist );
215
216                                 } else {
217                                         fprintf( stderr,
218                                                 "%s: line %d: expecting <what> got \"%s\"\n",
219                                             fname, lineno, left );
220                                         acl_usage();
221                                 }
222                         }
223
224                         if ( a->acl_dn_pat != NULL && strcmp(a->acl_dn_pat, "*") == 0) {
225                                 free( a->acl_dn_pat );
226                                 a->acl_dn_pat = NULL;
227                         }
228                         
229                         if( a->acl_dn_pat != NULL ) {
230                                 if ( a->acl_dn_style != ACL_STYLE_REGEX )
231                                 {
232                                         dn_normalize(a->acl_dn_pat);
233
234                                 } else {
235                                         int e = regcomp( &a->acl_dn_re, a->acl_dn_pat,
236                                                          REG_EXTENDED | REG_ICASE );
237                                         if ( e ) {
238                                                 char buf[512];
239                                                 regerror( e, &a->acl_dn_re, buf, sizeof(buf) );
240                                                 fprintf( stderr,
241                                         "%s: line %d: regular expression \"%s\" bad because of %s\n",
242                                                          fname, lineno, right, buf );
243                                                 acl_usage();
244                                         }
245                                 }
246                         }
247
248                 /* by clause - select who has what access to entries */
249                 } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
250                         if ( a == NULL ) {
251                                 fprintf( stderr,
252                                         "%s: line %d: to clause required before by clause in access line\n",
253                                     fname, lineno );
254                                 acl_usage();
255                         }
256
257                         /*
258                          * by clause consists of <who> and <access>
259                          */
260
261                         b = (Access *) ch_calloc( 1, sizeof(Access) );
262
263                         ACL_INVALIDATE( b->a_access_mask );
264
265                         if ( ++i == argc ) {
266                                 fprintf( stderr,
267                             "%s: line %d: premature eol: expecting <who>\n",
268                                     fname, lineno );
269                                 acl_usage();
270                         }
271
272                         /* get <who> */
273                         for ( ; i < argc; i++ ) {
274                                 char *pat;
275                                 slap_style_t sty = ACL_STYLE_REGEX;
276
277                                 split( argv[i], '=', &left, &right );
278                                 split( left, '.', &left, &style );
279                                 if ( style == NULL || *style == '\0'
280                                         || strcasecmp( style, "regex" ) == 0 )
281                                 {
282                                         sty = ACL_STYLE_REGEX;
283                                 } else if ( strcasecmp( style, "exact" ) == 0 ) {
284                                         sty = ACL_STYLE_EXACT;
285                                 } else if ( strcasecmp( style, "base" ) == 0 ) {
286                                         sty = ACL_STYLE_BASE;
287                                 } else if ( strcasecmp( style, "one" ) == 0 ) {
288                                         sty = ACL_STYLE_ONE;
289                                 } else if ( strcasecmp( style, "subtree" ) == 0 ) {
290                                         sty = ACL_STYLE_SUBTREE;
291                                 } else if ( strcasecmp( style, "children" ) == 0 ) {
292                                         sty = ACL_STYLE_CHILDREN;
293                                 } else {
294                                         fprintf( stderr,
295                                                 "%s: line %d: unknown style \"%s\" in by clause\n",
296                                             fname, lineno, style );
297                                         acl_usage();
298                                 }
299
300                                 if ( strcasecmp( argv[i], "*" ) == 0 ) {
301                                         pat = ch_strdup( "*" );
302
303                                 } else if ( strcasecmp( argv[i], "anonymous" ) == 0 ) {
304                                         pat = ch_strdup( "anonymous" );
305
306                                 } else if ( strcasecmp( argv[i], "self" ) == 0 ) {
307                                         pat = ch_strdup( "self" );
308
309                                 } else if ( strcasecmp( argv[i], "users" ) == 0 ) {
310                                         pat = ch_strdup( "users" );
311
312                                 } else if ( strcasecmp( left, "dn" ) == 0 ) {
313                                         if ( sty == ACL_STYLE_REGEX ) {
314                                                 b->a_dn_style = ACL_STYLE_REGEX;
315                                                 if( right == NULL ) {
316                                                         /* no '=' */
317                                                         pat = ch_strdup( "users" );
318
319                                                 } else if (*right == '\0' ) {
320                                                         /* dn="" */
321                                                         pat = ch_strdup( "anonymous" );
322
323                                                 } else if ( strcmp( right, "*" ) == 0 ) {
324                                                         /* dn=* */
325                                                         /* any or users?  users for now */
326                                                         pat = ch_strdup( "users" );
327
328                                                 } else if ( strcmp( right, ".+" ) == 0
329                                                         || strcmp( right, "^.+" ) == 0
330                                                         || strcmp( right, ".+$" ) == 0
331                                                         || strcmp( right, "^.+$" ) == 0
332                                                         || strcmp( right, ".+$$" ) == 0
333                                                         || strcmp( right, "^.+$$" ) == 0 )
334                                                 {
335                                                         pat = ch_strdup( "users" );
336
337                                                 } else if ( strcmp( right, ".*" ) == 0
338                                                         || strcmp( right, "^.*" ) == 0
339                                                         || strcmp( right, ".*$" ) == 0
340                                                         || strcmp( right, "^.*$" ) == 0
341                                                         || strcmp( right, ".*$$" ) == 0
342                                                         || strcmp( right, "^.*$$" ) == 0 )
343                                                 {
344                                                         pat = ch_strdup( "*" );
345
346                                                 } else {
347                                                         pat = acl_regex_normalized_dn( right );
348                                                         regtest(fname, lineno, pat);
349                                                 }
350                                         } else if ( right == NULL || *right == '\0' ) {
351                                                 fprintf( stderr,
352                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
353                                                     fname, lineno, left );
354                                                 acl_usage();
355
356                                         } else {
357                                                 pat = ch_strdup( right );
358                                         }
359
360                                 } else {
361                                         pat = NULL;
362                                 }
363
364                                 if( pat != NULL ) {
365                                         if( b->a_dn_pat != NULL ) {
366                                                 fprintf( stderr,
367                                                     "%s: line %d: dn pattern already specified.\n",
368                                                     fname, lineno );
369                                                 acl_usage();
370                                         }
371
372                                         b->a_dn_pat = pat;
373                                         b->a_dn_style = sty;
374                                         if ( sty != ACL_STYLE_REGEX )
375                                                 dn_normalize(pat);
376                                         continue;
377                                 }
378
379                                 if ( strcasecmp( left, "dnattr" ) == 0 ) {
380                                         if ( right == NULL || right[ 0 ] == '\0' ) {
381                                                 fprintf( stderr,
382                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
383                                                         fname, lineno, left );
384                                                 acl_usage();
385                                         }
386
387                                         if( b->a_dn_at != NULL ) {
388                                                 fprintf( stderr,
389                                                         "%s: line %d: dnattr already specified.\n",
390                                                         fname, lineno );
391                                                 acl_usage();
392                                         }
393
394                                         rc = slap_str2ad( right, &b->a_dn_at, &text );
395
396                                         if( rc != LDAP_SUCCESS ) {
397                                                 fprintf( stderr,
398                                                         "%s: line %d: dnattr \"%s\": %s\n",
399                                                         fname, lineno, right, text );
400                                                 acl_usage();
401                                         }
402
403
404                                         if( !is_at_syntax( b->a_dn_at->ad_type,
405                                                 SLAPD_DN_SYNTAX ) &&
406                                                 !is_at_syntax( b->a_dn_at->ad_type,
407                                                 SLAPD_NAMEUID_SYNTAX ))
408                                         {
409                                                 fprintf( stderr,
410                                                         "%s: line %d: dnattr \"%s\": "
411                                                         "inappropriate syntax: %s\n",
412                                                         fname, lineno, right,
413                                                         b->a_dn_at->ad_type->sat_syntax_oid );
414                                                 acl_usage();
415                                         }
416
417                                         continue;
418                                 }
419
420                                 if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
421                                         fprintf( stderr,
422                                                 "%s: line %d: inappropriate style \"%s\" in by clause\n",
423                                             fname, lineno, style );
424                                         acl_usage();
425                                 }
426
427                                 if ( strncasecmp( left, "group", sizeof("group")-1 ) == 0 ) {
428                                         char *name = NULL;
429                                         char *value = NULL;
430
431                                         if ( right == NULL || right[ 0 ] == '\0' ) {
432                                                 fprintf( stderr,
433                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
434                                                         fname, lineno, left );
435                                                 acl_usage();
436                                         }
437
438                                         if( b->a_group_pat != NULL ) {
439                                                 fprintf( stderr,
440                                                         "%s: line %d: group pattern already specified.\n",
441                                                         fname, lineno );
442                                                 acl_usage();
443                                         }
444
445                                         /* format of string is "group/objectClassValue/groupAttrName" */
446                                         if ((value = strchr(left, '/')) != NULL) {
447                                                 *value++ = '\0';
448                                                 if (*value
449                                                         && (name = strchr(value, '/')) != NULL)
450                                                 {
451                                                         *name++ = '\0';
452                                                 }
453                                         }
454
455                                         b->a_group_style = sty;
456                                         if (sty == ACL_STYLE_REGEX) {
457                                                 char *tmp = acl_regex_normalized_dn( right );
458                                                 regtest(fname, lineno, tmp);
459                                                 b->a_group_pat = tmp;
460                                         } else {
461                                                 b->a_group_pat = ch_strdup( right );
462                                                 dn_normalize(b->a_group_pat);
463                                         }
464
465                                         if (value && *value) {
466                                                 b->a_group_oc = oc_find( value );
467                                                 *--value = '/';
468
469                                                 if( b->a_group_oc == NULL ) {
470                                                         fprintf( stderr,
471                                                                 "%s: line %d: group objectclass "
472                                                                 "\"%s\" unknown\n",
473                                                                 fname, lineno, value );
474                                                         acl_usage();
475                                                 }
476                                         } else {
477                                                 b->a_group_oc = oc_find(SLAPD_GROUP_CLASS);
478
479                                                 if( b->a_group_oc == NULL ) {
480                                                         fprintf( stderr,
481                                                                 "%s: line %d: group default objectclass "
482                                                                 "\"%s\" unknown\n",
483                                                                 fname, lineno, SLAPD_GROUP_CLASS );
484                                                         acl_usage();
485                                                 }
486                                         }
487
488                                         if( is_object_subclass( b->a_group_oc,
489                                                 slap_schema.si_oc_referral ) )
490                                         {
491                                                 fprintf( stderr,
492                                                         "%s: line %d: group objectclass \"%s\" "
493                                                         "is subclass of referral\n",
494                                                         fname, lineno, value );
495                                                 acl_usage();
496                                         }
497
498                                         if( is_object_subclass( b->a_group_oc,
499                                                 slap_schema.si_oc_alias ) )
500                                         {
501                                                 fprintf( stderr,
502                                                         "%s: line %d: group objectclass \"%s\" "
503                                                         "is subclass of alias\n",
504                                                         fname, lineno, value );
505                                                 acl_usage();
506                                         }
507
508                                         if (name && *name) {
509                                                 rc = slap_str2ad( name, &b->a_group_at, &text );
510
511                                                 if( rc != LDAP_SUCCESS ) {
512                                                         fprintf( stderr,
513                                                                 "%s: line %d: group \"%s\": %s\n",
514                                                                 fname, lineno, right, text );
515                                                         acl_usage();
516                                                 }
517                                                 *--name = '/';
518                                         } else {
519                                                 rc = slap_str2ad( SLAPD_GROUP_ATTR, &b->a_group_at, &text );
520
521                                                 if( rc != LDAP_SUCCESS ) {
522                                                         fprintf( stderr,
523                                                                 "%s: line %d: group \"%s\": %s\n",
524                                                                 fname, lineno, SLAPD_GROUP_ATTR, text );
525                                                         acl_usage();
526                                                 }
527                                         }
528
529                                         if( !is_at_syntax( b->a_group_at->ad_type,
530                                                 SLAPD_DN_SYNTAX ) &&
531                                             !is_at_syntax( b->a_group_at->ad_type,
532                                                 SLAPD_NAMEUID_SYNTAX ) )
533                                         {
534                                                 fprintf( stderr,
535                                                         "%s: line %d: group \"%s\": inappropriate syntax: %s\n",
536                                                         fname, lineno, right,
537                                                         b->a_group_at->ad_type->sat_syntax_oid );
538                                                 acl_usage();
539                                         }
540
541
542                                         {
543                                                 int rc;
544                                                 struct berval val;
545                                                 struct berval *vals[2];
546
547                                                 val.bv_val = b->a_group_oc->soc_oid;
548                                                 val.bv_len = strlen(val.bv_val);
549                                                 vals[0] = &val;
550                                                 vals[1] = NULL;
551
552
553                                                 rc = oc_check_allowed( b->a_group_at->ad_type, vals );
554
555                                                 if( rc != 0 ) {
556                                                         fprintf( stderr,
557                                                                 "%s: line %d: group: \"%s\" not allowed by \"%s\"\n",
558                                                                 fname, lineno,
559                                                                 b->a_group_at->ad_cname.bv_val,
560                                                                 b->a_group_oc->soc_oid );
561                                                         acl_usage();
562                                                 }
563                                         }
564                                         continue;
565                                 }
566
567                                 if ( strcasecmp( left, "peername" ) == 0 ) {
568                                         if ( right == NULL || right[ 0 ] == '\0' ) {
569                                                 fprintf( stderr,
570                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
571                                                         fname, lineno, left );
572                                                 acl_usage();
573                                         }
574
575                                         if( b->a_peername_pat != NULL ) {
576                                                 fprintf( stderr,
577                                                         "%s: line %d: peername pattern already specified.\n",
578                                                         fname, lineno );
579                                                 acl_usage();
580                                         }
581
582                                         b->a_peername_style = sty;
583                                         if (sty == ACL_STYLE_REGEX) {
584                                                 char *tmp = acl_regex_normalized_dn( right );
585                                                 regtest(fname, lineno, tmp);
586                                                 b->a_peername_pat = tmp;
587                                         } else {
588                                                 b->a_peername_pat = ch_strdup( right );
589                                         }
590                                         continue;
591                                 }
592
593                                 if ( strcasecmp( left, "sockname" ) == 0 ) {
594                                         if ( right == NULL || right[ 0 ] == '\0' ) {
595                                                 fprintf( stderr,
596                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
597                                                         fname, lineno, left );
598                                                 acl_usage();
599                                         }
600
601                                         if( b->a_sockname_pat != NULL ) {
602                                                 fprintf( stderr,
603                                                         "%s: line %d: sockname pattern already specified.\n",
604                                                         fname, lineno );
605                                                 acl_usage();
606                                         }
607
608                                         b->a_sockname_style = sty;
609                                         if (sty == ACL_STYLE_REGEX) {
610                                                 char *tmp = acl_regex_normalized_dn( right );
611                                                 regtest(fname, lineno, tmp);
612                                                 b->a_sockname_pat = tmp;
613                                         } else {
614                                                 b->a_sockname_pat = ch_strdup( right );
615                                         }
616                                         continue;
617                                 }
618
619                                 if ( strcasecmp( left, "domain" ) == 0 ) {
620                                         if ( right == NULL || right[ 0 ] == '\0' ) {
621                                                 fprintf( stderr,
622                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
623                                                         fname, lineno, left );
624                                                 acl_usage();
625                                         }
626
627                                         if( b->a_domain_pat != NULL ) {
628                                                 fprintf( stderr,
629                                                         "%s: line %d: domain pattern already specified.\n",
630                                                         fname, lineno );
631                                                 acl_usage();
632                                         }
633
634                                         b->a_domain_style = sty;
635                                         if (sty == ACL_STYLE_REGEX) {
636                                                 char *tmp = acl_regex_normalized_dn( right );
637                                                 regtest(fname, lineno, tmp);
638                                                 b->a_domain_pat = tmp;
639                                         } else {
640                                                 b->a_domain_pat = ch_strdup( right );
641                                         }
642                                         continue;
643                                 }
644
645                                 if ( strcasecmp( left, "sockurl" ) == 0 ) {
646                                         if ( right == NULL || right[ 0 ] == '\0' ) {
647                                                 fprintf( stderr,
648                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
649                                                         fname, lineno, left );
650                                                 acl_usage();
651                                         }
652
653                                         if( b->a_sockurl_pat != NULL ) {
654                                                 fprintf( stderr,
655                                                         "%s: line %d: sockurl pattern already specified.\n",
656                                                         fname, lineno );
657                                                 acl_usage();
658                                         }
659
660                                         b->a_sockurl_style = sty;
661                                         if (sty == ACL_STYLE_REGEX) {
662                                                 char *tmp = acl_regex_normalized_dn( right );
663                                                 regtest(fname, lineno, tmp);
664                                                 b->a_sockurl_pat = tmp;
665                                         } else {
666                                                 b->a_sockurl_pat = ch_strdup( right );
667                                         }
668                                         continue;
669                                 }
670
671                                 if ( strcasecmp( left, "set" ) == 0 ) {
672                                         if( b->a_set_pat != NULL ) {
673                                                 fprintf( stderr,
674                                                         "%s: line %d: set attribute already specified.\n",
675                                                         fname, lineno );
676                                                 acl_usage();
677                                         }
678
679                                         if ( right == NULL || *right == '\0' ) {
680                                                 fprintf( stderr,
681                                                         "%s: line %d: no set is defined\n",
682                                                         fname, lineno );
683                                                 acl_usage();
684                                         }
685
686                                         b->a_set_style = sty;
687                                         b->a_set_pat = ch_strdup(right);
688
689                                         continue;
690                                 }
691
692 #ifdef SLAPD_ACI_ENABLED
693                                 if ( strcasecmp( left, "aci" ) == 0 ) {
694                                         if( b->a_aci_at != NULL ) {
695                                                 fprintf( stderr,
696                                                         "%s: line %d: aci attribute already specified.\n",
697                                                         fname, lineno );
698                                                 acl_usage();
699                                         }
700
701                                         if ( right != NULL && *right != '\0' ) {
702                                                 rc = slap_str2ad( right, &b->a_aci_at, &text );
703
704                                                 if( rc != LDAP_SUCCESS ) {
705                                                         fprintf( stderr,
706                                                                 "%s: line %d: aci \"%s\": %s\n",
707                                                                 fname, lineno, right, text );
708                                                         acl_usage();
709                                                 }
710
711                                         } else {
712                                                 rc = slap_str2ad( SLAPD_ACI_ATTR, &b->a_aci_at, &text );
713
714                                                 if( rc != LDAP_SUCCESS ) {
715                                                         fprintf( stderr,
716                                                                 "%s: line %d: aci \"%s\": %s\n",
717                                                                 fname, lineno, SLAPD_ACI_ATTR, text );
718                                                         acl_usage();
719                                                 }
720                                         }
721
722                                         if( !is_at_syntax( b->a_aci_at->ad_type,
723                                                 SLAPD_ACI_SYNTAX) )
724                                         {
725                                                 fprintf( stderr,
726                                                         "%s: line %d: aci \"%s\": inappropriate syntax: %s\n",
727                                                         fname, lineno, right,
728                                                         b->a_aci_at->ad_type->sat_syntax_oid );
729                                                 acl_usage();
730                                         }
731
732                                         continue;
733                                 }
734 #endif /* SLAPD_ACI_ENABLED */
735
736                                 if ( strcasecmp( left, "ssf" ) == 0 ) {
737                                         if( b->a_authz.sai_ssf ) {
738                                                 fprintf( stderr,
739                                                         "%s: line %d: ssf attribute already specified.\n",
740                                                         fname, lineno );
741                                                 acl_usage();
742                                         }
743
744                                         if ( right == NULL || *right == '\0' ) {
745                                                 fprintf( stderr,
746                                                         "%s: line %d: no ssf is defined\n",
747                                                         fname, lineno );
748                                                 acl_usage();
749                                         }
750
751                                         b->a_authz.sai_ssf = atoi( right );
752
753                                         if( !b->a_authz.sai_ssf ) {
754                                                 fprintf( stderr,
755                                                         "%s: line %d: invalid ssf value (%s)\n",
756                                                         fname, lineno, right );
757                                                 acl_usage();
758                                         }
759                                         continue;
760                                 }
761
762                                 if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
763                                         if( b->a_authz.sai_transport_ssf ) {
764                                                 fprintf( stderr,
765                                                         "%s: line %d: transport_ssf attribute already specified.\n",
766                                                         fname, lineno );
767                                                 acl_usage();
768                                         }
769
770                                         if ( right == NULL || *right == '\0' ) {
771                                                 fprintf( stderr,
772                                                         "%s: line %d: no transport_ssf is defined\n",
773                                                         fname, lineno );
774                                                 acl_usage();
775                                         }
776
777                                         b->a_authz.sai_transport_ssf = atoi( right );
778
779                                         if( !b->a_authz.sai_transport_ssf ) {
780                                                 fprintf( stderr,
781                                                         "%s: line %d: invalid transport_ssf value (%s)\n",
782                                                         fname, lineno, right );
783                                                 acl_usage();
784                                         }
785                                         continue;
786                                 }
787
788                                 if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
789                                         if( b->a_authz.sai_tls_ssf ) {
790                                                 fprintf( stderr,
791                                                         "%s: line %d: tls_ssf attribute already specified.\n",
792                                                         fname, lineno );
793                                                 acl_usage();
794                                         }
795
796                                         if ( right == NULL || *right == '\0' ) {
797                                                 fprintf( stderr,
798                                                         "%s: line %d: no tls_ssf is defined\n",
799                                                         fname, lineno );
800                                                 acl_usage();
801                                         }
802
803                                         b->a_authz.sai_tls_ssf = atoi( right );
804
805                                         if( !b->a_authz.sai_tls_ssf ) {
806                                                 fprintf( stderr,
807                                                         "%s: line %d: invalid tls_ssf value (%s)\n",
808                                                         fname, lineno, right );
809                                                 acl_usage();
810                                         }
811                                         continue;
812                                 }
813
814                                 if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
815                                         if( b->a_authz.sai_sasl_ssf ) {
816                                                 fprintf( stderr,
817                                                         "%s: line %d: sasl_ssf attribute already specified.\n",
818                                                         fname, lineno );
819                                                 acl_usage();
820                                         }
821
822                                         if ( right == NULL || *right == '\0' ) {
823                                                 fprintf( stderr,
824                                                         "%s: line %d: no sasl_ssf is defined\n",
825                                                         fname, lineno );
826                                                 acl_usage();
827                                         }
828
829                                         b->a_authz.sai_sasl_ssf = atoi( right );
830
831                                         if( !b->a_authz.sai_sasl_ssf ) {
832                                                 fprintf( stderr,
833                                                         "%s: line %d: invalid sasl_ssf value (%s)\n",
834                                                         fname, lineno, right );
835                                                 acl_usage();
836                                         }
837                                         continue;
838                                 }
839
840                                 if( right != NULL ) {
841                                         /* unsplit */
842                                         right[-1] = '=';
843                                 }
844                                 break;
845                         }
846
847                         if( i == argc || ( strcasecmp( left, "stop" ) == 0 )) { 
848                                 /* out of arguments or plain stop */
849
850                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
851                                 b->a_type = ACL_STOP;
852
853                                 access_append( &a->acl_access, b );
854                                 continue;
855                         }
856
857                         if( strcasecmp( left, "continue" ) == 0 ) {
858                                 /* plain continue */
859
860                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
861                                 b->a_type = ACL_CONTINUE;
862
863                                 access_append( &a->acl_access, b );
864                                 continue;
865                         }
866
867                         if( strcasecmp( left, "break" ) == 0 ) {
868                                 /* plain continue */
869
870                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
871                                 b->a_type = ACL_BREAK;
872
873                                 access_append( &a->acl_access, b );
874                                 continue;
875                         }
876
877                         if ( strcasecmp( left, "by" ) == 0 ) {
878                                 /* we've gone too far */
879                                 --i;
880                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
881                                 b->a_type = ACL_STOP;
882
883                                 access_append( &a->acl_access, b );
884                                 continue;
885                         }
886
887                         /* get <access> */
888                         if( strncasecmp( left, "self", 4 ) == 0 ) {
889                                 b->a_dn_self = 1;
890                                 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( &left[4] ) );
891
892                         } else {
893                                 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( left ) );
894                         }
895
896                         if( ACL_IS_INVALID( b->a_access_mask ) ) {
897                                 fprintf( stderr,
898                                         "%s: line %d: expecting <access> got \"%s\"\n",
899                                         fname, lineno, left );
900                                 acl_usage();
901                         }
902
903                         b->a_type = ACL_STOP;
904
905                         if( ++i == argc ) {
906                                 /* out of arguments or plain stop */
907                                 access_append( &a->acl_access, b );
908                                 continue;
909                         }
910
911                         if( strcasecmp( argv[i], "continue" ) == 0 ) {
912                                 /* plain continue */
913                                 b->a_type = ACL_CONTINUE;
914
915                         } else if( strcasecmp( argv[i], "break" ) == 0 ) {
916                                 /* plain continue */
917                                 b->a_type = ACL_BREAK;
918
919                         } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
920                                 /* gone to far */
921                                 i--;
922                         }
923
924                         access_append( &a->acl_access, b );
925
926                 } else {
927                         fprintf( stderr,
928                     "%s: line %d: expecting \"to\" or \"by\" got \"%s\"\n",
929                             fname, lineno, argv[i] );
930                         acl_usage();
931                 }
932         }
933
934         /* if we have no real access clause, complain and do nothing */
935         if ( a == NULL ) {
936                         fprintf( stderr,
937                                 "%s: line %d: warning: no access clause(s) specified in access line\n",
938                             fname, lineno );
939
940         } else {
941 #ifdef LDAP_DEBUG
942                 if (ldap_debug & LDAP_DEBUG_ACL)
943                         print_acl(be, a);
944 #endif
945         
946                 if ( a->acl_access == NULL ) {
947                         fprintf( stderr,
948                         "%s: line %d: warning: no by clause(s) specified in access line\n",
949                             fname, lineno );
950                 }
951
952                 if ( be != NULL ) {
953                         acl_append( &be->be_acl, a );
954                 } else {
955                         acl_append( &global_acl, a );
956                 }
957         }
958 }
959
960 char *
961 accessmask2str( slap_mask_t mask, char *buf )
962 {
963         int none=1;
964
965         assert( buf != NULL );
966
967         if ( ACL_IS_INVALID( mask ) ) {
968                 return "invalid";
969         }
970
971         buf[0] = '\0';
972
973         if ( ACL_IS_LEVEL( mask ) ) {
974                 if ( ACL_LVL_IS_NONE(mask) ) {
975                         strcat( buf, "none" );
976
977                 } else if ( ACL_LVL_IS_AUTH(mask) ) {
978                         strcat( buf, "auth" );
979
980                 } else if ( ACL_LVL_IS_COMPARE(mask) ) {
981                         strcat( buf, "compare" );
982
983                 } else if ( ACL_LVL_IS_SEARCH(mask) ) {
984                         strcat( buf, "search" );
985
986                 } else if ( ACL_LVL_IS_READ(mask) ) {
987                         strcat( buf, "read" );
988
989                 } else if ( ACL_LVL_IS_WRITE(mask) ) {
990                         strcat( buf, "write" );
991                 } else {
992                         strcat( buf, "unknown" );
993                 }
994                 
995                 strcat(buf, " (");
996         }
997
998         if( ACL_IS_ADDITIVE( mask ) ) {
999                 strcat( buf, "+" );
1000
1001         } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
1002                 strcat( buf, "-" );
1003
1004         } else {
1005                 strcat( buf, "=" );
1006         }
1007
1008         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
1009                 none = 0;
1010                 strcat( buf, "w" );
1011         } 
1012
1013         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
1014                 none = 0;
1015                 strcat( buf, "r" );
1016         } 
1017
1018         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
1019                 none = 0;
1020                 strcat( buf, "s" );
1021         } 
1022
1023         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
1024                 none = 0;
1025                 strcat( buf, "c" );
1026         } 
1027
1028         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
1029                 none = 0;
1030                 strcat( buf, "x" );
1031         } 
1032
1033         if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
1034                 none = 0;
1035                 strcat( buf, "n" );
1036         } 
1037
1038         if ( none ) {
1039                 strcat( buf, "0" );
1040         }
1041
1042         if ( ACL_IS_LEVEL( mask ) ) {
1043                 strcat(buf, ")");
1044         } 
1045         return buf;
1046 }
1047
1048 slap_mask_t
1049 str2accessmask( const char *str )
1050 {
1051         slap_mask_t     mask;
1052
1053         if( !ASCII_ALPHA(str[0]) ) {
1054                 int i;
1055
1056                 if ( str[0] == '=' ) {
1057                         ACL_INIT(mask);
1058
1059                 } else if( str[0] == '+' ) {
1060                         ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
1061
1062                 } else if( str[0] == '-' ) {
1063                         ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
1064
1065                 } else {
1066                         ACL_INVALIDATE(mask);
1067                         return mask;
1068                 }
1069
1070                 for( i=1; str[i] != '\0'; i++ ) {
1071                         if( TOLOWER(str[i]) == 'w' ) {
1072                                 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
1073
1074                         } else if( TOLOWER(str[i]) == 'r' ) {
1075                                 ACL_PRIV_SET(mask, ACL_PRIV_READ);
1076
1077                         } else if( TOLOWER(str[i]) == 's' ) {
1078                                 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
1079
1080                         } else if( TOLOWER(str[i]) == 'c' ) {
1081                                 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
1082
1083                         } else if( TOLOWER(str[i]) == 'x' ) {
1084                                 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
1085
1086                         } else if( str[i] != '0' ) {
1087                                 ACL_INVALIDATE(mask);
1088                                 return mask;
1089                         }
1090                 }
1091
1092                 return mask;
1093         }
1094
1095         if ( strcasecmp( str, "none" ) == 0 ) {
1096                 ACL_LVL_ASSIGN_NONE(mask);
1097
1098         } else if ( strcasecmp( str, "auth" ) == 0 ) {
1099                 ACL_LVL_ASSIGN_AUTH(mask);
1100
1101         } else if ( strcasecmp( str, "compare" ) == 0 ) {
1102                 ACL_LVL_ASSIGN_COMPARE(mask);
1103
1104         } else if ( strcasecmp( str, "search" ) == 0 ) {
1105                 ACL_LVL_ASSIGN_SEARCH(mask);
1106
1107         } else if ( strcasecmp( str, "read" ) == 0 ) {
1108                 ACL_LVL_ASSIGN_READ(mask);
1109
1110         } else if ( strcasecmp( str, "write" ) == 0 ) {
1111                 ACL_LVL_ASSIGN_WRITE(mask);
1112
1113         } else {
1114                 ACL_INVALIDATE( mask );
1115         }
1116
1117         return mask;
1118 }
1119
1120 static void
1121 acl_usage( void )
1122 {
1123         fprintf( stderr, "\n"
1124                 "<access clause> ::= access to <what> "
1125                                 "[ by <who> <access> [ <control> ] ]+ \n"
1126                 "<what> ::= * | [dn[.<dnstyle>]=<regex>] [filter=<ldapfilter>] [attrs=<attrlist>]\n"
1127                 "<attrlist> ::= <attr> | <attr> , <attrlist>\n"
1128                 "<attr> ::= <attrname> | entry | children\n"
1129                 "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<regex> ]\n"
1130                         "\t[dnattr=<attrname>]\n"
1131                         "\t[group[/<objectclass>[/<attrname>]][.<style>]=<regex>]\n"
1132                         "\t[peername[.<style>]=<regex>] [sockname[.<style>]=<regex>]\n"
1133                         "\t[domain[.<style>]=<regex>] [sockurl[.<style>]=<regex>]\n"
1134 #ifdef SLAPD_ACI_ENABLED
1135                         "\t[aci=<attrname>]\n"
1136 #endif
1137                         "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
1138                 "<dnstyle> ::= regex | base | exact (alias of base) | one | sub | children\n"
1139                 "<style> ::= regex | base | exact (alias of base)\n"
1140                 "<groupflags> ::= R\n"
1141                 "<access> ::= [self]{<level>|<priv>}\n"
1142                 "<level> ::= none | auth | compare | search | read | write\n"
1143                 "<priv> ::= {=|+|-}{w|r|s|c|x}+\n"
1144                 "<control> ::= [ stop | continue | break ]\n"
1145                 );
1146         exit( EXIT_FAILURE );
1147 }
1148
1149 /*
1150  * At present it simply eats the (optional) space after 
1151  * a RDN separator (,)
1152  * Eventually will evolve in a more complete normalization
1153  */
1154 static char *
1155 acl_regex_normalized_dn(
1156         const char *pattern
1157 )
1158 {
1159         char *str, *p;
1160
1161         str = ch_strdup( pattern );
1162
1163         if ( str == NULL ) {
1164                 return( NULL );
1165         }
1166
1167         for ( p = str; p[ 0 ]; p++ ) {
1168                 /* escape */
1169                 if ( p[ 0 ] == '\\' ) {
1170                         p++;
1171                 }
1172
1173                 if ( p[ 0 ] == ',' ) {
1174                         if ( p[ 1 ] == ' ' ) {
1175                                 char *q;
1176                         
1177                                 for ( q = &p[ 2 ]; q[ 0 ] == ' '; q++ ) {
1178                                         /* DO NOTHING */ ;
1179                                 }
1180                                 AC_MEMCPY( &p[ 1 ], &q[ 0 ], strlen( q ) + 1 );
1181                         }
1182                 }
1183         }
1184
1185         return( str );
1186 }
1187
1188 static void
1189 split(
1190     char        *line,
1191     int         splitchar,
1192     char        **left,
1193     char        **right
1194 )
1195 {
1196         *left = line;
1197         if ( (*right = strchr( line, splitchar )) != NULL ) {
1198                 *((*right)++) = '\0';
1199         }
1200 }
1201
1202 static void
1203 access_append( Access **l, Access *a )
1204 {
1205         for ( ; *l != NULL; l = &(*l)->a_next )
1206                 ;       /* NULL */
1207
1208         *l = a;
1209 }
1210
1211 void
1212 acl_append( AccessControl **l, AccessControl *a )
1213 {
1214         for ( ; *l != NULL; l = &(*l)->acl_next )
1215                 ;       /* NULL */
1216
1217         *l = a;
1218 }
1219
1220 char *
1221 access2str( slap_access_t access )
1222 {
1223         if ( access == ACL_NONE ) {
1224                 return "none";
1225
1226         } else if ( access == ACL_AUTH ) {
1227                 return "auth";
1228
1229         } else if ( access == ACL_COMPARE ) {
1230                 return "compare";
1231
1232         } else if ( access == ACL_SEARCH ) {
1233                 return "search";
1234
1235         } else if ( access == ACL_READ ) {
1236                 return "read";
1237
1238         } else if ( access == ACL_WRITE ) {
1239                 return "write";
1240         }
1241
1242         return "unknown";
1243 }
1244
1245 slap_access_t
1246 str2access( const char *str )
1247 {
1248         if ( strcasecmp( str, "none" ) == 0 ) {
1249                 return ACL_NONE;
1250
1251         } else if ( strcasecmp( str, "auth" ) == 0 ) {
1252                 return ACL_AUTH;
1253
1254         } else if ( strcasecmp( str, "compare" ) == 0 ) {
1255                 return ACL_COMPARE;
1256
1257         } else if ( strcasecmp( str, "search" ) == 0 ) {
1258                 return ACL_SEARCH;
1259
1260         } else if ( strcasecmp( str, "read" ) == 0 ) {
1261                 return ACL_READ;
1262
1263         } else if ( strcasecmp( str, "write" ) == 0 ) {
1264                 return ACL_WRITE;
1265         }
1266
1267         return( ACL_INVALID_ACCESS );
1268 }
1269
1270 #ifdef LDAP_DEBUG
1271
1272 static char *style_strings[5] = {
1273                         "regex",
1274                         "base",
1275                         "one",
1276                         "subtree",
1277                         "children"
1278                 };
1279
1280
1281 static void
1282 print_access( Access *b )
1283 {
1284         char maskbuf[ACCESSMASK_MAXLEN];
1285
1286         fprintf( stderr, "\tby" );
1287
1288         if ( b->a_dn_pat != NULL ) {
1289                 if( strcmp(b->a_dn_pat, "*") == 0
1290                         || strcmp(b->a_dn_pat, "users") == 0 
1291                         || strcmp(b->a_dn_pat, "anonymous") == 0 
1292                         || strcmp(b->a_dn_pat, "self") == 0 )
1293                 {
1294                         fprintf( stderr, " %s", b->a_dn_pat );
1295
1296                 } else {
1297                         fprintf( stderr, " dn.%s=%s", style_strings[b->a_dn_style], b->a_dn_pat );
1298                 }
1299         }
1300
1301         if ( b->a_dn_at != NULL ) {
1302                 fprintf( stderr, " dnattr=%s", b->a_dn_at->ad_cname.bv_val );
1303         }
1304
1305         if ( b->a_group_pat != NULL ) {
1306                 fprintf( stderr, " group=%s", b->a_group_pat );
1307
1308                 if ( b->a_group_oc ) {
1309                         fprintf( stderr, " objectClass: %s",
1310                                 b->a_group_oc->soc_oclass.oc_oid );
1311
1312                         if ( b->a_group_at ) {
1313                                 fprintf( stderr, " attributeType: %s", b->a_group_at->ad_cname.bv_val );
1314                         }
1315                 }
1316     }
1317
1318         if ( b->a_peername_pat != NULL ) {
1319                 fprintf( stderr, " peername=%s", b->a_peername_pat );
1320         }
1321
1322         if ( b->a_sockname_pat != NULL ) {
1323                 fprintf( stderr, " sockname=%s", b->a_sockname_pat );
1324         }
1325
1326         if ( b->a_domain_pat != NULL ) {
1327                 fprintf( stderr, " domain=%s", b->a_domain_pat );
1328         }
1329
1330         if ( b->a_sockurl_pat != NULL ) {
1331                 fprintf( stderr, " sockurl=%s", b->a_sockurl_pat );
1332         }
1333
1334 #ifdef SLAPD_ACI_ENABLED
1335         if ( b->a_aci_at != NULL ) {
1336                 fprintf( stderr, " aci=%s", b->a_aci_at->ad_cname.bv_val );
1337         }
1338 #endif
1339
1340         /* Security Strength Factors */
1341         if ( b->a_authz.sai_ssf ) {
1342                 fprintf( stderr, " ssf=%u",
1343                         b->a_authz.sai_ssf );
1344         }
1345         if ( b->a_authz.sai_transport_ssf ) {
1346                 fprintf( stderr, " transport_ssf=%u",
1347                         b->a_authz.sai_transport_ssf );
1348         }
1349         if ( b->a_authz.sai_tls_ssf ) {
1350                 fprintf( stderr, " tls_ssf=%u",
1351                         b->a_authz.sai_tls_ssf );
1352         }
1353         if ( b->a_authz.sai_sasl_ssf ) {
1354                 fprintf( stderr, " sasl_ssf=%u",
1355                         b->a_authz.sai_sasl_ssf );
1356         }
1357
1358         fprintf( stderr, " %s%s",
1359                 b->a_dn_self ? "self" : "",
1360                 accessmask2str( b->a_access_mask, maskbuf ) );
1361
1362         if( b->a_type == ACL_BREAK ) {
1363                 fprintf( stderr, " break" );
1364
1365         } else if( b->a_type == ACL_CONTINUE ) {
1366                 fprintf( stderr, " continue" );
1367
1368         } else if( b->a_type != ACL_STOP ) {
1369                 fprintf( stderr, " unknown-control" );
1370         }
1371
1372         fprintf( stderr, "\n" );
1373 }
1374
1375
1376 static void
1377 print_acl( Backend *be, AccessControl *a )
1378 {
1379         int             to = 0;
1380         Access  *b;
1381
1382         fprintf( stderr, "%s ACL: access to",
1383                 be == NULL ? "Global" : "Backend" );
1384
1385         if ( a->acl_dn_pat != NULL ) {
1386                 to++;
1387                 fprintf( stderr, " dn.%s=%s\n",
1388                         style_strings[a->acl_dn_style], a->acl_dn_pat );
1389         }
1390
1391         if ( a->acl_filter != NULL ) {
1392                 to++;
1393                 fprintf( stderr, " filter=" );
1394                 filter_print( a->acl_filter );
1395                 fprintf( stderr, "\n" );
1396         }
1397
1398         if ( a->acl_attrs != NULL ) {
1399                 int     i, first = 1;
1400                 to++;
1401
1402                 fprintf( stderr, " attrs=" );
1403                 for ( i = 0; a->acl_attrs[i] != NULL; i++ ) {
1404                         if ( ! first ) {
1405                                 fprintf( stderr, "," );
1406                         }
1407                         fprintf( stderr, a->acl_attrs[i] );
1408                         first = 0;
1409                 }
1410                 fprintf(  stderr, "\n" );
1411         }
1412
1413         if( !to ) {
1414                 fprintf( stderr, " *\n" );
1415         }
1416
1417         for ( b = a->acl_access; b != NULL; b = b->a_next ) {
1418                 print_access( b );
1419         }
1420
1421         fprintf( stderr, "\n" );
1422 }
1423
1424 #endif /* LDAP_DEBUG */