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