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