]> git.sur5r.net Git - openldap/blob - servers/slapd/aclparse.c
7a5d9fddf6840c7be4b8c4df6686c6501857593c
[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                                                 !is_at_subtype( b->a_group_at->ad_type, slap_schema.si_ad_labeledURI->ad_type ))
655                                         {
656                                                 fprintf( stderr,
657                                                         "%s: line %d: group \"%s\": inappropriate syntax: %s\n",
658                                                         fname, lineno, right,
659                                                         b->a_group_at->ad_type->sat_syntax_oid );
660                                                 acl_usage();
661                                         }
662
663
664                                         {
665                                                 int rc;
666                                                 struct berval vals[2];
667
668                                                 vals[0].bv_val = b->a_group_oc->soc_oid;
669                                                 vals[0].bv_len = strlen(vals[0].bv_val);
670                                                 vals[1].bv_val = NULL;
671
672
673                                                 rc = oc_check_allowed( b->a_group_at->ad_type,
674                                                         vals, NULL );
675
676                                                 if( rc != 0 ) {
677                                                         fprintf( stderr, "%s: line %d: "
678                                                                 "group: \"%s\" not allowed by \"%s\"\n",
679                                                                 fname, lineno,
680                                                                 b->a_group_at->ad_cname.bv_val,
681                                                                 b->a_group_oc->soc_oid );
682                                                         acl_usage();
683                                                 }
684                                         }
685                                         continue;
686                                 }
687
688                                 if ( strcasecmp( left, "peername" ) == 0 ) {
689                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
690                                                 fprintf( stderr, "%s: line %d: "
691                                                         "inappropriate style \"%s\" in by clause\n",
692                                                     fname, lineno, style );
693                                                 acl_usage();
694                                         }
695
696                                         if ( right == NULL || right[ 0 ] == '\0' ) {
697                                                 fprintf( stderr, "%s: line %d: "
698                                                         "missing \"=\" in (or value after) \"%s\" "
699                                                         "in by clause\n",
700                                                         fname, lineno, left );
701                                                 acl_usage();
702                                         }
703
704                                         if( b->a_peername_pat.bv_len ) {
705                                                 fprintf( stderr, "%s: line %d: "
706                                                         "peername pattern already specified.\n",
707                                                         fname, lineno );
708                                                 acl_usage();
709                                         }
710
711                                         b->a_peername_style = sty;
712                                         if (sty == ACL_STYLE_REGEX) {
713                                                 acl_regex_normalized_dn( right, &bv );
714                                                 if ( !ber_bvccmp( &bv, '*' ) ) {
715                                                         regtest(fname, lineno, bv.bv_val);
716                                                 }
717                                                 b->a_peername_pat = bv;
718                                         } else {
719                                                 ber_str2bv( right, 0, 1, &b->a_peername_pat );
720                                         }
721                                         continue;
722                                 }
723
724                                 if ( strcasecmp( left, "sockname" ) == 0 ) {
725                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
726                                                 fprintf( stderr, "%s: line %d: "
727                                                         "inappropriate style \"%s\" in by clause\n",
728                                                     fname, lineno, style );
729                                                 acl_usage();
730                                         }
731
732                                         if ( right == NULL || right[ 0 ] == '\0' ) {
733                                                 fprintf( stderr, "%s: line %d: "
734                                                         "missing \"=\" in (or value after) \"%s\" "
735                                                         "in by clause\n",
736                                                         fname, lineno, left );
737                                                 acl_usage();
738                                         }
739
740                                         if( b->a_sockname_pat.bv_len ) {
741                                                 fprintf( stderr, "%s: line %d: "
742                                                         "sockname pattern already specified.\n",
743                                                         fname, lineno );
744                                                 acl_usage();
745                                         }
746
747                                         b->a_sockname_style = sty;
748                                         if (sty == ACL_STYLE_REGEX) {
749                                                 acl_regex_normalized_dn( right, &bv );
750                                                 if ( !ber_bvccmp( &bv, '*' ) ) {
751                                                         regtest(fname, lineno, bv.bv_val);
752                                                 }
753                                                 b->a_sockname_pat = bv;
754                                         } else {
755                                                 ber_str2bv( right, 0, 1, &b->a_sockname_pat );
756                                         }
757                                         continue;
758                                 }
759
760                                 if ( strcasecmp( left, "domain" ) == 0 ) {
761                                         switch ( sty ) {
762                                         case ACL_STYLE_REGEX:
763                                         case ACL_STYLE_BASE:
764                                         case ACL_STYLE_SUBTREE:
765                                                 break;
766
767                                         default:
768                                                 fprintf( stderr,
769                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
770                                                     fname, lineno, style );
771                                                 acl_usage();
772                                         }
773
774                                         if ( right == NULL || right[ 0 ] == '\0' ) {
775                                                 fprintf( stderr,
776                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
777                                                         fname, lineno, left );
778                                                 acl_usage();
779                                         }
780
781                                         if( b->a_domain_pat.bv_len ) {
782                                                 fprintf( stderr,
783                                                         "%s: line %d: domain pattern already specified.\n",
784                                                         fname, lineno );
785                                                 acl_usage();
786                                         }
787
788                                         b->a_domain_style = sty;
789                                         b->a_domain_expand = expand;
790                                         if (sty == ACL_STYLE_REGEX) {
791                                                 acl_regex_normalized_dn( right, &bv );
792                                                 if ( !ber_bvccmp( &bv, '*' ) ) {
793                                                         regtest(fname, lineno, bv.bv_val);
794                                                 }
795                                                 b->a_domain_pat = bv;
796                                         } else {
797                                                 ber_str2bv( right, 0, 1, &b->a_domain_pat );
798                                         }
799                                         continue;
800                                 }
801
802                                 if ( strcasecmp( left, "sockurl" ) == 0 ) {
803                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
804                                                 fprintf( stderr,
805                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
806                                                     fname, lineno, style );
807                                                 acl_usage();
808                                         }
809
810                                         if ( right == NULL || right[ 0 ] == '\0' ) {
811                                                 fprintf( stderr,
812                                                         "%s: line %d: missing \"=\" in (or value after) \"%s\" in by clause\n",
813                                                         fname, lineno, left );
814                                                 acl_usage();
815                                         }
816
817                                         if( b->a_sockurl_pat.bv_len ) {
818                                                 fprintf( stderr,
819                                                         "%s: line %d: sockurl pattern already specified.\n",
820                                                         fname, lineno );
821                                                 acl_usage();
822                                         }
823
824                                         b->a_sockurl_style = sty;
825                                         if (sty == ACL_STYLE_REGEX) {
826                                                 acl_regex_normalized_dn( right, &bv );
827                                                 if ( !ber_bvccmp( &bv, '*' ) ) {
828                                                         regtest(fname, lineno, bv.bv_val);
829                                                 }
830                                                 b->a_sockurl_pat = bv;
831                                         } else {
832                                                 ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
833                                         }
834                                         continue;
835                                 }
836
837                                 if ( strcasecmp( left, "set" ) == 0 ) {
838                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
839                                                 fprintf( stderr,
840                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
841                                                     fname, lineno, style );
842                                                 acl_usage();
843                                         }
844
845                                         if( b->a_set_pat.bv_len != 0 ) {
846                                                 fprintf( stderr,
847                                                         "%s: line %d: set attribute already specified.\n",
848                                                         fname, lineno );
849                                                 acl_usage();
850                                         }
851
852                                         if ( right == NULL || *right == '\0' ) {
853                                                 fprintf( stderr,
854                                                         "%s: line %d: no set is defined\n",
855                                                         fname, lineno );
856                                                 acl_usage();
857                                         }
858
859                                         b->a_set_style = sty;
860                                         ber_str2bv( right, 0, 1, &b->a_set_pat );
861
862                                         continue;
863                                 }
864
865 #ifdef SLAPD_ACI_ENABLED
866                                 if ( strcasecmp( left, "aci" ) == 0 ) {
867                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
868                                                 fprintf( stderr,
869                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
870                                                     fname, lineno, style );
871                                                 acl_usage();
872                                         }
873
874                                         if( b->a_aci_at != NULL ) {
875                                                 fprintf( stderr,
876                                                         "%s: line %d: aci attribute already specified.\n",
877                                                         fname, lineno );
878                                                 acl_usage();
879                                         }
880
881                                         if ( right != NULL && *right != '\0' ) {
882                                                 rc = slap_str2ad( right, &b->a_aci_at, &text );
883
884                                                 if( rc != LDAP_SUCCESS ) {
885                                                         fprintf( stderr,
886                                                                 "%s: line %d: aci \"%s\": %s\n",
887                                                                 fname, lineno, right, text );
888                                                         acl_usage();
889                                                 }
890
891                                         } else {
892                                                 b->a_aci_at = slap_schema.si_ad_aci;
893                                         }
894
895                                         if( !is_at_syntax( b->a_aci_at->ad_type,
896                                                 SLAPD_ACI_SYNTAX) )
897                                         {
898                                                 fprintf( stderr,
899                                                         "%s: line %d: aci \"%s\": inappropriate syntax: %s\n",
900                                                         fname, lineno, right,
901                                                         b->a_aci_at->ad_type->sat_syntax_oid );
902                                                 acl_usage();
903                                         }
904
905                                         continue;
906                                 }
907 #endif /* SLAPD_ACI_ENABLED */
908
909                                 if ( strcasecmp( left, "ssf" ) == 0 ) {
910                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
911                                                 fprintf( stderr,
912                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
913                                                     fname, lineno, style );
914                                                 acl_usage();
915                                         }
916
917                                         if( b->a_authz.sai_ssf ) {
918                                                 fprintf( stderr,
919                                                         "%s: line %d: ssf attribute already specified.\n",
920                                                         fname, lineno );
921                                                 acl_usage();
922                                         }
923
924                                         if ( right == NULL || *right == '\0' ) {
925                                                 fprintf( stderr,
926                                                         "%s: line %d: no ssf is defined\n",
927                                                         fname, lineno );
928                                                 acl_usage();
929                                         }
930
931                                         b->a_authz.sai_ssf = atoi( right );
932
933                                         if( !b->a_authz.sai_ssf ) {
934                                                 fprintf( stderr,
935                                                         "%s: line %d: invalid ssf value (%s)\n",
936                                                         fname, lineno, right );
937                                                 acl_usage();
938                                         }
939                                         continue;
940                                 }
941
942                                 if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
943                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
944                                                 fprintf( stderr,
945                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
946                                                     fname, lineno, style );
947                                                 acl_usage();
948                                         }
949
950                                         if( b->a_authz.sai_transport_ssf ) {
951                                                 fprintf( stderr,
952                                                         "%s: line %d: transport_ssf attribute already specified.\n",
953                                                         fname, lineno );
954                                                 acl_usage();
955                                         }
956
957                                         if ( right == NULL || *right == '\0' ) {
958                                                 fprintf( stderr,
959                                                         "%s: line %d: no transport_ssf is defined\n",
960                                                         fname, lineno );
961                                                 acl_usage();
962                                         }
963
964                                         b->a_authz.sai_transport_ssf = atoi( right );
965
966                                         if( !b->a_authz.sai_transport_ssf ) {
967                                                 fprintf( stderr,
968                                                         "%s: line %d: invalid transport_ssf value (%s)\n",
969                                                         fname, lineno, right );
970                                                 acl_usage();
971                                         }
972                                         continue;
973                                 }
974
975                                 if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
976                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
977                                                 fprintf( stderr,
978                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
979                                                     fname, lineno, style );
980                                                 acl_usage();
981                                         }
982
983                                         if( b->a_authz.sai_tls_ssf ) {
984                                                 fprintf( stderr,
985                                                         "%s: line %d: tls_ssf attribute already specified.\n",
986                                                         fname, lineno );
987                                                 acl_usage();
988                                         }
989
990                                         if ( right == NULL || *right == '\0' ) {
991                                                 fprintf( stderr,
992                                                         "%s: line %d: no tls_ssf is defined\n",
993                                                         fname, lineno );
994                                                 acl_usage();
995                                         }
996
997                                         b->a_authz.sai_tls_ssf = atoi( right );
998
999                                         if( !b->a_authz.sai_tls_ssf ) {
1000                                                 fprintf( stderr,
1001                                                         "%s: line %d: invalid tls_ssf value (%s)\n",
1002                                                         fname, lineno, right );
1003                                                 acl_usage();
1004                                         }
1005                                         continue;
1006                                 }
1007
1008                                 if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
1009                                         if (sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE) {
1010                                                 fprintf( stderr,
1011                                                         "%s: line %d: inappropriate style \"%s\" in by clause\n",
1012                                                     fname, lineno, style );
1013                                                 acl_usage();
1014                                         }
1015
1016                                         if( b->a_authz.sai_sasl_ssf ) {
1017                                                 fprintf( stderr,
1018                                                         "%s: line %d: sasl_ssf attribute already specified.\n",
1019                                                         fname, lineno );
1020                                                 acl_usage();
1021                                         }
1022
1023                                         if ( right == NULL || *right == '\0' ) {
1024                                                 fprintf( stderr,
1025                                                         "%s: line %d: no sasl_ssf is defined\n",
1026                                                         fname, lineno );
1027                                                 acl_usage();
1028                                         }
1029
1030                                         b->a_authz.sai_sasl_ssf = atoi( right );
1031
1032                                         if( !b->a_authz.sai_sasl_ssf ) {
1033                                                 fprintf( stderr,
1034                                                         "%s: line %d: invalid sasl_ssf value (%s)\n",
1035                                                         fname, lineno, right );
1036                                                 acl_usage();
1037                                         }
1038                                         continue;
1039                                 }
1040
1041                                 if( right != NULL ) {
1042                                         /* unsplit */
1043                                         right[-1] = '=';
1044                                 }
1045                                 break;
1046                         }
1047
1048                         if( i == argc || ( strcasecmp( left, "stop" ) == 0 )) { 
1049                                 /* out of arguments or plain stop */
1050
1051                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1052                                 b->a_type = ACL_STOP;
1053
1054                                 access_append( &a->acl_access, b );
1055                                 continue;
1056                         }
1057
1058                         if( strcasecmp( left, "continue" ) == 0 ) {
1059                                 /* plain continue */
1060
1061                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1062                                 b->a_type = ACL_CONTINUE;
1063
1064                                 access_append( &a->acl_access, b );
1065                                 continue;
1066                         }
1067
1068                         if( strcasecmp( left, "break" ) == 0 ) {
1069                                 /* plain continue */
1070
1071                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1072                                 b->a_type = ACL_BREAK;
1073
1074                                 access_append( &a->acl_access, b );
1075                                 continue;
1076                         }
1077
1078                         if ( strcasecmp( left, "by" ) == 0 ) {
1079                                 /* we've gone too far */
1080                                 --i;
1081                                 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1082                                 b->a_type = ACL_STOP;
1083
1084                                 access_append( &a->acl_access, b );
1085                                 continue;
1086                         }
1087
1088                         /* get <access> */
1089                         if( strncasecmp( left, "self", 4 ) == 0 ) {
1090                                 b->a_dn_self = 1;
1091                                 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( &left[4] ) );
1092
1093                         } else {
1094                                 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( left ) );
1095                         }
1096
1097                         if( ACL_IS_INVALID( b->a_access_mask ) ) {
1098                                 fprintf( stderr,
1099                                         "%s: line %d: expecting <access> got \"%s\"\n",
1100                                         fname, lineno, left );
1101                                 acl_usage();
1102                         }
1103
1104                         b->a_type = ACL_STOP;
1105
1106                         if( ++i == argc ) {
1107                                 /* out of arguments or plain stop */
1108                                 access_append( &a->acl_access, b );
1109                                 continue;
1110                         }
1111
1112                         if( strcasecmp( argv[i], "continue" ) == 0 ) {
1113                                 /* plain continue */
1114                                 b->a_type = ACL_CONTINUE;
1115
1116                         } else if( strcasecmp( argv[i], "break" ) == 0 ) {
1117                                 /* plain continue */
1118                                 b->a_type = ACL_BREAK;
1119
1120                         } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
1121                                 /* gone to far */
1122                                 i--;
1123                         }
1124
1125                         access_append( &a->acl_access, b );
1126
1127                 } else {
1128                         fprintf( stderr,
1129                     "%s: line %d: expecting \"to\" or \"by\" got \"%s\"\n",
1130                             fname, lineno, argv[i] );
1131                         acl_usage();
1132                 }
1133         }
1134
1135         /* if we have no real access clause, complain and do nothing */
1136         if ( a == NULL ) {
1137                         fprintf( stderr,
1138                                 "%s: line %d: warning: no access clause(s) specified in access line\n",
1139                             fname, lineno );
1140
1141         } else {
1142 #ifdef LDAP_DEBUG
1143                 if (ldap_debug & LDAP_DEBUG_ACL)
1144                         print_acl(be, a);
1145 #endif
1146         
1147                 if ( a->acl_access == NULL ) {
1148                         fprintf( stderr,
1149                         "%s: line %d: warning: no by clause(s) specified in access line\n",
1150                             fname, lineno );
1151                 }
1152
1153                 if ( be != NULL ) {
1154                         acl_append( &be->be_acl, a );
1155                 } else {
1156                         acl_append( &global_acl, a );
1157                 }
1158         }
1159 }
1160
1161 char *
1162 accessmask2str( slap_mask_t mask, char *buf )
1163 {
1164         int none=1;
1165         char *ptr = buf;
1166
1167         assert( buf != NULL );
1168
1169         if ( ACL_IS_INVALID( mask ) ) {
1170                 return "invalid";
1171         }
1172
1173         buf[0] = '\0';
1174
1175         if ( ACL_IS_LEVEL( mask ) ) {
1176                 if ( ACL_LVL_IS_NONE(mask) ) {
1177                         ptr = lutil_strcopy( ptr, "none" );
1178
1179                 } else if ( ACL_LVL_IS_AUTH(mask) ) {
1180                         ptr = lutil_strcopy( ptr, "auth" );
1181
1182                 } else if ( ACL_LVL_IS_COMPARE(mask) ) {
1183                         ptr = lutil_strcopy( ptr, "compare" );
1184
1185                 } else if ( ACL_LVL_IS_SEARCH(mask) ) {
1186                         ptr = lutil_strcopy( ptr, "search" );
1187
1188                 } else if ( ACL_LVL_IS_READ(mask) ) {
1189                         ptr = lutil_strcopy( ptr, "read" );
1190
1191                 } else if ( ACL_LVL_IS_WRITE(mask) ) {
1192                         ptr = lutil_strcopy( ptr, "write" );
1193                 } else {
1194                         ptr = lutil_strcopy( ptr, "unknown" );
1195                 }
1196                 
1197                 *ptr++ = '(';
1198         }
1199
1200         if( ACL_IS_ADDITIVE( mask ) ) {
1201                 *ptr++ = '+';
1202
1203         } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
1204                 *ptr++ = '-';
1205
1206         } else {
1207                 *ptr++ = '=';
1208         }
1209
1210         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
1211                 none = 0;
1212                 *ptr++ = 'w';
1213         } 
1214
1215         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
1216                 none = 0;
1217                 *ptr++ = 'r';
1218         } 
1219
1220         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
1221                 none = 0;
1222                 *ptr++ = 's';
1223         } 
1224
1225         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
1226                 none = 0;
1227                 *ptr++ = 'c';
1228         } 
1229
1230         if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
1231                 none = 0;
1232                 *ptr++ = 'x';
1233         } 
1234
1235         if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
1236                 none = 0;
1237                 *ptr++ = 'n';
1238         } 
1239
1240         if ( none ) {
1241                 *ptr++ = '0';
1242         }
1243
1244         if ( ACL_IS_LEVEL( mask ) ) {
1245                 *ptr++ = ')';
1246         }
1247
1248         *ptr = '\0';
1249
1250         return buf;
1251 }
1252
1253 slap_mask_t
1254 str2accessmask( const char *str )
1255 {
1256         slap_mask_t     mask;
1257
1258         if( !ASCII_ALPHA(str[0]) ) {
1259                 int i;
1260
1261                 if ( str[0] == '=' ) {
1262                         ACL_INIT(mask);
1263
1264                 } else if( str[0] == '+' ) {
1265                         ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
1266
1267                 } else if( str[0] == '-' ) {
1268                         ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
1269
1270                 } else {
1271                         ACL_INVALIDATE(mask);
1272                         return mask;
1273                 }
1274
1275                 for( i=1; str[i] != '\0'; i++ ) {
1276                         if( TOLOWER((unsigned char) str[i]) == 'w' ) {
1277                                 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
1278
1279                         } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
1280                                 ACL_PRIV_SET(mask, ACL_PRIV_READ);
1281
1282                         } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
1283                                 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
1284
1285                         } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
1286                                 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
1287
1288                         } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
1289                                 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
1290
1291                         } else if( str[i] != '0' ) {
1292                                 ACL_INVALIDATE(mask);
1293                                 return mask;
1294                         }
1295                 }
1296
1297                 return mask;
1298         }
1299
1300         if ( strcasecmp( str, "none" ) == 0 ) {
1301                 ACL_LVL_ASSIGN_NONE(mask);
1302
1303         } else if ( strcasecmp( str, "auth" ) == 0 ) {
1304                 ACL_LVL_ASSIGN_AUTH(mask);
1305
1306         } else if ( strcasecmp( str, "compare" ) == 0 ) {
1307                 ACL_LVL_ASSIGN_COMPARE(mask);
1308
1309         } else if ( strcasecmp( str, "search" ) == 0 ) {
1310                 ACL_LVL_ASSIGN_SEARCH(mask);
1311
1312         } else if ( strcasecmp( str, "read" ) == 0 ) {
1313                 ACL_LVL_ASSIGN_READ(mask);
1314
1315         } else if ( strcasecmp( str, "write" ) == 0 ) {
1316                 ACL_LVL_ASSIGN_WRITE(mask);
1317
1318         } else {
1319                 ACL_INVALIDATE( mask );
1320         }
1321
1322         return mask;
1323 }
1324
1325 static void
1326 acl_usage( void )
1327 {
1328         fprintf( stderr, "%s%s\n",
1329                 "<access clause> ::= access to <what> "
1330                                 "[ by <who> <access> [ <control> ] ]+ \n"
1331                 "<what> ::= * | [dn[.<dnstyle>]=<DN>] [filter=<filter>] [attrs=<attrlist>]\n"
1332                 "<attrlist> ::= <attr> [val[.<style>]=<value>] | <attr> , <attrlist>\n"
1333                 "<attr> ::= <attrname> | entry | children\n"
1334                 "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
1335                         "\t[dnattr=<attrname>]\n"
1336                         "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
1337                         "\t[peername[.<style>]=<peer>] [sockname[.<style>]=<name>]\n",
1338                         "\t[domain[.<style>]=<domain>] [sockurl[.<style>]=<url>]\n"
1339 #ifdef SLAPD_ACI_ENABLED
1340                         "\t[aci=<attrname>]\n"
1341 #endif
1342                         "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
1343                 "<dnstyle> ::= base | exact | one | subtree | children | regex\n"
1344                 "<style> ::= regex | base | exact\n"
1345                 "<access> ::= [self]{<level>|<priv>}\n"
1346                 "<level> ::= none | auth | compare | search | read | write\n"
1347                 "<priv> ::= {=|+|-}{w|r|s|c|x}+\n"
1348                 "<control> ::= [ stop | continue | break ]\n"
1349         );
1350         exit( EXIT_FAILURE );
1351 }
1352
1353 /*
1354  * Set pattern to a "normalized" DN from src.
1355  * At present it simply eats the (optional) space after 
1356  * a RDN separator (,)
1357  * Eventually will evolve in a more complete normalization
1358  */
1359 static void
1360 acl_regex_normalized_dn(
1361         const char *src,
1362         struct berval *pattern
1363 )
1364 {
1365         char *str, *p;
1366         ber_len_t len;
1367
1368         str = ch_strdup( src );
1369         len = strlen( src );
1370
1371         for ( p = str; p && p[ 0 ]; p++ ) {
1372                 /* escape */
1373                 if ( p[ 0 ] == '\\' && p[ 1 ] ) {
1374                         /* 
1375                          * if escaping a hex pair we should
1376                          * increment p twice; however, in that 
1377                          * case the second hex number does 
1378                          * no harm
1379                          */
1380                         p++;
1381                 }
1382
1383                 if ( p[ 0 ] == ',' ) {
1384                         if ( p[ 1 ] == ' ' ) {
1385                                 char *q;
1386                         
1387                                 /*
1388                                  * too much space should be 
1389                                  * an error if we are pedantic
1390                                  */
1391                                 for ( q = &p[ 2 ]; q[ 0 ] == ' '; q++ ) {
1392                                         /* DO NOTHING */ ;
1393                                 }
1394                                 AC_MEMCPY( p+1, q, len-(q-str)+1);
1395                         }
1396                 }
1397         }
1398         pattern->bv_val = str;
1399         pattern->bv_len = p-str;
1400
1401         return;
1402 }
1403
1404 static void
1405 split(
1406     char        *line,
1407     int         splitchar,
1408     char        **left,
1409     char        **right
1410 )
1411 {
1412         *left = line;
1413         if ( (*right = strchr( line, splitchar )) != NULL ) {
1414                 *((*right)++) = '\0';
1415         }
1416 }
1417
1418 static void
1419 access_append( Access **l, Access *a )
1420 {
1421         for ( ; *l != NULL; l = &(*l)->a_next )
1422                 ;       /* NULL */
1423
1424         *l = a;
1425 }
1426
1427 void
1428 acl_append( AccessControl **l, AccessControl *a )
1429 {
1430         for ( ; *l != NULL; l = &(*l)->acl_next )
1431                 ;       /* NULL */
1432
1433         *l = a;
1434 }
1435
1436 static void
1437 access_free( Access *a )
1438 {
1439         if ( a->a_dn_pat.bv_val )
1440                 free ( a->a_dn_pat.bv_val );
1441         if ( a->a_peername_pat.bv_val )
1442                 free ( a->a_peername_pat.bv_val );
1443         if ( a->a_sockname_pat.bv_val )
1444                 free ( a->a_sockname_pat.bv_val );
1445         if ( a->a_domain_pat.bv_val )
1446                 free ( a->a_domain_pat.bv_val );
1447         if ( a->a_sockurl_pat.bv_val )
1448                 free ( a->a_sockurl_pat.bv_val );
1449         if ( a->a_set_pat.bv_len )
1450                 free ( a->a_set_pat.bv_val );
1451         if ( a->a_group_pat.bv_len )
1452                 free ( a->a_group_pat.bv_val );
1453         free( a );
1454 }
1455
1456 void
1457 acl_free( AccessControl *a )
1458 {
1459         Access *n;
1460         AttributeName *an;
1461
1462         if ( a->acl_filter )
1463                 filter_free( a->acl_filter );
1464         if ( a->acl_dn_pat.bv_len )
1465                 free ( a->acl_dn_pat.bv_val );
1466         if ( a->acl_attrs ) {
1467                 for ( an = a->acl_attrs; an->an_name.bv_val; an++ ) {
1468                         free( an->an_name.bv_val );
1469                 }
1470                 free( a->acl_attrs );
1471         }
1472         for (; a->acl_access; a->acl_access = n) {
1473                 n = a->acl_access->a_next;
1474                 access_free( a->acl_access );
1475         }
1476         free( a );
1477 }
1478
1479 /* Because backend_startup uses acl_append to tack on the global_acl to
1480  * the end of each backend's acl, we cannot just take one argument and
1481  * merrily free our way to the end of the list. backend_destroy calls us
1482  * with the be_acl in arg1, and global_acl in arg2 to give us a stopping
1483  * point. config_destroy calls us with global_acl in arg1 and NULL in
1484  * arg2, so we then proceed to polish off the global_acl.
1485  */
1486 void
1487 acl_destroy( AccessControl *a, AccessControl *end )
1488 {
1489         AccessControl *n;
1490
1491         for (; a && a!= end; a=n) {
1492                 n = a->acl_next;
1493                 acl_free( a );
1494         }
1495 }
1496
1497 char *
1498 access2str( slap_access_t access )
1499 {
1500         if ( access == ACL_NONE ) {
1501                 return "none";
1502
1503         } else if ( access == ACL_AUTH ) {
1504                 return "auth";
1505
1506         } else if ( access == ACL_COMPARE ) {
1507                 return "compare";
1508
1509         } else if ( access == ACL_SEARCH ) {
1510                 return "search";
1511
1512         } else if ( access == ACL_READ ) {
1513                 return "read";
1514
1515         } else if ( access == ACL_WRITE ) {
1516                 return "write";
1517         }
1518
1519         return "unknown";
1520 }
1521
1522 slap_access_t
1523 str2access( const char *str )
1524 {
1525         if ( strcasecmp( str, "none" ) == 0 ) {
1526                 return ACL_NONE;
1527
1528         } else if ( strcasecmp( str, "auth" ) == 0 ) {
1529                 return ACL_AUTH;
1530
1531         } else if ( strcasecmp( str, "compare" ) == 0 ) {
1532                 return ACL_COMPARE;
1533
1534         } else if ( strcasecmp( str, "search" ) == 0 ) {
1535                 return ACL_SEARCH;
1536
1537         } else if ( strcasecmp( str, "read" ) == 0 ) {
1538                 return ACL_READ;
1539
1540         } else if ( strcasecmp( str, "write" ) == 0 ) {
1541                 return ACL_WRITE;
1542         }
1543
1544         return( ACL_INVALID_ACCESS );
1545 }
1546
1547 #ifdef LDAP_DEBUG
1548
1549 static char *style_strings[5] = { "regex",
1550         "base", "one", "subtree", "children" };
1551
1552 static void
1553 print_access( Access *b )
1554 {
1555         char maskbuf[ACCESSMASK_MAXLEN];
1556
1557         fprintf( stderr, "\tby" );
1558
1559         if ( b->a_dn_pat.bv_len != 0 ) {
1560                 if( strcmp(b->a_dn_pat.bv_val, "*") == 0
1561                         || strcmp(b->a_dn_pat.bv_val, "users") == 0 
1562                         || strcmp(b->a_dn_pat.bv_val, "anonymous") == 0 
1563                         || strcmp(b->a_dn_pat.bv_val, "self") == 0 )
1564                 {
1565                         fprintf( stderr, " %s", b->a_dn_pat.bv_val );
1566
1567                 } else {
1568                         fprintf( stderr, " dn.%s=\"%s\"",
1569                                 style_strings[b->a_dn_style], b->a_dn_pat.bv_val );
1570                 }
1571         }
1572
1573         if ( b->a_dn_at != NULL ) {
1574                 fprintf( stderr, " dnattr=%s", b->a_dn_at->ad_cname.bv_val );
1575         }
1576
1577         if ( b->a_group_pat.bv_len ) {
1578                 fprintf( stderr, " group/%s/%s.%s=\"%s\"",
1579                         b->a_group_oc ? b->a_group_oc->soc_cname.bv_val : "groupOfNames",
1580                         b->a_group_at ? b->a_group_at->ad_cname.bv_val : "member",
1581                         style_strings[b->a_group_style],
1582                         b->a_group_pat.bv_val );
1583     }
1584
1585         if ( b->a_peername_pat.bv_len != 0 ) {
1586                 fprintf( stderr, " peername=\"%s\"", b->a_peername_pat.bv_val );
1587         }
1588
1589         if ( b->a_sockname_pat.bv_len != 0 ) {
1590                 fprintf( stderr, " sockname=\"%s\"", b->a_sockname_pat.bv_val );
1591         }
1592
1593         if ( b->a_domain_pat.bv_len != 0 ) {
1594                 fprintf( stderr, " domain=%s", b->a_domain_pat.bv_val );
1595         }
1596
1597         if ( b->a_sockurl_pat.bv_len != 0 ) {
1598                 fprintf( stderr, " sockurl=\"%s\"", b->a_sockurl_pat.bv_val );
1599         }
1600
1601 #ifdef SLAPD_ACI_ENABLED
1602         if ( b->a_aci_at != NULL ) {
1603                 fprintf( stderr, " aci=%s", b->a_aci_at->ad_cname.bv_val );
1604         }
1605 #endif
1606
1607         /* Security Strength Factors */
1608         if ( b->a_authz.sai_ssf ) {
1609                 fprintf( stderr, " ssf=%u",
1610                         b->a_authz.sai_ssf );
1611         }
1612         if ( b->a_authz.sai_transport_ssf ) {
1613                 fprintf( stderr, " transport_ssf=%u",
1614                         b->a_authz.sai_transport_ssf );
1615         }
1616         if ( b->a_authz.sai_tls_ssf ) {
1617                 fprintf( stderr, " tls_ssf=%u",
1618                         b->a_authz.sai_tls_ssf );
1619         }
1620         if ( b->a_authz.sai_sasl_ssf ) {
1621                 fprintf( stderr, " sasl_ssf=%u",
1622                         b->a_authz.sai_sasl_ssf );
1623         }
1624
1625         fprintf( stderr, " %s%s",
1626                 b->a_dn_self ? "self" : "",
1627                 accessmask2str( b->a_access_mask, maskbuf ) );
1628
1629         if( b->a_type == ACL_BREAK ) {
1630                 fprintf( stderr, " break" );
1631
1632         } else if( b->a_type == ACL_CONTINUE ) {
1633                 fprintf( stderr, " continue" );
1634
1635         } else if( b->a_type != ACL_STOP ) {
1636                 fprintf( stderr, " unknown-control" );
1637         }
1638
1639         fprintf( stderr, "\n" );
1640 }
1641
1642
1643 static void
1644 print_acl( Backend *be, AccessControl *a )
1645 {
1646         int             to = 0;
1647         Access  *b;
1648
1649         fprintf( stderr, "%s ACL: access to",
1650                 be == NULL ? "Global" : "Backend" );
1651
1652         if ( a->acl_dn_pat.bv_len != 0 ) {
1653                 to++;
1654                 fprintf( stderr, " dn.%s=\"%s\"\n",
1655                         style_strings[a->acl_dn_style], a->acl_dn_pat.bv_val );
1656         }
1657
1658         if ( a->acl_filter != NULL ) {
1659                 struct berval bv = { 0, NULL };
1660                 to++;
1661                 filter2bv( a->acl_filter, &bv );
1662                 fprintf( stderr, " filter=%s\n", bv.bv_val );
1663                 ch_free( bv.bv_val );
1664         }
1665
1666         if ( a->acl_attrs != NULL ) {
1667                 int     first = 1;
1668                 AttributeName *an;
1669                 to++;
1670
1671                 fprintf( stderr, " attrs=" );
1672                 for ( an = a->acl_attrs; an && an->an_name.bv_val; an++ ) {
1673                         if ( ! first ) {
1674                                 fprintf( stderr, "," );
1675                         }
1676                         fputs( an->an_name.bv_val, stderr );
1677                         first = 0;
1678                 }
1679                 fprintf(  stderr, "\n" );
1680         }
1681
1682         if ( a->acl_attrval.bv_len != 0 ) {
1683                 to++;
1684                 fprintf( stderr, " val.%s=\"%s\"\n",
1685                         style_strings[a->acl_attrval_style], a->acl_attrval.bv_val );
1686
1687         }
1688
1689         if( !to ) {
1690                 fprintf( stderr, " *\n" );
1691         }
1692
1693         for ( b = a->acl_access; b != NULL; b = b->a_next ) {
1694                 print_access( b );
1695         }
1696
1697         fprintf( stderr, "\n" );
1698 }
1699
1700 #endif /* LDAP_DEBUG */