1 /* $NetBSD: aclparse.c,v 1.3 2021/08/14 16:14:58 christos Exp $ */
2
3 /* aclparse.c - routines to parse and check acl's */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1998-2021 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
19 * All rights reserved.
20 *
21 * Redistribution and use in source and binary forms are permitted
22 * provided that this notice is preserved and that due credit is given
23 * to the University of Michigan at Ann Arbor. The name of the University
24 * may not be used to endorse or promote products derived from this
25 * software without specific prior written permission. This software
26 * is provided ``as is'' without express or implied warranty.
27 */
28
29 #include <sys/cdefs.h>
30 __RCSID("$NetBSD: aclparse.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
31
32 #include "portable.h"
33
34 #include <stdio.h>
35
36 #include <ac/ctype.h>
37 #include <ac/regex.h>
38 #include <ac/socket.h>
39 #include <ac/string.h>
40 #include <ac/unistd.h>
41
42 #include "slap.h"
43 #include "lber_pvt.h"
44 #include "lutil.h"
45
46 static const char style_base[] = "base";
47 const char *style_strings[] = {
48 "regex",
49 "expand",
50 "exact",
51 "one",
52 "subtree",
53 "children",
54 "level",
55 "attrof",
56 "anonymous",
57 "users",
58 "self",
59 "ip",
60 "ipv6",
61 "path",
62 NULL
63 };
64
65 #define ACLBUF_CHUNKSIZE 8192
66 static struct berval aclbuf;
67
68 static void split(char *line, int splitchar, char **left, char **right);
69 static void access_append(Access **l, Access *a);
70 static void access_free( Access *a );
71 static int acl_usage(void);
72
73 static void acl_regex_normalized_dn(const char *src, struct berval *pat);
74
75 #ifdef LDAP_DEBUG
76 static void print_acl(Backend *be, AccessControl *a);
77 #endif
78
79 static int check_scope( BackendDB *be, AccessControl *a );
80
81 #ifdef SLAP_DYNACL
82 static int
slap_dynacl_config(const char * fname,int lineno,Access * b,const char * name,const char * opts,slap_style_t sty,const char * right)83 slap_dynacl_config(
84 const char *fname,
85 int lineno,
86 Access *b,
87 const char *name,
88 const char *opts,
89 slap_style_t sty,
90 const char *right )
91 {
92 slap_dynacl_t *da, *tmp;
93 int rc = 0;
94
95 for ( da = b->a_dynacl; da; da = da->da_next ) {
96 if ( strcasecmp( da->da_name, name ) == 0 ) {
97 Debug( LDAP_DEBUG_ANY,
98 "%s: line %d: dynacl \"%s\" already specified.\n",
99 fname, lineno, name );
100 return acl_usage();
101 }
102 }
103
104 da = slap_dynacl_get( name );
105 if ( da == NULL ) {
106 return -1;
107 }
108
109 tmp = ch_malloc( sizeof( slap_dynacl_t ) );
110 *tmp = *da;
111
112 if ( tmp->da_parse ) {
113 rc = ( *tmp->da_parse )( fname, lineno, opts, sty, right, &tmp->da_private );
114 if ( rc ) {
115 ch_free( tmp );
116 return rc;
117 }
118 }
119
120 tmp->da_next = b->a_dynacl;
121 b->a_dynacl = tmp;
122
123 return 0;
124 }
125 #endif /* SLAP_DYNACL */
126
127 static void
regtest(const char * fname,int lineno,char * pat)128 regtest(const char *fname, int lineno, char *pat) {
129 int e;
130 regex_t re;
131
132 char buf[ SLAP_TEXT_BUFLEN ];
133 unsigned size;
134
135 char *sp;
136 char *dp;
137 int flag;
138
139 sp = pat;
140 dp = buf;
141 size = 0;
142 buf[0] = '\0';
143
144 for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
145 if (flag) {
146 if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
147 *dp++ = *sp;
148 size++;
149 }
150 flag = 0;
151
152 } else {
153 if (*sp == '$') {
154 flag = 1;
155 } else {
156 *dp++ = *sp;
157 size++;
158 }
159 }
160 }
161
162 *dp = '\0';
163 if ( size >= (sizeof(buf) - 1) ) {
164 Debug( LDAP_DEBUG_ANY,
165 "%s: line %d: regular expression \"%s\" too large\n",
166 fname, lineno, pat );
167 (void)acl_usage();
168 exit( EXIT_FAILURE );
169 }
170
171 if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
172 char error[ SLAP_TEXT_BUFLEN ];
173
174 regerror(e, &re, error, sizeof(error));
175
176 Debug(LDAP_DEBUG_ANY,
177 "%s: line %d: regular expression \"%s\" bad because of %s\n",
178 fname, lineno, pat, error );
179 acl_usage();
180 exit( EXIT_FAILURE );
181 }
182 regfree(&re);
183 }
184
185 /*
186 * Experimental
187 *
188 * Check if the pattern of an ACL, if any, matches the scope
189 * of the backend it is defined within.
190 */
191 #define ACL_SCOPE_UNKNOWN (-2)
192 #define ACL_SCOPE_ERR (-1)
193 #define ACL_SCOPE_OK (0)
194 #define ACL_SCOPE_PARTIAL (1)
195 #define ACL_SCOPE_WARN (2)
196
197 static int
check_scope(BackendDB * be,AccessControl * a)198 check_scope( BackendDB *be, AccessControl *a )
199 {
200 ber_len_t patlen;
201 struct berval dn;
202
203 dn = be->be_nsuffix[0];
204
205 if ( BER_BVISEMPTY( &dn ) ) {
206 return ACL_SCOPE_OK;
207 }
208
209 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
210 a->acl_dn_style != ACL_STYLE_REGEX )
211 {
212 slap_style_t style = a->acl_dn_style;
213
214 if ( style == ACL_STYLE_REGEX ) {
215 char dnbuf[SLAP_LDAPDN_MAXLEN + 2];
216 char rebuf[SLAP_LDAPDN_MAXLEN + 1];
217 ber_len_t rebuflen;
218 regex_t re;
219 int rc;
220
221 /* add trailing '$' to database suffix to form
222 * a simple trial regex pattern "<suffix>$" */
223 AC_MEMCPY( dnbuf, be->be_nsuffix[0].bv_val,
224 be->be_nsuffix[0].bv_len );
225 dnbuf[be->be_nsuffix[0].bv_len] = '$';
226 dnbuf[be->be_nsuffix[0].bv_len + 1] = '\0';
227
228 if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) {
229 return ACL_SCOPE_WARN;
230 }
231
232 /* remove trailing ')$', if any, from original
233 * regex pattern */
234 rebuflen = a->acl_dn_pat.bv_len;
235 AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val, rebuflen + 1 );
236 if ( rebuf[rebuflen - 1] == '$' ) {
237 rebuf[--rebuflen] = '\0';
238 }
239 while ( rebuflen > be->be_nsuffix[0].bv_len && rebuf[rebuflen - 1] == ')' ) {
240 rebuf[--rebuflen] = '\0';
241 }
242 if ( rebuflen == be->be_nsuffix[0].bv_len ) {
243 rc = ACL_SCOPE_WARN;
244 goto regex_done;
245 }
246
247 /* not a clear indication of scoping error, though */
248 rc = regexec( &re, rebuf, 0, NULL, 0 )
249 ? ACL_SCOPE_WARN : ACL_SCOPE_OK;
250
251 regex_done:;
252 regfree( &re );
253 return rc;
254 }
255
256 patlen = a->acl_dn_pat.bv_len;
257 /* If backend suffix is longer than pattern,
258 * it is a potential mismatch (in the sense
259 * that a superior naming context could
260 * match */
261 if ( dn.bv_len > patlen ) {
262 /* base is blatantly wrong */
263 if ( style == ACL_STYLE_BASE ) return ACL_SCOPE_ERR;
264
265 /* a style of one can be wrong if there is
266 * more than one level between the suffix
267 * and the pattern */
268 if ( style == ACL_STYLE_ONE ) {
269 ber_len_t rdnlen = 0;
270 int sep = 0;
271
272 if ( patlen > 0 ) {
273 if ( !DN_SEPARATOR( dn.bv_val[dn.bv_len - patlen - 1] )) {
274 return ACL_SCOPE_ERR;
275 }
276 sep = 1;
277 }
278
279 rdnlen = dn_rdnlen( NULL, &dn );
280 if ( rdnlen != dn.bv_len - patlen - sep )
281 return ACL_SCOPE_ERR;
282 }
283
284 /* if the trailing part doesn't match,
285 * then it's an error */
286 if ( strcmp( a->acl_dn_pat.bv_val,
287 &dn.bv_val[dn.bv_len - patlen] ) != 0 )
288 {
289 return ACL_SCOPE_ERR;
290 }
291
292 return ACL_SCOPE_PARTIAL;
293 }
294
295 switch ( style ) {
296 case ACL_STYLE_BASE:
297 case ACL_STYLE_ONE:
298 case ACL_STYLE_CHILDREN:
299 case ACL_STYLE_SUBTREE:
300 break;
301
302 default:
303 assert( 0 );
304 break;
305 }
306
307 if ( dn.bv_len < patlen &&
308 !DN_SEPARATOR( a->acl_dn_pat.bv_val[patlen - dn.bv_len - 1] ))
309 {
310 return ACL_SCOPE_ERR;
311 }
312
313 if ( strcmp( &a->acl_dn_pat.bv_val[patlen - dn.bv_len], dn.bv_val )
314 != 0 )
315 {
316 return ACL_SCOPE_ERR;
317 }
318
319 return ACL_SCOPE_OK;
320 }
321
322 return ACL_SCOPE_UNKNOWN;
323 }
324
325 int
parse_acl(Backend * be,const char * fname,int lineno,int argc,char ** argv,int pos)326 parse_acl(
327 Backend *be,
328 const char *fname,
329 int lineno,
330 int argc,
331 char **argv,
332 int pos )
333 {
334 int i;
335 char *left, *right, *style;
336 struct berval bv;
337 AccessControl *a = NULL;
338 Access *b = NULL;
339 int rc;
340 const char *text;
341
342 for ( i = 1; i < argc; i++ ) {
343 /* to clause - select which entries are protected */
344 if ( strcasecmp( argv[i], "to" ) == 0 ) {
345 if ( a != NULL ) {
346 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
347 "only one to clause allowed in access line\n",
348 fname, lineno );
349 goto fail;
350 }
351 a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
352 a->acl_attrval_style = ACL_STYLE_NONE;
353 for ( ++i; i < argc; i++ ) {
354 if ( strcasecmp( argv[i], "by" ) == 0 ) {
355 i--;
356 break;
357 }
358
359 if ( strcasecmp( argv[i], "*" ) == 0 ) {
360 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
361 a->acl_dn_style != ACL_STYLE_REGEX )
362 {
363 Debug( LDAP_DEBUG_ANY,
364 "%s: line %d: dn pattern"
365 " already specified in to clause.\n",
366 fname, lineno );
367 goto fail;
368 }
369
370 ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
371 continue;
372 }
373
374 split( argv[i], '=', &left, &right );
375 split( left, '.', &left, &style );
376
377 if ( right == NULL ) {
378 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
379 "missing \"=\" in \"%s\" in to clause\n",
380 fname, lineno, left );
381 goto fail;
382 }
383
384 if ( strcasecmp( left, "dn" ) == 0 ) {
385 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
386 a->acl_dn_style != ACL_STYLE_REGEX )
387 {
388 Debug( LDAP_DEBUG_ANY,
389 "%s: line %d: dn pattern"
390 " already specified in to clause.\n",
391 fname, lineno );
392 goto fail;
393 }
394
395 if ( style == NULL || *style == '\0' ||
396 strcasecmp( style, "baseObject" ) == 0 ||
397 strcasecmp( style, "base" ) == 0 ||
398 strcasecmp( style, "exact" ) == 0 )
399 {
400 a->acl_dn_style = ACL_STYLE_BASE;
401 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
402
403 } else if ( strcasecmp( style, "oneLevel" ) == 0 ||
404 strcasecmp( style, "one" ) == 0 )
405 {
406 a->acl_dn_style = ACL_STYLE_ONE;
407 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
408
409 } else if ( strcasecmp( style, "subtree" ) == 0 ||
410 strcasecmp( style, "sub" ) == 0 )
411 {
412 if( *right == '\0' ) {
413 ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
414
415 } else {
416 a->acl_dn_style = ACL_STYLE_SUBTREE;
417 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
418 }
419
420 } else if ( strcasecmp( style, "children" ) == 0 ) {
421 a->acl_dn_style = ACL_STYLE_CHILDREN;
422 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
423
424 } else if ( strcasecmp( style, "regex" ) == 0 ) {
425 a->acl_dn_style = ACL_STYLE_REGEX;
426
427 if ( *right == '\0' ) {
428 /* empty regex should match empty DN */
429 a->acl_dn_style = ACL_STYLE_BASE;
430 ber_str2bv( right, 0, 1, &a->acl_dn_pat );
431
432 } else if ( strcmp(right, "*") == 0
433 || strcmp(right, ".*") == 0
434 || strcmp(right, ".*$") == 0
435 || strcmp(right, "^.*") == 0
436 || strcmp(right, "^.*$") == 0
437 || strcmp(right, ".*$$") == 0
438 || strcmp(right, "^.*$$") == 0 )
439 {
440 ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat );
441
442 } else {
443 acl_regex_normalized_dn( right, &a->acl_dn_pat );
444 }
445
446 } else {
447 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
448 "unknown dn style \"%s\" in to clause\n",
449 fname, lineno, style );
450 goto fail;
451 }
452
453 continue;
454 }
455
456 if ( strcasecmp( left, "filter" ) == 0 ) {
457 if ( (a->acl_filter = str2filter( right )) == NULL ) {
458 Debug( LDAP_DEBUG_ANY,
459 "%s: line %d: bad filter \"%s\" in to clause\n",
460 fname, lineno, right );
461 goto fail;
462 }
463
464 } else if ( strcasecmp( left, "attr" ) == 0 /* TOLERATED */
465 || strcasecmp( left, "attrs" ) == 0 ) /* DOCUMENTED */
466 {
467 if ( strcasecmp( left, "attr" ) == 0 ) {
468 Debug( LDAP_DEBUG_ANY,
469 "%s: line %d: \"attr\" "
470 "is deprecated (and undocumented); "
471 "use \"attrs\" instead.\n",
472 fname, lineno );
473 }
474
475 a->acl_attrs = str2anlist( a->acl_attrs,
476 right, "," );
477 if ( a->acl_attrs == NULL ) {
478 Debug( LDAP_DEBUG_ANY,
479 "%s: line %d: unknown attr \"%s\" in to clause\n",
480 fname, lineno, right );
481 goto fail;
482 }
483
484 } else if ( strncasecmp( left, "val", 3 ) == 0 ) {
485 struct berval bv;
486 char *mr;
487
488 if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
489 Debug( LDAP_DEBUG_ANY,
490 "%s: line %d: attr val already specified in to clause.\n",
491 fname, lineno );
492 goto fail;
493 }
494 if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) )
495 {
496 Debug( LDAP_DEBUG_ANY,
497 "%s: line %d: attr val requires a single attribute.\n",
498 fname, lineno );
499 goto fail;
500 }
501
502 ber_str2bv( right, 0, 0, &bv );
503 a->acl_attrval_style = ACL_STYLE_BASE;
504
505 mr = strchr( left, '/' );
506 if ( mr != NULL ) {
507 mr[ 0 ] = '\0';
508 mr++;
509
510 a->acl_attrval_mr = mr_find( mr );
511 if ( a->acl_attrval_mr == NULL ) {
512 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
513 "invalid matching rule \"%s\".\n",
514 fname, lineno, mr );
515 goto fail;
516 }
517
518 if( !mr_usable_with_at( a->acl_attrval_mr, a->acl_attrs[ 0 ].an_desc->ad_type ) )
519 {
520 Debug(LDAP_DEBUG_ANY,
521 "%s: line %d: matching rule \"%s\" use " "with attr \"%s\" not appropriate.\n",
522 fname, lineno,
523 mr,
524 a->acl_attrs[0].an_name.bv_val );
525 goto fail;
526 }
527 }
528
529 if ( style != NULL ) {
530 if ( strcasecmp( style, "regex" ) == 0 ) {
531 int e = regcomp( &a->acl_attrval_re, bv.bv_val,
532 REG_EXTENDED | REG_ICASE );
533 if ( e ) {
534 char err[SLAP_TEXT_BUFLEN];
535
536 regerror( e, &a->acl_attrval_re, err, sizeof( err ) );
537 Debug(LDAP_DEBUG_ANY,
538 "%s: line %d: regular expression \"%s\" bad because of %s\n",
539 fname, lineno, right, err );
540 goto fail;
541 }
542 a->acl_attrval_style = ACL_STYLE_REGEX;
543
544 } else {
545 /* FIXME: if the attribute has DN syntax, we might
546 * allow one, subtree and children styles as well */
547 if ( !strcasecmp( style, "base" ) ||
548 !strcasecmp( style, "exact" ) ) {
549 a->acl_attrval_style = ACL_STYLE_BASE;
550
551 } else if ( a->acl_attrs[0].an_desc->ad_type->
552 sat_syntax == slap_schema.si_syn_distinguishedName )
553 {
554 if ( !strcasecmp( style, "baseObject" ) ||
555 !strcasecmp( style, "base" ) )
556 {
557 a->acl_attrval_style = ACL_STYLE_BASE;
558 } else if ( !strcasecmp( style, "onelevel" ) ||
559 !strcasecmp( style, "one" ) )
560 {
561 a->acl_attrval_style = ACL_STYLE_ONE;
562 } else if ( !strcasecmp( style, "subtree" ) ||
563 !strcasecmp( style, "sub" ) )
564 {
565 a->acl_attrval_style = ACL_STYLE_SUBTREE;
566 } else if ( !strcasecmp( style, "children" ) ) {
567 a->acl_attrval_style = ACL_STYLE_CHILDREN;
568 } else {
569 Debug(LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
570 "%s: line %d: unknown val.<style> \"%s\" for attributeType \"%s\" " "with DN syntax.\n",
571 fname,
572 lineno,
573 style,
574 a->acl_attrs[0].an_desc->ad_cname.bv_val );
575 goto fail;
576 }
577
578 rc = dnNormalize( 0, NULL, NULL, &bv, &a->acl_attrval, NULL );
579 if ( rc != LDAP_SUCCESS ) {
580 Debug(LDAP_DEBUG_ANY,
581 "%s: line %d: unable to normalize DN \"%s\" " "for attributeType \"%s\" (%d).\n",
582 fname,
583 lineno,
584 bv.bv_val,
585 a->acl_attrs[0].an_desc->ad_cname.bv_val,
586 rc );
587 goto fail;
588 }
589
590 } else {
591 Debug(LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
592 "%s: line %d: unknown val.<style> \"%s\" for attributeType \"%s\".\n",
593 fname,
594 lineno,
595 style,
596 a->acl_attrs[0].an_desc->ad_cname.bv_val );
597 goto fail;
598 }
599 }
600 }
601
602 /* Check for appropriate matching rule */
603 if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
604 ber_dupbv( &a->acl_attrval, &bv );
605
606 } else if ( BER_BVISNULL( &a->acl_attrval ) ) {
607 int rc;
608 const char *text;
609
610 if ( a->acl_attrval_mr == NULL ) {
611 a->acl_attrval_mr = a->acl_attrs[ 0 ].an_desc->ad_type->sat_equality;
612 }
613
614 if ( a->acl_attrval_mr == NULL ) {
615 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
616 "attr \"%s\" does not have an EQUALITY matching rule.\n",
617 fname, lineno, a->acl_attrs[ 0 ].an_name.bv_val );
618 goto fail;
619 }
620
621 rc = asserted_value_validate_normalize(
622 a->acl_attrs[ 0 ].an_desc,
623 a->acl_attrval_mr,
624 SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
625 &bv,
626 &a->acl_attrval,
627 &text,
628 NULL );
629 if ( rc != LDAP_SUCCESS ) {
630 Debug(LDAP_DEBUG_ANY,
631 "%s: line %d: %s: line %d: " " attr \"%s\" normalization failed (%d: %s).\n",
632 fname, lineno,
633 fname, lineno,
634 a->acl_attrs[0].an_name.bv_val,
635 rc, text );
636 goto fail;
637 }
638 }
639
640 } else {
641 Debug( LDAP_DEBUG_ANY,
642 "%s: line %d: expecting <what> got \"%s\"\n",
643 fname, lineno, left );
644 goto fail;
645 }
646 }
647
648 if ( !BER_BVISNULL( &a->acl_dn_pat ) &&
649 ber_bvccmp( &a->acl_dn_pat, '*' ) )
650 {
651 free( a->acl_dn_pat.bv_val );
652 BER_BVZERO( &a->acl_dn_pat );
653 a->acl_dn_style = ACL_STYLE_REGEX;
654 }
655
656 if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
657 a->acl_dn_style != ACL_STYLE_REGEX )
658 {
659 if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
660 struct berval bv;
661 rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
662 if ( rc != LDAP_SUCCESS ) {
663 Debug( LDAP_DEBUG_ANY,
664 "%s: line %d: bad DN \"%s\" in to DN clause\n",
665 fname, lineno, a->acl_dn_pat.bv_val );
666 goto fail;
667 }
668 free( a->acl_dn_pat.bv_val );
669 a->acl_dn_pat = bv;
670
671 } else {
672 int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
673 REG_EXTENDED | REG_ICASE );
674 if ( e ) {
675 char err[ SLAP_TEXT_BUFLEN ];
676
677 regerror( e, &a->acl_dn_re, err, sizeof( err ) );
678 Debug(LDAP_DEBUG_ANY,
679 "%s: line %d: regular expression \"%s\" bad because of %s\n",
680 fname, lineno, right, err );
681 goto fail;
682 }
683 }
684 }
685
686 /* by clause - select who has what access to entries */
687 } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
688 if ( a == NULL ) {
689 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
690 "to clause required before by clause in access line\n",
691 fname, lineno );
692 goto fail;
693 }
694
695 /*
696 * by clause consists of <who> and <access>
697 */
698
699 if ( ++i == argc ) {
700 Debug( LDAP_DEBUG_ANY,
701 "%s: line %d: premature EOL: expecting <who>\n",
702 fname, lineno );
703 goto fail;
704 }
705
706 b = (Access *) ch_calloc( 1, sizeof(Access) );
707
708 ACL_INVALIDATE( b->a_access_mask );
709
710 /* get <who> */
711 for ( ; i < argc; i++ ) {
712 slap_style_t sty = ACL_STYLE_REGEX;
713 char *style_modifier = NULL;
714 char *style_level = NULL;
715 int level = 0;
716 int expand = 0;
717 slap_dn_access *bdn = &b->a_dn;
718 int is_realdn = 0;
719
720 split( argv[i], '=', &left, &right );
721 split( left, '.', &left, &style );
722 if ( style ) {
723 split( style, ',', &style, &style_modifier );
724
725 if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
726 split( style, '{', &style, &style_level );
727 if ( style_level != NULL ) {
728 char *p = strchr( style_level, '}' );
729 if ( p == NULL ) {
730 Debug( LDAP_DEBUG_ANY,
731 "%s: line %d: premature eol: "
732 "expecting closing '}' in \"level{n}\"\n",
733 fname, lineno );
734 goto fail;
735 } else if ( p == style_level ) {
736 Debug( LDAP_DEBUG_ANY,
737 "%s: line %d: empty level "
738 "in \"level{n}\"\n",
739 fname, lineno );
740 goto fail;
741 }
742 p[0] = '\0';
743 }
744 }
745 }
746
747 if ( style == NULL || *style == '\0' ||
748 strcasecmp( style, "exact" ) == 0 ||
749 strcasecmp( style, "baseObject" ) == 0 ||
750 strcasecmp( style, "base" ) == 0 )
751 {
752 sty = ACL_STYLE_BASE;
753
754 } else if ( strcasecmp( style, "onelevel" ) == 0 ||
755 strcasecmp( style, "one" ) == 0 )
756 {
757 sty = ACL_STYLE_ONE;
758
759 } else if ( strcasecmp( style, "subtree" ) == 0 ||
760 strcasecmp( style, "sub" ) == 0 )
761 {
762 sty = ACL_STYLE_SUBTREE;
763
764 } else if ( strcasecmp( style, "children" ) == 0 ) {
765 sty = ACL_STYLE_CHILDREN;
766
767 } else if ( strcasecmp( style, "level" ) == 0 )
768 {
769 if ( lutil_atoi( &level, style_level ) != 0 ) {
770 Debug( LDAP_DEBUG_ANY,
771 "%s: line %d: unable to parse level "
772 "in \"level{n}\"\n",
773 fname, lineno );
774 goto fail;
775 }
776
777 sty = ACL_STYLE_LEVEL;
778
779 } else if ( strcasecmp( style, "regex" ) == 0 ) {
780 sty = ACL_STYLE_REGEX;
781
782 } else if ( strcasecmp( style, "expand" ) == 0 ) {
783 sty = ACL_STYLE_EXPAND;
784
785 } else if ( strcasecmp( style, "ip" ) == 0 ) {
786 sty = ACL_STYLE_IP;
787
788 } else if ( strcasecmp( style, "ipv6" ) == 0 ) {
789 #ifndef LDAP_PF_INET6
790 Debug( LDAP_DEBUG_ANY,
791 "%s: line %d: IPv6 not supported\n",
792 fname, lineno );
793 #endif /* ! LDAP_PF_INET6 */
794 sty = ACL_STYLE_IPV6;
795
796 } else if ( strcasecmp( style, "path" ) == 0 ) {
797 sty = ACL_STYLE_PATH;
798 #ifndef LDAP_PF_LOCAL
799 Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
800 "%s: line %d: "
801 "\"path\" style modifier is useless without local.\n",
802 fname, lineno );
803 goto fail;
804 #endif /* LDAP_PF_LOCAL */
805
806 } else {
807 Debug( LDAP_DEBUG_ANY,
808 "%s: line %d: unknown style \"%s\" in by clause\n",
809 fname, lineno, style );
810 goto fail;
811 }
812
813 if ( style_modifier &&
814 strcasecmp( style_modifier, "expand" ) == 0 )
815 {
816 switch ( sty ) {
817 case ACL_STYLE_REGEX:
818 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
819 "\"regex\" style implies \"expand\" modifier.\n",
820 fname, lineno );
821 goto fail;
822 break;
823
824 case ACL_STYLE_EXPAND:
825 break;
826
827 default:
828 /* we'll see later if it's pertinent */
829 expand = 1;
830 break;
831 }
832 }
833
834 if ( strncasecmp( left, "real", STRLENOF( "real" ) ) == 0 ) {
835 is_realdn = 1;
836 bdn = &b->a_realdn;
837 left += STRLENOF( "real" );
838 }
839
840 if ( strcasecmp( left, "*" ) == 0 ) {
841 if ( is_realdn ) {
842 goto fail;
843 }
844
845 ber_str2bv( "*", STRLENOF( "*" ), 1, &bv );
846 sty = ACL_STYLE_REGEX;
847
848 } else if ( strcasecmp( left, "anonymous" ) == 0 ) {
849 ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv);
850 sty = ACL_STYLE_ANONYMOUS;
851
852 } else if ( strcasecmp( left, "users" ) == 0 ) {
853 ber_str2bv("users", STRLENOF( "users" ), 1, &bv);
854 sty = ACL_STYLE_USERS;
855
856 } else if ( strcasecmp( left, "self" ) == 0 ) {
857 ber_str2bv("self", STRLENOF( "self" ), 1, &bv);
858 sty = ACL_STYLE_SELF;
859
860 } else if ( strcasecmp( left, "dn" ) == 0 ) {
861 if ( sty == ACL_STYLE_REGEX ) {
862 bdn->a_style = ACL_STYLE_REGEX;
863 if ( right == NULL ) {
864 /* no '=' */
865 ber_str2bv("users",
866 STRLENOF( "users" ),
867 1, &bv);
868 bdn->a_style = ACL_STYLE_USERS;
869
870 } else if (*right == '\0' ) {
871 /* dn="" */
872 ber_str2bv("anonymous",
873 STRLENOF( "anonymous" ),
874 1, &bv);
875 bdn->a_style = ACL_STYLE_ANONYMOUS;
876
877 } else if ( strcmp( right, "*" ) == 0 ) {
878 /* dn=* */
879 /* any or users? users for now */
880 ber_str2bv("users",
881 STRLENOF( "users" ),
882 1, &bv);
883 bdn->a_style = ACL_STYLE_USERS;
884
885 } else if ( strcmp( right, ".+" ) == 0
886 || strcmp( right, "^.+" ) == 0
887 || strcmp( right, ".+$" ) == 0
888 || strcmp( right, "^.+$" ) == 0
889 || strcmp( right, ".+$$" ) == 0
890 || strcmp( right, "^.+$$" ) == 0 )
891 {
892 ber_str2bv("users",
893 STRLENOF( "users" ),
894 1, &bv);
895 bdn->a_style = ACL_STYLE_USERS;
896
897 } else if ( strcmp( right, ".*" ) == 0
898 || strcmp( right, "^.*" ) == 0
899 || strcmp( right, ".*$" ) == 0
900 || strcmp( right, "^.*$" ) == 0
901 || strcmp( right, ".*$$" ) == 0
902 || strcmp( right, "^.*$$" ) == 0 )
903 {
904 ber_str2bv("*",
905 STRLENOF( "*" ),
906 1, &bv);
907
908 } else {
909 acl_regex_normalized_dn( right, &bv );
910 if ( !ber_bvccmp( &bv, '*' ) ) {
911 regtest( fname, lineno, bv.bv_val );
912 }
913 }
914
915 } else if ( right == NULL || *right == '\0' ) {
916 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
917 "missing \"=\" in (or value after) \"%s\" "
918 "in by clause\n",
919 fname, lineno, left );
920 goto fail;
921
922 } else {
923 ber_str2bv( right, 0, 1, &bv );
924 }
925
926 } else {
927 BER_BVZERO( &bv );
928 }
929
930 if ( !BER_BVISNULL( &bv ) ) {
931 if ( !BER_BVISEMPTY( &bdn->a_pat ) ) {
932 Debug( LDAP_DEBUG_ANY,
933 "%s: line %d: dn pattern already specified.\n",
934 fname, lineno );
935 goto fail;
936 }
937
938 if ( sty != ACL_STYLE_REGEX &&
939 sty != ACL_STYLE_ANONYMOUS &&
940 sty != ACL_STYLE_USERS &&
941 sty != ACL_STYLE_SELF &&
942 expand == 0 )
943 {
944 rc = dnNormalize(0, NULL, NULL,
945 &bv, &bdn->a_pat, NULL);
946 if ( rc != LDAP_SUCCESS ) {
947 Debug( LDAP_DEBUG_ANY,
948 "%s: line %d: bad DN \"%s\" in by DN clause\n",
949 fname, lineno, bv.bv_val );
950 goto fail;
951 }
952 free( bv.bv_val );
953 if ( sty == ACL_STYLE_BASE
954 && be != NULL
955 && !BER_BVISNULL( &be->be_rootndn )
956 && dn_match( &bdn->a_pat, &be->be_rootndn ) )
957 {
958 Debug( LDAP_DEBUG_ANY,
959 "%s: line %d: rootdn is always granted "
960 "unlimited privileges.\n",
961 fname, lineno );
962 }
963
964 } else {
965 bdn->a_pat = bv;
966 }
967 bdn->a_style = sty;
968 if ( expand ) {
969 char *exp;
970 int gotit = 0;
971
972 for ( exp = strchr( bdn->a_pat.bv_val, '$' );
973 exp && (ber_len_t)(exp - bdn->a_pat.bv_val)
974 < bdn->a_pat.bv_len;
975 exp = strchr( exp, '$' ) )
976 {
977 if ( ( isdigit( (unsigned char) exp[ 1 ] ) ||
978 exp[ 1 ] == '{' ) ) {
979 gotit = 1;
980 break;
981 }
982 }
983
984 if ( gotit == 1 ) {
985 bdn->a_expand = expand;
986
987 } else {
988 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
989 "\"expand\" used with no expansions in \"pattern\".\n",
990 fname, lineno );
991 goto fail;
992 }
993 }
994 if ( sty == ACL_STYLE_SELF ) {
995 bdn->a_self_level = level;
996
997 } else {
998 if ( level < 0 ) {
999 Debug( LDAP_DEBUG_ANY,
1000 "%s: line %d: bad negative level \"%d\" "
1001 "in by DN clause\n",
1002 fname, lineno, level );
1003 goto fail;
1004 } else if ( level == 1 ) {
1005 Debug( LDAP_DEBUG_ANY,
1006 "%s: line %d: \"onelevel\" should be used "
1007 "instead of \"level{1}\" in by DN clause\n",
1008 fname, lineno );
1009 } else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
1010 Debug( LDAP_DEBUG_ANY,
1011 "%s: line %d: \"base\" should be used "
1012 "instead of \"level{0}\" in by DN clause\n",
1013 fname, lineno );
1014 }
1015
1016 bdn->a_level = level;
1017 }
1018 continue;
1019 }
1020
1021 if ( strcasecmp( left, "dnattr" ) == 0 ) {
1022 if ( right == NULL || right[0] == '\0' ) {
1023 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1024 "missing \"=\" in (or value after) \"%s\" "
1025 "in by clause\n",
1026 fname, lineno, left );
1027 goto fail;
1028 }
1029
1030 if( bdn->a_at != NULL ) {
1031 Debug( LDAP_DEBUG_ANY,
1032 "%s: line %d: dnattr already specified.\n",
1033 fname, lineno );
1034 goto fail;
1035 }
1036
1037 rc = slap_str2ad( right, &bdn->a_at, &text );
1038
1039 if( rc != LDAP_SUCCESS ) {
1040 Debug(LDAP_DEBUG_ANY,
1041 "%s: line %d: dnattr \"%s\": %s\n",
1042 fname, lineno, right,
1043 text );
1044 goto fail;
1045 }
1046
1047
1048 if( !is_at_syntax( bdn->a_at->ad_type,
1049 SLAPD_DN_SYNTAX ) &&
1050 !is_at_syntax( bdn->a_at->ad_type,
1051 SLAPD_NAMEUID_SYNTAX ))
1052 {
1053 Debug(LDAP_DEBUG_ANY,
1054 "%s: line %d: dnattr \"%s\": " "inappropriate syntax: %s\n\n",
1055 fname, lineno, right,
1056 bdn->a_at->ad_type->sat_syntax_oid );
1057 goto fail;
1058 }
1059
1060 if( bdn->a_at->ad_type->sat_equality == NULL ) {
1061 Debug( LDAP_DEBUG_ANY,
1062 "%s: line %d: dnattr \"%s\": "
1063 "inappropriate matching (no EQUALITY)\n",
1064 fname, lineno, right );
1065 goto fail;
1066 }
1067
1068 continue;
1069 }
1070
1071 if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) {
1072 char *name = NULL;
1073 char *value = NULL;
1074 char *attr_name = SLAPD_GROUP_ATTR;
1075
1076 switch ( sty ) {
1077 case ACL_STYLE_REGEX:
1078 /* legacy, tolerated */
1079 Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
1080 "%s: line %d: "
1081 "deprecated group style \"regex\"; "
1082 "use \"expand\" instead.\n",
1083 fname, lineno );
1084 sty = ACL_STYLE_EXPAND;
1085 break;
1086
1087 case ACL_STYLE_BASE:
1088 /* legal, traditional */
1089 case ACL_STYLE_EXPAND:
1090 /* legal, substring expansion; supersedes regex */
1091 break;
1092
1093 default:
1094 /* unknown */
1095 Debug( LDAP_DEBUG_ANY,
1096 "%s: line %d: "
1097 "inappropriate style \"%s\" in by clause.\n",
1098 fname, lineno, style );
1099 goto fail;
1100 }
1101
1102 if ( right == NULL || right[0] == '\0' ) {
1103 Debug( LDAP_DEBUG_ANY,
1104 "%s: line %d: "
1105 "missing \"=\" in (or value after) \"%s\" "
1106 "in by clause.\n",
1107 fname, lineno, left );
1108 goto fail;
1109 }
1110
1111 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
1112 Debug( LDAP_DEBUG_ANY,
1113 "%s: line %d: group pattern already specified.\n",
1114 fname, lineno );
1115 goto fail;
1116 }
1117
1118 /* format of string is
1119 "group/objectClassValue/groupAttrName" */
1120 if ( ( value = strchr(left, '/') ) != NULL ) {
1121 *value++ = '\0';
1122 if ( *value && ( name = strchr( value, '/' ) ) != NULL ) {
1123 *name++ = '\0';
1124 }
1125 }
1126
1127 b->a_group_style = sty;
1128 if ( sty == ACL_STYLE_EXPAND ) {
1129 acl_regex_normalized_dn( right, &bv );
1130 if ( !ber_bvccmp( &bv, '*' ) ) {
1131 regtest( fname, lineno, bv.bv_val );
1132 }
1133 b->a_group_pat = bv;
1134
1135 } else {
1136 ber_str2bv( right, 0, 0, &bv );
1137 rc = dnNormalize( 0, NULL, NULL, &bv,
1138 &b->a_group_pat, NULL );
1139 if ( rc != LDAP_SUCCESS ) {
1140 Debug( LDAP_DEBUG_ANY,
1141 "%s: line %d: bad DN \"%s\".\n",
1142 fname, lineno, right );
1143 goto fail;
1144 }
1145 }
1146
1147 if ( value && *value ) {
1148 b->a_group_oc = oc_find( value );
1149 *--value = '/';
1150
1151 if ( b->a_group_oc == NULL ) {
1152 Debug( LDAP_DEBUG_ANY,
1153 "%s: line %d: group objectclass "
1154 "\"%s\" unknown.\n",
1155 fname, lineno, value );
1156 goto fail;
1157 }
1158
1159 } else {
1160 b->a_group_oc = oc_find( SLAPD_GROUP_CLASS );
1161
1162 if( b->a_group_oc == NULL ) {
1163 Debug( LDAP_DEBUG_ANY,
1164 "%s: line %d: group default objectclass "
1165 "\"%s\" unknown.\n",
1166 fname, lineno, SLAPD_GROUP_CLASS );
1167 goto fail;
1168 }
1169 }
1170
1171 if ( is_object_subclass( slap_schema.si_oc_referral,
1172 b->a_group_oc ) )
1173 {
1174 Debug( LDAP_DEBUG_ANY,
1175 "%s: line %d: group objectclass \"%s\" "
1176 "is subclass of referral.\n",
1177 fname, lineno, value );
1178 goto fail;
1179 }
1180
1181 if ( is_object_subclass( slap_schema.si_oc_alias,
1182 b->a_group_oc ) )
1183 {
1184 Debug( LDAP_DEBUG_ANY,
1185 "%s: line %d: group objectclass \"%s\" "
1186 "is subclass of alias.\n",
1187 fname, lineno, value );
1188 goto fail;
1189 }
1190
1191 if ( name && *name ) {
1192 attr_name = name;
1193 *--name = '/';
1194
1195 }
1196
1197 rc = slap_str2ad( attr_name, &b->a_group_at, &text );
1198 if ( rc != LDAP_SUCCESS ) {
1199 Debug(LDAP_DEBUG_ANY,
1200 "%s: line %d: group \"%s\": %s.\n",
1201 fname, lineno, right,
1202 text );
1203 goto fail;
1204 }
1205
1206 if ( !is_at_syntax( b->a_group_at->ad_type,
1207 SLAPD_DN_SYNTAX ) /* e.g. "member" */
1208 && !is_at_syntax( b->a_group_at->ad_type,
1209 SLAPD_NAMEUID_SYNTAX ) /* e.g. memberUID */
1210 && !is_at_subtype( b->a_group_at->ad_type,
1211 slap_schema.si_ad_labeledURI->ad_type ) /* e.g. memberURL */ )
1212 {
1213 Debug(LDAP_DEBUG_ANY,
1214 "%s: line %d: group \"%s\" attr \"%s\": inappropriate syntax: %s; " "must be " SLAPD_DN_SYNTAX " (DN), " SLAPD_NAMEUID_SYNTAX " (NameUID) " "or a subtype of labeledURI.\n",
1215 fname, lineno, right,
1216 attr_name,
1217 at_syntax(b->a_group_at->ad_type) );
1218 goto fail;
1219 }
1220
1221
1222 {
1223 int rc;
1224 ObjectClass *ocs[2];
1225
1226 ocs[0] = b->a_group_oc;
1227 ocs[1] = NULL;
1228
1229 rc = oc_check_allowed( b->a_group_at->ad_type,
1230 ocs, NULL );
1231
1232 if( rc != 0 ) {
1233 Debug(LDAP_DEBUG_ANY,
1234 "%s: line %d: group: \"%s\" not allowed by \"%s\".\n",
1235 fname, lineno,
1236 b->a_group_at->ad_cname.bv_val,
1237 b->a_group_oc->soc_oid );
1238 goto fail;
1239 }
1240 }
1241 continue;
1242 }
1243
1244 if ( strcasecmp( left, "peername" ) == 0 ) {
1245 switch ( sty ) {
1246 case ACL_STYLE_REGEX:
1247 case ACL_STYLE_BASE:
1248 /* legal, traditional */
1249 case ACL_STYLE_EXPAND:
1250 /* cheap replacement to regex for simple expansion */
1251 case ACL_STYLE_IP:
1252 case ACL_STYLE_IPV6:
1253 case ACL_STYLE_PATH:
1254 /* legal, peername specific */
1255 break;
1256
1257 default:
1258 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1259 "inappropriate style \"%s\" in by clause.\n",
1260 fname, lineno, style );
1261 goto fail;
1262 }
1263
1264 if ( right == NULL || right[0] == '\0' ) {
1265 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1266 "missing \"=\" in (or value after) \"%s\" "
1267 "in by clause.\n",
1268 fname, lineno, left );
1269 goto fail;
1270 }
1271
1272 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
1273 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1274 "peername pattern already specified.\n",
1275 fname, lineno );
1276 goto fail;
1277 }
1278
1279 b->a_peername_style = sty;
1280 if ( sty == ACL_STYLE_REGEX ) {
1281 acl_regex_normalized_dn( right, &bv );
1282 if ( !ber_bvccmp( &bv, '*' ) ) {
1283 regtest( fname, lineno, bv.bv_val );
1284 }
1285 b->a_peername_pat = bv;
1286
1287 } else {
1288 ber_str2bv( right, 0, 1, &b->a_peername_pat );
1289
1290 if ( sty == ACL_STYLE_IP ) {
1291 char *addr = NULL,
1292 *mask = NULL,
1293 *port = NULL;
1294
1295 split( right, '{', &addr, &port );
1296 split( addr, '%', &addr, &mask );
1297
1298 b->a_peername_addr = inet_addr( addr );
1299 if ( b->a_peername_addr == (unsigned long)(-1) ) {
1300 /* illegal address */
1301 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1302 "illegal peername address \"%s\".\n",
1303 fname, lineno, addr );
1304 goto fail;
1305 }
1306
1307 b->a_peername_mask = (unsigned long)(-1);
1308 if ( mask != NULL ) {
1309 b->a_peername_mask = inet_addr( mask );
1310 if ( b->a_peername_mask ==
1311 (unsigned long)(-1) )
1312 {
1313 /* illegal mask */
1314 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1315 "illegal peername address mask "
1316 "\"%s\".\n",
1317 fname, lineno, mask );
1318 goto fail;
1319 }
1320 }
1321
1322 b->a_peername_port = -1;
1323 if ( port ) {
1324 char *end = NULL;
1325
1326 b->a_peername_port = strtol( port, &end, 10 );
1327 if ( end == port || end[0] != '}' ) {
1328 /* illegal port */
1329 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1330 "illegal peername port specification "
1331 "\"{%s}\".\n",
1332 fname, lineno, port );
1333 goto fail;
1334 }
1335 }
1336
1337 #ifdef LDAP_PF_INET6
1338 } else if ( sty == ACL_STYLE_IPV6 ) {
1339 char *addr = NULL,
1340 *mask = NULL,
1341 *port = NULL;
1342
1343 split( right, '{', &addr, &port );
1344 split( addr, '%', &addr, &mask );
1345
1346 if ( inet_pton( AF_INET6, addr, &b->a_peername_addr6 ) != 1 ) {
1347 /* illegal address */
1348 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1349 "illegal peername address \"%s\".\n",
1350 fname, lineno, addr );
1351 goto fail;
1352 }
1353
1354 if ( mask == NULL ) {
1355 mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF";
1356 }
1357
1358 if ( inet_pton( AF_INET6, mask, &b->a_peername_mask6 ) != 1 ) {
1359 /* illegal mask */
1360 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1361 "illegal peername address mask "
1362 "\"%s\".\n",
1363 fname, lineno, mask );
1364 goto fail;
1365 }
1366
1367 b->a_peername_port = -1;
1368 if ( port ) {
1369 char *end = NULL;
1370
1371 b->a_peername_port = strtol( port, &end, 10 );
1372 if ( end == port || end[0] != '}' ) {
1373 /* illegal port */
1374 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1375 "illegal peername port specification "
1376 "\"{%s}\".\n",
1377 fname, lineno, port );
1378 goto fail;
1379 }
1380 }
1381 #endif /* LDAP_PF_INET6 */
1382 }
1383 }
1384 continue;
1385 }
1386
1387 if ( strcasecmp( left, "sockname" ) == 0 ) {
1388 switch ( sty ) {
1389 case ACL_STYLE_REGEX:
1390 case ACL_STYLE_BASE:
1391 /* legal, traditional */
1392 case ACL_STYLE_EXPAND:
1393 /* cheap replacement to regex for simple expansion */
1394 break;
1395
1396 default:
1397 /* unknown */
1398 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1399 "inappropriate style \"%s\" in by clause\n",
1400 fname, lineno, style );
1401 goto fail;
1402 }
1403
1404 if ( right == NULL || right[0] == '\0' ) {
1405 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1406 "missing \"=\" in (or value after) \"%s\" "
1407 "in by clause\n",
1408 fname, lineno, left );
1409 goto fail;
1410 }
1411
1412 if ( !BER_BVISNULL( &b->a_sockname_pat ) ) {
1413 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1414 "sockname pattern already specified.\n",
1415 fname, lineno );
1416 goto fail;
1417 }
1418
1419 b->a_sockname_style = sty;
1420 if ( sty == ACL_STYLE_REGEX ) {
1421 acl_regex_normalized_dn( right, &bv );
1422 if ( !ber_bvccmp( &bv, '*' ) ) {
1423 regtest( fname, lineno, bv.bv_val );
1424 }
1425 b->a_sockname_pat = bv;
1426
1427 } else {
1428 ber_str2bv( right, 0, 1, &b->a_sockname_pat );
1429 }
1430 continue;
1431 }
1432
1433 if ( strcasecmp( left, "domain" ) == 0 ) {
1434 switch ( sty ) {
1435 case ACL_STYLE_REGEX:
1436 case ACL_STYLE_BASE:
1437 case ACL_STYLE_SUBTREE:
1438 /* legal, traditional */
1439 break;
1440
1441 case ACL_STYLE_EXPAND:
1442 /* tolerated: means exact,expand */
1443 if ( expand ) {
1444 Debug( LDAP_DEBUG_ANY,
1445 "%s: line %d: "
1446 "\"expand\" modifier "
1447 "with \"expand\" style.\n",
1448 fname, lineno );
1449 }
1450 sty = ACL_STYLE_BASE;
1451 expand = 1;
1452 break;
1453
1454 default:
1455 /* unknown */
1456 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1457 "inappropriate style \"%s\" in by clause.\n",
1458 fname, lineno, style );
1459 goto fail;
1460 }
1461
1462 if ( right == NULL || right[0] == '\0' ) {
1463 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1464 "missing \"=\" in (or value after) \"%s\" "
1465 "in by clause.\n",
1466 fname, lineno, left );
1467 goto fail;
1468 }
1469
1470 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
1471 Debug( LDAP_DEBUG_ANY,
1472 "%s: line %d: domain pattern already specified.\n",
1473 fname, lineno );
1474 goto fail;
1475 }
1476
1477 b->a_domain_style = sty;
1478 b->a_domain_expand = expand;
1479 if ( sty == ACL_STYLE_REGEX ) {
1480 acl_regex_normalized_dn( right, &bv );
1481 if ( !ber_bvccmp( &bv, '*' ) ) {
1482 regtest( fname, lineno, bv.bv_val );
1483 }
1484 b->a_domain_pat = bv;
1485
1486 } else {
1487 ber_str2bv( right, 0, 1, &b->a_domain_pat );
1488 }
1489 continue;
1490 }
1491
1492 if ( strcasecmp( left, "sockurl" ) == 0 ) {
1493 switch ( sty ) {
1494 case ACL_STYLE_REGEX:
1495 case ACL_STYLE_BASE:
1496 /* legal, traditional */
1497 case ACL_STYLE_EXPAND:
1498 /* cheap replacement to regex for simple expansion */
1499 break;
1500
1501 default:
1502 /* unknown */
1503 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1504 "inappropriate style \"%s\" in by clause.\n",
1505 fname, lineno, style );
1506 goto fail;
1507 }
1508
1509 if ( right == NULL || right[0] == '\0' ) {
1510 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1511 "missing \"=\" in (or value after) \"%s\" "
1512 "in by clause.\n",
1513 fname, lineno, left );
1514 goto fail;
1515 }
1516
1517 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
1518 Debug( LDAP_DEBUG_ANY,
1519 "%s: line %d: sockurl pattern already specified.\n",
1520 fname, lineno );
1521 goto fail;
1522 }
1523
1524 b->a_sockurl_style = sty;
1525 if ( sty == ACL_STYLE_REGEX ) {
1526 acl_regex_normalized_dn( right, &bv );
1527 if ( !ber_bvccmp( &bv, '*' ) ) {
1528 regtest( fname, lineno, bv.bv_val );
1529 }
1530 b->a_sockurl_pat = bv;
1531
1532 } else {
1533 ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
1534 }
1535 continue;
1536 }
1537
1538 if ( strcasecmp( left, "set" ) == 0 ) {
1539 switch ( sty ) {
1540 /* deprecated */
1541 case ACL_STYLE_REGEX:
1542 Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
1543 "%s: line %d: "
1544 "deprecated set style "
1545 "\"regex\" in <by> clause; "
1546 "use \"expand\" instead.\n",
1547 fname, lineno );
1548 sty = ACL_STYLE_EXPAND;
1549 /* FALLTHRU */
1550
1551 case ACL_STYLE_BASE:
1552 case ACL_STYLE_EXPAND:
1553 break;
1554
1555 default:
1556 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1557 "inappropriate style \"%s\" in by clause.\n",
1558 fname, lineno, style );
1559 goto fail;
1560 }
1561
1562 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
1563 Debug( LDAP_DEBUG_ANY,
1564 "%s: line %d: set attribute already specified.\n",
1565 fname, lineno );
1566 goto fail;
1567 }
1568
1569 if ( right == NULL || *right == '\0' ) {
1570 Debug( LDAP_DEBUG_ANY,
1571 "%s: line %d: no set is defined.\n",
1572 fname, lineno );
1573 goto fail;
1574 }
1575
1576 b->a_set_style = sty;
1577 ber_str2bv( right, 0, 1, &b->a_set_pat );
1578
1579 continue;
1580 }
1581
1582 #ifdef SLAP_DYNACL
1583 {
1584 char *name = NULL,
1585 *opts = NULL;
1586
1587 #if 1 /* tolerate legacy "aci" <who> */
1588 if ( strcasecmp( left, "aci" ) == 0 ) {
1589 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1590 "undocumented deprecated \"aci\" directive "
1591 "is superseded by \"dynacl/aci\".\n",
1592 fname, lineno );
1593 name = "aci";
1594
1595 } else
1596 #endif /* tolerate legacy "aci" <who> */
1597 if ( strncasecmp( left, "dynacl/", STRLENOF( "dynacl/" ) ) == 0 ) {
1598 name = &left[ STRLENOF( "dynacl/" ) ];
1599 opts = strchr( name, '/' );
1600 if ( opts ) {
1601 opts[ 0 ] = '\0';
1602 opts++;
1603 }
1604 }
1605
1606 if ( name ) {
1607 if ( slap_dynacl_config( fname, lineno, b, name, opts, sty, right ) ) {
1608 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1609 "unable to configure dynacl \"%s\".\n",
1610 fname, lineno, name );
1611 goto fail;
1612 }
1613
1614 continue;
1615 }
1616 }
1617 #endif /* SLAP_DYNACL */
1618
1619 if ( strcasecmp( left, "ssf" ) == 0 ) {
1620 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1621 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1622 "inappropriate style \"%s\" in by clause.\n",
1623 fname, lineno, style );
1624 goto fail;
1625 }
1626
1627 if ( b->a_authz.sai_ssf ) {
1628 Debug( LDAP_DEBUG_ANY,
1629 "%s: line %d: ssf attribute already specified.\n",
1630 fname, lineno );
1631 goto fail;
1632 }
1633
1634 if ( right == NULL || *right == '\0' ) {
1635 Debug( LDAP_DEBUG_ANY,
1636 "%s: line %d: no ssf is defined.\n",
1637 fname, lineno );
1638 goto fail;
1639 }
1640
1641 if ( lutil_atou( &b->a_authz.sai_ssf, right ) != 0 ) {
1642 Debug( LDAP_DEBUG_ANY,
1643 "%s: line %d: unable to parse ssf value (%s).\n",
1644 fname, lineno, right );
1645 goto fail;
1646 }
1647
1648 if ( !b->a_authz.sai_ssf ) {
1649 Debug( LDAP_DEBUG_ANY,
1650 "%s: line %d: invalid ssf value (%s).\n",
1651 fname, lineno, right );
1652 goto fail;
1653 }
1654 continue;
1655 }
1656
1657 if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
1658 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1659 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1660 "inappropriate style \"%s\" in by clause.\n",
1661 fname, lineno, style );
1662 goto fail;
1663 }
1664
1665 if ( b->a_authz.sai_transport_ssf ) {
1666 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1667 "transport_ssf attribute already specified.\n",
1668 fname, lineno );
1669 goto fail;
1670 }
1671
1672 if ( right == NULL || *right == '\0' ) {
1673 Debug( LDAP_DEBUG_ANY,
1674 "%s: line %d: no transport_ssf is defined.\n",
1675 fname, lineno );
1676 goto fail;
1677 }
1678
1679 if ( lutil_atou( &b->a_authz.sai_transport_ssf, right ) != 0 ) {
1680 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1681 "unable to parse transport_ssf value (%s).\n",
1682 fname, lineno, right );
1683 goto fail;
1684 }
1685
1686 if ( !b->a_authz.sai_transport_ssf ) {
1687 Debug( LDAP_DEBUG_ANY,
1688 "%s: line %d: invalid transport_ssf value (%s).\n",
1689 fname, lineno, right );
1690 goto fail;
1691 }
1692 continue;
1693 }
1694
1695 if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
1696 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1697 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1698 "inappropriate style \"%s\" in by clause.\n",
1699 fname, lineno, style );
1700 goto fail;
1701 }
1702
1703 if ( b->a_authz.sai_tls_ssf ) {
1704 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1705 "tls_ssf attribute already specified.\n",
1706 fname, lineno );
1707 goto fail;
1708 }
1709
1710 if ( right == NULL || *right == '\0' ) {
1711 Debug( LDAP_DEBUG_ANY,
1712 "%s: line %d: no tls_ssf is defined\n",
1713 fname, lineno );
1714 goto fail;
1715 }
1716
1717 if ( lutil_atou( &b->a_authz.sai_tls_ssf, right ) != 0 ) {
1718 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1719 "unable to parse tls_ssf value (%s).\n",
1720 fname, lineno, right );
1721 goto fail;
1722 }
1723
1724 if ( !b->a_authz.sai_tls_ssf ) {
1725 Debug( LDAP_DEBUG_ANY,
1726 "%s: line %d: invalid tls_ssf value (%s).\n",
1727 fname, lineno, right );
1728 goto fail;
1729 }
1730 continue;
1731 }
1732
1733 if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
1734 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
1735 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1736 "inappropriate style \"%s\" in by clause.\n",
1737 fname, lineno, style );
1738 goto fail;
1739 }
1740
1741 if ( b->a_authz.sai_sasl_ssf ) {
1742 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1743 "sasl_ssf attribute already specified.\n",
1744 fname, lineno );
1745 goto fail;
1746 }
1747
1748 if ( right == NULL || *right == '\0' ) {
1749 Debug( LDAP_DEBUG_ANY,
1750 "%s: line %d: no sasl_ssf is defined.\n",
1751 fname, lineno );
1752 goto fail;
1753 }
1754
1755 if ( lutil_atou( &b->a_authz.sai_sasl_ssf, right ) != 0 ) {
1756 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1757 "unable to parse sasl_ssf value (%s).\n",
1758 fname, lineno, right );
1759 goto fail;
1760 }
1761
1762 if ( !b->a_authz.sai_sasl_ssf ) {
1763 Debug( LDAP_DEBUG_ANY,
1764 "%s: line %d: invalid sasl_ssf value (%s).\n",
1765 fname, lineno, right );
1766 goto fail;
1767 }
1768 continue;
1769 }
1770
1771 if ( right != NULL ) {
1772 /* unsplit */
1773 right[-1] = '=';
1774 }
1775 break;
1776 }
1777
1778 if ( i == argc || ( strcasecmp( left, "stop" ) == 0 ) ) {
1779 /* out of arguments or plain stop */
1780
1781 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1782 ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1783 b->a_type = ACL_STOP;
1784
1785 access_append( &a->acl_access, b );
1786 continue;
1787 }
1788
1789 if ( strcasecmp( left, "continue" ) == 0 ) {
1790 /* plain continue */
1791
1792 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1793 ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1794 b->a_type = ACL_CONTINUE;
1795
1796 access_append( &a->acl_access, b );
1797 continue;
1798 }
1799
1800 if ( strcasecmp( left, "break" ) == 0 ) {
1801 /* plain continue */
1802
1803 ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
1804 ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1805 b->a_type = ACL_BREAK;
1806
1807 access_append( &a->acl_access, b );
1808 continue;
1809 }
1810
1811 if ( strcasecmp( left, "by" ) == 0 ) {
1812 /* we've gone too far */
1813 --i;
1814 ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
1815 ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
1816 b->a_type = ACL_STOP;
1817
1818 access_append( &a->acl_access, b );
1819 continue;
1820 }
1821
1822 /* get <access> */
1823 {
1824 char *lleft = left;
1825
1826 if ( strncasecmp( left, "self", STRLENOF( "self" ) ) == 0 ) {
1827 b->a_dn_self = 1;
1828 lleft = &left[ STRLENOF( "self" ) ];
1829
1830 } else if ( strncasecmp( left, "realself", STRLENOF( "realself" ) ) == 0 ) {
1831 b->a_realdn_self = 1;
1832 lleft = &left[ STRLENOF( "realself" ) ];
1833 }
1834
1835 ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( lleft ) );
1836 }
1837
1838 if ( ACL_IS_INVALID( b->a_access_mask ) ) {
1839 Debug( LDAP_DEBUG_ANY,
1840 "%s: line %d: expecting <access> got \"%s\".\n",
1841 fname, lineno, left );
1842 goto fail;
1843 }
1844
1845 b->a_type = ACL_STOP;
1846
1847 if ( ++i == argc ) {
1848 /* out of arguments or plain stop */
1849 access_append( &a->acl_access, b );
1850 continue;
1851 }
1852
1853 if ( strcasecmp( argv[i], "continue" ) == 0 ) {
1854 /* plain continue */
1855 b->a_type = ACL_CONTINUE;
1856
1857 } else if ( strcasecmp( argv[i], "break" ) == 0 ) {
1858 /* plain continue */
1859 b->a_type = ACL_BREAK;
1860
1861 } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
1862 /* gone to far */
1863 i--;
1864 }
1865
1866 access_append( &a->acl_access, b );
1867 b = NULL;
1868
1869 } else {
1870 Debug( LDAP_DEBUG_ANY,
1871 "%s: line %d: expecting \"to\" "
1872 "or \"by\" got \"%s\"\n",
1873 fname, lineno, argv[i] );
1874 goto fail;
1875 }
1876 }
1877
1878 /* if we have no real access clause, complain and do nothing */
1879 if ( a == NULL ) {
1880 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1881 "warning: no access clause(s) specified in access line.\n",
1882 fname, lineno );
1883 goto fail;
1884
1885 } else {
1886 #ifdef LDAP_DEBUG
1887 if ( slap_debug & LDAP_DEBUG_ACL ) {
1888 print_acl( be, a );
1889 }
1890 #endif
1891
1892 if ( a->acl_access == NULL ) {
1893 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1894 "warning: no by clause(s) specified in access line.\n",
1895 fname, lineno );
1896 goto fail;
1897 }
1898
1899 if ( be != NULL ) {
1900 if ( be->be_nsuffix == NULL ) {
1901 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1902 "scope checking needs suffix before ACLs.\n",
1903 fname, lineno );
1904 /* go ahead, since checking is not authoritative */
1905 } else if ( !BER_BVISNULL( &be->be_nsuffix[ 1 ] ) ) {
1906 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1907 "scope checking only applies to single-valued "
1908 "suffix databases\n",
1909 fname, lineno );
1910 /* go ahead, since checking is not authoritative */
1911 } else {
1912 switch ( check_scope( be, a ) ) {
1913 case ACL_SCOPE_UNKNOWN:
1914 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1915 "cannot assess the validity of the ACL scope within "
1916 "backend naming context\n",
1917 fname, lineno );
1918 break;
1919
1920 case ACL_SCOPE_WARN:
1921 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1922 "ACL could be out of scope within backend naming context\n",
1923 fname, lineno );
1924 break;
1925
1926 case ACL_SCOPE_PARTIAL:
1927 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1928 "ACL appears to be partially out of scope within "
1929 "backend naming context\n",
1930 fname, lineno );
1931 break;
1932
1933 case ACL_SCOPE_ERR:
1934 Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
1935 "ACL appears to be out of scope within "
1936 "backend naming context\n",
1937 fname, lineno );
1938 break;
1939
1940 default:
1941 break;
1942 }
1943 }
1944 acl_append( &be->be_acl, a, pos );
1945
1946 } else {
1947 acl_append( &frontendDB->be_acl, a, pos );
1948 }
1949 }
1950
1951 return 0;
1952
1953 fail:
1954 if ( b ) access_free( b );
1955 if ( a ) acl_free( a );
1956 return acl_usage();
1957 }
1958
1959 char *
accessmask2str(slap_mask_t mask,char * buf,int debug)1960 accessmask2str( slap_mask_t mask, char *buf, int debug )
1961 {
1962 int none = 1;
1963 char *ptr = buf;
1964
1965 assert( buf != NULL );
1966
1967 if ( ACL_IS_INVALID( mask ) ) {
1968 return "invalid";
1969 }
1970
1971 buf[0] = '\0';
1972
1973 if ( ACL_IS_LEVEL( mask ) ) {
1974 if ( ACL_LVL_IS_NONE(mask) ) {
1975 ptr = lutil_strcopy( ptr, "none" );
1976
1977 } else if ( ACL_LVL_IS_DISCLOSE(mask) ) {
1978 ptr = lutil_strcopy( ptr, "disclose" );
1979
1980 } else if ( ACL_LVL_IS_AUTH(mask) ) {
1981 ptr = lutil_strcopy( ptr, "auth" );
1982
1983 } else if ( ACL_LVL_IS_COMPARE(mask) ) {
1984 ptr = lutil_strcopy( ptr, "compare" );
1985
1986 } else if ( ACL_LVL_IS_SEARCH(mask) ) {
1987 ptr = lutil_strcopy( ptr, "search" );
1988
1989 } else if ( ACL_LVL_IS_READ(mask) ) {
1990 ptr = lutil_strcopy( ptr, "read" );
1991
1992 } else if ( ACL_LVL_IS_WRITE(mask) ) {
1993 ptr = lutil_strcopy( ptr, "write" );
1994
1995 } else if ( ACL_LVL_IS_WADD(mask) ) {
1996 ptr = lutil_strcopy( ptr, "add" );
1997
1998 } else if ( ACL_LVL_IS_WDEL(mask) ) {
1999 ptr = lutil_strcopy( ptr, "delete" );
2000
2001 } else if ( ACL_LVL_IS_MANAGE(mask) ) {
2002 ptr = lutil_strcopy( ptr, "manage" );
2003
2004 } else {
2005 ptr = lutil_strcopy( ptr, "unknown" );
2006 }
2007
2008 if ( !debug ) {
2009 *ptr = '\0';
2010 return buf;
2011 }
2012 *ptr++ = '(';
2013 }
2014
2015 if( ACL_IS_ADDITIVE( mask ) ) {
2016 *ptr++ = '+';
2017
2018 } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
2019 *ptr++ = '-';
2020
2021 } else {
2022 *ptr++ = '=';
2023 }
2024
2025 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_MANAGE) ) {
2026 none = 0;
2027 *ptr++ = 'm';
2028 }
2029
2030 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
2031 none = 0;
2032 *ptr++ = 'w';
2033
2034 } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WADD) ) {
2035 none = 0;
2036 *ptr++ = 'a';
2037
2038 } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WDEL) ) {
2039 none = 0;
2040 *ptr++ = 'z';
2041 }
2042
2043 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
2044 none = 0;
2045 *ptr++ = 'r';
2046 }
2047
2048 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
2049 none = 0;
2050 *ptr++ = 's';
2051 }
2052
2053 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
2054 none = 0;
2055 *ptr++ = 'c';
2056 }
2057
2058 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
2059 none = 0;
2060 *ptr++ = 'x';
2061 }
2062
2063 if ( ACL_PRIV_ISSET(mask, ACL_PRIV_DISCLOSE) ) {
2064 none = 0;
2065 *ptr++ = 'd';
2066 }
2067
2068 if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
2069 none = 0;
2070 *ptr++ = '0';
2071 }
2072
2073 if ( none ) {
2074 ptr = buf;
2075 }
2076
2077 if ( ACL_IS_LEVEL( mask ) ) {
2078 *ptr++ = ')';
2079 }
2080
2081 *ptr = '\0';
2082
2083 return buf;
2084 }
2085
2086 slap_mask_t
str2accessmask(const char * str)2087 str2accessmask( const char *str )
2088 {
2089 slap_mask_t mask;
2090
2091 if( !ASCII_ALPHA(str[0]) ) {
2092 int i;
2093
2094 if ( str[0] == '=' ) {
2095 ACL_INIT(mask);
2096
2097 } else if( str[0] == '+' ) {
2098 ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
2099
2100 } else if( str[0] == '-' ) {
2101 ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
2102
2103 } else {
2104 ACL_INVALIDATE(mask);
2105 return mask;
2106 }
2107
2108 for( i=1; str[i] != '\0'; i++ ) {
2109 if( TOLOWER((unsigned char) str[i]) == 'm' ) {
2110 ACL_PRIV_SET(mask, ACL_PRIV_MANAGE);
2111
2112 } else if( TOLOWER((unsigned char) str[i]) == 'w' ) {
2113 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
2114
2115 } else if( TOLOWER((unsigned char) str[i]) == 'a' ) {
2116 ACL_PRIV_SET(mask, ACL_PRIV_WADD);
2117
2118 } else if( TOLOWER((unsigned char) str[i]) == 'z' ) {
2119 ACL_PRIV_SET(mask, ACL_PRIV_WDEL);
2120
2121 } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
2122 ACL_PRIV_SET(mask, ACL_PRIV_READ);
2123
2124 } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
2125 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
2126
2127 } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
2128 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
2129
2130 } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
2131 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
2132
2133 } else if( TOLOWER((unsigned char) str[i]) == 'd' ) {
2134 ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
2135
2136 } else if( str[i] == '0' ) {
2137 ACL_PRIV_SET(mask, ACL_PRIV_NONE);
2138
2139 } else {
2140 ACL_INVALIDATE(mask);
2141 return mask;
2142 }
2143 }
2144
2145 return mask;
2146 }
2147
2148 if ( strcasecmp( str, "none" ) == 0 ) {
2149 ACL_LVL_ASSIGN_NONE(mask);
2150
2151 } else if ( strcasecmp( str, "disclose" ) == 0 ) {
2152 ACL_LVL_ASSIGN_DISCLOSE(mask);
2153
2154 } else if ( strcasecmp( str, "auth" ) == 0 ) {
2155 ACL_LVL_ASSIGN_AUTH(mask);
2156
2157 } else if ( strcasecmp( str, "compare" ) == 0 ) {
2158 ACL_LVL_ASSIGN_COMPARE(mask);
2159
2160 } else if ( strcasecmp( str, "search" ) == 0 ) {
2161 ACL_LVL_ASSIGN_SEARCH(mask);
2162
2163 } else if ( strcasecmp( str, "read" ) == 0 ) {
2164 ACL_LVL_ASSIGN_READ(mask);
2165
2166 } else if ( strcasecmp( str, "add" ) == 0 ) {
2167 ACL_LVL_ASSIGN_WADD(mask);
2168
2169 } else if ( strcasecmp( str, "delete" ) == 0 ) {
2170 ACL_LVL_ASSIGN_WDEL(mask);
2171
2172 } else if ( strcasecmp( str, "write" ) == 0 ) {
2173 ACL_LVL_ASSIGN_WRITE(mask);
2174
2175 } else if ( strcasecmp( str, "manage" ) == 0 ) {
2176 ACL_LVL_ASSIGN_MANAGE(mask);
2177
2178 } else {
2179 ACL_INVALIDATE( mask );
2180 }
2181
2182 return mask;
2183 }
2184
2185 static int
acl_usage(void)2186 acl_usage( void )
2187 {
2188 char *access =
2189 "<access clause> ::= access to <what> "
2190 "[ by <who> [ <access> ] [ <control> ] ]+ \n";
2191 char *what =
2192 "<what> ::= * | dn[.<dnstyle>=<DN>] [filter=<filter>] [attrs=<attrspec>]\n"
2193 "<attrspec> ::= <attrname> [val[/<matchingRule>][.<attrstyle>]=<value>] | <attrlist>\n"
2194 "<attrlist> ::= <attr> [ , <attrlist> ]\n"
2195 "<attr> ::= <attrname> | @<objectClass> | !<objectClass> | entry | children\n";
2196
2197 char *who =
2198 "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
2199 "\t[ realanonymous | realusers | realself | realdn[.<dnstyle>]=<DN> ]\n"
2200 "\t[dnattr=<attrname>]\n"
2201 "\t[realdnattr=<attrname>]\n"
2202 "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
2203 "\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n"
2204 "\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n"
2205 #ifdef SLAP_DYNACL
2206 "\t[dynacl/<name>[/<options>][.<dynstyle>][=<pattern>]]\n"
2207 #endif /* SLAP_DYNACL */
2208 "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
2209 "<style> ::= exact | regex | base(Object)\n"
2210 "<dnstyle> ::= base(Object) | one(level) | sub(tree) | children | "
2211 "exact | regex\n"
2212 "<attrstyle> ::= exact | regex | base(Object) | one(level) | "
2213 "sub(tree) | children\n"
2214 "<peernamestyle> ::= exact | regex | ip | ipv6 | path\n"
2215 "<domainstyle> ::= exact | regex | base(Object) | sub(tree)\n"
2216 "<access> ::= [[real]self]{<level>|<priv>}\n"
2217 "<level> ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage\n"
2218 "<priv> ::= {=|+|-}{0|d|x|c|s|r|{w|a|z}|m}+\n"
2219 "<control> ::= [ stop | continue | break ]\n"
2220 #ifdef SLAP_DYNACL
2221 #ifdef SLAPD_ACI_ENABLED
2222 "dynacl:\n"
2223 "\t<name>=ACI\t<pattern>=<attrname>\n"
2224 #endif /* SLAPD_ACI_ENABLED */
2225 #endif /* ! SLAP_DYNACL */
2226 "";
2227
2228 Debug( LDAP_DEBUG_ANY, "%s%s%s\n", access, what, who );
2229
2230 return 1;
2231 }
2232
2233 /*
2234 * Set pattern to a "normalized" DN from src.
2235 * At present it simply eats the (optional) space after
2236 * a RDN separator (,)
2237 * Eventually will evolve in a more complete normalization
2238 */
2239 static void
acl_regex_normalized_dn(const char * src,struct berval * pattern)2240 acl_regex_normalized_dn(
2241 const char *src,
2242 struct berval *pattern )
2243 {
2244 char *str, *p;
2245 ber_len_t len;
2246
2247 str = ch_strdup( src );
2248 len = strlen( src );
2249
2250 for ( p = str; p && p[0]; p++ ) {
2251 /* escape */
2252 if ( p[0] == '\\' && p[1] ) {
2253 /*
2254 * if escaping a hex pair we should
2255 * increment p twice; however, in that
2256 * case the second hex number does
2257 * no harm
2258 */
2259 p++;
2260 }
2261
2262 if ( p[0] == ',' && p[1] == ' ' ) {
2263 char *q;
2264
2265 /*
2266 * too much space should be an error if we are pedantic
2267 */
2268 for ( q = &p[2]; q[0] == ' '; q++ ) {
2269 /* DO NOTHING */ ;
2270 }
2271 AC_MEMCPY( p+1, q, len-(q-str)+1);
2272 }
2273 }
2274 pattern->bv_val = str;
2275 pattern->bv_len = p - str;
2276
2277 return;
2278 }
2279
2280 static void
split(char * line,int splitchar,char ** left,char ** right)2281 split(
2282 char *line,
2283 int splitchar,
2284 char **left,
2285 char **right )
2286 {
2287 *left = line;
2288 if ( (*right = strchr( line, splitchar )) != NULL ) {
2289 *((*right)++) = '\0';
2290 }
2291 }
2292
2293 static void
access_append(Access ** l,Access * a)2294 access_append( Access **l, Access *a )
2295 {
2296 for ( ; *l != NULL; l = &(*l)->a_next ) {
2297 ; /* Empty */
2298 }
2299
2300 *l = a;
2301 }
2302
2303 void
acl_append(AccessControl ** l,AccessControl * a,int pos)2304 acl_append( AccessControl **l, AccessControl *a, int pos )
2305 {
2306 int i;
2307
2308 for (i=0 ; i != pos && *l != NULL; l = &(*l)->acl_next, i++ ) {
2309 ; /* Empty */
2310 }
2311 if ( *l && a )
2312 a->acl_next = *l;
2313 *l = a;
2314 }
2315
2316 static void
access_free(Access * a)2317 access_free( Access *a )
2318 {
2319 if ( !BER_BVISNULL( &a->a_dn_pat ) ) {
2320 free( a->a_dn_pat.bv_val );
2321 }
2322 if ( !BER_BVISNULL( &a->a_realdn_pat ) ) {
2323 free( a->a_realdn_pat.bv_val );
2324 }
2325 if ( !BER_BVISNULL( &a->a_peername_pat ) ) {
2326 free( a->a_peername_pat.bv_val );
2327 }
2328 if ( !BER_BVISNULL( &a->a_sockname_pat ) ) {
2329 free( a->a_sockname_pat.bv_val );
2330 }
2331 if ( !BER_BVISNULL( &a->a_domain_pat ) ) {
2332 free( a->a_domain_pat.bv_val );
2333 }
2334 if ( !BER_BVISNULL( &a->a_sockurl_pat ) ) {
2335 free( a->a_sockurl_pat.bv_val );
2336 }
2337 if ( !BER_BVISNULL( &a->a_set_pat ) ) {
2338 free( a->a_set_pat.bv_val );
2339 }
2340 if ( !BER_BVISNULL( &a->a_group_pat ) ) {
2341 free( a->a_group_pat.bv_val );
2342 }
2343 #ifdef SLAP_DYNACL
2344 if ( a->a_dynacl != NULL ) {
2345 slap_dynacl_t *da;
2346 for ( da = a->a_dynacl; da; ) {
2347 slap_dynacl_t *tmp = da;
2348
2349 da = da->da_next;
2350
2351 if ( tmp->da_destroy ) {
2352 tmp->da_destroy( tmp->da_private );
2353 }
2354
2355 ch_free( tmp );
2356 }
2357 }
2358 #endif /* SLAP_DYNACL */
2359 free( a );
2360 }
2361
2362 void
acl_free(AccessControl * a)2363 acl_free( AccessControl *a )
2364 {
2365 Access *n;
2366 AttributeName *an;
2367
2368 if ( a->acl_filter ) {
2369 filter_free( a->acl_filter );
2370 }
2371 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
2372 if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
2373 regfree( &a->acl_dn_re );
2374 }
2375 free ( a->acl_dn_pat.bv_val );
2376 }
2377 if ( a->acl_attrs ) {
2378 for ( an = a->acl_attrs; !BER_BVISNULL( &an->an_name ); an++ ) {
2379 free( an->an_name.bv_val );
2380 }
2381 free( a->acl_attrs );
2382
2383 if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
2384 regfree( &a->acl_attrval_re );
2385 }
2386
2387 if ( !BER_BVISNULL( &a->acl_attrval ) ) {
2388 ber_memfree( a->acl_attrval.bv_val );
2389 }
2390 }
2391 for ( ; a->acl_access; a->acl_access = n ) {
2392 n = a->acl_access->a_next;
2393 access_free( a->acl_access );
2394 }
2395 free( a );
2396 }
2397
2398 void
acl_destroy(AccessControl * a)2399 acl_destroy( AccessControl *a )
2400 {
2401 AccessControl *n;
2402
2403 for ( ; a; a = n ) {
2404 n = a->acl_next;
2405 acl_free( a );
2406 }
2407
2408 if ( !BER_BVISNULL( &aclbuf ) ) {
2409 ch_free( aclbuf.bv_val );
2410 BER_BVZERO( &aclbuf );
2411 }
2412 }
2413
2414 char *
access2str(slap_access_t access)2415 access2str( slap_access_t access )
2416 {
2417 if ( access == ACL_NONE ) {
2418 return "none";
2419
2420 } else if ( access == ACL_DISCLOSE ) {
2421 return "disclose";
2422
2423 } else if ( access == ACL_AUTH ) {
2424 return "auth";
2425
2426 } else if ( access == ACL_COMPARE ) {
2427 return "compare";
2428
2429 } else if ( access == ACL_SEARCH ) {
2430 return "search";
2431
2432 } else if ( access == ACL_READ ) {
2433 return "read";
2434
2435 } else if ( access == ACL_WRITE ) {
2436 return "write";
2437
2438 } else if ( access == ACL_WADD ) {
2439 return "add";
2440
2441 } else if ( access == ACL_WDEL ) {
2442 return "delete";
2443
2444 } else if ( access == ACL_MANAGE ) {
2445 return "manage";
2446
2447 }
2448
2449 return "unknown";
2450 }
2451
2452 slap_access_t
str2access(const char * str)2453 str2access( const char *str )
2454 {
2455 if ( strcasecmp( str, "none" ) == 0 ) {
2456 return ACL_NONE;
2457
2458 } else if ( strcasecmp( str, "disclose" ) == 0 ) {
2459 return ACL_DISCLOSE;
2460
2461 } else if ( strcasecmp( str, "auth" ) == 0 ) {
2462 return ACL_AUTH;
2463
2464 } else if ( strcasecmp( str, "compare" ) == 0 ) {
2465 return ACL_COMPARE;
2466
2467 } else if ( strcasecmp( str, "search" ) == 0 ) {
2468 return ACL_SEARCH;
2469
2470 } else if ( strcasecmp( str, "read" ) == 0 ) {
2471 return ACL_READ;
2472
2473 } else if ( strcasecmp( str, "write" ) == 0 ) {
2474 return ACL_WRITE;
2475
2476 } else if ( strcasecmp( str, "add" ) == 0 ) {
2477 return ACL_WADD;
2478
2479 } else if ( strcasecmp( str, "delete" ) == 0 ) {
2480 return ACL_WDEL;
2481
2482 } else if ( strcasecmp( str, "manage" ) == 0 ) {
2483 return ACL_MANAGE;
2484 }
2485
2486 return( ACL_INVALID_ACCESS );
2487 }
2488
2489 static char *
safe_strncopy(char * ptr,const char * src,size_t n,struct berval * buf)2490 safe_strncopy( char *ptr, const char *src, size_t n, struct berval *buf )
2491 {
2492 while ( ptr + n >= buf->bv_val + buf->bv_len ) {
2493 char *tmp = ch_realloc( buf->bv_val, 2*buf->bv_len );
2494 if ( tmp == NULL ) {
2495 return NULL;
2496 }
2497 ptr = tmp + (ptr - buf->bv_val);
2498 buf->bv_val = tmp;
2499 buf->bv_len *= 2;
2500 }
2501
2502 return lutil_strncopy( ptr, src, n );
2503 }
2504
2505 static char *
safe_strcopy(char * ptr,const char * s,struct berval * buf)2506 safe_strcopy( char *ptr, const char *s, struct berval *buf )
2507 {
2508 size_t n = strlen( s );
2509
2510 return safe_strncopy( ptr, s, n, buf );
2511 }
2512
2513 static char *
safe_strbvcopy(char * ptr,const struct berval * bv,struct berval * buf)2514 safe_strbvcopy( char *ptr, const struct berval *bv, struct berval *buf )
2515 {
2516 return safe_strncopy( ptr, bv->bv_val, bv->bv_len, buf );
2517 }
2518
2519 #define acl_safe_strcopy( ptr, s ) safe_strcopy( (ptr), (s), &aclbuf )
2520 #define acl_safe_strncopy( ptr, s, n ) safe_strncopy( (ptr), (s), (n), &aclbuf )
2521 #define acl_safe_strbvcopy( ptr, bv ) safe_strbvcopy( (ptr), (bv), &aclbuf )
2522
2523 static char *
dnaccess2text(slap_dn_access * bdn,char * ptr,int is_realdn)2524 dnaccess2text( slap_dn_access *bdn, char *ptr, int is_realdn )
2525 {
2526 *ptr++ = ' ';
2527
2528 if ( is_realdn ) {
2529 ptr = acl_safe_strcopy( ptr, "real" );
2530 }
2531
2532 if ( ber_bvccmp( &bdn->a_pat, '*' ) ||
2533 bdn->a_style == ACL_STYLE_ANONYMOUS ||
2534 bdn->a_style == ACL_STYLE_USERS ||
2535 bdn->a_style == ACL_STYLE_SELF )
2536 {
2537 if ( is_realdn ) {
2538 assert( ! ber_bvccmp( &bdn->a_pat, '*' ) );
2539 }
2540
2541 ptr = acl_safe_strbvcopy( ptr, &bdn->a_pat );
2542 if ( bdn->a_style == ACL_STYLE_SELF && bdn->a_self_level != 0 ) {
2543 char buf[SLAP_TEXT_BUFLEN];
2544 int n = snprintf( buf, sizeof(buf), ".level{%d}", bdn->a_self_level );
2545 if ( n > 0 ) {
2546 ptr = acl_safe_strncopy( ptr, buf, n );
2547 } /* else ? */
2548 }
2549
2550 } else {
2551 ptr = acl_safe_strcopy( ptr, "dn." );
2552 if ( bdn->a_style == ACL_STYLE_BASE )
2553 ptr = acl_safe_strcopy( ptr, style_base );
2554 else
2555 ptr = acl_safe_strcopy( ptr, style_strings[bdn->a_style] );
2556 if ( bdn->a_style == ACL_STYLE_LEVEL ) {
2557 char buf[SLAP_TEXT_BUFLEN];
2558 int n = snprintf( buf, sizeof(buf), "{%d}", bdn->a_level );
2559 if ( n > 0 ) {
2560 ptr = acl_safe_strncopy( ptr, buf, n );
2561 } /* else ? */
2562 }
2563 if ( bdn->a_expand ) {
2564 ptr = acl_safe_strcopy( ptr, ",expand" );
2565 }
2566 ptr = acl_safe_strcopy( ptr, "=\"" );
2567 ptr = acl_safe_strbvcopy( ptr, &bdn->a_pat );
2568 ptr = acl_safe_strcopy( ptr, "\"" );
2569 }
2570 return ptr;
2571 }
2572
2573 static char *
access2text(Access * b,char * ptr)2574 access2text( Access *b, char *ptr )
2575 {
2576 char maskbuf[ACCESSMASK_MAXLEN];
2577
2578 ptr = acl_safe_strcopy( ptr, "\tby" );
2579
2580 if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
2581 ptr = dnaccess2text( &b->a_dn, ptr, 0 );
2582 }
2583 if ( b->a_dn_at ) {
2584 ptr = acl_safe_strcopy( ptr, " dnattr=" );
2585 ptr = acl_safe_strbvcopy( ptr, &b->a_dn_at->ad_cname );
2586 }
2587
2588 if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
2589 ptr = dnaccess2text( &b->a_realdn, ptr, 1 );
2590 }
2591 if ( b->a_realdn_at ) {
2592 ptr = acl_safe_strcopy( ptr, " realdnattr=" );
2593 ptr = acl_safe_strbvcopy( ptr, &b->a_realdn_at->ad_cname );
2594 }
2595
2596 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
2597 ptr = acl_safe_strcopy( ptr, " group/" );
2598 ptr = acl_safe_strcopy( ptr, b->a_group_oc ?
2599 b->a_group_oc->soc_cname.bv_val : SLAPD_GROUP_CLASS );
2600 ptr = acl_safe_strcopy( ptr, "/" );
2601 ptr = acl_safe_strcopy( ptr, b->a_group_at ?
2602 b->a_group_at->ad_cname.bv_val : SLAPD_GROUP_ATTR );
2603 ptr = acl_safe_strcopy( ptr, "." );
2604 ptr = acl_safe_strcopy( ptr, style_strings[b->a_group_style] );
2605 ptr = acl_safe_strcopy( ptr, "=\"" );
2606 ptr = acl_safe_strbvcopy( ptr, &b->a_group_pat );
2607 ptr = acl_safe_strcopy( ptr, "\"" );
2608 }
2609
2610 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
2611 ptr = acl_safe_strcopy( ptr, " peername" );
2612 ptr = acl_safe_strcopy( ptr, "." );
2613 ptr = acl_safe_strcopy( ptr, style_strings[b->a_peername_style] );
2614 ptr = acl_safe_strcopy( ptr, "=\"" );
2615 ptr = acl_safe_strbvcopy( ptr, &b->a_peername_pat );
2616 ptr = acl_safe_strcopy( ptr, "\"" );
2617 }
2618
2619 if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
2620 ptr = acl_safe_strcopy( ptr, " sockname" );
2621 ptr = acl_safe_strcopy( ptr, "." );
2622 ptr = acl_safe_strcopy( ptr, style_strings[b->a_sockname_style] );
2623 ptr = acl_safe_strcopy( ptr, "=\"" );
2624 ptr = acl_safe_strbvcopy( ptr, &b->a_sockname_pat );
2625 ptr = acl_safe_strcopy( ptr, "\"" );
2626 }
2627
2628 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
2629 ptr = acl_safe_strcopy( ptr, " domain" );
2630 ptr = acl_safe_strcopy( ptr, "." );
2631 ptr = acl_safe_strcopy( ptr, style_strings[b->a_domain_style] );
2632 if ( b->a_domain_expand ) {
2633 ptr = acl_safe_strcopy( ptr, ",expand" );
2634 }
2635 ptr = acl_safe_strcopy( ptr, "=" );
2636 ptr = acl_safe_strbvcopy( ptr, &b->a_domain_pat );
2637 }
2638
2639 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
2640 ptr = acl_safe_strcopy( ptr, " sockurl" );
2641 ptr = acl_safe_strcopy( ptr, "." );
2642 ptr = acl_safe_strcopy( ptr, style_strings[b->a_sockurl_style] );
2643 ptr = acl_safe_strcopy( ptr, "=\"" );
2644 ptr = acl_safe_strbvcopy( ptr, &b->a_sockurl_pat );
2645 ptr = acl_safe_strcopy( ptr, "\"" );
2646 }
2647
2648 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
2649 ptr = acl_safe_strcopy( ptr, " set" );
2650 ptr = acl_safe_strcopy( ptr, "." );
2651 ptr = acl_safe_strcopy( ptr, style_strings[b->a_set_style] );
2652 ptr = acl_safe_strcopy( ptr, "=\"" );
2653 ptr = acl_safe_strbvcopy( ptr, &b->a_set_pat );
2654 ptr = acl_safe_strcopy( ptr, "\"" );
2655 }
2656
2657 #ifdef SLAP_DYNACL
2658 if ( b->a_dynacl ) {
2659 slap_dynacl_t *da;
2660
2661 for ( da = b->a_dynacl; da; da = da->da_next ) {
2662 if ( da->da_unparse ) {
2663 struct berval bv = BER_BVNULL;
2664 (void)( *da->da_unparse )( da->da_private, &bv );
2665 assert( !BER_BVISNULL( &bv ) );
2666 ptr = acl_safe_strbvcopy( ptr, &bv );
2667 ch_free( bv.bv_val );
2668 }
2669 }
2670 }
2671 #endif /* SLAP_DYNACL */
2672
2673 /* Security Strength Factors */
2674 if ( b->a_authz.sai_ssf ) {
2675 char buf[SLAP_TEXT_BUFLEN];
2676 int n = snprintf( buf, sizeof(buf), " ssf=%u",
2677 b->a_authz.sai_ssf );
2678 ptr = acl_safe_strncopy( ptr, buf, n );
2679 }
2680 if ( b->a_authz.sai_transport_ssf ) {
2681 char buf[SLAP_TEXT_BUFLEN];
2682 int n = snprintf( buf, sizeof(buf), " transport_ssf=%u",
2683 b->a_authz.sai_transport_ssf );
2684 ptr = acl_safe_strncopy( ptr, buf, n );
2685 }
2686 if ( b->a_authz.sai_tls_ssf ) {
2687 char buf[SLAP_TEXT_BUFLEN];
2688 int n = snprintf( buf, sizeof(buf), " tls_ssf=%u",
2689 b->a_authz.sai_tls_ssf );
2690 ptr = acl_safe_strncopy( ptr, buf, n );
2691 }
2692 if ( b->a_authz.sai_sasl_ssf ) {
2693 char buf[SLAP_TEXT_BUFLEN];
2694 int n = snprintf( buf, sizeof(buf), " sasl_ssf=%u",
2695 b->a_authz.sai_sasl_ssf );
2696 ptr = acl_safe_strncopy( ptr, buf, n );
2697 }
2698
2699 ptr = acl_safe_strcopy( ptr, " " );
2700 if ( b->a_dn_self ) {
2701 ptr = acl_safe_strcopy( ptr, "self" );
2702 } else if ( b->a_realdn_self ) {
2703 ptr = acl_safe_strcopy( ptr, "realself" );
2704 }
2705 ptr = acl_safe_strcopy( ptr, accessmask2str( b->a_access_mask, maskbuf, 0 ));
2706 if ( !maskbuf[0] ) ptr--;
2707
2708 if( b->a_type == ACL_BREAK ) {
2709 ptr = acl_safe_strcopy( ptr, " break" );
2710
2711 } else if( b->a_type == ACL_CONTINUE ) {
2712 ptr = acl_safe_strcopy( ptr, " continue" );
2713
2714 } else if( b->a_type != ACL_STOP ) {
2715 ptr = acl_safe_strcopy( ptr, " unknown-control" );
2716 } else {
2717 if ( !maskbuf[0] ) ptr = acl_safe_strcopy( ptr, " stop" );
2718 }
2719 ptr = acl_safe_strcopy( ptr, "\n" );
2720
2721 return ptr;
2722 }
2723
2724 void
acl_unparse(AccessControl * a,struct berval * bv)2725 acl_unparse( AccessControl *a, struct berval *bv )
2726 {
2727 Access *b;
2728 char *ptr;
2729 int to = 0;
2730
2731 if ( BER_BVISNULL( &aclbuf ) ) {
2732 aclbuf.bv_val = ch_malloc( ACLBUF_CHUNKSIZE );
2733 aclbuf.bv_len = ACLBUF_CHUNKSIZE;
2734 }
2735
2736 bv->bv_len = 0;
2737
2738 ptr = aclbuf.bv_val;
2739
2740 ptr = acl_safe_strcopy( ptr, "to" );
2741 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
2742 to++;
2743 ptr = acl_safe_strcopy( ptr, " dn." );
2744 if ( a->acl_dn_style == ACL_STYLE_BASE )
2745 ptr = acl_safe_strcopy( ptr, style_base );
2746 else
2747 ptr = acl_safe_strcopy( ptr, style_strings[a->acl_dn_style] );
2748 ptr = acl_safe_strcopy( ptr, "=\"" );
2749 ptr = acl_safe_strbvcopy( ptr, &a->acl_dn_pat );
2750 ptr = acl_safe_strcopy( ptr, "\"\n" );
2751 }
2752
2753 if ( a->acl_filter != NULL ) {
2754 struct berval fbv = BER_BVNULL;
2755
2756 to++;
2757 filter2bv( a->acl_filter, &fbv );
2758 ptr = acl_safe_strcopy( ptr, " filter=\"" );
2759 ptr = acl_safe_strbvcopy( ptr, &fbv );
2760 ptr = acl_safe_strcopy( ptr, "\"\n" );
2761 ch_free( fbv.bv_val );
2762 }
2763
2764 if ( a->acl_attrs != NULL ) {
2765 int first = 1;
2766 AttributeName *an;
2767 to++;
2768
2769 ptr = acl_safe_strcopy( ptr, " attrs=" );
2770 for ( an = a->acl_attrs; an && !BER_BVISNULL( &an->an_name ); an++ ) {
2771 if ( ! first ) ptr = acl_safe_strcopy( ptr, ",");
2772 if (an->an_oc) {
2773 ptr = acl_safe_strcopy( ptr, ( an->an_flags & SLAP_AN_OCEXCLUDE ) ? "!" : "@" );
2774 ptr = acl_safe_strbvcopy( ptr, &an->an_oc->soc_cname );
2775
2776 } else {
2777 ptr = acl_safe_strbvcopy( ptr, &an->an_name );
2778 }
2779 first = 0;
2780 }
2781 ptr = acl_safe_strcopy( ptr, "\n" );
2782 }
2783
2784 if ( !BER_BVISNULL( &a->acl_attrval ) ) {
2785 to++;
2786 ptr = acl_safe_strcopy( ptr, " val." );
2787 if ( a->acl_attrval_style == ACL_STYLE_BASE &&
2788 a->acl_attrs[0].an_desc->ad_type->sat_syntax ==
2789 slap_schema.si_syn_distinguishedName )
2790 ptr = acl_safe_strcopy( ptr, style_base );
2791 else
2792 ptr = acl_safe_strcopy( ptr, style_strings[a->acl_attrval_style] );
2793 ptr = acl_safe_strcopy( ptr, "=\"" );
2794 ptr = acl_safe_strbvcopy( ptr, &a->acl_attrval );
2795 ptr = acl_safe_strcopy( ptr, "\"\n" );
2796 }
2797
2798 if ( !to ) {
2799 ptr = acl_safe_strcopy( ptr, " *\n" );
2800 }
2801
2802 for ( b = a->acl_access; b != NULL; b = b->a_next ) {
2803 ptr = access2text( b, ptr );
2804 }
2805 *ptr = '\0';
2806 bv->bv_val = aclbuf.bv_val;
2807 bv->bv_len = ptr - bv->bv_val;
2808 }
2809
2810 #ifdef LDAP_DEBUG
2811 static void
print_acl(Backend * be,AccessControl * a)2812 print_acl( Backend *be, AccessControl *a )
2813 {
2814 struct berval bv;
2815
2816 acl_unparse( a, &bv );
2817 fprintf( stderr, "%s ACL: access %s\n",
2818 be == NULL ? "Global" : "Backend", bv.bv_val );
2819 }
2820 #endif /* LDAP_DEBUG */
2821