]> git.sur5r.net Git - openldap/blob - servers/slapd/aclparse.c
improve parsing - first step
[openldap] / servers / slapd / aclparse.c
1 /* aclparse.c - routines to parse and check acl's */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2004 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
17  * All rights reserved.
18  *
19  * Redistribution and use in source and binary forms are permitted
20  * provided that this notice is preserved and that due credit is given
21  * to the University of Michigan at Ann Arbor. The name of the University
22  * may not be used to endorse or promote products derived from this
23  * software without specific prior written permission. This software
24  * is provided ``as is'' without express or implied warranty.
25  */
26
27 #include "portable.h"
28
29 #include <stdio.h>
30
31 #include <ac/ctype.h>
32 #include <ac/regex.h>
33 #include <ac/socket.h>
34 #include <ac/string.h>
35 #include <ac/unistd.h>
36
37 #include "slap.h"
38 #include "lber_pvt.h"
39 #include "lutil.h"
40
41 static char *style_strings[] = {
42         "regex",
43         "expand",
44         "base",
45         "one",
46         "subtree",
47         "children",
48         "attrof",
49         "ip",
50         "path",
51         NULL
52 };
53
54 static void             split(char *line, int splitchar, char **left, char **right);
55 static void             access_append(Access **l, Access *a);
56 static void             acl_usage(void) LDAP_GCCATTR((noreturn));
57
58 static void             acl_regex_normalized_dn(const char *src, struct berval *pat);
59
60 #ifdef LDAP_DEBUG
61 static void             print_acl(Backend *be, AccessControl *a);
62 static void             print_access(Access *b);
63 #endif
64
65 #ifdef LDAP_DEVEL
66 static int
67 check_scope( BackendDB *be, AccessControl *a );
68 #endif /* LDAP_DEVEL */
69
70 static void
71 regtest(const char *fname, int lineno, char *pat) {
72         int e;
73         regex_t re;
74
75         char buf[512];
76         unsigned size;
77
78         char *sp;
79         char *dp;
80         int  flag;
81
82         sp = pat;
83         dp = buf;
84         size = 0;
85         buf[0] = '\0';
86
87         for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
88                 if (flag) {
89                         if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
90                                 *dp++ = *sp;
91                                 size++;
92                         }
93                         flag = 0;
94
95                 } else {
96                         if (*sp == '$') {
97                                 flag = 1;
98                         } else {
99                                 *dp++ = *sp;
100                                 size++;
101                         }
102                 }
103         }
104
105         *dp = '\0';
106         if ( size >= (sizeof(buf)-1) ) {
107                 fprintf( stderr,
108                         "%s: line %d: regular expression \"%s\" too large\n",
109                         fname, lineno, pat );
110                 acl_usage();
111         }
112
113         if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
114                 char error[512];
115                 regerror(e, &re, error, sizeof(error));
116                 fprintf( stderr,
117                         "%s: line %d: regular expression \"%s\" bad because of %s\n",
118                         fname, lineno, pat, error );
119                 acl_usage();
120         }
121         regfree(&re);
122 }
123
124 #ifdef LDAP_DEVEL
125
126 /*
127  * Experimental
128  *
129  * Check if the pattern of an ACL, if any, matches the scope
130  * of the backend it is defined within.
131  */
132 #define ACL_SCOPE_UNKNOWN       (-2)
133 #define ACL_SCOPE_ERR           (-1)
134 #define ACL_SCOPE_OK            (0)
135 #define ACL_SCOPE_PARTIAL       (1)
136 #define ACL_SCOPE_WARN          (2)
137
138 static int
139 check_scope( BackendDB *be, AccessControl *a )
140 {
141         int             patlen;
142         struct berval   dn;
143
144         dn = be->be_nsuffix[ 0 ];
145
146         if ( a->acl_dn_pat.bv_len || a->acl_dn_style != ACL_STYLE_REGEX ) {
147                 slap_style_t    style = a->acl_dn_style;
148
149                 if ( style == ACL_STYLE_REGEX ) {
150                         char    dnbuf[ SLAP_LDAPDN_MAXLEN + 2 ];
151                         char    rebuf[ SLAP_LDAPDN_MAXLEN + 1 ];
152                         regex_t re;
153                         int     rc;
154                         
155                         /* add trailing '$' */
156                         AC_MEMCPY( dnbuf, be->be_nsuffix[ 0 ].bv_val,
157                                         be->be_nsuffix[ 0 ].bv_len );
158                         dnbuf[ be->be_nsuffix[ 0 ].bv_len ] = '$';
159                         dnbuf[ be->be_nsuffix[ 0 ].bv_len + 1 ] = '\0';
160
161                         if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) {
162                                 return ACL_SCOPE_WARN;
163                         }
164
165                         /* remove trailing '$' */
166                         AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val,
167                                         a->acl_dn_pat.bv_len + 1 );
168                         if ( a->acl_dn_pat.bv_val[ a->acl_dn_pat.bv_len - 1 ] == '$' ) {
169                                 rebuf[ a->acl_dn_pat.bv_len - 1 ] = '\0';
170                         }
171
172                         /* not a clear indication of scoping error, though */
173                         rc = regexec( &re, rebuf, 0, NULL, 0 )
174                                 ? ACL_SCOPE_WARN : ACL_SCOPE_OK;
175
176                         regfree( &re );
177
178                         return rc;
179                 }
180
181                 patlen = a->acl_dn_pat.bv_len;
182                 /* If backend suffix is longer than pattern,
183                  * it is a potential mismatch (in the sense
184                  * that a superior naming context could
185                  * match */
186                 if ( dn.bv_len > patlen ) {
187                         /* base is blatantly wrong */
188                         if ( style == ACL_STYLE_BASE ) {
189                                 return ACL_SCOPE_ERR;
190                         }
191
192                         /* one can be wrong if there is more
193                          * than one level between the suffix
194                          * and the pattern */
195                         if ( style == ACL_STYLE_ONE ) {
196                                 int     rdnlen = -1, sep = 0;
197
198                                 if ( patlen > 0 ) {
199                                         if ( !DN_SEPARATOR( dn.bv_val[ dn.bv_len - patlen - 1 ] ) )
200                                                 return ACL_SCOPE_ERR;
201                                         sep = 1;
202                                 }
203
204                                 rdnlen = dn_rdnlen( NULL, &dn );
205                                 if ( rdnlen != dn.bv_len - patlen - sep )
206                                         return ACL_SCOPE_ERR;
207                         }
208
209                         /* if the trailing part doesn't match,
210                          * then it's an error */
211                         if ( strcmp( a->acl_dn_pat.bv_val, &dn.bv_val[ dn.bv_len - patlen ] ) != 0 ) {
212                                 return ACL_SCOPE_ERR;
213                         }
214
215                         return ACL_SCOPE_PARTIAL;
216                 }
217
218                 switch ( style ) {
219                 case ACL_STYLE_BASE:
220                 case ACL_STYLE_ONE:
221                 case ACL_STYLE_CHILDREN:
222                 case ACL_STYLE_SUBTREE:
223                         break;
224
225                 default:
226                         assert( 0 );
227                         break;
228                 }
229
230                 if ( dn.bv_len < patlen && !DN_SEPARATOR( a->acl_dn_pat.bv_val[ patlen -dn.bv_len - 1 ] ) ) {
231                         return ACL_SCOPE_ERR;
232                 }
233
234                 if ( strcmp( &a->acl_dn_pat.bv_val[ patlen - dn.bv_len ], dn.bv_val ) != 0 ) {
235                         return ACL_SCOPE_ERR;
236                 }
237
238                 return ACL_SCOPE_OK;
239         }
240
241         return ACL_SCOPE_UNKNOWN;
242 }
243 #endif /* LDAP_DEVEL */
244
245 void
246 parse_acl(
247     Backend     *be,
248     const char  *fname,
249     int         lineno,
250     int         argc,
251     char        **argv
252 )
253 {
254         int             i;
255         char            *left, *right, *style, *next;
256         struct berval   bv;
257         AccessControl   *a;
258         Access  *b;
259         int rc;
260         const char *text;
261
262         a = NULL;
263         for ( i = 1; i < argc; i++ ) {
264                 /* to clause - select which entries are protected */
265                 if ( strcasecmp( argv[i], "to" ) == 0 ) {
266                         if ( a != NULL ) {
267                                 fprintf( stderr, "%s: line %d: "
268                                         "only one to clause allowed in access line\n",
269                                     fname, lineno );
270                                 acl_usage();
271                         }
272                         a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
273                         for ( ++i; i < argc; i++ ) {
274                                 if ( strcasecmp( argv[i], "by" ) == 0 ) {
275                                         i--;
276                                         break;
277                                 }
278
279                                 if ( strcasecmp( argv[i], "*" ) == 0 ) {
280                                         if( a->acl_dn_pat.bv_len ||
281                                                 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
282                                         {
283                                                 fprintf( stderr,
284                                                         "%s: line %d: dn pattern"
285                                                         " already specified in to clause.\n",
286                                                         fname, lineno );
287                                                 acl_usage();
288                                         }
289
290                                         a->acl_dn_pat.bv_val = ch_strdup( "*" );
291                                         a->acl_dn_pat.bv_len = 1;
292                                         continue;
293                                 }
294
295                                 split( argv[i], '=', &left, &right );
296                                 split( left, '.', &left, &style );
297
298                                 if ( right == NULL ) {
299                                         fprintf( stderr, "%s: line %d: "
300                                                 "missing \"=\" in \"%s\" in to clause\n",
301                                             fname, lineno, left );
302                                         acl_usage();
303                                 }
304
305                                 if ( strcasecmp( left, "dn" ) == 0 ) {
306                                         if( a->acl_dn_pat.bv_len != 0 ||
307                                                 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
308                                         {
309                                                 fprintf( stderr,
310                                                         "%s: line %d: dn pattern"
311                                                         " already specified in to clause.\n",
312                                                         fname, lineno );
313                                                 acl_usage();
314                                         }
315
316                                         if ( style == NULL || *style == '\0' ||
317                                                 ( strcasecmp( style, "base" ) == 0 ) ||
318                                                 ( strcasecmp( style, "exact" ) == 0 ))
319                                         {
320                                                 a->acl_dn_style = ACL_STYLE_BASE;
321                                                 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
322
323                                         } else if ( strcasecmp( style, "onelevel" ) == 0
324                                                 || strcasecmp( style, "one" ) == 0 ) {
325                                                 a->acl_dn_style = ACL_STYLE_ONE;
326                                                 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
327
328                                         } else if ( strcasecmp( style, "subtree" ) == 0
329                                                 || strcasecmp( style, "sub" ) == 0 )
330                                         {
331                                                 if( *right == '\0' ) {
332                                                         a->acl_dn_pat.bv_val = ch_strdup( "*" );
333                                                         a->acl_dn_pat.bv_len = 1;
334
335                                                 } else {
336                                                         a->acl_dn_style = ACL_STYLE_SUBTREE;
337                                                         ber_str2bv( right, 0, 1, &a->acl_dn_pat );
338                                                 }
339
340                                         } else if ( strcasecmp( style, "children" ) == 0 ) {
341                                                 a->acl_dn_style = ACL_STYLE_CHILDREN;
342                                                 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
343
344                                         } else if ( strcasecmp( style, "regex" ) == 0 ) {
345                                                 a->acl_dn_style = ACL_STYLE_REGEX;
346
347                                                 if ( *right == '\0' ) {
348                                                         /* empty regex should match empty DN */
349                                                         a->acl_dn_style = ACL_STYLE_BASE;
350                                                         ber_str2bv( right, 0, 1, &a->acl_dn_pat );
351
352                                                 } else if ( strcmp(right, "*") == 0 
353                                                         || strcmp(right, ".*") == 0 
354                                                         || strcmp(right, ".*$") == 0 
355                                                         || strcmp(right, "^.*") == 0 
356                                                         || strcmp(right, "^.*$") == 0
357                                                         || strcmp(right, ".*$$") == 0 
358                                                         || strcmp(right, "^.*$$") == 0 )
359                                                 {
360                                                         a->acl_dn_pat.bv_val = ch_strdup( "*" );
361                                                         a->acl_dn_pat.bv_len = sizeof("*")-1;
362
363                                                 } else {
364                                                         acl_regex_normalized_dn( right, &a->acl_dn_pat );
365                                                 }
366
367                                         } else {
368                                                 fprintf( stderr, "%s: line %d: "
369                                                         "unknown dn style \"%s\" in to clause\n",
370                                                     fname, lineno, style );
371                                                 acl_usage();
372                                         }
373
374                                         continue;
375                                 }
376
377                                 if ( strcasecmp( left, "filter" ) == 0 ) {
378                                         if ( (a->acl_filter = str2filter( right )) == NULL ) {
379                                                 fprintf( stderr,
380                                 "%s: line %d: bad filter \"%s\" in to clause\n",
381                                                     fname, lineno, right );
382                                                 acl_usage();
383                                         }
384
385                                 } else if ( strcasecmp( left, "attr" ) == 0
386                                                 || strcasecmp( left, "attrs" ) == 0 ) {
387                                         a->acl_attrs = str2anlist( a->acl_attrs,
388                                                 right, "," );
389                                         if ( a->acl_attrs == NULL ) {
390                                                 fprintf( stderr,
391                                 "%s: line %d: unknown attr \"%s\" in to clause\n",
392                                                     fname, lineno, right );
393                                                 acl_usage();
394                                         }
395
396                                 } else if ( strncasecmp( left, "val", 3 ) == 0 ) {
397                                         if ( a->acl_attrval.bv_len ) {
398                                                 fprintf( stderr,
399                                 "%s: line %d: attr val already specified in to clause.\n",
400                                                         fname, lineno );
401                                                 acl_usage();
402                                         }
403                                         if ( a->acl_attrs == NULL || a->acl_attrs[1].an_name.bv_val ) {
404                                                 fprintf( stderr,
405                                 "%s: line %d: attr val requires a single attribute.\n",
406                                                         fname, lineno );
407                                                 acl_usage();
408                                         }
409                                         ber_str2bv( right, 0, 1, &a->acl_attrval );
410                                         if ( style && strcasecmp( style, "regex" ) == 0 ) {
411                                                 int e = regcomp( &a->acl_attrval_re, a->acl_attrval.bv_val,
412                                                         REG_EXTENDED | REG_ICASE | REG_NOSUB );
413                                                 if ( e ) {
414                                                         char buf[512];
415                                                         regerror( e, &a->acl_attrval_re, buf, sizeof(buf) );
416                                                         fprintf( stderr, "%s: line %d: "
417                                                                 "regular expression \"%s\" bad because of %s\n",
418                                                                 fname, lineno, right, buf );
419                                                         acl_usage();
420                                                 }
421                                                 a->acl_attrval_style = ACL_STYLE_REGEX;
422                                         } else {
423                                                 /* FIXME: if the attribute has DN syntax,
424                                                  * we might allow one, subtree and children styles as well */
425                                                 if ( !strcasecmp( style, "exact" ) ) {
426                                                         a->acl_attrval_style = ACL_STYLE_BASE;
427
428                                                 } else if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) {
429                                                         if ( !strcasecmp( style, "base" ) ) {
430                                                                 a->acl_attrval_style = ACL_STYLE_BASE;
431                                                         } else if ( !strcasecmp( style, "onelevel" ) || !strcasecmp( style, "one" ) ) {
432                                                                 a->acl_attrval_style = ACL_STYLE_ONE;
433                                                         } else if ( !strcasecmp( style, "subtree" ) || !strcasecmp( style, "sub" ) ) {
434                                                                 a->acl_attrval_style = ACL_STYLE_SUBTREE;
435                                                         } else if ( !strcasecmp( style, "children" ) ) {
436                                                                 a->acl_attrval_style = ACL_STYLE_CHILDREN;
437                                                         } else {
438                                                                 fprintf( stderr, 
439                                                                         "%s: line %d: unknown val.<style> \"%s\" "
440                                                                         "for attributeType \"%s\" with DN syntax; using \"base\"\n",
441                                                                         fname, lineno, style,
442                                                                         a->acl_attrs[0].an_desc->ad_cname.bv_val );
443                                                                 a->acl_attrval_style = ACL_STYLE_BASE;
444                                                         }
445                                                         
446                                                 } else {
447                                                         fprintf( stderr, 
448                                                                 "%s: line %d: unknown val.<style> \"%s\" "
449                                                                 "for attributeType \"%s\"; using \"exact\"\n",
450                                                                 fname, lineno, style,
451                                                                 a->acl_attrs[0].an_desc->ad_cname.bv_val );
452                                                         a->acl_attrval_style = ACL_STYLE_BASE;
453                                                 }
454                                         }
455                                         
456                                 } else {
457                                         fprintf( stderr,
458                                                 "%s: line %d: expecting <what> got \"%s\"\n",
459                                             fname, lineno, left );
460                                         acl_usage();
461                                 }
462                         }
463
464                         if ( a->acl_dn_pat.bv_len != 0 &&
465                                 strcmp(a->acl_dn_pat.bv_val, "*") == 0 )
466                         {
467                                 free( a->acl_dn_pat.bv_val );
468                                 a->acl_dn_pat.bv_val = NULL;
469                                 a->acl_dn_pat.bv_len = 0;
470                         }
471                         
472                         if( a->acl_dn_pat.bv_len != 0 ||
473                                 ( a->acl_dn_style != ACL_STYLE_REGEX ) )
474                         {
475                                 if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
476                                         struct berval bv;
477                                         rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
478                                         if ( rc != LDAP_SUCCESS ) {
479                                                 fprintf( stderr,
480                                                         "%s: line %d: bad DN \"%s\" in to DN clause\n",
481                                                         fname, lineno, a->acl_dn_pat.bv_val );
482                                                 acl_usage();
483                                         }
484                                         free( a->acl_dn_pat.bv_val );
485                                         a->acl_dn_pat = bv;
486                                 } else {
487                                         int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
488                                                 REG_EXTENDED | REG_ICASE );
489                                         if ( e ) {
490                                                 char buf[512];
491                                                 regerror( e, &a->acl_dn_re, buf, sizeof(buf) );
492                                                 fprintf( stderr, "%s: line %d: "
493                                                         "regular expression \"%s\" bad because of %s\n",
494                                                         fname, lineno, right, buf );
495                                                 acl_usage();
496                                         }
497                                 }
498                         }
499
500                 /* by clause - select who has what access to entries */
501                 } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
502                         if ( a == NULL ) {
503                                 fprintf( stderr, "%s: line %d: "
504                                         "to clause required before by clause in access line\n",
505                                     fname, lineno );
506                                 acl_usage();
507                         }
508
509                         /*
510                          * by clause consists of <who> and <access>
511                          */
512
513                         b = (Access *) ch_calloc( 1, sizeof(Access) );
514
515                         ACL_INVALIDATE( b->a_access_mask );
516
517                         if ( ++i == argc ) {
518                                 fprintf( stderr,
519                             "%s: line %d: premature eol: expecting <who>\n",
520                                     fname, lineno );
521                                 acl_usage();
522                         }
523
524                         /* get <who> */
525                         for ( ; i < argc; i++ ) {
526                                 slap_style_t sty = ACL_STYLE_REGEX;
527                                 char *style_modifier = NULL;
528                                 int expand = 0;
529
530                                 split( argv[i], '=', &left, &right );
531                                 split( left, '.', &left, &style );
532                                 if ( style ) {
533                                         split( style, ',', &style, &style_modifier);
534                                 }
535
536                                 if ( style == NULL || *style == '\0' ||
537                                         strcasecmp( style, "exact" ) == 0 ||
538                                         strcasecmp( style, "base" ) == 0 )
539                                 {
540                                         sty = ACL_STYLE_BASE;
541
542                                 } else if ( strcasecmp( style, "onelevel" ) == 0 ||
543                                         strcasecmp( style, "one" ) == 0 ) {
544                                         sty = ACL_STYLE_ONE;
545
546                                 } else if ( strcasecmp( style, "subtree" ) == 0 ||
547                                         strcasecmp( style, "sub" ) == 0 )
548                                 {
549                                         sty = ACL_STYLE_SUBTREE;
550
551                                 } else if ( strcasecmp( style, "children" ) == 0 ) {
552                                         sty = ACL_STYLE_CHILDREN;
553
554                                 } else if ( strcasecmp( style, "regex" ) == 0 ) {
555                                         sty = ACL_STYLE_REGEX;
556
557                                 } else if ( strcasecmp( style, "expand" ) == 0 ) {
558                                         sty = ACL_STYLE_EXPAND;
559
560                                 } else if ( strcasecmp( style, "ip" ) == 0 ) {
561                                         sty = ACL_STYLE_IP;
562
563                                 } else if ( strcasecmp( style, "path" ) == 0 ) {
564                                         sty = ACL_STYLE_PATH;
565 #ifndef LDAP_PF_LOCAL
566                                         fprintf( stderr, "%s: line %d: "
567                                                 "path style modifier is useless without local\n",
568                                                 fname, lineno );
569 #endif /* LDAP_PF_LOCAL */
570
571                                 } else {
572                                         fprintf( stderr,
573                                                 "%s: line %d: unknown style \"%s\" in by clause\n",
574                                             fname, lineno, style );
575                                         acl_usage();
576                                 }
577
578                                 if ( style_modifier &&
579                                         strcasecmp( style_modifier, "expand" ) == 0 )
580                                 {
581                                         switch ( sty ) {
582                                         case ACL_STYLE_REGEX:
583                                                 fprintf( stderr, "%s: line %d: "
584                                                         "\"regex\" style implies "
585                                                         "\"expand\" modifier (ignored)\n",
586                                                         fname, lineno );
587                                                 break;
588
589                                         case ACL_STYLE_EXPAND:
590                                                 fprintf( stderr, "%s: line %d: "
591                                                         "\"expand\" style used "
592                                                         "in conjunction with "
593                                                         "\"expand\" modifier (ignored)\n",
594                                                         fname, lineno );
595                                                 break;
596
597                                         default:
598                                                 /* we'll see later if it's pertinent */
599                                                 expand = 1;
600                                                 break;
601                                         }
602                                 }
603
604                                 /* expand in <who> needs regex in <what> */
605                                 if ( ( sty == ACL_STYLE_EXPAND || expand )
606                                                 && a->acl_dn_style != ACL_STYLE_REGEX )
607                                 {
608                                         fprintf( stderr, "%s: line %d: "
609                                                 "\"expand\" style or modifier used "
610                                                 "in conjunction with "
611                                                 "a non-regex <what> clause\n",
612                                                 fname, lineno );
613                                 }
614
615
616                                 if ( strcasecmp( argv[i], "*" ) == 0 ) {
617                                         bv.bv_val = ch_strdup( "*" );
618                                         bv.bv_len = 1;
619                                         sty = ACL_STYLE_REGEX;
620
621                                 } else if ( strcasecmp( argv[i], "anonymous" ) == 0 ) {
622                                         ber_str2bv("anonymous", sizeof("anonymous")-1, 1, &bv);
623                                         sty = ACL_STYLE_REGEX;
624
625                                 } else if ( strcasecmp( argv[i], "self" ) == 0 ) {
626                                         ber_str2bv("self", sizeof("self")-1, 1, &bv);
627                                         sty = ACL_STYLE_REGEX;
628
629                                 } else if ( strcasecmp( argv[i], "users" ) == 0 ) {
630                                         ber_str2bv("users", sizeof("users")-1, 1, &bv);
631                                         sty = ACL_STYLE_REGEX;
632
633                                 } else if ( strcasecmp( left, "dn" ) == 0 ) {
634                                         if ( sty == ACL_STYLE_REGEX ) {
635                                                 b->a_dn_style = ACL_STYLE_REGEX;
636                                                 if( right == NULL ) {
637                                                         /* no '=' */
638                                                         ber_str2bv("users",
639                                                                 sizeof("users")-1,
640                                                                 1, &bv);
641                                                 } else if (*right == '\0' ) {
642                                                         /* dn="" */
643                                                         ber_str2bv("anonymous",
644                                                                 sizeof("anonymous")-1,
645                                                                 1, &bv);
646                                                 } else if ( strcmp( right, "*" ) == 0 ) {
647                                                         /* dn=* */
648                                                         /* any or users?  users for now */
649                                                         ber_str2bv("users",
650                                                                 sizeof("users")-1,
651                                                                 1, &bv);
652                                                 } else if ( strcmp( right, ".+" ) == 0
653                                                         || strcmp( right, "^.+" ) == 0
654                                                         || strcmp( right, ".+$" ) == 0
655                                                         || strcmp( right, "^.+$" ) == 0
656                                                         || strcmp( right, ".+$$" ) == 0
657                                                         || strcmp( right, "^.+$$" ) == 0 )
658                                                 {
659                                                         ber_str2bv("users",
660                                                                 sizeof("users")-1,
661                                                                 1, &bv);
662                                                 } else if ( strcmp( right, ".*" ) == 0
663                                                         || strcmp( right, "^.*" ) == 0
664                                                         || strcmp( right, ".*$" ) == 0
665                                                         || strcmp( right, "^.*$" ) == 0
666                                                         || strcmp( right, ".*$$" ) == 0
667                                                         || strcmp( right, "^.*$$" ) == 0 )
668                                                 {
669                                                         ber_str2bv("*",
670                                                                 sizeof("*")-1,
671                                                                 1, &bv);
672
673                                                 } else {
674                                                         acl_regex_normalized_dn( right, &bv );
675                                                         if ( !ber_bvccmp( &bv, '*' ) ) {
676                                                                 regtest(fname, lineno, bv.bv_val);
677                                                         }
678                                                 }
679                                         } else if ( right == NULL || *right == '\0' ) {
680                                                 fprintf( stderr, "%s: line %d: "
681                                                         "missing \"=\" in (or value after) \"%s\" "
682                                                         "in by clause\n",
683                                                     fname, lineno, left );
684                                                 acl_usage();
685
686                                         } else {
687                                                 ber_str2bv( right, 0, 1, &bv );
688                                         }
689
690                                 } else {
691                                         bv.bv_val = NULL;
692                                 }
693
694                                 if( bv.bv_val != NULL ) {
695                                         if( b->a_dn_pat.bv_len != 0 ) {
696                                                 fprintf( stderr,
697                                                     "%s: line %d: dn pattern already specified.\n",
698                                                     fname, lineno );
699                                                 acl_usage();
700                                         }
701
702                                         if ( sty != ACL_STYLE_REGEX && expand == 0 ) {
703                                                 rc = dnNormalize(0, NULL, NULL,
704                                                         &bv, &b->a_dn_pat, NULL);
705                                                 if ( rc != LDAP_SUCCESS ) {
706                                                         fprintf( stderr,
707                                                                 "%s: line %d: bad DN \"%s\" in by DN clause\n",
708                                                                 fname, lineno, bv.bv_val );
709                                                         acl_usage();
710                                                 }
711                                                 free(bv.bv_val);
712                                         } else {
713                                                 b->a_dn_pat = bv;
714                                         }
715                                         b->a_dn_style = sty;
716                                         b->a_dn_expand = expand;
717                                         continue;
718                                 }
719
720                                 if ( strcasecmp( left, "dnattr" ) == 0 ) {
721                                         if ( right == NULL || right[ 0 ] == '\0' ) {
722                                                 fprintf( stderr,
723                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
724                                                         fname, lineno, left );
725                                                 acl_usage();
726                                         }
727
728                                         if( b->a_dn_at != NULL ) {
729                                                 fprintf( stderr,
730                                                         "%s: line %d: dnattr already specified.\n",
731                                                         fname, lineno );
732                                                 acl_usage();
733                                         }
734
735                                         rc = slap_str2ad( right, &b->a_dn_at, &text );
736
737                                         if( rc != LDAP_SUCCESS ) {
738                                                 fprintf( stderr,
739                                                         "%s: line %d: dnattr \"%s\": %s\n",
740                                                         fname, lineno, right, text );
741                                                 acl_usage();
742                                         }
743
744
745                                         if( !is_at_syntax( b->a_dn_at->ad_type,
746                                                 SLAPD_DN_SYNTAX ) &&
747                                                 !is_at_syntax( b->a_dn_at->ad_type,
748                                                 SLAPD_NAMEUID_SYNTAX ))
749                                         {
750                                                 fprintf( stderr,
751                                                         "%s: line %d: dnattr \"%s\": "
752                                                         "inappropriate syntax: %s\n",
753                                                         fname, lineno, right,
754                                                         b->a_dn_at->ad_type->sat_syntax_oid );
755                                                 acl_usage();
756                                         }
757
758                                         if( b->a_dn_at->ad_type->sat_equality == NULL ) {
759                                                 fprintf( stderr,
760                                                         "%s: line %d: dnattr \"%s\": "
761                                                         "inappropriate matching (no EQUALITY)\n",
762                                                         fname, lineno, right );
763                                                 acl_usage();
764                                         }
765
766                                         continue;
767                                 }
768
769                                 if ( strncasecmp( left, "group", sizeof("group")-1 ) == 0 ) {
770                                         char *name = NULL;
771                                         char *value = NULL;
772
773                                         switch ( sty ) {
774                                         case ACL_STYLE_REGEX:
775                                                 /* legacy, tolerated */
776                                                 fprintf( stderr, "%s: line %d: "
777                                                         "deprecated group style \"regex\"; "
778                                                         "use \"expand\" instead\n",
779                                                         fname, lineno, style );
780                                                 sty = ACL_STYLE_EXPAND;
781                                                 break;
782
783                                         case ACL_STYLE_BASE:
784                                                 /* legal, traditional */
785                                         case ACL_STYLE_EXPAND:
786                                                 /* legal, substring expansion; supersedes regex */
787                                                 break;
788
789                                         default:
790                                                 /* unknown */
791                                                 fprintf( stderr, "%s: line %d: "
792                                                         "inappropriate style \"%s\" in by clause\n",
793                                                         fname, lineno, style );
794                                                 acl_usage();
795                                         }
796
797                                         if ( right == NULL || right[ 0 ] == '\0' ) {
798                                                 fprintf( stderr, "%s: line %d: "
799                                                         "missing \"=\" in (or value after) \"%s\" "
800                                                         "in by clause\n",
801                                                         fname, lineno, left );
802                                                 acl_usage();
803                                         }
804
805                                         if( b->a_group_pat.bv_len ) {
806                                                 fprintf( stderr,
807                                                         "%s: line %d: group pattern already specified.\n",
808                                                         fname, lineno );
809                                                 acl_usage();
810                                         }
811
812                                         /* format of string is
813                                                 "group/objectClassValue/groupAttrName" */
814                                         if ((value = strchr(left, '/')) != NULL) {
815                                                 *value++ = '\0';
816                                                 if (*value && (name = strchr(value, '/')) != NULL) {
817                                                         *name++ = '\0';
818                                                 }
819                                         }
820
821                                         b->a_group_style = sty;
822                                         if (sty == ACL_STYLE_EXPAND) {
823                                                 acl_regex_normalized_dn( right, &bv );
824                                                 if ( !ber_bvccmp( &bv, '*' ) ) {
825                                                         regtest(fname, lineno, bv.bv_val);
826                                                 }
827                                                 b->a_group_pat = bv;
828                                         } else {
829                                                 ber_str2bv( right, 0, 0, &bv );
830                                                 rc = dnNormalize( 0, NULL, NULL, &bv,
831                                                         &b->a_group_pat, NULL );
832                                                 if ( rc != LDAP_SUCCESS ) {
833                                                         fprintf( stderr,
834                                                                 "%s: line %d: bad DN \"%s\"\n",
835                                                                 fname, lineno, right );
836                                                         acl_usage();
837                                                 }
838                                         }
839
840                                         if (value && *value) {
841                                                 b->a_group_oc = oc_find( value );
842                                                 *--value = '/';
843
844                                                 if( b->a_group_oc == NULL ) {
845                                                         fprintf( stderr,
846                                                                 "%s: line %d: group objectclass "
847                                                                 "\"%s\" unknown\n",
848                                                                 fname, lineno, value );
849                                                         acl_usage();
850                                                 }
851                                         } else {
852                                                 b->a_group_oc = oc_find(SLAPD_GROUP_CLASS);
853
854                                                 if( b->a_group_oc == NULL ) {
855                                                         fprintf( stderr,
856                                                                 "%s: line %d: group default objectclass "
857                                                                 "\"%s\" unknown\n",
858                                                                 fname, lineno, SLAPD_GROUP_CLASS );
859                                                         acl_usage();
860                                                 }
861                                         }
862
863                                         if( is_object_subclass( slap_schema.si_oc_referral,
864                                                 b->a_group_oc ))
865                                         {
866                                                 fprintf( stderr,
867                                                         "%s: line %d: group objectclass \"%s\" "
868                                                         "is subclass of referral\n",
869                                                         fname, lineno, value );
870                                                 acl_usage();
871                                         }
872
873                                         if( is_object_subclass( slap_schema.si_oc_alias,
874                                                 b->a_group_oc ))
875                                         {
876                                                 fprintf( stderr,
877                                                         "%s: line %d: group objectclass \"%s\" "
878                                                         "is subclass of alias\n",
879                                                         fname, lineno, value );
880                                                 acl_usage();
881                                         }
882
883                                         if (name && *name) {
884                                                 rc = slap_str2ad( name, &b->a_group_at, &text );
885
886                                                 if( rc != LDAP_SUCCESS ) {
887                                                         fprintf( stderr,
888                                                                 "%s: line %d: group \"%s\": %s\n",
889                                                                 fname, lineno, right, text );
890                                                         acl_usage();
891                                                 }
892                                                 *--name = '/';
893                                         } else {
894                                                 rc = slap_str2ad( SLAPD_GROUP_ATTR, &b->a_group_at, &text );
895
896                                                 if( rc != LDAP_SUCCESS ) {
897                                                         fprintf( stderr,
898                                                                 "%s: line %d: group \"%s\": %s\n",
899                                                                 fname, lineno, SLAPD_GROUP_ATTR, text );
900                                                         acl_usage();
901                                                 }
902                                         }
903
904                                         if( !is_at_syntax( b->a_group_at->ad_type,
905                                                 SLAPD_DN_SYNTAX ) &&
906                                             !is_at_syntax( b->a_group_at->ad_type,
907                                                 SLAPD_NAMEUID_SYNTAX ) &&
908                                                 !is_at_subtype( b->a_group_at->ad_type, slap_schema.si_ad_labeledURI->ad_type ))
909                                         {
910                                                 fprintf( stderr,
911                                                         "%s: line %d: group \"%s\": inappropriate syntax: %s\n",
912                                                         fname, lineno, right,
913                                                         b->a_group_at->ad_type->sat_syntax_oid );
914                                                 acl_usage();
915                                         }
916
917
918                                         {
919                                                 int rc;
920                                                 struct berval vals[2];
921
922                                                 vals[0].bv_val = b->a_group_oc->soc_oid;
923                                                 vals[0].bv_len = strlen(vals[0].bv_val);
924                                                 vals[1].bv_val = NULL;
925
926
927                                                 rc = oc_check_allowed( b->a_group_at->ad_type,
928                                                         vals, NULL );
929
930                                                 if( rc != 0 ) {
931                                                         fprintf( stderr, "%s: line %d: "
932                                                                 "group: \"%s\" not allowed by \"%s\"\n",
933                                                                 fname, lineno,
934                                                                 b->a_group_at->ad_cname.bv_val,
935                                                                 b->a_group_oc->soc_oid );
936                                                         acl_usage();
937                                                 }
938                                         }
939                                         continue;
940                                 }
941
942                                 if ( strcasecmp( left, "peername" ) == 0 ) {
943                                         switch (sty) {
944                                         case ACL_STYLE_REGEX:
945                                         case ACL_STYLE_BASE:
946                                                 /* legal, traditional */
947                                         case ACL_STYLE_EXPAND:
948                                                 /* cheap replacement to regex for simple expansion */
949                                         case ACL_STYLE_IP:
950                                         case ACL_STYLE_PATH:
951                                                 /* legal, peername specific */
952                                                 break;
953
954                                         default:
955                                                 fprintf( stderr, "%s: line %d: "
956                                                         "inappropriate style \"%s\" in by clause\n",
957                                                     fname, lineno, style );
958                                                 acl_usage();
959                                         }
960
961                                         if ( right == NULL || right[ 0 ] == '\0' ) {
962                                                 fprintf( stderr, "%s: line %d: "
963                                                         "missing \"=\" in (or value after) \"%s\" "
964                                                         "in by clause\n",
965                                                         fname, lineno, left );
966                                                 acl_usage();
967                                         }
968
969                                         if( b->a_peername_pat.bv_len ) {
970                                                 fprintf( stderr, "%s: line %d: "
971                                                         "peername pattern already specified.\n",
972                                                         fname, lineno );
973                                                 acl_usage();
974                                         }
975
976                                         b->a_peername_style = sty;
977                                         if (sty == ACL_STYLE_REGEX) {
978                                                 acl_regex_normalized_dn( right, &bv );
979                                                 if ( !ber_bvccmp( &bv, '*' ) ) {
980                                                         regtest(fname, lineno, bv.bv_val);
981                                                 }
982                                                 b->a_peername_pat = bv;
983
984                                         } else {
985                                                 ber_str2bv( right, 0, 1, &b->a_peername_pat );
986
987                                                 if ( sty == ACL_STYLE_IP ) {
988                                                         char            *addr = NULL,
989                                                                         *mask = NULL,
990                                                                         *port = NULL;
991
992                                                         split( right, '{', &addr, &port );
993                                                         split( addr, '%', &addr, &mask );
994
995                                                         b->a_peername_addr = inet_addr( addr );
996                                                         if ( b->a_peername_addr == (unsigned long)(-1)) {
997                                                                 /* illegal address */
998                                                                 fprintf( stderr, "%s: line %d: "
999                                                                         "illegal peername address \"%s\".\n",
1000                                                                         fname, lineno, addr );
1001                                                                 acl_usage();
1002                                                         }
1003
1004                                                         b->a_peername_mask = (unsigned long)(-1);
1005                                                         if ( mask != NULL ) {
1006                                                                 b->a_peername_mask = inet_addr( mask );
1007                                                                 if ( b->a_peername_mask == (unsigned long)(-1)) {
1008                                                                         /* illegal mask */
1009                                                                         fprintf( stderr, "%s: line %d: "
1010                                                                                 "illegal peername address mask \"%s\".\n",
1011                                                                                 fname, lineno, mask );
1012                                                                         acl_usage();
1013                                                                 }
1014                                                         } 
1015
1016                                                         b->a_peername_port = -1;
1017                                                         if ( port ) {
1018                                                                 char    *end = NULL;
1019
1020                                                                 b->a_peername_port = strtol( port, &end, 10 );
1021                                                                 if ( end[ 0 ] != '}' ) {
1022                                                                         /* illegal port */
1023                                                                         fprintf( stderr, "%s: line %d: "
1024                                                                                 "illegal peername port specification \"{%s}\".\n",
1025                                                                                 fname, lineno, port );
1026                                                                         acl_usage();
1027                                                                 }
1028                                                         }
1029                                                 }
1030                                         }
1031                                         continue;
1032                                 }
1033
1034                                 if ( strcasecmp( left, "sockname" ) == 0 ) {
1035                                         switch (sty) {
1036                                         case ACL_STYLE_REGEX:
1037                                         case ACL_STYLE_BASE:
1038                                                 /* legal, traditional */
1039                                         case ACL_STYLE_EXPAND:
1040                                                 /* cheap replacement to regex for simple expansion */
1041                                                 break;
1042
1043                                         default:
1044                                                 /* unknown */
1045                                                 fprintf( stderr, "%s: line %d: "
1046                                                         "inappropriate style \"%s\" in by clause\n",
1047                                                     fname, lineno, style );
1048                                                 acl_usage();
1049                                         }
1050
1051                                         if ( right == NULL || right[ 0 ] == '\0' ) {
1052                                                 fprintf( stderr, "%s: line %d: "
1053                                                         "missing \"=\" in (or value after) \"%s\" "
1054                                                         "in by clause\n",
1055                                                         fname, lineno, left );
1056                                                 acl_usage();
1057                                         }
1058
1059                                         if( b->a_sockname_pat.bv_len ) {
1060                                                 fprintf( stderr, "%s: line %d: "
1061                                                         "sockname pattern already specified.\n",
1062                                                         fname, lineno );
1063                                                 acl_usage();
1064                                         }
1065
1066                                         b->a_sockname_style = sty;
1067                                         if (sty == ACL_STYLE_REGEX) {
1068                                                 acl_regex_normalized_dn( right, &bv );
1069                                                 if ( !ber_bvccmp( &bv, '*' ) ) {
1070                                                         regtest(fname, lineno, bv.bv_val);
1071                                                 }
1072                                                 b->a_sockname_pat = bv;
1073                                         } else {
1074                                                 ber_str2bv( right, 0, 1, &b->a_sockname_pat );
1075                                         }
1076                                         continue;
1077                                 }
1078
1079                                 if ( strcasecmp( left, "domain" ) == 0 ) {
1080                                         switch ( sty ) {
1081                                         case ACL_STYLE_REGEX:
1082                                         case ACL_STYLE_BASE:
1083                                         case ACL_STYLE_SUBTREE:
1084                                                 /* legal, traditional */
1085                                                 break;
1086
1087                                         case ACL_STYLE_EXPAND:
1088                                                 /* tolerated: means exact,expand */
1089                                                 if ( expand ) {
1090                                                         fprintf( stderr,
1091                                                                 "%s: line %d: "
1092                                                                 "\"expand\" modifier with \"expand\" style\n",
1093                                                                 fname, lineno );
1094                                                 }
1095                                                 sty = ACL_STYLE_BASE;
1096                                                 expand = 1;
1097                                                 break;
1098
1099                                         default:
1100                                                 /* unknown */
1101                                                 fprintf( stderr,
1102                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
1103                                                     fname, lineno, style );
1104                                                 acl_usage();
1105                                         }
1106
1107                                         if ( right == NULL || right[ 0 ] == '\0' ) {
1108                                                 fprintf( stderr,
1109                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
1110                                                         fname, lineno, left );
1111                                                 acl_usage();
1112                                         }
1113
1114                                         if( b->a_domain_pat.bv_len ) {
1115                                                 fprintf( stderr,
1116                                                         "%s: line %d: domain pattern already specified.\n",
1117                                                         fname, lineno );
1118                                                 acl_usage();
1119                                         }
1120
1121                                         b->a_domain_style = sty;
1122                                         b->a_domain_expand = expand;
1123                                         if (sty == ACL_STYLE_REGEX) {
1124                                                 acl_regex_normalized_dn( right, &bv );
1125                                                 if ( !ber_bvccmp( &bv, '*' ) ) {
1126                                                         regtest(fname, lineno, bv.bv_val);
1127                                                 }
1128                                                 b->a_domain_pat = bv;
1129                                         } else {
1130                                                 ber_str2bv( right, 0, 1, &b->a_domain_pat );
1131                                         }
1132                                         continue;
1133                                 }
1134
1135                                 if ( strcasecmp( left, "sockurl" ) == 0 ) {
1136                                         switch (sty) {
1137                                         case ACL_STYLE_REGEX:
1138                                         case ACL_STYLE_BASE:
1139                                                 /* legal, traditional */
1140                                         case ACL_STYLE_EXPAND:
1141                                                 /* cheap replacement to regex for simple expansion */
1142                                                 break;
1143
1144                                         default:
1145                                                 /* unknown */
1146                                                 fprintf( stderr, "%s: line %d: "
1147                                                         "inappropriate style \"%s\" in by clause\n",
1148                                                     fname, lineno, style );
1149                                                 acl_usage();
1150                                         }
1151
1152                                         if ( right == NULL || right[ 0 ] == '\0' ) {
1153                                                 fprintf( stderr,
1154                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
1155                                                         fname, lineno, left );
1156                                                 acl_usage();
1157                                         }
1158
1159                                         if( b->a_sockurl_pat.bv_len ) {
1160                                                 fprintf( stderr,
1161                                                         "%s: line %d: sockurl pattern already specified.\n",
1162                                                         fname, lineno );
1163                                                 acl_usage();
1164                                         }
1165
1166                                         b->a_sockurl_style = sty;
1167                                         if (sty == ACL_STYLE_REGEX) {
1168                                                 acl_regex_normalized_dn( right, &bv );
1169                                                 if ( !ber_bvccmp( &bv, '*' ) ) {
1170                                                         regtest(fname, lineno, bv.bv_val);
1171                                                 }
1172                                                 b->a_sockurl_pat = bv;
1173                                         } else {
1174                                                 ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
1175                                         }
1176                                         continue;
1177                                 }
1178
1179                                 if ( strcasecmp( left, "set" ) == 0 ) {
1180                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1181                                                 fprintf( stderr,
1182                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
1183                                                     fname, lineno, style );
1184                                                 acl_usage();
1185                                         }
1186
1187                                         if( b->a_set_pat.bv_len != 0 ) {
1188                                                 fprintf( stderr,
1189                                                         "%s: line %d: set attribute already specified.\n",
1190                                                         fname, lineno );
1191                                                 acl_usage();
1192                                         }
1193
1194                                         if ( right == NULL || *right == '\0' ) {
1195                                                 fprintf( stderr,
1196                                                         "%s: line %d: no set is defined\n",
1197                                                         fname, lineno );
1198                                                 acl_usage();
1199                                         }
1200
1201                                         b->a_set_style = sty;
1202                                         ber_str2bv( right, 0, 1, &b->a_set_pat );
1203
1204                                         continue;
1205                                 }
1206
1207 #ifdef SLAPD_ACI_ENABLED
1208                                 if ( strcasecmp( left, "aci" ) == 0 ) {
1209                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1210                                                 fprintf( stderr,
1211                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
1212                                                     fname, lineno, style );
1213                                                 acl_usage();
1214                                         }
1215
1216                                         if( b->a_aci_at != NULL ) {
1217                                                 fprintf( stderr,
1218                                                         "%s: line %d: aci attribute already specified.\n",
1219                                                         fname, lineno );
1220                                                 acl_usage();
1221                                         }
1222
1223                                         if ( right != NULL && *right != '\0' ) {
1224                                                 rc = slap_str2ad( right, &b->a_aci_at, &text );
1225
1226                                                 if( rc != LDAP_SUCCESS ) {
1227                                                         fprintf( stderr,
1228                                                                 "%s: line %d: aci \"%s\": %s\n",
1229                                                                 fname, lineno, right, text );
1230                                                         acl_usage();
1231                                                 }
1232
1233                                         } else {
1234                                                 b->a_aci_at = slap_schema.si_ad_aci;
1235                                         }
1236
1237                                         if( !is_at_syntax( b->a_aci_at->ad_type,
1238                                                 SLAPD_ACI_SYNTAX) )
1239                                         {
1240                                                 fprintf( stderr,
1241                                                         "%s: line %d: aci \"%s\": inappropriate syntax: %s\n",
1242                                                         fname, lineno, right,
1243                                                         b->a_aci_at->ad_type->sat_syntax_oid );
1244                                                 acl_usage();
1245                                         }
1246
1247                                         continue;
1248                                 }
1249 #endif /* SLAPD_ACI_ENABLED */
1250
1251                                 if ( strcasecmp( left, "ssf" ) == 0 ) {
1252                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1253                                                 fprintf( stderr,
1254                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
1255                                                     fname, lineno, style );
1256                                                 acl_usage();
1257                                         }
1258
1259                                         if( b->a_authz.sai_ssf ) {
1260                                                 fprintf( stderr,
1261                                                         "%s: line %d: ssf attribute already specified.\n",
1262                                                         fname, lineno );
1263                                                 acl_usage();
1264                                         }
1265
1266                                         if ( right == NULL || *right == '\0' ) {
1267                                                 fprintf( stderr,
1268                                                         "%s: line %d: no ssf is defined\n",
1269                                                         fname, lineno );
1270                                                 acl_usage();
1271                                         }
1272
1273                                         b->a_authz.sai_ssf = strtol( right, *next, 10 );
1274                                         if ( next == NULL || next[0] != '\0' ) {
1275                                                 fprintf( stderr,
1276                                                         "%s: line %d: unable to parse ssf value (%s)\n",
1277                                                         fname, lineno, right );
1278                                                 acl_usage();
1279                                         }
1280
1281                                         if( !b->a_authz.sai_ssf ) {
1282                                                 fprintf( stderr,
1283                                                         "%s: line %d: invalid ssf value (%s)\n",
1284                                                         fname, lineno, right );
1285                                                 acl_usage();
1286                                         }
1287                                         continue;
1288                                 }
1289
1290                                 if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
1291                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1292                                                 fprintf( stderr,
1293                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
1294                                                     fname, lineno, style );
1295                                                 acl_usage();
1296                                         }
1297
1298                                         if( b->a_authz.sai_transport_ssf ) {
1299                                                 fprintf( stderr,
1300                                                         "%s: line %d: transport_ssf attribute already specified.\n",
1301                                                         fname, lineno );
1302                                                 acl_usage();
1303                                         }
1304
1305                                         if ( right == NULL || *right == '\0' ) {
1306                                                 fprintf( stderr,
1307                                                         "%s: line %d: no transport_ssf is defined\n",
1308                                                         fname, lineno );
1309                                                 acl_usage();
1310                                         }
1311
1312                                         b->a_authz.sai_transport_ssf = strtol( right, &next, 10 );
1313                                         if ( next == NULL || next[0] != '\0' ) {
1314                                                 fprintf( stderr,
1315                                                         "%s: line %d: unable to parse transport_ssf value (%s)\n",
1316                                                         fname, lineno, right );
1317                                                 acl_usage();
1318                                         }
1319
1320                                         if( !b->a_authz.sai_transport_ssf ) {
1321                                                 fprintf( stderr,
1322                                                         "%s: line %d: invalid transport_ssf value (%s)\n",
1323                                                         fname, lineno, right );
1324                                                 acl_usage();
1325                                         }
1326                                         continue;
1327                                 }
1328
1329                                 if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
1330                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1331                                                 fprintf( stderr,
1332                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
1333                                                     fname, lineno, style );
1334                                                 acl_usage();
1335                                         }
1336
1337                                         if( b->a_authz.sai_tls_ssf ) {
1338                                                 fprintf( stderr,
1339                                                         "%s: line %d: tls_ssf attribute already specified.\n",
1340                                                         fname, lineno );
1341                                                 acl_usage();
1342                                         }
1343
1344                                         if ( right == NULL || *right == '\0' ) {
1345                                                 fprintf( stderr,
1346                                                         "%s: line %d: no tls_ssf is defined\n",
1347                                                         fname, lineno );
1348                                                 acl_usage();
1349                                         }
1350
1351                                         b->a_authz.sai_tls_ssf = strtol( right, &next, 10 );
1352                                         if ( next == NULL || next[0] != '\0' ) {
1353                                                 fprintf( stderr,
1354                                                         "%s: line %d: unable to parse tls_ssf value (%s)\n",
1355                                                         fname, lineno, right );
1356                                                 acl_usage();
1357                                         }
1358
1359                                         if( !b->a_authz.sai_tls_ssf ) {
1360                                                 fprintf( stderr,
1361                                                         "%s: line %d: invalid tls_ssf value (%s)\n",
1362                                                         fname, lineno, right );
1363                                                 acl_usage();
1364                                         }
1365                                         continue;
1366                                 }
1367
1368                                 if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
1369                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1370                                                 fprintf( stderr,
1371                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
1372                                                     fname, lineno, style );
1373                                                 acl_usage();
1374                                         }
1375
1376                                         if( b->a_authz.sai_sasl_ssf ) {
1377                                                 fprintf( stderr,
1378                                                         "%s: line %d: sasl_ssf attribute already specified.\n",
1379                                                         fname, lineno );
1380                                                 acl_usage();
1381                                         }
1382
1383                                         if ( right == NULL || *right == '\0' ) {
1384                                                 fprintf( stderr,
1385                                                         "%s: line %d: no sasl_ssf is defined\n",
1386                                                         fname, lineno );
1387                                                 acl_usage();
1388                                         }
1389
1390                                         b->a_authz.sai_sasl_ssf = strtol( right, &next, 10 );
1391                                         if ( next == NULL || next[0] != '\0' ) {
1392                                                 fprintf( stderr,
1393                                                         "%s: line %d: unable to parse sasl_ssf value (%s)\n",
1394                                                         fname, lineno, right );
1395                                                 acl_usage();
1396                                         }
1397
1398                                         if( !b->a_authz.sai_sasl_ssf ) {
1399                                                 fprintf( stderr,
1400                                                         "%s: line %d: invalid sasl_ssf value (%s)\n",
1401                                                         fname, lineno, right );
1402                                                 acl_usage();
1403                                         }
1404                                         continue;
1405                                 }
1406
1407                                 if( right != NULL ) {
1408                                         /* unsplit */
1409                                         right[-1] = '=';
1410                                 }
1411                                 break;
1412                         }
1413
1414                         if( i == argc || ( strcasecmp( left, "stop" ) == 0 )) { 
1415                                 /* out of arguments or plain stop */
1416
1417                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1418                                 b->a_type = ACL_STOP;
1419
1420                                 access_append( &a->acl_access, b );
1421                                 continue;
1422                         }
1423
1424                         if( strcasecmp( left, "continue" ) == 0 ) {
1425                                 /* plain continue */
1426
1427                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1428                                 b->a_type = ACL_CONTINUE;
1429
1430                                 access_append( &a->acl_access, b );
1431                                 continue;
1432                         }
1433
1434                         if( strcasecmp( left, "break" ) == 0 ) {
1435                                 /* plain continue */
1436
1437                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1438                                 b->a_type = ACL_BREAK;
1439
1440                                 access_append( &a->acl_access, b );
1441                                 continue;
1442                         }
1443
1444                         if ( strcasecmp( left, "by" ) == 0 ) {
1445                                 /* we've gone too far */
1446                                 --i;
1447                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1448                                 b->a_type = ACL_STOP;
1449
1450                                 access_append( &a->acl_access, b );
1451                                 continue;
1452                         }
1453
1454                         /* get <access> */
1455                         if( strncasecmp( left, "self", 4 ) == 0 ) {
1456                                 b->a_dn_self = 1;
1457                                 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( &left[4] ) );
1458
1459                         } else {
1460                                 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( left ) );
1461                         }
1462
1463                         if( ACL_IS_INVALID( b->a_access_mask ) ) {
1464                                 fprintf( stderr,
1465                                         "%s: line %d: expecting <access> got \"%s\"\n",
1466                                         fname, lineno, left );
1467                                 acl_usage();
1468                         }
1469
1470                         b->a_type = ACL_STOP;
1471
1472                         if( ++i == argc ) {
1473                                 /* out of arguments or plain stop */
1474                                 access_append( &a->acl_access, b );
1475                                 continue;
1476                         }
1477
1478                         if( strcasecmp( argv[i], "continue" ) == 0 ) {
1479                                 /* plain continue */
1480                                 b->a_type = ACL_CONTINUE;
1481
1482                         } else if( strcasecmp( argv[i], "break" ) == 0 ) {
1483                                 /* plain continue */
1484                                 b->a_type = ACL_BREAK;
1485
1486                         } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
1487                                 /* gone to far */
1488                                 i--;
1489                         }
1490
1491                         access_append( &a->acl_access, b );
1492
1493                 } else {
1494                         fprintf( stderr,
1495                     "%s: line %d: expecting \"to\" or \"by\" got \"%s\"\n",
1496                             fname, lineno, argv[i] );
1497                         acl_usage();
1498                 }
1499         }
1500
1501         /* if we have no real access clause, complain and do nothing */
1502         if ( a == NULL ) {
1503                         fprintf( stderr,
1504                                 "%s: line %d: warning: no access clause(s) specified in access line\n",
1505                             fname, lineno );
1506
1507         } else {
1508 #ifdef LDAP_DEBUG
1509                 if (ldap_debug & LDAP_DEBUG_ACL)
1510                         print_acl(be, a);
1511 #endif
1512         
1513                 if ( a->acl_access == NULL ) {
1514                         fprintf( stderr,
1515                         "%s: line %d: warning: no by clause(s) specified in access line\n",
1516                             fname, lineno );
1517                 }
1518
1519                 if ( be != NULL ) {
1520 #ifdef LDAP_DEVEL
1521                         switch ( check_scope( be, a ) ) {
1522                         case ACL_SCOPE_UNKNOWN:
1523                                 fprintf( stderr, "%s: line %d: warning: "
1524                                                 "cannot assess the validity "
1525                                                 "of the ACL scope within "
1526                                                 "backend naming context\n",
1527                                                 fname, lineno );
1528                                 break;
1529
1530                         case ACL_SCOPE_WARN:
1531                                 fprintf( stderr, "%s: line %d: warning: "
1532                                                 "ACL could be out of "
1533                                                 "scope within "
1534                                                 "backend naming context\n",
1535                                                 fname, lineno );
1536                                 break;
1537
1538                         case ACL_SCOPE_PARTIAL:
1539                                 fprintf( stderr, "%s: line %d: warning: "
1540                                                 "ACL appears to be partially "
1541                                                 "out of scope within "
1542                                                 "backend naming context\n",
1543                                                 fname, lineno );
1544                                 break;
1545
1546                         case ACL_SCOPE_ERR:
1547                                 fprintf( stderr, "%s: line %d: warning: "
1548                                                 "ACL appears to be out of "
1549                                                 "scope within "
1550                                                 "backend naming context\n",
1551                                                 fname, lineno );
1552                                 break;
1553
1554                         default:
1555                                 break;
1556                         }
1557 #endif /* LDAP_DEVEL */
1558                         acl_append( &be->be_acl, a );
1559                 } else {
1560                         acl_append( &global_acl, a );
1561                 }
1562         }
1563 }
1564
1565 char *
1566 accessmask2str( slap_mask_t mask, char *buf )
1567 {
1568         int none=1;
1569         char *ptr = buf;
1570
1571         assert( buf != NULL );
1572
1573         if ( ACL_IS_INVALID( mask ) ) {
1574                 return "invalid";
1575         }
1576
1577         buf[0] = '\0';
1578
1579         if ( ACL_IS_LEVEL( mask ) ) {
1580                 if ( ACL_LVL_IS_NONE(mask) ) {
1581                         ptr = lutil_strcopy( ptr, "none" );
1582
1583                 } else if ( ACL_LVL_IS_AUTH(mask) ) {
1584                         ptr = lutil_strcopy( ptr, "auth" );
1585
1586                 } else if ( ACL_LVL_IS_COMPARE(mask) ) {
1587                         ptr = lutil_strcopy( ptr, "compare" );
1588
1589                 } else if ( ACL_LVL_IS_SEARCH(mask) ) {
1590                         ptr = lutil_strcopy( ptr, "search" );
1591
1592                 } else if ( ACL_LVL_IS_READ(mask) ) {
1593                         ptr = lutil_strcopy( ptr, "read" );
1594
1595                 } else if ( ACL_LVL_IS_WRITE(mask) ) {
1596                         ptr = lutil_strcopy( ptr, "write" );
1597                 } else {
1598                         ptr = lutil_strcopy( ptr, "unknown" );
1599                 }
1600                 
1601                 *ptr++ = '(';
1602         }
1603
1604         if( ACL_IS_ADDITIVE( mask ) ) {
1605                 *ptr++ = '+';
1606
1607         } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
1608                 *ptr++ = '-';
1609
1610         } else {
1611                 *ptr++ = '=';
1612         }
1613
1614         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
1615                 none = 0;
1616                 *ptr++ = 'w';
1617         } 
1618
1619         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
1620                 none = 0;
1621                 *ptr++ = 'r';
1622         } 
1623
1624         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
1625                 none = 0;
1626                 *ptr++ = 's';
1627         } 
1628
1629         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
1630                 none = 0;
1631                 *ptr++ = 'c';
1632         } 
1633
1634         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
1635                 none = 0;
1636                 *ptr++ = 'x';
1637         } 
1638
1639         if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
1640                 none = 0;
1641                 *ptr++ = 'n';
1642         } 
1643
1644         if ( none ) {
1645                 *ptr++ = '0';
1646         }
1647
1648         if ( ACL_IS_LEVEL( mask ) ) {
1649                 *ptr++ = ')';
1650         }
1651
1652         *ptr = '\0';
1653
1654         return buf;
1655 }
1656
1657 slap_mask_t
1658 str2accessmask( const char *str )
1659 {
1660         slap_mask_t     mask;
1661
1662         if( !ASCII_ALPHA(str[0]) ) {
1663                 int i;
1664
1665                 if ( str[0] == '=' ) {
1666                         ACL_INIT(mask);
1667
1668                 } else if( str[0] == '+' ) {
1669                         ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
1670
1671                 } else if( str[0] == '-' ) {
1672                         ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
1673
1674                 } else {
1675                         ACL_INVALIDATE(mask);
1676                         return mask;
1677                 }
1678
1679                 for( i=1; str[i] != '\0'; i++ ) {
1680                         if( TOLOWER((unsigned char) str[i]) == 'w' ) {
1681                                 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
1682
1683                         } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
1684                                 ACL_PRIV_SET(mask, ACL_PRIV_READ);
1685
1686                         } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
1687                                 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
1688
1689                         } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
1690                                 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
1691
1692                         } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
1693                                 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
1694
1695                         } else if( str[i] != '0' ) {
1696                                 ACL_INVALIDATE(mask);
1697                                 return mask;
1698                         }
1699                 }
1700
1701                 return mask;
1702         }
1703
1704         if ( strcasecmp( str, "none" ) == 0 ) {
1705                 ACL_LVL_ASSIGN_NONE(mask);
1706
1707         } else if ( strcasecmp( str, "auth" ) == 0 ) {
1708                 ACL_LVL_ASSIGN_AUTH(mask);
1709
1710         } else if ( strcasecmp( str, "compare" ) == 0 ) {
1711                 ACL_LVL_ASSIGN_COMPARE(mask);
1712
1713         } else if ( strcasecmp( str, "search" ) == 0 ) {
1714                 ACL_LVL_ASSIGN_SEARCH(mask);
1715
1716         } else if ( strcasecmp( str, "read" ) == 0 ) {
1717                 ACL_LVL_ASSIGN_READ(mask);
1718
1719         } else if ( strcasecmp( str, "write" ) == 0 ) {
1720                 ACL_LVL_ASSIGN_WRITE(mask);
1721
1722         } else {
1723                 ACL_INVALIDATE( mask );
1724         }
1725
1726         return mask;
1727 }
1728
1729 static void
1730 acl_usage( void )
1731 {
1732         fprintf( stderr, "%s%s\n",
1733                 "<access clause> ::= access to <what> "
1734                                 "[ by <who> <access> [ <control> ] ]+ \n"
1735                 "<what> ::= * | [dn[.<dnstyle>]=<DN>] [filter=<filter>] [attrs=<attrlist>]\n"
1736                 "<attrlist> ::= <attr> [val[.<style>]=<value>] | <attr> , <attrlist>\n"
1737                 "<attr> ::= <attrname> | entry | children\n"
1738                 "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
1739                         "\t[dnattr=<attrname>]\n"
1740                         "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
1741                         "\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n",
1742                         "\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n"
1743 #ifdef SLAPD_ACI_ENABLED
1744                         "\t[aci=<attrname>]\n"
1745 #endif
1746                         "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
1747                 "<dnstyle> ::= base | exact | one(level) | sub(tree) | children | regex\n"
1748                 "<style> ::= regex | base | exact\n"
1749                 "<peernamestyle> ::= regex | exact | ip | path\n"
1750                 "<domainstyle> ::= regex | base | exact | sub(tree)\n"
1751                 "<access> ::= [self]{<level>|<priv>}\n"
1752                 "<level> ::= none | auth | compare | search | read | write\n"
1753                 "<priv> ::= {=|+|-}{w|r|s|c|x|0}+\n"
1754                 "<control> ::= [ stop | continue | break ]\n"
1755         );
1756         exit( EXIT_FAILURE );
1757 }
1758
1759 /*
1760  * Set pattern to a "normalized" DN from src.
1761  * At present it simply eats the (optional) space after 
1762  * a RDN separator (,)
1763  * Eventually will evolve in a more complete normalization
1764  */
1765 static void
1766 acl_regex_normalized_dn(
1767         const char *src,
1768         struct berval *pattern
1769 )
1770 {
1771         char *str, *p;
1772         ber_len_t len;
1773
1774         str = ch_strdup( src );
1775         len = strlen( src );
1776
1777         for ( p = str; p && p[ 0 ]; p++ ) {
1778                 /* escape */
1779                 if ( p[ 0 ] == '\\' && p[ 1 ] ) {
1780                         /* 
1781                          * if escaping a hex pair we should
1782                          * increment p twice; however, in that 
1783                          * case the second hex number does 
1784                          * no harm
1785                          */
1786                         p++;
1787                 }
1788
1789                 if ( p[ 0 ] == ',' ) {
1790                         if ( p[ 1 ] == ' ' ) {
1791                                 char *q;
1792                         
1793                                 /*
1794                                  * too much space should be 
1795                                  * an error if we are pedantic
1796                                  */
1797                                 for ( q = &p[ 2 ]; q[ 0 ] == ' '; q++ ) {
1798                                         /* DO NOTHING */ ;
1799                                 }
1800                                 AC_MEMCPY( p+1, q, len-(q-str)+1);
1801                         }
1802                 }
1803         }
1804         pattern->bv_val = str;
1805         pattern->bv_len = p-str;
1806
1807         return;
1808 }
1809
1810 static void
1811 split(
1812     char        *line,
1813     int         splitchar,
1814     char        **left,
1815     char        **right
1816 )
1817 {
1818         *left = line;
1819         if ( (*right = strchr( line, splitchar )) != NULL ) {
1820                 *((*right)++) = '\0';
1821         }
1822 }
1823
1824 static void
1825 access_append( Access **l, Access *a )
1826 {
1827         for ( ; *l != NULL; l = &(*l)->a_next )
1828                 ;       /* NULL */
1829
1830         *l = a;
1831 }
1832
1833 void
1834 acl_append( AccessControl **l, AccessControl *a )
1835 {
1836         for ( ; *l != NULL; l = &(*l)->acl_next )
1837                 ;       /* NULL */
1838
1839         *l = a;
1840 }
1841
1842 static void
1843 access_free( Access *a )
1844 {
1845         if ( a->a_dn_pat.bv_val )
1846                 free ( a->a_dn_pat.bv_val );
1847         if ( a->a_peername_pat.bv_val )
1848                 free ( a->a_peername_pat.bv_val );
1849         if ( a->a_sockname_pat.bv_val )
1850                 free ( a->a_sockname_pat.bv_val );
1851         if ( a->a_domain_pat.bv_val )
1852                 free ( a->a_domain_pat.bv_val );
1853         if ( a->a_sockurl_pat.bv_val )
1854                 free ( a->a_sockurl_pat.bv_val );
1855         if ( a->a_set_pat.bv_len )
1856                 free ( a->a_set_pat.bv_val );
1857         if ( a->a_group_pat.bv_len )
1858                 free ( a->a_group_pat.bv_val );
1859         free( a );
1860 }
1861
1862 void
1863 acl_free( AccessControl *a )
1864 {
1865         Access *n;
1866         AttributeName *an;
1867
1868         if ( a->acl_filter )
1869                 filter_free( a->acl_filter );
1870         if ( a->acl_dn_pat.bv_len )
1871                 free ( a->acl_dn_pat.bv_val );
1872         if ( a->acl_attrs ) {
1873                 for ( an = a->acl_attrs; an->an_name.bv_val; an++ ) {
1874                         free( an->an_name.bv_val );
1875                 }
1876                 free( a->acl_attrs );
1877         }
1878         for (; a->acl_access; a->acl_access = n) {
1879                 n = a->acl_access->a_next;
1880                 access_free( a->acl_access );
1881         }
1882         free( a );
1883 }
1884
1885 /* Because backend_startup uses acl_append to tack on the global_acl to
1886  * the end of each backend's acl, we cannot just take one argument and
1887  * merrily free our way to the end of the list. backend_destroy calls us
1888  * with the be_acl in arg1, and global_acl in arg2 to give us a stopping
1889  * point. config_destroy calls us with global_acl in arg1 and NULL in
1890  * arg2, so we then proceed to polish off the global_acl.
1891  */
1892 void
1893 acl_destroy( AccessControl *a, AccessControl *end )
1894 {
1895         AccessControl *n;
1896
1897         for (; a && a!= end; a=n) {
1898                 n = a->acl_next;
1899                 acl_free( a );
1900         }
1901 }
1902
1903 char *
1904 access2str( slap_access_t access )
1905 {
1906         if ( access == ACL_NONE ) {
1907                 return "none";
1908
1909         } else if ( access == ACL_AUTH ) {
1910                 return "auth";
1911
1912         } else if ( access == ACL_COMPARE ) {
1913                 return "compare";
1914
1915         } else if ( access == ACL_SEARCH ) {
1916                 return "search";
1917
1918         } else if ( access == ACL_READ ) {
1919                 return "read";
1920
1921         } else if ( access == ACL_WRITE ) {
1922                 return "write";
1923         }
1924
1925         return "unknown";
1926 }
1927
1928 slap_access_t
1929 str2access( const char *str )
1930 {
1931         if ( strcasecmp( str, "none" ) == 0 ) {
1932                 return ACL_NONE;
1933
1934         } else if ( strcasecmp( str, "auth" ) == 0 ) {
1935                 return ACL_AUTH;
1936
1937         } else if ( strcasecmp( str, "compare" ) == 0 ) {
1938                 return ACL_COMPARE;
1939
1940         } else if ( strcasecmp( str, "search" ) == 0 ) {
1941                 return ACL_SEARCH;
1942
1943         } else if ( strcasecmp( str, "read" ) == 0 ) {
1944                 return ACL_READ;
1945
1946         } else if ( strcasecmp( str, "write" ) == 0 ) {
1947                 return ACL_WRITE;
1948         }
1949
1950         return( ACL_INVALID_ACCESS );
1951 }
1952
1953 #ifdef LDAP_DEBUG
1954
1955 static void
1956 print_access( Access *b )
1957 {
1958         char maskbuf[ACCESSMASK_MAXLEN];
1959
1960         fprintf( stderr, "\tby" );
1961
1962         if ( b->a_dn_pat.bv_len != 0 ) {
1963                 if( strcmp(b->a_dn_pat.bv_val, "*") == 0
1964                         || strcmp(b->a_dn_pat.bv_val, "users") == 0 
1965                         || strcmp(b->a_dn_pat.bv_val, "anonymous") == 0 
1966                         || strcmp(b->a_dn_pat.bv_val, "self") == 0 )
1967                 {
1968                         fprintf( stderr, " %s", b->a_dn_pat.bv_val );
1969
1970                 } else {
1971                         fprintf( stderr, " dn.%s=\"%s\"",
1972                                 style_strings[b->a_dn_style], b->a_dn_pat.bv_val );
1973                 }
1974         }
1975
1976         if ( b->a_dn_at != NULL ) {
1977                 fprintf( stderr, " dnattr=%s", b->a_dn_at->ad_cname.bv_val );
1978         }
1979
1980         if ( b->a_group_pat.bv_len ) {
1981                 fprintf( stderr, " group/%s/%s.%s=\"%s\"",
1982                         b->a_group_oc ? b->a_group_oc->soc_cname.bv_val : "groupOfNames",
1983                         b->a_group_at ? b->a_group_at->ad_cname.bv_val : "member",
1984                         style_strings[b->a_group_style],
1985                         b->a_group_pat.bv_val );
1986     }
1987
1988         if ( b->a_peername_pat.bv_len != 0 ) {
1989                 fprintf( stderr, " peername=\"%s\"", b->a_peername_pat.bv_val );
1990         }
1991
1992         if ( b->a_sockname_pat.bv_len != 0 ) {
1993                 fprintf( stderr, " sockname=\"%s\"", b->a_sockname_pat.bv_val );
1994         }
1995
1996         if ( b->a_domain_pat.bv_len != 0 ) {
1997                 fprintf( stderr, " domain=%s", b->a_domain_pat.bv_val );
1998         }
1999
2000         if ( b->a_sockurl_pat.bv_len != 0 ) {
2001                 fprintf( stderr, " sockurl=\"%s\"", b->a_sockurl_pat.bv_val );
2002         }
2003
2004         if ( b->a_set_pat.bv_len != 0 ) {
2005                 fprintf( stderr, " set=\"%s\"", b->a_set_pat.bv_val );
2006         }
2007
2008 #ifdef SLAPD_ACI_ENABLED
2009         if ( b->a_aci_at != NULL ) {
2010                 fprintf( stderr, " aci=%s", b->a_aci_at->ad_cname.bv_val );
2011         }
2012 #endif
2013
2014         /* Security Strength Factors */
2015         if ( b->a_authz.sai_ssf ) {
2016                 fprintf( stderr, " ssf=%u",
2017                         b->a_authz.sai_ssf );
2018         }
2019         if ( b->a_authz.sai_transport_ssf ) {
2020                 fprintf( stderr, " transport_ssf=%u",
2021                         b->a_authz.sai_transport_ssf );
2022         }
2023         if ( b->a_authz.sai_tls_ssf ) {
2024                 fprintf( stderr, " tls_ssf=%u",
2025                         b->a_authz.sai_tls_ssf );
2026         }
2027         if ( b->a_authz.sai_sasl_ssf ) {
2028                 fprintf( stderr, " sasl_ssf=%u",
2029                         b->a_authz.sai_sasl_ssf );
2030         }
2031
2032         fprintf( stderr, " %s%s",
2033                 b->a_dn_self ? "self" : "",
2034                 accessmask2str( b->a_access_mask, maskbuf ) );
2035
2036         if( b->a_type == ACL_BREAK ) {
2037                 fprintf( stderr, " break" );
2038
2039         } else if( b->a_type == ACL_CONTINUE ) {
2040                 fprintf( stderr, " continue" );
2041
2042         } else if( b->a_type != ACL_STOP ) {
2043                 fprintf( stderr, " unknown-control" );
2044         }
2045
2046         fprintf( stderr, "\n" );
2047 }
2048
2049
2050 static void
2051 print_acl( Backend *be, AccessControl *a )
2052 {
2053         int             to = 0;
2054         Access  *b;
2055
2056         fprintf( stderr, "%s ACL: access to",
2057                 be == NULL ? "Global" : "Backend" );
2058
2059         if ( a->acl_dn_pat.bv_len != 0 ) {
2060                 to++;
2061                 fprintf( stderr, " dn.%s=\"%s\"\n",
2062                         style_strings[a->acl_dn_style], a->acl_dn_pat.bv_val );
2063         }
2064
2065         if ( a->acl_filter != NULL ) {
2066                 struct berval bv = BER_BVNULL;
2067                 to++;
2068                 filter2bv( a->acl_filter, &bv );
2069                 fprintf( stderr, " filter=%s\n", bv.bv_val );
2070                 ch_free( bv.bv_val );
2071         }
2072
2073         if ( a->acl_attrs != NULL ) {
2074                 int     first = 1;
2075                 AttributeName *an;
2076                 to++;
2077
2078                 fprintf( stderr, " attrs=" );
2079                 for ( an = a->acl_attrs; an && an->an_name.bv_val; an++ ) {
2080                         if ( ! first ) {
2081                                 fprintf( stderr, "," );
2082                         }
2083                         if (an->an_oc) {
2084                                 fputc( an->an_oc_exclude ? '!' : '@', stderr);
2085                         }
2086                         fputs( an->an_name.bv_val, stderr );
2087                         first = 0;
2088                 }
2089                 fprintf(  stderr, "\n" );
2090         }
2091
2092         if ( a->acl_attrval.bv_len != 0 ) {
2093                 to++;
2094                 fprintf( stderr, " val.%s=\"%s\"\n",
2095                         style_strings[a->acl_attrval_style], a->acl_attrval.bv_val );
2096
2097         }
2098
2099         if( !to ) {
2100                 fprintf( stderr, " *\n" );
2101         }
2102
2103         for ( b = a->acl_access; b != NULL; b = b->a_next ) {
2104                 print_access( b );
2105         }
2106
2107         fprintf( stderr, "\n" );
2108 }
2109
2110 #endif /* LDAP_DEBUG */