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