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