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