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