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