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