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