]> git.sur5r.net Git - openldap/blob - servers/slapd/aclparse.c
b554fe0f2a619b72b2c825417dc72942d5928cdc
[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;
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 = atoi( right );
1274
1275                                         if( !b->a_authz.sai_ssf ) {
1276                                                 fprintf( stderr,
1277                                                         "%s: line %d: invalid ssf value (%s)\n",
1278                                                         fname, lineno, right );
1279                                                 acl_usage();
1280                                         }
1281                                         continue;
1282                                 }
1283
1284                                 if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
1285                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1286                                                 fprintf( stderr,
1287                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
1288                                                     fname, lineno, style );
1289                                                 acl_usage();
1290                                         }
1291
1292                                         if( b->a_authz.sai_transport_ssf ) {
1293                                                 fprintf( stderr,
1294                                                         "%s: line %d: transport_ssf attribute already specified.\n",
1295                                                         fname, lineno );
1296                                                 acl_usage();
1297                                         }
1298
1299                                         if ( right == NULL || *right == '\0' ) {
1300                                                 fprintf( stderr,
1301                                                         "%s: line %d: no transport_ssf is defined\n",
1302                                                         fname, lineno );
1303                                                 acl_usage();
1304                                         }
1305
1306                                         b->a_authz.sai_transport_ssf = atoi( right );
1307
1308                                         if( !b->a_authz.sai_transport_ssf ) {
1309                                                 fprintf( stderr,
1310                                                         "%s: line %d: invalid transport_ssf value (%s)\n",
1311                                                         fname, lineno, right );
1312                                                 acl_usage();
1313                                         }
1314                                         continue;
1315                                 }
1316
1317                                 if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
1318                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1319                                                 fprintf( stderr,
1320                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
1321                                                     fname, lineno, style );
1322                                                 acl_usage();
1323                                         }
1324
1325                                         if( b->a_authz.sai_tls_ssf ) {
1326                                                 fprintf( stderr,
1327                                                         "%s: line %d: tls_ssf attribute already specified.\n",
1328                                                         fname, lineno );
1329                                                 acl_usage();
1330                                         }
1331
1332                                         if ( right == NULL || *right == '\0' ) {
1333                                                 fprintf( stderr,
1334                                                         "%s: line %d: no tls_ssf is defined\n",
1335                                                         fname, lineno );
1336                                                 acl_usage();
1337                                         }
1338
1339                                         b->a_authz.sai_tls_ssf = atoi( right );
1340
1341                                         if( !b->a_authz.sai_tls_ssf ) {
1342                                                 fprintf( stderr,
1343                                                         "%s: line %d: invalid tls_ssf value (%s)\n",
1344                                                         fname, lineno, right );
1345                                                 acl_usage();
1346                                         }
1347                                         continue;
1348                                 }
1349
1350                                 if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
1351                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1352                                                 fprintf( stderr,
1353                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
1354                                                     fname, lineno, style );
1355                                                 acl_usage();
1356                                         }
1357
1358                                         if( b->a_authz.sai_sasl_ssf ) {
1359                                                 fprintf( stderr,
1360                                                         "%s: line %d: sasl_ssf attribute already specified.\n",
1361                                                         fname, lineno );
1362                                                 acl_usage();
1363                                         }
1364
1365                                         if ( right == NULL || *right == '\0' ) {
1366                                                 fprintf( stderr,
1367                                                         "%s: line %d: no sasl_ssf is defined\n",
1368                                                         fname, lineno );
1369                                                 acl_usage();
1370                                         }
1371
1372                                         b->a_authz.sai_sasl_ssf = atoi( right );
1373
1374                                         if( !b->a_authz.sai_sasl_ssf ) {
1375                                                 fprintf( stderr,
1376                                                         "%s: line %d: invalid sasl_ssf value (%s)\n",
1377                                                         fname, lineno, right );
1378                                                 acl_usage();
1379                                         }
1380                                         continue;
1381                                 }
1382
1383                                 if( right != NULL ) {
1384                                         /* unsplit */
1385                                         right[-1] = '=';
1386                                 }
1387                                 break;
1388                         }
1389
1390                         if( i == argc || ( strcasecmp( left, "stop" ) == 0 )) { 
1391                                 /* out of arguments or plain stop */
1392
1393                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1394                                 b->a_type = ACL_STOP;
1395
1396                                 access_append( &a->acl_access, b );
1397                                 continue;
1398                         }
1399
1400                         if( strcasecmp( left, "continue" ) == 0 ) {
1401                                 /* plain continue */
1402
1403                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1404                                 b->a_type = ACL_CONTINUE;
1405
1406                                 access_append( &a->acl_access, b );
1407                                 continue;
1408                         }
1409
1410                         if( strcasecmp( left, "break" ) == 0 ) {
1411                                 /* plain continue */
1412
1413                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1414                                 b->a_type = ACL_BREAK;
1415
1416                                 access_append( &a->acl_access, b );
1417                                 continue;
1418                         }
1419
1420                         if ( strcasecmp( left, "by" ) == 0 ) {
1421                                 /* we've gone too far */
1422                                 --i;
1423                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1424                                 b->a_type = ACL_STOP;
1425
1426                                 access_append( &a->acl_access, b );
1427                                 continue;
1428                         }
1429
1430                         /* get <access> */
1431                         if( strncasecmp( left, "self", 4 ) == 0 ) {
1432                                 b->a_dn_self = 1;
1433                                 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( &left[4] ) );
1434
1435                         } else {
1436                                 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( left ) );
1437                         }
1438
1439                         if( ACL_IS_INVALID( b->a_access_mask ) ) {
1440                                 fprintf( stderr,
1441                                         "%s: line %d: expecting <access> got \"%s\"\n",
1442                                         fname, lineno, left );
1443                                 acl_usage();
1444                         }
1445
1446                         b->a_type = ACL_STOP;
1447
1448                         if( ++i == argc ) {
1449                                 /* out of arguments or plain stop */
1450                                 access_append( &a->acl_access, b );
1451                                 continue;
1452                         }
1453
1454                         if( strcasecmp( argv[i], "continue" ) == 0 ) {
1455                                 /* plain continue */
1456                                 b->a_type = ACL_CONTINUE;
1457
1458                         } else if( strcasecmp( argv[i], "break" ) == 0 ) {
1459                                 /* plain continue */
1460                                 b->a_type = ACL_BREAK;
1461
1462                         } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
1463                                 /* gone to far */
1464                                 i--;
1465                         }
1466
1467                         access_append( &a->acl_access, b );
1468
1469                 } else {
1470                         fprintf( stderr,
1471                     "%s: line %d: expecting \"to\" or \"by\" got \"%s\"\n",
1472                             fname, lineno, argv[i] );
1473                         acl_usage();
1474                 }
1475         }
1476
1477         /* if we have no real access clause, complain and do nothing */
1478         if ( a == NULL ) {
1479                         fprintf( stderr,
1480                                 "%s: line %d: warning: no access clause(s) specified in access line\n",
1481                             fname, lineno );
1482
1483         } else {
1484 #ifdef LDAP_DEBUG
1485                 if (ldap_debug & LDAP_DEBUG_ACL)
1486                         print_acl(be, a);
1487 #endif
1488         
1489                 if ( a->acl_access == NULL ) {
1490                         fprintf( stderr,
1491                         "%s: line %d: warning: no by clause(s) specified in access line\n",
1492                             fname, lineno );
1493                 }
1494
1495                 if ( be != NULL ) {
1496 #ifdef LDAP_DEVEL
1497                         switch ( check_scope( be, a ) ) {
1498                         case ACL_SCOPE_UNKNOWN:
1499                                 fprintf( stderr, "%s: line %d: warning: "
1500                                                 "cannot assess the validity "
1501                                                 "of the ACL scope within "
1502                                                 "backend naming context\n",
1503                                                 fname, lineno );
1504                                 break;
1505
1506                         case ACL_SCOPE_WARN:
1507                                 fprintf( stderr, "%s: line %d: warning: "
1508                                                 "ACL could be out of "
1509                                                 "scope within "
1510                                                 "backend naming context\n",
1511                                                 fname, lineno );
1512                                 break;
1513
1514                         case ACL_SCOPE_PARTIAL:
1515                                 fprintf( stderr, "%s: line %d: warning: "
1516                                                 "ACL appears to be partially "
1517                                                 "out of scope within "
1518                                                 "backend naming context\n",
1519                                                 fname, lineno );
1520                                 break;
1521
1522                         case ACL_SCOPE_ERR:
1523                                 fprintf( stderr, "%s: line %d: warning: "
1524                                                 "ACL appears to be out of "
1525                                                 "scope within "
1526                                                 "backend naming context\n",
1527                                                 fname, lineno );
1528                                 break;
1529
1530                         default:
1531                                 break;
1532                         }
1533 #endif /* LDAP_DEVEL */
1534                         acl_append( &be->be_acl, a );
1535                 } else {
1536                         acl_append( &global_acl, a );
1537                 }
1538         }
1539 }
1540
1541 char *
1542 accessmask2str( slap_mask_t mask, char *buf )
1543 {
1544         int none=1;
1545         char *ptr = buf;
1546
1547         assert( buf != NULL );
1548
1549         if ( ACL_IS_INVALID( mask ) ) {
1550                 return "invalid";
1551         }
1552
1553         buf[0] = '\0';
1554
1555         if ( ACL_IS_LEVEL( mask ) ) {
1556                 if ( ACL_LVL_IS_NONE(mask) ) {
1557                         ptr = lutil_strcopy( ptr, "none" );
1558
1559                 } else if ( ACL_LVL_IS_AUTH(mask) ) {
1560                         ptr = lutil_strcopy( ptr, "auth" );
1561
1562                 } else if ( ACL_LVL_IS_COMPARE(mask) ) {
1563                         ptr = lutil_strcopy( ptr, "compare" );
1564
1565                 } else if ( ACL_LVL_IS_SEARCH(mask) ) {
1566                         ptr = lutil_strcopy( ptr, "search" );
1567
1568                 } else if ( ACL_LVL_IS_READ(mask) ) {
1569                         ptr = lutil_strcopy( ptr, "read" );
1570
1571                 } else if ( ACL_LVL_IS_WRITE(mask) ) {
1572                         ptr = lutil_strcopy( ptr, "write" );
1573                 } else {
1574                         ptr = lutil_strcopy( ptr, "unknown" );
1575                 }
1576                 
1577                 *ptr++ = '(';
1578         }
1579
1580         if( ACL_IS_ADDITIVE( mask ) ) {
1581                 *ptr++ = '+';
1582
1583         } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
1584                 *ptr++ = '-';
1585
1586         } else {
1587                 *ptr++ = '=';
1588         }
1589
1590         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
1591                 none = 0;
1592                 *ptr++ = 'w';
1593         } 
1594
1595         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
1596                 none = 0;
1597                 *ptr++ = 'r';
1598         } 
1599
1600         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
1601                 none = 0;
1602                 *ptr++ = 's';
1603         } 
1604
1605         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
1606                 none = 0;
1607                 *ptr++ = 'c';
1608         } 
1609
1610         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
1611                 none = 0;
1612                 *ptr++ = 'x';
1613         } 
1614
1615         if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
1616                 none = 0;
1617                 *ptr++ = 'n';
1618         } 
1619
1620         if ( none ) {
1621                 *ptr++ = '0';
1622         }
1623
1624         if ( ACL_IS_LEVEL( mask ) ) {
1625                 *ptr++ = ')';
1626         }
1627
1628         *ptr = '\0';
1629
1630         return buf;
1631 }
1632
1633 slap_mask_t
1634 str2accessmask( const char *str )
1635 {
1636         slap_mask_t     mask;
1637
1638         if( !ASCII_ALPHA(str[0]) ) {
1639                 int i;
1640
1641                 if ( str[0] == '=' ) {
1642                         ACL_INIT(mask);
1643
1644                 } else if( str[0] == '+' ) {
1645                         ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
1646
1647                 } else if( str[0] == '-' ) {
1648                         ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
1649
1650                 } else {
1651                         ACL_INVALIDATE(mask);
1652                         return mask;
1653                 }
1654
1655                 for( i=1; str[i] != '\0'; i++ ) {
1656                         if( TOLOWER((unsigned char) str[i]) == 'w' ) {
1657                                 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
1658
1659                         } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
1660                                 ACL_PRIV_SET(mask, ACL_PRIV_READ);
1661
1662                         } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
1663                                 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
1664
1665                         } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
1666                                 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
1667
1668                         } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
1669                                 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
1670
1671                         } else if( str[i] != '0' ) {
1672                                 ACL_INVALIDATE(mask);
1673                                 return mask;
1674                         }
1675                 }
1676
1677                 return mask;
1678         }
1679
1680         if ( strcasecmp( str, "none" ) == 0 ) {
1681                 ACL_LVL_ASSIGN_NONE(mask);
1682
1683         } else if ( strcasecmp( str, "auth" ) == 0 ) {
1684                 ACL_LVL_ASSIGN_AUTH(mask);
1685
1686         } else if ( strcasecmp( str, "compare" ) == 0 ) {
1687                 ACL_LVL_ASSIGN_COMPARE(mask);
1688
1689         } else if ( strcasecmp( str, "search" ) == 0 ) {
1690                 ACL_LVL_ASSIGN_SEARCH(mask);
1691
1692         } else if ( strcasecmp( str, "read" ) == 0 ) {
1693                 ACL_LVL_ASSIGN_READ(mask);
1694
1695         } else if ( strcasecmp( str, "write" ) == 0 ) {
1696                 ACL_LVL_ASSIGN_WRITE(mask);
1697
1698         } else {
1699                 ACL_INVALIDATE( mask );
1700         }
1701
1702         return mask;
1703 }
1704
1705 static void
1706 acl_usage( void )
1707 {
1708         fprintf( stderr, "%s%s\n",
1709                 "<access clause> ::= access to <what> "
1710                                 "[ by <who> <access> [ <control> ] ]+ \n"
1711                 "<what> ::= * | [dn[.<dnstyle>]=<DN>] [filter=<filter>] [attrs=<attrlist>]\n"
1712                 "<attrlist> ::= <attr> [val[.<style>]=<value>] | <attr> , <attrlist>\n"
1713                 "<attr> ::= <attrname> | entry | children\n"
1714                 "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
1715                         "\t[dnattr=<attrname>]\n"
1716                         "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
1717                         "\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n",
1718                         "\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n"
1719 #ifdef SLAPD_ACI_ENABLED
1720                         "\t[aci=<attrname>]\n"
1721 #endif
1722                         "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
1723                 "<dnstyle> ::= base | exact | one(level) | sub(tree) | children | regex\n"
1724                 "<style> ::= regex | base | exact\n"
1725                 "<peernamestyle> ::= regex | exact | ip | path\n"
1726                 "<domainstyle> ::= regex | base | exact | sub(tree)\n"
1727                 "<access> ::= [self]{<level>|<priv>}\n"
1728                 "<level> ::= none | auth | compare | search | read | write\n"
1729                 "<priv> ::= {=|+|-}{w|r|s|c|x|0}+\n"
1730                 "<control> ::= [ stop | continue | break ]\n"
1731         );
1732         exit( EXIT_FAILURE );
1733 }
1734
1735 /*
1736  * Set pattern to a "normalized" DN from src.
1737  * At present it simply eats the (optional) space after 
1738  * a RDN separator (,)
1739  * Eventually will evolve in a more complete normalization
1740  */
1741 static void
1742 acl_regex_normalized_dn(
1743         const char *src,
1744         struct berval *pattern
1745 )
1746 {
1747         char *str, *p;
1748         ber_len_t len;
1749
1750         str = ch_strdup( src );
1751         len = strlen( src );
1752
1753         for ( p = str; p && p[ 0 ]; p++ ) {
1754                 /* escape */
1755                 if ( p[ 0 ] == '\\' && p[ 1 ] ) {
1756                         /* 
1757                          * if escaping a hex pair we should
1758                          * increment p twice; however, in that 
1759                          * case the second hex number does 
1760                          * no harm
1761                          */
1762                         p++;
1763                 }
1764
1765                 if ( p[ 0 ] == ',' ) {
1766                         if ( p[ 1 ] == ' ' ) {
1767                                 char *q;
1768                         
1769                                 /*
1770                                  * too much space should be 
1771                                  * an error if we are pedantic
1772                                  */
1773                                 for ( q = &p[ 2 ]; q[ 0 ] == ' '; q++ ) {
1774                                         /* DO NOTHING */ ;
1775                                 }
1776                                 AC_MEMCPY( p+1, q, len-(q-str)+1);
1777                         }
1778                 }
1779         }
1780         pattern->bv_val = str;
1781         pattern->bv_len = p-str;
1782
1783         return;
1784 }
1785
1786 static void
1787 split(
1788     char        *line,
1789     int         splitchar,
1790     char        **left,
1791     char        **right
1792 )
1793 {
1794         *left = line;
1795         if ( (*right = strchr( line, splitchar )) != NULL ) {
1796                 *((*right)++) = '\0';
1797         }
1798 }
1799
1800 static void
1801 access_append( Access **l, Access *a )
1802 {
1803         for ( ; *l != NULL; l = &(*l)->a_next )
1804                 ;       /* NULL */
1805
1806         *l = a;
1807 }
1808
1809 void
1810 acl_append( AccessControl **l, AccessControl *a )
1811 {
1812         for ( ; *l != NULL; l = &(*l)->acl_next )
1813                 ;       /* NULL */
1814
1815         *l = a;
1816 }
1817
1818 static void
1819 access_free( Access *a )
1820 {
1821         if ( a->a_dn_pat.bv_val )
1822                 free ( a->a_dn_pat.bv_val );
1823         if ( a->a_peername_pat.bv_val )
1824                 free ( a->a_peername_pat.bv_val );
1825         if ( a->a_sockname_pat.bv_val )
1826                 free ( a->a_sockname_pat.bv_val );
1827         if ( a->a_domain_pat.bv_val )
1828                 free ( a->a_domain_pat.bv_val );
1829         if ( a->a_sockurl_pat.bv_val )
1830                 free ( a->a_sockurl_pat.bv_val );
1831         if ( a->a_set_pat.bv_len )
1832                 free ( a->a_set_pat.bv_val );
1833         if ( a->a_group_pat.bv_len )
1834                 free ( a->a_group_pat.bv_val );
1835         free( a );
1836 }
1837
1838 void
1839 acl_free( AccessControl *a )
1840 {
1841         Access *n;
1842         AttributeName *an;
1843
1844         if ( a->acl_filter )
1845                 filter_free( a->acl_filter );
1846         if ( a->acl_dn_pat.bv_len )
1847                 free ( a->acl_dn_pat.bv_val );
1848         if ( a->acl_attrs ) {
1849                 for ( an = a->acl_attrs; an->an_name.bv_val; an++ ) {
1850                         free( an->an_name.bv_val );
1851                 }
1852                 free( a->acl_attrs );
1853         }
1854         for (; a->acl_access; a->acl_access = n) {
1855                 n = a->acl_access->a_next;
1856                 access_free( a->acl_access );
1857         }
1858         free( a );
1859 }
1860
1861 /* Because backend_startup uses acl_append to tack on the global_acl to
1862  * the end of each backend's acl, we cannot just take one argument and
1863  * merrily free our way to the end of the list. backend_destroy calls us
1864  * with the be_acl in arg1, and global_acl in arg2 to give us a stopping
1865  * point. config_destroy calls us with global_acl in arg1 and NULL in
1866  * arg2, so we then proceed to polish off the global_acl.
1867  */
1868 void
1869 acl_destroy( AccessControl *a, AccessControl *end )
1870 {
1871         AccessControl *n;
1872
1873         for (; a && a!= end; a=n) {
1874                 n = a->acl_next;
1875                 acl_free( a );
1876         }
1877 }
1878
1879 char *
1880 access2str( slap_access_t access )
1881 {
1882         if ( access == ACL_NONE ) {
1883                 return "none";
1884
1885         } else if ( access == ACL_AUTH ) {
1886                 return "auth";
1887
1888         } else if ( access == ACL_COMPARE ) {
1889                 return "compare";
1890
1891         } else if ( access == ACL_SEARCH ) {
1892                 return "search";
1893
1894         } else if ( access == ACL_READ ) {
1895                 return "read";
1896
1897         } else if ( access == ACL_WRITE ) {
1898                 return "write";
1899         }
1900
1901         return "unknown";
1902 }
1903
1904 slap_access_t
1905 str2access( const char *str )
1906 {
1907         if ( strcasecmp( str, "none" ) == 0 ) {
1908                 return ACL_NONE;
1909
1910         } else if ( strcasecmp( str, "auth" ) == 0 ) {
1911                 return ACL_AUTH;
1912
1913         } else if ( strcasecmp( str, "compare" ) == 0 ) {
1914                 return ACL_COMPARE;
1915
1916         } else if ( strcasecmp( str, "search" ) == 0 ) {
1917                 return ACL_SEARCH;
1918
1919         } else if ( strcasecmp( str, "read" ) == 0 ) {
1920                 return ACL_READ;
1921
1922         } else if ( strcasecmp( str, "write" ) == 0 ) {
1923                 return ACL_WRITE;
1924         }
1925
1926         return( ACL_INVALID_ACCESS );
1927 }
1928
1929 #ifdef LDAP_DEBUG
1930
1931 static void
1932 print_access( Access *b )
1933 {
1934         char maskbuf[ACCESSMASK_MAXLEN];
1935
1936         fprintf( stderr, "\tby" );
1937
1938         if ( b->a_dn_pat.bv_len != 0 ) {
1939                 if( strcmp(b->a_dn_pat.bv_val, "*") == 0
1940                         || strcmp(b->a_dn_pat.bv_val, "users") == 0 
1941                         || strcmp(b->a_dn_pat.bv_val, "anonymous") == 0 
1942                         || strcmp(b->a_dn_pat.bv_val, "self") == 0 )
1943                 {
1944                         fprintf( stderr, " %s", b->a_dn_pat.bv_val );
1945
1946                 } else {
1947                         fprintf( stderr, " dn.%s=\"%s\"",
1948                                 style_strings[b->a_dn_style], b->a_dn_pat.bv_val );
1949                 }
1950         }
1951
1952         if ( b->a_dn_at != NULL ) {
1953                 fprintf( stderr, " dnattr=%s", b->a_dn_at->ad_cname.bv_val );
1954         }
1955
1956         if ( b->a_group_pat.bv_len ) {
1957                 fprintf( stderr, " group/%s/%s.%s=\"%s\"",
1958                         b->a_group_oc ? b->a_group_oc->soc_cname.bv_val : "groupOfNames",
1959                         b->a_group_at ? b->a_group_at->ad_cname.bv_val : "member",
1960                         style_strings[b->a_group_style],
1961                         b->a_group_pat.bv_val );
1962     }
1963
1964         if ( b->a_peername_pat.bv_len != 0 ) {
1965                 fprintf( stderr, " peername=\"%s\"", b->a_peername_pat.bv_val );
1966         }
1967
1968         if ( b->a_sockname_pat.bv_len != 0 ) {
1969                 fprintf( stderr, " sockname=\"%s\"", b->a_sockname_pat.bv_val );
1970         }
1971
1972         if ( b->a_domain_pat.bv_len != 0 ) {
1973                 fprintf( stderr, " domain=%s", b->a_domain_pat.bv_val );
1974         }
1975
1976         if ( b->a_sockurl_pat.bv_len != 0 ) {
1977                 fprintf( stderr, " sockurl=\"%s\"", b->a_sockurl_pat.bv_val );
1978         }
1979
1980         if ( b->a_set_pat.bv_len != 0 ) {
1981                 fprintf( stderr, " set=\"%s\"", b->a_set_pat.bv_val );
1982         }
1983
1984 #ifdef SLAPD_ACI_ENABLED
1985         if ( b->a_aci_at != NULL ) {
1986                 fprintf( stderr, " aci=%s", b->a_aci_at->ad_cname.bv_val );
1987         }
1988 #endif
1989
1990         /* Security Strength Factors */
1991         if ( b->a_authz.sai_ssf ) {
1992                 fprintf( stderr, " ssf=%u",
1993                         b->a_authz.sai_ssf );
1994         }
1995         if ( b->a_authz.sai_transport_ssf ) {
1996                 fprintf( stderr, " transport_ssf=%u",
1997                         b->a_authz.sai_transport_ssf );
1998         }
1999         if ( b->a_authz.sai_tls_ssf ) {
2000                 fprintf( stderr, " tls_ssf=%u",
2001                         b->a_authz.sai_tls_ssf );
2002         }
2003         if ( b->a_authz.sai_sasl_ssf ) {
2004                 fprintf( stderr, " sasl_ssf=%u",
2005                         b->a_authz.sai_sasl_ssf );
2006         }
2007
2008         fprintf( stderr, " %s%s",
2009                 b->a_dn_self ? "self" : "",
2010                 accessmask2str( b->a_access_mask, maskbuf ) );
2011
2012         if( b->a_type == ACL_BREAK ) {
2013                 fprintf( stderr, " break" );
2014
2015         } else if( b->a_type == ACL_CONTINUE ) {
2016                 fprintf( stderr, " continue" );
2017
2018         } else if( b->a_type != ACL_STOP ) {
2019                 fprintf( stderr, " unknown-control" );
2020         }
2021
2022         fprintf( stderr, "\n" );
2023 }
2024
2025
2026 static void
2027 print_acl( Backend *be, AccessControl *a )
2028 {
2029         int             to = 0;
2030         Access  *b;
2031
2032         fprintf( stderr, "%s ACL: access to",
2033                 be == NULL ? "Global" : "Backend" );
2034
2035         if ( a->acl_dn_pat.bv_len != 0 ) {
2036                 to++;
2037                 fprintf( stderr, " dn.%s=\"%s\"\n",
2038                         style_strings[a->acl_dn_style], a->acl_dn_pat.bv_val );
2039         }
2040
2041         if ( a->acl_filter != NULL ) {
2042                 struct berval bv = BER_BVNULL;
2043                 to++;
2044                 filter2bv( a->acl_filter, &bv );
2045                 fprintf( stderr, " filter=%s\n", bv.bv_val );
2046                 ch_free( bv.bv_val );
2047         }
2048
2049         if ( a->acl_attrs != NULL ) {
2050                 int     first = 1;
2051                 AttributeName *an;
2052                 to++;
2053
2054                 fprintf( stderr, " attrs=" );
2055                 for ( an = a->acl_attrs; an && an->an_name.bv_val; an++ ) {
2056                         if ( ! first ) {
2057                                 fprintf( stderr, "," );
2058                         }
2059                         if (an->an_oc) {
2060                                 fputc( an->an_oc_exclude ? '!' : '@', stderr);
2061                         }
2062                         fputs( an->an_name.bv_val, stderr );
2063                         first = 0;
2064                 }
2065                 fprintf(  stderr, "\n" );
2066         }
2067
2068         if ( a->acl_attrval.bv_len != 0 ) {
2069                 to++;
2070                 fprintf( stderr, " val.%s=\"%s\"\n",
2071                         style_strings[a->acl_attrval_style], a->acl_attrval.bv_val );
2072
2073         }
2074
2075         if( !to ) {
2076                 fprintf( stderr, " *\n" );
2077         }
2078
2079         for ( b = a->acl_access; b != NULL; b = b->a_next ) {
2080                 print_access( b );
2081         }
2082
2083         fprintf( stderr, "\n" );
2084 }
2085
2086 #endif /* LDAP_DEBUG */