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