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