1 /* acl.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-2021 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/regex.h>
32 #include <ac/socket.h>
33 #include <ac/string.h>
34
35 #include "slap.h"
36 #include "sets.h"
37 #include "lber_pvt.h"
38 #include "lutil.h"
39
40 #define ACL_BUF_SIZE 1024 /* use most appropriate size */
41
42 static const struct berval acl_bv_ip_eq = BER_BVC( "IP=" );
43 #ifdef LDAP_PF_INET6
44 static const struct berval acl_bv_ipv6_eq = BER_BVC( "IP=[" );
45 #endif /* LDAP_PF_INET6 */
46 #ifdef LDAP_PF_LOCAL
47 static const struct berval acl_bv_path_eq = BER_BVC("PATH=");
48 #endif /* LDAP_PF_LOCAL */
49
50 static AccessControl * slap_acl_get(
51 AccessControl *ac, int *count,
52 Operation *op, Entry *e,
53 AttributeDescription *desc,
54 struct berval *val,
55 AclRegexMatches *matches,
56 slap_mask_t *mask,
57 AccessControlState *state );
58
59 static slap_control_t slap_acl_mask(
60 AccessControl *ac,
61 AccessControl *prev,
62 slap_mask_t *mask,
63 Operation *op, Entry *e,
64 AttributeDescription *desc,
65 struct berval *val,
66 AclRegexMatches *matches,
67 int count,
68 AccessControlState *state,
69 slap_access_t access );
70
71 static int regex_matches(
72 struct berval *pat, char *str,
73 struct berval *dn_matches, struct berval *val_matches,
74 AclRegexMatches *matches);
75
76 typedef struct AclSetCookie {
77 SetCookie asc_cookie;
78 #define asc_op asc_cookie.set_op
79 Entry *asc_e;
80 } AclSetCookie;
81
82
83 SLAP_SET_GATHER acl_set_gather;
84 SLAP_SET_GATHER acl_set_gather2;
85
86 /*
87 * access_allowed - check whether op->o_ndn is allowed the requested access
88 * to entry e, attribute attr, value val. if val is null, access to
89 * the whole attribute is assumed (all values).
90 *
91 * This routine loops through all access controls and calls
92 * slap_acl_mask() on each applicable access control.
93 * The loop exits when a definitive answer is reached or
94 * or no more controls remain.
95 *
96 * returns:
97 * 0 access denied
98 * 1 access granted
99 *
100 * Notes:
101 * - can be legally called with op == NULL
102 * - can be legally called with op->o_bd == NULL
103 */
104
105 int
slap_access_always_allowed(Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,slap_access_t access,AccessControlState * state,slap_mask_t * maskp)106 slap_access_always_allowed(
107 Operation *op,
108 Entry *e,
109 AttributeDescription *desc,
110 struct berval *val,
111 slap_access_t access,
112 AccessControlState *state,
113 slap_mask_t *maskp )
114 {
115 assert( maskp != NULL );
116
117 /* assign all */
118 ACL_LVL_ASSIGN_MANAGE( *maskp );
119
120 return 1;
121 }
122
123 #define MATCHES_DNMAXCOUNT(m) \
124 ( sizeof ( (m)->dn_data ) / sizeof( *(m)->dn_data ) )
125 #define MATCHES_VALMAXCOUNT(m) \
126 ( sizeof ( (m)->val_data ) / sizeof( *(m)->val_data ) )
127 #define MATCHES_MEMSET(m) do { \
128 memset( (m)->dn_data, '\0', sizeof( (m)->dn_data ) ); \
129 memset( (m)->val_data, '\0', sizeof( (m)->val_data ) ); \
130 (m)->dn_count = MATCHES_DNMAXCOUNT( (m) ); \
131 (m)->val_count = MATCHES_VALMAXCOUNT( (m) ); \
132 } while ( 0 /* CONSTCOND */ )
133
134 int
slap_access_allowed(Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,slap_access_t access,AccessControlState * state,slap_mask_t * maskp)135 slap_access_allowed(
136 Operation *op,
137 Entry *e,
138 AttributeDescription *desc,
139 struct berval *val,
140 slap_access_t access,
141 AccessControlState *state,
142 slap_mask_t *maskp )
143 {
144 int ret = 1;
145 int count;
146 AccessControl *a, *prev;
147
148 #ifdef LDAP_DEBUG
149 char accessmaskbuf[ACCESSMASK_MAXLEN];
150 #endif
151 slap_mask_t mask;
152 slap_control_t control;
153 slap_access_t access_level;
154 const char *attr;
155 AclRegexMatches matches;
156 AccessControlState acl_state = ACL_STATE_INIT;
157 static AccessControlState state_init = ACL_STATE_INIT;
158
159 assert( op != NULL );
160 assert( e != NULL );
161 assert( desc != NULL );
162 assert( maskp != NULL );
163
164 access_level = ACL_LEVEL( access );
165 attr = desc->ad_cname.bv_val;
166
167 assert( attr != NULL );
168
169 ACL_INIT( mask );
170
171 /* grant database root access */
172 if ( be_isroot( op ) ) {
173 Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 );
174 mask = ACL_LVL_MANAGE;
175 goto done;
176 }
177
178 /*
179 * no-user-modification operational attributes are ignored
180 * by ACL_WRITE checking as any found here are not provided
181 * by the user
182 *
183 * NOTE: but they are not ignored for ACL_MANAGE, because
184 * if we get here it means a non-root user is trying to
185 * manage data, so we need to check its privileges.
186 */
187 if ( access_level == ACL_WRITE_
188 && is_at_no_user_mod( desc->ad_type )
189 && desc != slap_schema.si_ad_entry
190 && desc != slap_schema.si_ad_children )
191 {
192 Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
193 " %s access granted\n",
194 attr, 0, 0 );
195 goto done;
196 }
197
198 /* use backend default access if no backend acls */
199 if ( op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) {
200 int i;
201
202 Debug( LDAP_DEBUG_ACL,
203 "=> slap_access_allowed: backend default %s "
204 "access %s to \"%s\"\n",
205 access2str( access ),
206 op->o_bd->be_dfltaccess >= access_level ? "granted" : "denied",
207 op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
208 ret = op->o_bd->be_dfltaccess >= access_level;
209
210 mask = ACL_PRIV_LEVEL;
211 for ( i = ACL_NONE; i <= op->o_bd->be_dfltaccess; i++ ) {
212 ACL_PRIV_SET( mask, ACL_ACCESS2PRIV( i ) );
213 }
214
215 goto done;
216 }
217
218 ret = 0;
219 control = ACL_BREAK;
220
221 if ( state == NULL )
222 state = &acl_state;
223 if ( state->as_desc == desc &&
224 state->as_access == access &&
225 state->as_vd_acl_present )
226 {
227 a = state->as_vd_acl;
228 count = state->as_vd_acl_count;
229 if ( state->as_fe_done )
230 state->as_fe_done--;
231 ACL_PRIV_ASSIGN( mask, state->as_vd_mask );
232 } else {
233 *state = state_init;
234
235 a = NULL;
236 count = 0;
237 ACL_PRIV_ASSIGN( mask, *maskp );
238 }
239
240 MATCHES_MEMSET( &matches );
241 prev = a;
242
243 while ( ( a = slap_acl_get( a, &count, op, e, desc, val,
244 &matches, &mask, state ) ) != NULL )
245 {
246 int i;
247 int dnmaxcount = MATCHES_DNMAXCOUNT( &matches );
248 int valmaxcount = MATCHES_VALMAXCOUNT( &matches );
249 regmatch_t *dn_data = matches.dn_data;
250 regmatch_t *val_data = matches.val_data;
251
252 /* DN matches */
253 for ( i = 0; i < dnmaxcount && dn_data[i].rm_eo > 0; i++ ) {
254 char *data = e->e_ndn;
255
256 Debug( LDAP_DEBUG_ACL, "=> match[dn%d]: %d %d ", i,
257 (int)dn_data[i].rm_so,
258 (int)dn_data[i].rm_eo );
259 if ( dn_data[i].rm_so <= dn_data[0].rm_eo ) {
260 int n;
261 for ( n = dn_data[i].rm_so;
262 n < dn_data[i].rm_eo; n++ ) {
263 Debug( LDAP_DEBUG_ACL, "%c",
264 data[n], 0, 0 );
265 }
266 }
267 Debug( LDAP_DEBUG_ACL, "\n", 0, 0, 0 );
268 }
269
270 /* val matches */
271 for ( i = 0; i < valmaxcount && val_data[i].rm_eo > 0; i++ ) {
272 char *data = val->bv_val;
273
274 Debug( LDAP_DEBUG_ACL, "=> match[val%d]: %d %d ", i,
275 (int)val_data[i].rm_so,
276 (int)val_data[i].rm_eo );
277 if ( val_data[i].rm_so <= val_data[0].rm_eo ) {
278 int n;
279 for ( n = val_data[i].rm_so;
280 n < val_data[i].rm_eo; n++ ) {
281 Debug( LDAP_DEBUG_ACL, "%c",
282 data[n], 0, 0 );
283 }
284 }
285 Debug( LDAP_DEBUG_ACL, "\n", 0, 0, 0 );
286 }
287
288 control = slap_acl_mask( a, prev, &mask, op,
289 e, desc, val, &matches, count, state, access );
290
291 if ( control != ACL_BREAK ) {
292 break;
293 }
294
295 MATCHES_MEMSET( &matches );
296 prev = a;
297 }
298
299 if ( ACL_IS_INVALID( mask ) ) {
300 Debug( LDAP_DEBUG_ACL,
301 "=> slap_access_allowed: \"%s\" (%s) invalid!\n",
302 e->e_dn, attr, 0 );
303 ACL_PRIV_ASSIGN( mask, *maskp );
304
305 } else if ( control == ACL_BREAK ) {
306 Debug( LDAP_DEBUG_ACL,
307 "=> slap_access_allowed: no more rules\n", 0, 0, 0 );
308
309 goto done;
310 }
311
312 ret = ACL_GRANT( mask, access );
313
314 Debug( LDAP_DEBUG_ACL,
315 "=> slap_access_allowed: %s access %s by %s\n",
316 access2str( access ), ret ? "granted" : "denied",
317 accessmask2str( mask, accessmaskbuf, 1 ) );
318
319 done:
320 ACL_PRIV_ASSIGN( *maskp, mask );
321 return ret;
322 }
323
324 int
fe_access_allowed(Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,slap_access_t access,AccessControlState * state,slap_mask_t * maskp)325 fe_access_allowed(
326 Operation *op,
327 Entry *e,
328 AttributeDescription *desc,
329 struct berval *val,
330 slap_access_t access,
331 AccessControlState *state,
332 slap_mask_t *maskp )
333 {
334 BackendDB *be_orig;
335 int rc;
336
337 /*
338 * NOTE: control gets here if FIXME
339 * if an appropriate backend cannot be selected for the operation,
340 * we assume that the frontend should handle this
341 * FIXME: should select_backend() take care of this,
342 * and return frontendDB instead of NULL? maybe for some value
343 * of the flags?
344 */
345 be_orig = op->o_bd;
346
347 if ( op->o_bd == NULL ) {
348 op->o_bd = select_backend( &op->o_req_ndn, 0 );
349 if ( op->o_bd == NULL )
350 op->o_bd = frontendDB;
351 }
352 rc = slap_access_allowed( op, e, desc, val, access, state, maskp );
353 op->o_bd = be_orig;
354
355 return rc;
356 }
357
358 int
access_allowed_mask(Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,slap_access_t access,AccessControlState * state,slap_mask_t * maskp)359 access_allowed_mask(
360 Operation *op,
361 Entry *e,
362 AttributeDescription *desc,
363 struct berval *val,
364 slap_access_t access,
365 AccessControlState *state,
366 slap_mask_t *maskp )
367 {
368 int ret = 1;
369 int be_null = 0;
370
371 #ifdef LDAP_DEBUG
372 char accessmaskbuf[ACCESSMASK_MAXLEN];
373 #endif
374 slap_mask_t mask;
375 slap_access_t access_level;
376 const char *attr;
377
378 assert( e != NULL );
379 assert( desc != NULL );
380
381 access_level = ACL_LEVEL( access );
382
383 assert( access_level > ACL_NONE );
384
385 ACL_INIT( mask );
386 if ( maskp ) ACL_INVALIDATE( *maskp );
387
388 attr = desc->ad_cname.bv_val;
389
390 assert( attr != NULL );
391
392 if ( op ) {
393 if ( op->o_acl_priv != ACL_NONE ) {
394 access = op->o_acl_priv;
395
396 } else if ( op->o_is_auth_check &&
397 ( access_level == ACL_SEARCH || access_level == ACL_READ ) )
398 {
399 access = ACL_AUTH;
400
401 } else if ( get_relax( op ) && access_level == ACL_WRITE_ &&
402 desc == slap_schema.si_ad_entry )
403 {
404 access = ACL_MANAGE;
405 }
406 }
407
408 if ( state != NULL ) {
409 if ( state->as_desc == desc &&
410 state->as_access == access &&
411 state->as_result != -1 &&
412 !state->as_vd_acl_present )
413 {
414 Debug( LDAP_DEBUG_ACL,
415 "=> access_allowed: result was in cache (%s)\n",
416 attr, 0, 0 );
417 return state->as_result;
418 } else {
419 Debug( LDAP_DEBUG_ACL,
420 "=> access_allowed: result not in cache (%s)\n",
421 attr, 0, 0 );
422 }
423 }
424
425 Debug( LDAP_DEBUG_ACL,
426 "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
427 access2str( access ), e->e_dn, attr );
428
429 if ( op == NULL ) {
430 /* no-op call */
431 goto done;
432 }
433
434 if ( op->o_bd == NULL ) {
435 op->o_bd = LDAP_STAILQ_FIRST( &backendDB );
436 be_null = 1;
437
438 /* FIXME: experimental; use first backend rules
439 * iff there is no global_acl (ITS#3100)
440 */
441 if ( frontendDB->be_acl != NULL ) {
442 op->o_bd = frontendDB;
443 }
444 }
445 assert( op->o_bd != NULL );
446
447 /* this is enforced in backend_add() */
448 if ( op->o_bd->bd_info->bi_access_allowed ) {
449 /* delegate to backend */
450 ret = op->o_bd->bd_info->bi_access_allowed( op, e,
451 desc, val, access, state, &mask );
452
453 } else {
454 /* use default (but pass through frontend
455 * for global ACL overlays) */
456 ret = frontendDB->bd_info->bi_access_allowed( op, e,
457 desc, val, access, state, &mask );
458 }
459
460 if ( !ret ) {
461 if ( ACL_IS_INVALID( mask ) ) {
462 Debug( LDAP_DEBUG_ACL,
463 "=> access_allowed: \"%s\" (%s) invalid!\n",
464 e->e_dn, attr, 0 );
465 ACL_INIT( mask );
466
467 } else {
468 Debug( LDAP_DEBUG_ACL,
469 "=> access_allowed: no more rules\n", 0, 0, 0 );
470
471 goto done;
472 }
473 }
474
475 Debug( LDAP_DEBUG_ACL,
476 "=> access_allowed: %s access %s by %s\n",
477 access2str( access ), ret ? "granted" : "denied",
478 accessmask2str( mask, accessmaskbuf, 1 ) );
479
480 done:
481 if ( state != NULL ) {
482 state->as_access = access;
483 state->as_result = ret;
484 state->as_desc = desc;
485 }
486 if ( be_null ) op->o_bd = NULL;
487 if ( maskp ) ACL_PRIV_ASSIGN( *maskp, mask );
488 return ret;
489 }
490
491
492 /*
493 * slap_acl_get - return the acl applicable to entry e, attribute
494 * attr. the acl returned is suitable for use in subsequent calls to
495 * acl_access_allowed().
496 */
497
498 static AccessControl *
slap_acl_get(AccessControl * a,int * count,Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,AclRegexMatches * matches,slap_mask_t * mask,AccessControlState * state)499 slap_acl_get(
500 AccessControl *a,
501 int *count,
502 Operation *op,
503 Entry *e,
504 AttributeDescription *desc,
505 struct berval *val,
506 AclRegexMatches *matches,
507 slap_mask_t *mask,
508 AccessControlState *state )
509 {
510 const char *attr;
511 ber_len_t dnlen;
512 AccessControl *prev;
513
514 assert( e != NULL );
515 assert( count != NULL );
516 assert( desc != NULL );
517 assert( state != NULL );
518
519 attr = desc->ad_cname.bv_val;
520
521 assert( attr != NULL );
522
523 if( a == NULL ) {
524 if( op->o_bd == NULL || op->o_bd->be_acl == NULL ) {
525 a = frontendDB->be_acl;
526 } else {
527 a = op->o_bd->be_acl;
528 }
529 prev = NULL;
530
531 assert( a != NULL );
532 if ( a == frontendDB->be_acl )
533 state->as_fe_done = 1;
534 } else {
535 prev = a;
536 a = a->acl_next;
537 }
538
539 dnlen = e->e_nname.bv_len;
540
541 retry:
542 for ( ; a != NULL; prev = a, a = a->acl_next ) {
543 (*count) ++;
544
545 if ( a != frontendDB->be_acl && state->as_fe_done )
546 state->as_fe_done++;
547
548 if ( a->acl_dn_pat.bv_len || ( a->acl_dn_style != ACL_STYLE_REGEX )) {
549 if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
550 Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n",
551 *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub );
552 if ( regexec ( &a->acl_dn_re,
553 e->e_ndn,
554 matches->dn_count,
555 matches->dn_data, 0 ) )
556 continue;
557
558 } else {
559 ber_len_t patlen;
560
561 Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n",
562 *count, a->acl_dn_pat.bv_val, 0 );
563 patlen = a->acl_dn_pat.bv_len;
564 if ( dnlen < patlen )
565 continue;
566
567 if ( a->acl_dn_style == ACL_STYLE_BASE ) {
568 /* base dn -- entire object DN must match */
569 if ( dnlen != patlen )
570 continue;
571
572 } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
573 ber_len_t rdnlen = 0;
574 ber_len_t sep = 0;
575
576 if ( dnlen <= patlen )
577 continue;
578
579 if ( patlen > 0 ) {
580 if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
581 continue;
582 sep = 1;
583 }
584
585 rdnlen = dn_rdnlen( NULL, &e->e_nname );
586 if ( rdnlen + patlen + sep != dnlen )
587 continue;
588
589 } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
590 if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
591 continue;
592
593 } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
594 if ( dnlen <= patlen )
595 continue;
596 if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
597 continue;
598 }
599
600 if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
601 continue;
602 }
603
604 Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
605 *count, 0, 0 );
606 }
607
608 if ( a->acl_attrs && !ad_inlist( desc, a->acl_attrs ) ) {
609 matches->dn_data[0].rm_so = -1;
610 matches->dn_data[0].rm_eo = -1;
611 matches->val_data[0].rm_so = -1;
612 matches->val_data[0].rm_eo = -1;
613 continue;
614 }
615
616 /* Is this ACL only for a specific value? */
617 if ( a->acl_attrval.bv_val ) {
618 if ( val == NULL ) {
619 continue;
620 }
621
622 if ( !state->as_vd_acl_present ) {
623 state->as_vd_acl_present = 1;
624 state->as_vd_acl = prev;
625 state->as_vd_acl_count = *count - 1;
626 ACL_PRIV_ASSIGN ( state->as_vd_mask, *mask );
627 }
628
629 if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
630 Debug( LDAP_DEBUG_ACL,
631 "acl_get: valpat %s\n",
632 a->acl_attrval.bv_val, 0, 0 );
633 if ( regexec ( &a->acl_attrval_re,
634 val->bv_val,
635 matches->val_count,
636 matches->val_data, 0 ) )
637 {
638 continue;
639 }
640
641 } else {
642 int match = 0;
643 const char *text;
644 Debug( LDAP_DEBUG_ACL,
645 "acl_get: val %s\n",
646 a->acl_attrval.bv_val, 0, 0 );
647
648 if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
649 if (value_match( &match, desc,
650 a->acl_attrval_mr, 0,
651 val, &a->acl_attrval, &text ) != LDAP_SUCCESS ||
652 match )
653 continue;
654
655 } else {
656 ber_len_t patlen, vdnlen;
657
658 patlen = a->acl_attrval.bv_len;
659 vdnlen = val->bv_len;
660
661 if ( vdnlen < patlen )
662 continue;
663
664 if ( a->acl_attrval_style == ACL_STYLE_BASE ) {
665 if ( vdnlen > patlen )
666 continue;
667
668 } else if ( a->acl_attrval_style == ACL_STYLE_ONE ) {
669 ber_len_t rdnlen = 0;
670
671 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
672 continue;
673
674 rdnlen = dn_rdnlen( NULL, val );
675 if ( rdnlen + patlen + 1 != vdnlen )
676 continue;
677
678 } else if ( a->acl_attrval_style == ACL_STYLE_SUBTREE ) {
679 if ( vdnlen > patlen && !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
680 continue;
681
682 } else if ( a->acl_attrval_style == ACL_STYLE_CHILDREN ) {
683 if ( vdnlen <= patlen )
684 continue;
685
686 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
687 continue;
688 }
689
690 if ( strcmp( a->acl_attrval.bv_val, val->bv_val + vdnlen - patlen ) )
691 continue;
692 }
693 }
694 }
695
696 if ( a->acl_filter != NULL ) {
697 ber_int_t rc = test_filter( NULL, e, a->acl_filter );
698 if ( rc != LDAP_COMPARE_TRUE ) {
699 continue;
700 }
701 }
702
703 Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] attr %s\n",
704 *count, attr, 0);
705 return a;
706 }
707
708 if ( !state->as_fe_done ) {
709 state->as_fe_done = 1;
710 a = frontendDB->be_acl;
711 goto retry;
712 }
713
714 Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
715 return( NULL );
716 }
717
718 /*
719 * Record value-dependent access control state
720 */
721 #define ACL_RECORD_VALUE_STATE do { \
722 if( state && !state->as_vd_acl_present ) { \
723 state->as_vd_acl_present = 1; \
724 state->as_vd_acl = prev; \
725 state->as_vd_acl_count = count - 1; \
726 ACL_PRIV_ASSIGN( state->as_vd_mask, *mask ); \
727 } \
728 } while( 0 )
729
730 static int
acl_mask_dn(Operation * op,Entry * e,struct berval * val,AccessControl * a,AclRegexMatches * matches,slap_dn_access * bdn,struct berval * opndn)731 acl_mask_dn(
732 Operation *op,
733 Entry *e,
734 struct berval *val,
735 AccessControl *a,
736 AclRegexMatches *matches,
737 slap_dn_access *bdn,
738 struct berval *opndn )
739 {
740 /*
741 * if access applies to the entry itself, and the
742 * user is bound as somebody in the same namespace as
743 * the entry, OR the given dn matches the dn pattern
744 */
745 /*
746 * NOTE: styles "anonymous", "users" and "self"
747 * have been moved to enum slap_style_t, whose
748 * value is set in a_dn_style; however, the string
749 * is maintained in a_dn_pat.
750 */
751
752 if ( bdn->a_style == ACL_STYLE_ANONYMOUS ) {
753 if ( !BER_BVISEMPTY( opndn ) ) {
754 return 1;
755 }
756
757 } else if ( bdn->a_style == ACL_STYLE_USERS ) {
758 if ( BER_BVISEMPTY( opndn ) ) {
759 return 1;
760 }
761
762 } else if ( bdn->a_style == ACL_STYLE_SELF ) {
763 struct berval ndn, selfndn;
764 int level;
765
766 if ( BER_BVISEMPTY( opndn ) || BER_BVISNULL( &e->e_nname ) ) {
767 return 1;
768 }
769
770 level = bdn->a_self_level;
771 if ( level < 0 ) {
772 selfndn = *opndn;
773 ndn = e->e_nname;
774 level = -level;
775
776 } else {
777 ndn = *opndn;
778 selfndn = e->e_nname;
779 }
780
781 for ( ; level > 0; level-- ) {
782 if ( BER_BVISEMPTY( &ndn ) ) {
783 break;
784 }
785 dnParent( &ndn, &ndn );
786 }
787
788 if ( BER_BVISEMPTY( &ndn ) || !dn_match( &ndn, &selfndn ) )
789 {
790 return 1;
791 }
792
793 } else if ( bdn->a_style == ACL_STYLE_REGEX ) {
794 if ( !ber_bvccmp( &bdn->a_pat, '*' ) ) {
795 AclRegexMatches tmp_matches,
796 *tmp_matchesp = &tmp_matches;
797 int rc = 0;
798 regmatch_t *tmp_data;
799
800 MATCHES_MEMSET( &tmp_matches );
801 tmp_data = &tmp_matches.dn_data[0];
802
803 if ( a->acl_attrval_style == ACL_STYLE_REGEX )
804 tmp_matchesp = matches;
805 else switch ( a->acl_dn_style ) {
806 case ACL_STYLE_REGEX:
807 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
808 tmp_matchesp = matches;
809 break;
810 }
811 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
812
813 case ACL_STYLE_BASE:
814 tmp_data[0].rm_so = 0;
815 tmp_data[0].rm_eo = e->e_nname.bv_len;
816 tmp_matches.dn_count = 1;
817 break;
818
819 case ACL_STYLE_ONE:
820 case ACL_STYLE_SUBTREE:
821 case ACL_STYLE_CHILDREN:
822 tmp_data[0].rm_so = 0;
823 tmp_data[0].rm_eo = e->e_nname.bv_len;
824 tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
825 tmp_data[1].rm_eo = e->e_nname.bv_len;
826 tmp_matches.dn_count = 2;
827 break;
828
829 default:
830 /* error */
831 rc = 1;
832 break;
833 }
834
835 if ( rc ) {
836 return 1;
837 }
838
839 if ( !regex_matches( &bdn->a_pat, opndn->bv_val,
840 &e->e_nname, NULL, tmp_matchesp ) )
841 {
842 return 1;
843 }
844 }
845
846 } else {
847 struct berval pat;
848 ber_len_t patlen, odnlen;
849 int got_match = 0;
850
851 if ( e->e_dn == NULL )
852 return 1;
853
854 if ( bdn->a_expand ) {
855 struct berval bv;
856 char buf[ACL_BUF_SIZE];
857
858 AclRegexMatches tmp_matches,
859 *tmp_matchesp = &tmp_matches;
860 int rc = 0;
861 regmatch_t *tmp_data;
862
863 MATCHES_MEMSET( &tmp_matches );
864 tmp_data = &tmp_matches.dn_data[0];
865
866 bv.bv_len = sizeof( buf ) - 1;
867 bv.bv_val = buf;
868
869 /* Expand value regex */
870 if ( a->acl_attrval_style == ACL_STYLE_REGEX )
871 tmp_matchesp = matches;
872 else switch ( a->acl_dn_style ) {
873 case ACL_STYLE_REGEX:
874 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
875 tmp_matchesp = matches;
876 break;
877 }
878 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
879
880 case ACL_STYLE_BASE:
881 tmp_data[0].rm_so = 0;
882 tmp_data[0].rm_eo = e->e_nname.bv_len;
883 tmp_matches.dn_count = 1;
884 break;
885
886 case ACL_STYLE_ONE:
887 case ACL_STYLE_SUBTREE:
888 case ACL_STYLE_CHILDREN:
889 tmp_data[0].rm_so = 0;
890 tmp_data[0].rm_eo = e->e_nname.bv_len;
891 tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
892 tmp_data[1].rm_eo = e->e_nname.bv_len;
893 tmp_matches.dn_count = 2;
894 break;
895
896 default:
897 /* error */
898 rc = 1;
899 break;
900 }
901
902 if ( rc ) {
903 return 1;
904 }
905
906 if ( acl_string_expand( &bv, &bdn->a_pat,
907 &e->e_nname,
908 val, tmp_matchesp ) )
909 {
910 return 1;
911 }
912
913 if ( dnNormalize(0, NULL, NULL, &bv,
914 &pat, op->o_tmpmemctx )
915 != LDAP_SUCCESS )
916 {
917 /* did not expand to a valid dn */
918 return 1;
919 }
920
921 } else {
922 pat = bdn->a_pat;
923 }
924
925 patlen = pat.bv_len;
926 odnlen = opndn->bv_len;
927 if ( odnlen < patlen ) {
928 goto dn_match_cleanup;
929
930 }
931
932 if ( bdn->a_style == ACL_STYLE_BASE ) {
933 /* base dn -- entire object DN must match */
934 if ( odnlen != patlen ) {
935 goto dn_match_cleanup;
936 }
937
938 } else if ( bdn->a_style == ACL_STYLE_ONE ) {
939 ber_len_t rdnlen = 0;
940
941 if ( odnlen <= patlen ) {
942 goto dn_match_cleanup;
943 }
944
945 if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
946 goto dn_match_cleanup;
947 }
948
949 rdnlen = dn_rdnlen( NULL, opndn );
950 if ( rdnlen - ( odnlen - patlen - 1 ) != 0 ) {
951 goto dn_match_cleanup;
952 }
953
954 } else if ( bdn->a_style == ACL_STYLE_SUBTREE ) {
955 if ( odnlen > patlen && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
956 goto dn_match_cleanup;
957 }
958
959 } else if ( bdn->a_style == ACL_STYLE_CHILDREN ) {
960 if ( odnlen <= patlen ) {
961 goto dn_match_cleanup;
962 }
963
964 if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
965 goto dn_match_cleanup;
966 }
967
968 } else if ( bdn->a_style == ACL_STYLE_LEVEL ) {
969 int level = bdn->a_level;
970 struct berval ndn;
971
972 if ( odnlen <= patlen ) {
973 goto dn_match_cleanup;
974 }
975
976 if ( level > 0 && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) )
977 {
978 goto dn_match_cleanup;
979 }
980
981 ndn = *opndn;
982 for ( ; level > 0; level-- ) {
983 if ( BER_BVISEMPTY( &ndn ) ) {
984 goto dn_match_cleanup;
985 }
986 dnParent( &ndn, &ndn );
987 if ( ndn.bv_len < patlen ) {
988 goto dn_match_cleanup;
989 }
990 }
991
992 if ( ndn.bv_len != patlen ) {
993 goto dn_match_cleanup;
994 }
995 }
996
997 got_match = !strcmp( pat.bv_val, &opndn->bv_val[ odnlen - patlen ] );
998
999 dn_match_cleanup:;
1000 if ( pat.bv_val != bdn->a_pat.bv_val ) {
1001 slap_sl_free( pat.bv_val, op->o_tmpmemctx );
1002 }
1003
1004 if ( !got_match ) {
1005 return 1;
1006 }
1007 }
1008
1009 return 0;
1010 }
1011
1012 static int
acl_mask_dnattr(Operation * op,Entry * e,struct berval * val,AccessControl * a,int count,AccessControlState * state,slap_mask_t * mask,slap_dn_access * bdn,struct berval * opndn)1013 acl_mask_dnattr(
1014 Operation *op,
1015 Entry *e,
1016 struct berval *val,
1017 AccessControl *a,
1018 int count,
1019 AccessControlState *state,
1020 slap_mask_t *mask,
1021 slap_dn_access *bdn,
1022 struct berval *opndn )
1023 {
1024 Attribute *at;
1025 struct berval bv;
1026 int rc, match = 0;
1027 const char *text;
1028 const char *attr = bdn->a_at->ad_cname.bv_val;
1029
1030 assert( attr != NULL );
1031
1032 if ( BER_BVISEMPTY( opndn ) ) {
1033 return 1;
1034 }
1035
1036 Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n", attr, 0, 0 );
1037 bv = *opndn;
1038
1039 /* see if asker is listed in dnattr */
1040 for ( at = attrs_find( e->e_attrs, bdn->a_at );
1041 at != NULL;
1042 at = attrs_find( at->a_next, bdn->a_at ) )
1043 {
1044 if ( attr_valfind( at,
1045 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1046 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1047 &bv, NULL, op->o_tmpmemctx ) == 0 )
1048 {
1049 /* found it */
1050 match = 1;
1051 break;
1052 }
1053 }
1054
1055 if ( match ) {
1056 /* have a dnattr match. if this is a self clause then
1057 * the target must also match the op dn.
1058 */
1059 if ( bdn->a_self ) {
1060 /* check if the target is an attribute. */
1061 if ( val == NULL ) return 1;
1062
1063 /* target is attribute, check if the attribute value
1064 * is the op dn.
1065 */
1066 rc = value_match( &match, bdn->a_at,
1067 bdn->a_at->ad_type->sat_equality, 0,
1068 val, &bv, &text );
1069 /* on match error or no match, fail the ACL clause */
1070 if ( rc != LDAP_SUCCESS || match != 0 )
1071 return 1;
1072 }
1073
1074 } else {
1075 /* no dnattr match, check if this is a self clause */
1076 if ( ! bdn->a_self )
1077 return 1;
1078
1079 /* this is a self clause, check if the target is an
1080 * attribute.
1081 */
1082 if ( val == NULL )
1083 return 1;
1084
1085 /* target is attribute, check if the attribute value
1086 * is the op dn.
1087 */
1088 rc = value_match( &match, bdn->a_at,
1089 bdn->a_at->ad_type->sat_equality, 0,
1090 val, &bv, &text );
1091
1092 /* on match error or no match, fail the ACL clause */
1093 if ( rc != LDAP_SUCCESS || match != 0 )
1094 return 1;
1095 }
1096
1097 return 0;
1098 }
1099
1100
1101 /*
1102 * slap_acl_mask - modifies mask based upon the given acl and the
1103 * requested access to entry e, attribute attr, value val. if val
1104 * is null, access to the whole attribute is assumed (all values).
1105 *
1106 * returns 0 access NOT allowed
1107 * 1 access allowed
1108 */
1109
1110 static slap_control_t
slap_acl_mask(AccessControl * a,AccessControl * prev,slap_mask_t * mask,Operation * op,Entry * e,AttributeDescription * desc,struct berval * val,AclRegexMatches * matches,int count,AccessControlState * state,slap_access_t access)1111 slap_acl_mask(
1112 AccessControl *a,
1113 AccessControl *prev,
1114 slap_mask_t *mask,
1115 Operation *op,
1116 Entry *e,
1117 AttributeDescription *desc,
1118 struct berval *val,
1119 AclRegexMatches *matches,
1120 int count,
1121 AccessControlState *state,
1122 slap_access_t access )
1123 {
1124 int i;
1125 Access *b;
1126 #ifdef LDAP_DEBUG
1127 char accessmaskbuf[ACCESSMASK_MAXLEN];
1128 #endif /* DEBUG */
1129 const char *attr;
1130 #ifdef SLAP_DYNACL
1131 slap_mask_t a2pmask = ACL_ACCESS2PRIV( access );
1132 #endif /* SLAP_DYNACL */
1133
1134 assert( a != NULL );
1135 assert( mask != NULL );
1136 assert( desc != NULL );
1137
1138 attr = desc->ad_cname.bv_val;
1139
1140 assert( attr != NULL );
1141
1142 Debug( LDAP_DEBUG_ACL,
1143 "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
1144 e->e_dn, attr, 0 );
1145
1146 Debug( LDAP_DEBUG_ACL,
1147 "=> acl_mask: to %s by \"%s\", (%s) \n",
1148 val ? "value" : "all values",
1149 op->o_ndn.bv_val ? op->o_ndn.bv_val : "",
1150 accessmask2str( *mask, accessmaskbuf, 1 ) );
1151
1152
1153 b = a->acl_access;
1154 i = 1;
1155
1156 for ( ; b != NULL; b = b->a_next, i++ ) {
1157 slap_mask_t oldmask, modmask;
1158
1159 ACL_INVALIDATE( modmask );
1160
1161 /* check for the "self" modifier in the <access> field */
1162 if ( b->a_dn.a_self ) {
1163 const char *dummy;
1164 int rc, match = 0;
1165
1166 ACL_RECORD_VALUE_STATE;
1167
1168 /* must have DN syntax */
1169 if ( desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName &&
1170 !is_at_syntax( desc->ad_type, SLAPD_NAMEUID_SYNTAX )) continue;
1171
1172 /* check if the target is an attribute. */
1173 if ( val == NULL ) continue;
1174
1175 /* a DN must be present */
1176 if ( BER_BVISEMPTY( &op->o_ndn ) ) {
1177 continue;
1178 }
1179
1180 /* target is attribute, check if the attribute value
1181 * is the op dn.
1182 */
1183 rc = value_match( &match, desc,
1184 desc->ad_type->sat_equality, 0,
1185 val, &op->o_ndn, &dummy );
1186 /* on match error or no match, fail the ACL clause */
1187 if ( rc != LDAP_SUCCESS || match != 0 )
1188 continue;
1189 }
1190
1191 /* AND <who> clauses */
1192 if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
1193 Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
1194 b->a_dn_pat.bv_val, 0, 0);
1195 /*
1196 * if access applies to the entry itself, and the
1197 * user is bound as somebody in the same namespace as
1198 * the entry, OR the given dn matches the dn pattern
1199 */
1200 /*
1201 * NOTE: styles "anonymous", "users" and "self"
1202 * have been moved to enum slap_style_t, whose
1203 * value is set in a_dn_style; however, the string
1204 * is maintained in a_dn_pat.
1205 */
1206
1207 if ( acl_mask_dn( op, e, val, a, matches,
1208 &b->a_dn, &op->o_ndn ) )
1209 {
1210 continue;
1211 }
1212 }
1213
1214 if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
1215 struct berval ndn;
1216
1217 Debug( LDAP_DEBUG_ACL, "<= check a_realdn_pat: %s\n",
1218 b->a_realdn_pat.bv_val, 0, 0);
1219 /*
1220 * if access applies to the entry itself, and the
1221 * user is bound as somebody in the same namespace as
1222 * the entry, OR the given dn matches the dn pattern
1223 */
1224 /*
1225 * NOTE: styles "anonymous", "users" and "self"
1226 * have been moved to enum slap_style_t, whose
1227 * value is set in a_dn_style; however, the string
1228 * is maintained in a_dn_pat.
1229 */
1230
1231 if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
1232 {
1233 ndn = op->o_conn->c_ndn;
1234 } else {
1235 ndn = op->o_ndn;
1236 }
1237
1238 if ( acl_mask_dn( op, e, val, a, matches,
1239 &b->a_realdn, &ndn ) )
1240 {
1241 continue;
1242 }
1243 }
1244
1245 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
1246 if ( ! op->o_conn->c_listener ) {
1247 continue;
1248 }
1249 Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
1250 b->a_sockurl_pat.bv_val, 0, 0 );
1251
1252 if ( !ber_bvccmp( &b->a_sockurl_pat, '*' ) ) {
1253 if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
1254 if ( !regex_matches( &b->a_sockurl_pat, op->o_conn->c_listener_url.bv_val,
1255 &e->e_nname, val, matches ) )
1256 {
1257 continue;
1258 }
1259
1260 } else if ( b->a_sockurl_style == ACL_STYLE_EXPAND ) {
1261 struct berval bv;
1262 char buf[ACL_BUF_SIZE];
1263
1264 bv.bv_len = sizeof( buf ) - 1;
1265 bv.bv_val = buf;
1266 if ( acl_string_expand( &bv, &b->a_sockurl_pat, &e->e_nname, val, matches ) )
1267 {
1268 continue;
1269 }
1270
1271 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_listener_url ) != 0 )
1272 {
1273 continue;
1274 }
1275
1276 } else {
1277 if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &op->o_conn->c_listener_url ) != 0 )
1278 {
1279 continue;
1280 }
1281 }
1282 }
1283 }
1284
1285 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
1286 if ( !op->o_conn->c_peer_domain.bv_val ) {
1287 continue;
1288 }
1289 Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
1290 b->a_domain_pat.bv_val, 0, 0 );
1291 if ( !ber_bvccmp( &b->a_domain_pat, '*' ) ) {
1292 if ( b->a_domain_style == ACL_STYLE_REGEX) {
1293 if ( !regex_matches( &b->a_domain_pat, op->o_conn->c_peer_domain.bv_val,
1294 &e->e_nname, val, matches ) )
1295 {
1296 continue;
1297 }
1298 } else {
1299 char buf[ACL_BUF_SIZE];
1300
1301 struct berval cmp = op->o_conn->c_peer_domain;
1302 struct berval pat = b->a_domain_pat;
1303
1304 if ( b->a_domain_expand ) {
1305 struct berval bv;
1306
1307 bv.bv_len = sizeof(buf) - 1;
1308 bv.bv_val = buf;
1309
1310 if ( acl_string_expand(&bv, &b->a_domain_pat, &e->e_nname, val, matches) )
1311 {
1312 continue;
1313 }
1314 pat = bv;
1315 }
1316
1317 if ( b->a_domain_style == ACL_STYLE_SUBTREE ) {
1318 int offset = cmp.bv_len - pat.bv_len;
1319 if ( offset < 0 ) {
1320 continue;
1321 }
1322
1323 if ( offset == 1 || ( offset > 1 && cmp.bv_val[ offset - 1 ] != '.' ) ) {
1324 continue;
1325 }
1326
1327 /* trim the domain */
1328 cmp.bv_val = &cmp.bv_val[ offset ];
1329 cmp.bv_len -= offset;
1330 }
1331
1332 if ( ber_bvstrcasecmp( &pat, &cmp ) != 0 ) {
1333 continue;
1334 }
1335 }
1336 }
1337 }
1338
1339 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
1340 if ( !op->o_conn->c_peer_name.bv_val ) {
1341 continue;
1342 }
1343 Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
1344 b->a_peername_pat.bv_val, 0, 0 );
1345 if ( !ber_bvccmp( &b->a_peername_pat, '*' ) ) {
1346 if ( b->a_peername_style == ACL_STYLE_REGEX ) {
1347 if ( !regex_matches( &b->a_peername_pat, op->o_conn->c_peer_name.bv_val,
1348 &e->e_nname, val, matches ) )
1349 {
1350 continue;
1351 }
1352
1353 } else {
1354 /* try exact match */
1355 if ( b->a_peername_style == ACL_STYLE_BASE ) {
1356 if ( ber_bvstrcasecmp( &b->a_peername_pat, &op->o_conn->c_peer_name ) != 0 ) {
1357 continue;
1358 }
1359
1360 } else if ( b->a_peername_style == ACL_STYLE_EXPAND ) {
1361 struct berval bv;
1362 char buf[ACL_BUF_SIZE];
1363
1364 bv.bv_len = sizeof( buf ) - 1;
1365 bv.bv_val = buf;
1366 if ( acl_string_expand( &bv, &b->a_peername_pat, &e->e_nname, val, matches ) )
1367 {
1368 continue;
1369 }
1370
1371 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_peer_name ) != 0 ) {
1372 continue;
1373 }
1374
1375 /* extract IP and try exact match */
1376 } else if ( b->a_peername_style == ACL_STYLE_IP ) {
1377 char *port;
1378 char buf[STRLENOF("255.255.255.255") + 1];
1379 struct berval ip;
1380 unsigned long addr;
1381 int port_number = -1;
1382
1383 if ( strncasecmp( op->o_conn->c_peer_name.bv_val,
1384 acl_bv_ip_eq.bv_val,
1385 acl_bv_ip_eq.bv_len ) != 0 )
1386 continue;
1387
1388 ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ip_eq.bv_len;
1389 ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ip_eq.bv_len;
1390
1391 port = strrchr( ip.bv_val, ':' );
1392 if ( port ) {
1393 ip.bv_len = port - ip.bv_val;
1394 ++port;
1395 if ( lutil_atoi( &port_number, port ) != 0 )
1396 continue;
1397 }
1398
1399 /* the port check can be anticipated here */
1400 if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
1401 continue;
1402
1403 /* address longer than expected? */
1404 if ( ip.bv_len >= sizeof(buf) )
1405 continue;
1406
1407 AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
1408 buf[ ip.bv_len ] = '\0';
1409
1410 addr = inet_addr( buf );
1411
1412 /* unable to convert? */
1413 if ( addr == (unsigned long)(-1) )
1414 continue;
1415
1416 if ( (addr & b->a_peername_mask) != b->a_peername_addr )
1417 continue;
1418
1419 #ifdef LDAP_PF_INET6
1420 /* extract IPv6 and try exact match */
1421 } else if ( b->a_peername_style == ACL_STYLE_IPV6 ) {
1422 char *port;
1423 char buf[STRLENOF("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF") + 1];
1424 struct berval ip;
1425 struct in6_addr addr;
1426 int port_number = -1;
1427
1428 if ( strncasecmp( op->o_conn->c_peer_name.bv_val,
1429 acl_bv_ipv6_eq.bv_val,
1430 acl_bv_ipv6_eq.bv_len ) != 0 )
1431 continue;
1432
1433 ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ipv6_eq.bv_len;
1434 ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ipv6_eq.bv_len;
1435
1436 port = strrchr( ip.bv_val, ']' );
1437 if ( port ) {
1438 ip.bv_len = port - ip.bv_val;
1439 ++port;
1440 if ( port[0] == ':' && lutil_atoi( &port_number, ++port ) != 0 )
1441 continue;
1442 }
1443
1444 /* the port check can be anticipated here */
1445 if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
1446 continue;
1447
1448 /* address longer than expected? */
1449 if ( ip.bv_len >= sizeof(buf) )
1450 continue;
1451
1452 AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
1453 buf[ ip.bv_len ] = '\0';
1454
1455 if ( inet_pton( AF_INET6, buf, &addr ) != 1 )
1456 continue;
1457
1458 /* check mask */
1459 if ( !slap_addr6_mask( &addr, &b->a_peername_mask6, &b->a_peername_addr6 ) )
1460 continue;
1461 #endif /* LDAP_PF_INET6 */
1462
1463 #ifdef LDAP_PF_LOCAL
1464 /* extract path and try exact match */
1465 } else if ( b->a_peername_style == ACL_STYLE_PATH ) {
1466 struct berval path;
1467
1468 if ( strncmp( op->o_conn->c_peer_name.bv_val,
1469 acl_bv_path_eq.bv_val,
1470 acl_bv_path_eq.bv_len ) != 0 )
1471 continue;
1472
1473 path.bv_val = op->o_conn->c_peer_name.bv_val
1474 + acl_bv_path_eq.bv_len;
1475 path.bv_len = op->o_conn->c_peer_name.bv_len
1476 - acl_bv_path_eq.bv_len;
1477
1478 if ( ber_bvcmp( &b->a_peername_pat, &path ) != 0 )
1479 continue;
1480
1481 #endif /* LDAP_PF_LOCAL */
1482
1483 /* exact match (very unlikely...) */
1484 } else if ( ber_bvcmp( &op->o_conn->c_peer_name, &b->a_peername_pat ) != 0 ) {
1485 continue;
1486 }
1487 }
1488 }
1489 }
1490
1491 if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
1492 if ( BER_BVISNULL( &op->o_conn->c_sock_name ) ) {
1493 continue;
1494 }
1495 Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
1496 b->a_sockname_pat.bv_val, 0, 0 );
1497 if ( !ber_bvccmp( &b->a_sockname_pat, '*' ) ) {
1498 if ( b->a_sockname_style == ACL_STYLE_REGEX) {
1499 if ( !regex_matches( &b->a_sockname_pat, op->o_conn->c_sock_name.bv_val,
1500 &e->e_nname, val, matches ) )
1501 {
1502 continue;
1503 }
1504
1505 } else if ( b->a_sockname_style == ACL_STYLE_EXPAND ) {
1506 struct berval bv;
1507 char buf[ACL_BUF_SIZE];
1508
1509 bv.bv_len = sizeof( buf ) - 1;
1510 bv.bv_val = buf;
1511 if ( acl_string_expand( &bv, &b->a_sockname_pat, &e->e_nname, val, matches ) )
1512 {
1513 continue;
1514 }
1515
1516 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_sock_name ) != 0 ) {
1517 continue;
1518 }
1519
1520 } else {
1521 if ( ber_bvstrcasecmp( &b->a_sockname_pat, &op->o_conn->c_sock_name ) != 0 ) {
1522 continue;
1523 }
1524 }
1525 }
1526 }
1527
1528 if ( b->a_dn_at != NULL ) {
1529 if ( acl_mask_dnattr( op, e, val, a,
1530 count, state, mask,
1531 &b->a_dn, &op->o_ndn ) )
1532 {
1533 continue;
1534 }
1535 }
1536
1537 if ( b->a_realdn_at != NULL ) {
1538 struct berval ndn;
1539
1540 if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
1541 {
1542 ndn = op->o_conn->c_ndn;
1543 } else {
1544 ndn = op->o_ndn;
1545 }
1546
1547 if ( acl_mask_dnattr( op, e, val, a,
1548 count, state, mask,
1549 &b->a_realdn, &ndn ) )
1550 {
1551 continue;
1552 }
1553 }
1554
1555 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
1556 struct berval bv;
1557 struct berval ndn = BER_BVNULL;
1558 int rc;
1559
1560 if ( op->o_ndn.bv_len == 0 ) {
1561 continue;
1562 }
1563
1564 Debug( LDAP_DEBUG_ACL, "<= check a_group_pat: %s\n",
1565 b->a_group_pat.bv_val, 0, 0 );
1566
1567 /* b->a_group is an unexpanded entry name, expanded it should be an
1568 * entry with objectclass group* and we test to see if odn is one of
1569 * the values in the attribute group
1570 */
1571 /* see if asker is listed in dnattr */
1572 if ( b->a_group_style == ACL_STYLE_EXPAND ) {
1573 char buf[ACL_BUF_SIZE];
1574 AclRegexMatches tmp_matches,
1575 *tmp_matchesp = &tmp_matches;
1576 regmatch_t *tmp_data;
1577
1578 MATCHES_MEMSET( &tmp_matches );
1579 tmp_data = &tmp_matches.dn_data[0];
1580
1581 bv.bv_len = sizeof(buf) - 1;
1582 bv.bv_val = buf;
1583
1584 rc = 0;
1585
1586 if ( a->acl_attrval_style == ACL_STYLE_REGEX )
1587 tmp_matchesp = matches;
1588 else switch ( a->acl_dn_style ) {
1589 case ACL_STYLE_REGEX:
1590 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1591 tmp_matchesp = matches;
1592 break;
1593 }
1594
1595 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1596 case ACL_STYLE_BASE:
1597 tmp_data[0].rm_so = 0;
1598 tmp_data[0].rm_eo = e->e_nname.bv_len;
1599 tmp_matches.dn_count = 1;
1600 break;
1601
1602 case ACL_STYLE_ONE:
1603 case ACL_STYLE_SUBTREE:
1604 case ACL_STYLE_CHILDREN:
1605 tmp_data[0].rm_so = 0;
1606 tmp_data[0].rm_eo = e->e_nname.bv_len;
1607
1608 tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1609 tmp_data[1].rm_eo = e->e_nname.bv_len;
1610 tmp_matches.dn_count = 2;
1611 break;
1612
1613 default:
1614 /* error */
1615 rc = 1;
1616 break;
1617 }
1618
1619 if ( rc ) {
1620 continue;
1621 }
1622
1623 if ( acl_string_expand( &bv, &b->a_group_pat,
1624 &e->e_nname, val,
1625 tmp_matchesp ) )
1626 {
1627 continue;
1628 }
1629
1630 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn,
1631 op->o_tmpmemctx ) != LDAP_SUCCESS )
1632 {
1633 /* did not expand to a valid dn */
1634 continue;
1635 }
1636
1637 bv = ndn;
1638
1639 } else {
1640 bv = b->a_group_pat;
1641 }
1642
1643 rc = backend_group( op, e, &bv, &op->o_ndn,
1644 b->a_group_oc, b->a_group_at );
1645
1646 if ( ndn.bv_val ) {
1647 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
1648 }
1649
1650 if ( rc != 0 ) {
1651 continue;
1652 }
1653 }
1654
1655 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
1656 struct berval bv;
1657 char buf[ACL_BUF_SIZE];
1658
1659 Debug( LDAP_DEBUG_ACL, "<= check a_set_pat: %s\n",
1660 b->a_set_pat.bv_val, 0, 0 );
1661
1662 if ( b->a_set_style == ACL_STYLE_EXPAND ) {
1663 AclRegexMatches tmp_matches,
1664 *tmp_matchesp = &tmp_matches;
1665 int rc = 0;
1666 regmatch_t *tmp_data;
1667
1668 MATCHES_MEMSET( &tmp_matches );
1669 tmp_data = &tmp_matches.dn_data[0];
1670
1671 bv.bv_len = sizeof( buf ) - 1;
1672 bv.bv_val = buf;
1673
1674 rc = 0;
1675
1676 if ( a->acl_attrval_style == ACL_STYLE_REGEX )
1677 tmp_matchesp = matches;
1678 else switch ( a->acl_dn_style ) {
1679 case ACL_STYLE_REGEX:
1680 if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
1681 tmp_matchesp = matches;
1682 break;
1683 }
1684
1685 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1686 case ACL_STYLE_BASE:
1687 tmp_data[0].rm_so = 0;
1688 tmp_data[0].rm_eo = e->e_nname.bv_len;
1689 tmp_matches.dn_count = 1;
1690 break;
1691
1692 case ACL_STYLE_ONE:
1693 case ACL_STYLE_SUBTREE:
1694 case ACL_STYLE_CHILDREN:
1695 tmp_data[0].rm_so = 0;
1696 tmp_data[0].rm_eo = e->e_nname.bv_len;
1697 tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
1698 tmp_data[1].rm_eo = e->e_nname.bv_len; tmp_matches.dn_count = 2;
1699 break;
1700
1701 default:
1702 /* error */
1703 rc = 1;
1704 break;
1705 }
1706
1707 if ( rc ) {
1708 continue;
1709 }
1710
1711 if ( acl_string_expand( &bv, &b->a_set_pat,
1712 &e->e_nname, val,
1713 tmp_matchesp ) )
1714 {
1715 continue;
1716 }
1717
1718 } else {
1719 bv = b->a_set_pat;
1720 }
1721
1722 if ( acl_match_set( &bv, op, e, NULL ) == 0 ) {
1723 continue;
1724 }
1725 }
1726
1727 if ( b->a_authz.sai_ssf ) {
1728 Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
1729 b->a_authz.sai_ssf, op->o_ssf, 0 );
1730 if ( b->a_authz.sai_ssf > op->o_ssf ) {
1731 continue;
1732 }
1733 }
1734
1735 if ( b->a_authz.sai_transport_ssf ) {
1736 Debug( LDAP_DEBUG_ACL,
1737 "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
1738 b->a_authz.sai_transport_ssf, op->o_transport_ssf, 0 );
1739 if ( b->a_authz.sai_transport_ssf > op->o_transport_ssf ) {
1740 continue;
1741 }
1742 }
1743
1744 if ( b->a_authz.sai_tls_ssf ) {
1745 Debug( LDAP_DEBUG_ACL,
1746 "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
1747 b->a_authz.sai_tls_ssf, op->o_tls_ssf, 0 );
1748 if ( b->a_authz.sai_tls_ssf > op->o_tls_ssf ) {
1749 continue;
1750 }
1751 }
1752
1753 if ( b->a_authz.sai_sasl_ssf ) {
1754 Debug( LDAP_DEBUG_ACL,
1755 "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
1756 b->a_authz.sai_sasl_ssf, op->o_sasl_ssf, 0 );
1757 if ( b->a_authz.sai_sasl_ssf > op->o_sasl_ssf ) {
1758 continue;
1759 }
1760 }
1761
1762 #ifdef SLAP_DYNACL
1763 if ( b->a_dynacl ) {
1764 slap_dynacl_t *da;
1765 slap_access_t tgrant, tdeny;
1766
1767 Debug( LDAP_DEBUG_ACL, "<= check a_dynacl\n",
1768 0, 0, 0 );
1769
1770 /* this case works different from the others above.
1771 * since dynamic ACL's themselves give permissions, we need
1772 * to first check b->a_access_mask, the ACL's access level.
1773 */
1774 /* first check if the right being requested
1775 * is allowed by the ACL clause.
1776 */
1777 if ( ! ACL_PRIV_ISSET( b->a_access_mask, a2pmask ) ) {
1778 continue;
1779 }
1780
1781 /* start out with nothing granted, nothing denied */
1782 ACL_INVALIDATE(tgrant);
1783 ACL_INVALIDATE(tdeny);
1784
1785 for ( da = b->a_dynacl; da; da = da->da_next ) {
1786 slap_access_t grant,
1787 deny;
1788
1789 ACL_INVALIDATE(grant);
1790 ACL_INVALIDATE(deny);
1791
1792 Debug( LDAP_DEBUG_ACL, " <= check a_dynacl: %s\n",
1793 da->da_name, 0, 0 );
1794
1795 /*
1796 * XXXmanu Only DN matches are supplied
1797 * sending attribute values matches require
1798 * an API update
1799 */
1800 (void)da->da_mask( da->da_private, op, e, desc,
1801 val, matches->dn_count, matches->dn_data,
1802 &grant, &deny );
1803
1804 tgrant |= grant;
1805 tdeny |= deny;
1806 }
1807
1808 /* remove anything that the ACL clause does not allow */
1809 tgrant &= b->a_access_mask & ACL_PRIV_MASK;
1810 tdeny &= ACL_PRIV_MASK;
1811
1812 /* see if we have anything to contribute */
1813 if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) {
1814 continue;
1815 }
1816
1817 /* this could be improved by changing slap_acl_mask so that it can deal with
1818 * by clauses that return grant/deny pairs. Right now, it does either
1819 * additive or subtractive rights, but not both at the same time. So,
1820 * we need to combine the grant/deny pair into a single rights mask in
1821 * a smart way: if either grant or deny is "empty", then we use the
1822 * opposite as is, otherwise we remove any denied rights from the grant
1823 * rights mask and construct an additive mask.
1824 */
1825 if (ACL_IS_INVALID(tdeny)) {
1826 modmask = tgrant | ACL_PRIV_ADDITIVE;
1827
1828 } else if (ACL_IS_INVALID(tgrant)) {
1829 modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
1830
1831 } else {
1832 modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
1833 }
1834
1835 } else
1836 #endif /* SLAP_DYNACL */
1837 {
1838 modmask = b->a_access_mask;
1839 }
1840
1841 Debug( LDAP_DEBUG_ACL,
1842 "<= acl_mask: [%d] applying %s (%s)\n",
1843 i, accessmask2str( modmask, accessmaskbuf, 1 ),
1844 b->a_type == ACL_CONTINUE
1845 ? "continue"
1846 : b->a_type == ACL_BREAK
1847 ? "break"
1848 : "stop" );
1849 /* save old mask */
1850 oldmask = *mask;
1851
1852 if( ACL_IS_ADDITIVE(modmask) ) {
1853 /* add privs */
1854 ACL_PRIV_SET( *mask, modmask );
1855
1856 /* cleanup */
1857 ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
1858
1859 } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
1860 /* substract privs */
1861 ACL_PRIV_CLR( *mask, modmask );
1862
1863 /* cleanup */
1864 ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
1865
1866 } else {
1867 /* assign privs */
1868 *mask = modmask;
1869 }
1870
1871 Debug( LDAP_DEBUG_ACL,
1872 "<= acl_mask: [%d] mask: %s\n",
1873 i, accessmask2str(*mask, accessmaskbuf, 1), 0 );
1874
1875 if( b->a_type == ACL_CONTINUE ) {
1876 continue;
1877
1878 } else if ( b->a_type == ACL_BREAK ) {
1879 return ACL_BREAK;
1880
1881 } else {
1882 return ACL_STOP;
1883 }
1884 }
1885
1886 /* implicit "by * none" clause */
1887 ACL_INIT(*mask);
1888
1889 Debug( LDAP_DEBUG_ACL,
1890 "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
1891 accessmask2str(*mask, accessmaskbuf, 1), 0, 0 );
1892 return ACL_STOP;
1893 }
1894
1895 /*
1896 * acl_check_modlist - check access control on the given entry to see if
1897 * it allows the given modifications by the user associated with op.
1898 * returns 1 if mods allowed ok
1899 * 0 mods not allowed
1900 */
1901
1902 int
acl_check_modlist(Operation * op,Entry * e,Modifications * mlist)1903 acl_check_modlist(
1904 Operation *op,
1905 Entry *e,
1906 Modifications *mlist )
1907 {
1908 struct berval *bv;
1909 AccessControlState state = ACL_STATE_INIT;
1910 Backend *be;
1911 int be_null = 0;
1912 int ret = 1; /* default is access allowed */
1913
1914 be = op->o_bd;
1915 if ( be == NULL ) {
1916 be = LDAP_STAILQ_FIRST(&backendDB);
1917 be_null = 1;
1918 op->o_bd = be;
1919 }
1920 assert( be != NULL );
1921
1922 /* If ADD attribute checking is not enabled, just allow it */
1923 if ( op->o_tag == LDAP_REQ_ADD && !SLAP_DBACL_ADD( be ))
1924 return 1;
1925
1926 /* short circuit root database access */
1927 if ( be_isroot( op ) ) {
1928 Debug( LDAP_DEBUG_ACL,
1929 "<= acl_access_allowed: granted to database root\n",
1930 0, 0, 0 );
1931 goto done;
1932 }
1933
1934 /* use backend default access if no backend acls */
1935 if( op->o_bd != NULL && op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) {
1936 Debug( LDAP_DEBUG_ACL,
1937 "=> access_allowed: backend default %s access %s to \"%s\"\n",
1938 access2str( ACL_WRITE ),
1939 op->o_bd->be_dfltaccess >= ACL_WRITE
1940 ? "granted" : "denied",
1941 op->o_dn.bv_val );
1942 ret = (op->o_bd->be_dfltaccess >= ACL_WRITE);
1943 goto done;
1944 }
1945
1946 for ( ; mlist != NULL; mlist = mlist->sml_next ) {
1947 /*
1948 * Internal mods are ignored by ACL_WRITE checking
1949 */
1950 if ( mlist->sml_flags & SLAP_MOD_INTERNAL ) {
1951 Debug( LDAP_DEBUG_ACL, "acl: internal mod %s:"
1952 " modify access granted\n",
1953 mlist->sml_desc->ad_cname.bv_val, 0, 0 );
1954 continue;
1955 }
1956
1957 /*
1958 * no-user-modification operational attributes are ignored
1959 * by ACL_WRITE checking as any found here are not provided
1960 * by the user
1961 */
1962 if ( is_at_no_user_mod( mlist->sml_desc->ad_type )
1963 && ! ( mlist->sml_flags & SLAP_MOD_MANAGING ) )
1964 {
1965 Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
1966 " modify access granted\n",
1967 mlist->sml_desc->ad_cname.bv_val, 0, 0 );
1968 continue;
1969 }
1970
1971 switch ( mlist->sml_op ) {
1972 case LDAP_MOD_REPLACE:
1973 case LDAP_MOD_INCREMENT:
1974 /*
1975 * We must check both permission to delete the whole
1976 * attribute and permission to add the specific attributes.
1977 * This prevents abuse from selfwriters.
1978 */
1979 if ( ! access_allowed( op, e,
1980 mlist->sml_desc, NULL,
1981 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
1982 &state ) )
1983 {
1984 ret = 0;
1985 goto done;
1986 }
1987
1988 if ( mlist->sml_values == NULL ) break;
1989
1990 /* fall thru to check value to add */
1991
1992 case LDAP_MOD_ADD:
1993 case SLAP_MOD_ADD_IF_NOT_PRESENT:
1994 assert( mlist->sml_values != NULL );
1995
1996 if ( mlist->sml_op == SLAP_MOD_ADD_IF_NOT_PRESENT
1997 && attr_find( e->e_attrs, mlist->sml_desc ) )
1998 {
1999 break;
2000 }
2001
2002 for ( bv = mlist->sml_nvalues
2003 ? mlist->sml_nvalues : mlist->sml_values;
2004 bv->bv_val != NULL; bv++ )
2005 {
2006 if ( ! access_allowed( op, e,
2007 mlist->sml_desc, bv,
2008 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WADD,
2009 &state ) )
2010 {
2011 ret = 0;
2012 goto done;
2013 }
2014 }
2015 break;
2016
2017 case LDAP_MOD_DELETE:
2018 case SLAP_MOD_SOFTDEL:
2019 if ( mlist->sml_values == NULL ) {
2020 if ( ! access_allowed( op, e,
2021 mlist->sml_desc, NULL,
2022 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
2023 &state ) )
2024 {
2025 ret = 0;
2026 goto done;
2027 }
2028 break;
2029 }
2030 for ( bv = mlist->sml_nvalues
2031 ? mlist->sml_nvalues : mlist->sml_values;
2032 bv->bv_val != NULL; bv++ )
2033 {
2034 if ( ! access_allowed( op, e,
2035 mlist->sml_desc, bv,
2036 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
2037 &state ) )
2038 {
2039 ret = 0;
2040 goto done;
2041 }
2042 }
2043 break;
2044
2045 case SLAP_MOD_SOFTADD:
2046 /* allow adding attribute via modrdn thru */
2047 break;
2048
2049 default:
2050 assert( 0 );
2051 /* not reached */
2052 ret = 0;
2053 break;
2054 }
2055 }
2056
2057 done:
2058 if (be_null) op->o_bd = NULL;
2059 return( ret );
2060 }
2061
2062 int
acl_get_part(struct berval * list,int ix,char sep,struct berval * bv)2063 acl_get_part(
2064 struct berval *list,
2065 int ix,
2066 char sep,
2067 struct berval *bv )
2068 {
2069 int len;
2070 char *p;
2071
2072 if ( bv ) {
2073 BER_BVZERO( bv );
2074 }
2075 len = list->bv_len;
2076 p = list->bv_val;
2077 while ( len >= 0 && --ix >= 0 ) {
2078 while ( --len >= 0 && *p++ != sep )
2079 ;
2080 }
2081 while ( len >= 0 && *p == ' ' ) {
2082 len--;
2083 p++;
2084 }
2085 if ( len < 0 ) {
2086 return -1;
2087 }
2088
2089 if ( !bv ) {
2090 return 0;
2091 }
2092
2093 bv->bv_val = p;
2094 while ( --len >= 0 && *p != sep ) {
2095 bv->bv_len++;
2096 p++;
2097 }
2098 while ( bv->bv_len > 0 && *--p == ' ' ) {
2099 bv->bv_len--;
2100 }
2101
2102 return bv->bv_len;
2103 }
2104
2105 typedef struct acl_set_gather_t {
2106 SetCookie *cookie;
2107 BerVarray bvals;
2108 } acl_set_gather_t;
2109
2110 static int
acl_set_cb_gather(Operation * op,SlapReply * rs)2111 acl_set_cb_gather( Operation *op, SlapReply *rs )
2112 {
2113 acl_set_gather_t *p = (acl_set_gather_t *)op->o_callback->sc_private;
2114
2115 if ( rs->sr_type == REP_SEARCH ) {
2116 BerValue bvals[ 2 ];
2117 BerVarray bvalsp = NULL;
2118 int j;
2119
2120 for ( j = 0; !BER_BVISNULL( &rs->sr_attrs[ j ].an_name ); j++ ) {
2121 AttributeDescription *desc = rs->sr_attrs[ j ].an_desc;
2122
2123 if ( desc == NULL ) {
2124 continue;
2125 }
2126
2127 if ( desc == slap_schema.si_ad_entryDN ) {
2128 bvalsp = bvals;
2129 bvals[ 0 ] = rs->sr_entry->e_nname;
2130 BER_BVZERO( &bvals[ 1 ] );
2131
2132 } else {
2133 Attribute *a;
2134
2135 a = attr_find( rs->sr_entry->e_attrs, desc );
2136 if ( a != NULL ) {
2137 bvalsp = a->a_nvals;
2138 }
2139 }
2140
2141 if ( bvalsp ) {
2142 p->bvals = slap_set_join( p->cookie, p->bvals,
2143 ( '|' | SLAP_SET_RREF ), bvalsp );
2144 }
2145 }
2146
2147 } else {
2148 switch ( rs->sr_type ) {
2149 case REP_SEARCHREF:
2150 case REP_INTERMEDIATE:
2151 /* ignore */
2152 break;
2153
2154 default:
2155 assert( rs->sr_type == REP_RESULT );
2156 break;
2157 }
2158 }
2159
2160 return 0;
2161 }
2162
2163 BerVarray
acl_set_gather(SetCookie * cookie,struct berval * name,AttributeDescription * desc)2164 acl_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2165 {
2166 AclSetCookie *cp = (AclSetCookie *)cookie;
2167 int rc = 0;
2168 LDAPURLDesc *ludp = NULL;
2169 Operation op2 = { 0 };
2170 SlapReply rs = {REP_RESULT};
2171 AttributeName anlist[ 2 ], *anlistp = NULL;
2172 int nattrs = 0;
2173 slap_callback cb = { NULL, acl_set_cb_gather, NULL, NULL };
2174 acl_set_gather_t p = { 0 };
2175
2176 /* this routine needs to return the bervals instead of
2177 * plain strings, since syntax is not known. It should
2178 * also return the syntax or some "comparison cookie".
2179 */
2180 if ( strncasecmp( name->bv_val, "ldap:///", STRLENOF( "ldap:///" ) ) != 0 ) {
2181 return acl_set_gather2( cookie, name, desc );
2182 }
2183
2184 rc = ldap_url_parse( name->bv_val, &ludp );
2185 if ( rc != LDAP_URL_SUCCESS ) {
2186 Debug( LDAP_DEBUG_TRACE,
2187 "%s acl_set_gather: unable to parse URL=\"%s\"\n",
2188 cp->asc_op->o_log_prefix, name->bv_val, 0 );
2189
2190 rc = LDAP_PROTOCOL_ERROR;
2191 goto url_done;
2192 }
2193
2194 if ( ( ludp->lud_host && ludp->lud_host[0] ) || ludp->lud_exts )
2195 {
2196 /* host part must be empty */
2197 /* extensions parts must be empty */
2198 Debug( LDAP_DEBUG_TRACE,
2199 "%s acl_set_gather: host/exts must be absent in URL=\"%s\"\n",
2200 cp->asc_op->o_log_prefix, name->bv_val, 0 );
2201
2202 rc = LDAP_PROTOCOL_ERROR;
2203 goto url_done;
2204 }
2205
2206 /* Grab the searchbase and see if an appropriate database can be found */
2207 ber_str2bv( ludp->lud_dn, 0, 0, &op2.o_req_dn );
2208 rc = dnNormalize( 0, NULL, NULL, &op2.o_req_dn,
2209 &op2.o_req_ndn, cp->asc_op->o_tmpmemctx );
2210 BER_BVZERO( &op2.o_req_dn );
2211 if ( rc != LDAP_SUCCESS ) {
2212 Debug( LDAP_DEBUG_TRACE,
2213 "%s acl_set_gather: DN=\"%s\" normalize failed\n",
2214 cp->asc_op->o_log_prefix, ludp->lud_dn, 0 );
2215
2216 goto url_done;
2217 }
2218
2219 op2.o_bd = select_backend( &op2.o_req_ndn, 1 );
2220 if ( ( op2.o_bd == NULL ) || ( op2.o_bd->be_search == NULL ) ) {
2221 Debug( LDAP_DEBUG_TRACE,
2222 "%s acl_set_gather: no database could be selected for DN=\"%s\"\n",
2223 cp->asc_op->o_log_prefix, op2.o_req_ndn.bv_val, 0 );
2224
2225 rc = LDAP_NO_SUCH_OBJECT;
2226 goto url_done;
2227 }
2228
2229 /* Grab the filter */
2230 if ( ludp->lud_filter ) {
2231 ber_str2bv_x( ludp->lud_filter, 0, 0, &op2.ors_filterstr,
2232 cp->asc_op->o_tmpmemctx );
2233 op2.ors_filter = str2filter_x( cp->asc_op, op2.ors_filterstr.bv_val );
2234 if ( op2.ors_filter == NULL ) {
2235 Debug( LDAP_DEBUG_TRACE,
2236 "%s acl_set_gather: unable to parse filter=\"%s\"\n",
2237 cp->asc_op->o_log_prefix, op2.ors_filterstr.bv_val, 0 );
2238
2239 rc = LDAP_PROTOCOL_ERROR;
2240 goto url_done;
2241 }
2242
2243 } else {
2244 op2.ors_filterstr = *slap_filterstr_objectClass_pres;
2245 op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
2246 }
2247
2248
2249 /* Grab the scope */
2250 op2.ors_scope = ludp->lud_scope;
2251
2252 /* Grap the attributes */
2253 if ( ludp->lud_attrs ) {
2254 int i;
2255
2256 for ( ; ludp->lud_attrs[ nattrs ]; nattrs++ )
2257 ;
2258
2259 anlistp = slap_sl_calloc( sizeof( AttributeName ), nattrs + 2,
2260 cp->asc_op->o_tmpmemctx );
2261
2262 for ( i = 0, nattrs = 0; ludp->lud_attrs[ i ]; i++ ) {
2263 struct berval name;
2264 AttributeDescription *desc = NULL;
2265 const char *text = NULL;
2266
2267 ber_str2bv( ludp->lud_attrs[ i ], 0, 0, &name );
2268 rc = slap_bv2ad( &name, &desc, &text );
2269 if ( rc == LDAP_SUCCESS ) {
2270 anlistp[ nattrs ].an_name = name;
2271 anlistp[ nattrs ].an_desc = desc;
2272 nattrs++;
2273 }
2274 }
2275
2276 } else {
2277 anlistp = anlist;
2278 }
2279
2280 anlistp[ nattrs ].an_name = desc->ad_cname;
2281 anlistp[ nattrs ].an_desc = desc;
2282
2283 BER_BVZERO( &anlistp[ nattrs + 1 ].an_name );
2284
2285 p.cookie = cookie;
2286
2287 op2.o_hdr = cp->asc_op->o_hdr;
2288 op2.o_tag = LDAP_REQ_SEARCH;
2289 op2.o_ndn = op2.o_bd->be_rootndn;
2290 op2.o_callback = &cb;
2291 slap_op_time( &op2.o_time, &op2.o_tincr );
2292 op2.o_do_not_cache = 1;
2293 op2.o_is_auth_check = 0;
2294 ber_dupbv_x( &op2.o_req_dn, &op2.o_req_ndn, cp->asc_op->o_tmpmemctx );
2295 op2.ors_slimit = SLAP_NO_LIMIT;
2296 op2.ors_tlimit = SLAP_NO_LIMIT;
2297 op2.ors_attrs = anlistp;
2298 op2.ors_attrsonly = 0;
2299 op2.o_private = cp->asc_op->o_private;
2300 op2.o_extra = cp->asc_op->o_extra;
2301
2302 cb.sc_private = &p;
2303
2304 rc = op2.o_bd->be_search( &op2, &rs );
2305 if ( rc != 0 ) {
2306 goto url_done;
2307 }
2308
2309 url_done:;
2310 if ( op2.ors_filter && op2.ors_filter != slap_filter_objectClass_pres ) {
2311 filter_free_x( cp->asc_op, op2.ors_filter, 1 );
2312 }
2313 if ( !BER_BVISNULL( &op2.o_req_ndn ) ) {
2314 slap_sl_free( op2.o_req_ndn.bv_val, cp->asc_op->o_tmpmemctx );
2315 }
2316 if ( !BER_BVISNULL( &op2.o_req_dn ) ) {
2317 slap_sl_free( op2.o_req_dn.bv_val, cp->asc_op->o_tmpmemctx );
2318 }
2319 if ( ludp ) {
2320 ldap_free_urldesc( ludp );
2321 }
2322 if ( anlistp && anlistp != anlist ) {
2323 slap_sl_free( anlistp, cp->asc_op->o_tmpmemctx );
2324 }
2325
2326 return p.bvals;
2327 }
2328
2329 BerVarray
acl_set_gather2(SetCookie * cookie,struct berval * name,AttributeDescription * desc)2330 acl_set_gather2( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2331 {
2332 AclSetCookie *cp = (AclSetCookie *)cookie;
2333 BerVarray bvals = NULL;
2334 struct berval ndn;
2335 int rc = 0;
2336
2337 /* this routine needs to return the bervals instead of
2338 * plain strings, since syntax is not known. It should
2339 * also return the syntax or some "comparison cookie".
2340 */
2341 rc = dnNormalize( 0, NULL, NULL, name, &ndn, cp->asc_op->o_tmpmemctx );
2342 if ( rc == LDAP_SUCCESS ) {
2343 if ( desc == slap_schema.si_ad_entryDN ) {
2344 bvals = (BerVarray)slap_sl_malloc( sizeof( BerValue ) * 2,
2345 cp->asc_op->o_tmpmemctx );
2346 bvals[ 0 ] = ndn;
2347 BER_BVZERO( &bvals[ 1 ] );
2348 BER_BVZERO( &ndn );
2349
2350 } else {
2351 backend_attribute( cp->asc_op,
2352 cp->asc_e, &ndn, desc, &bvals, ACL_NONE );
2353 }
2354
2355 if ( !BER_BVISNULL( &ndn ) ) {
2356 slap_sl_free( ndn.bv_val, cp->asc_op->o_tmpmemctx );
2357 }
2358 }
2359
2360 return bvals;
2361 }
2362
2363 int
acl_match_set(struct berval * subj,Operation * op,Entry * e,struct berval * default_set_attribute)2364 acl_match_set (
2365 struct berval *subj,
2366 Operation *op,
2367 Entry *e,
2368 struct berval *default_set_attribute )
2369 {
2370 struct berval set = BER_BVNULL;
2371 int rc = 0;
2372 AclSetCookie cookie;
2373
2374 if ( default_set_attribute == NULL ) {
2375 set = *subj;
2376
2377 } else {
2378 struct berval subjdn, ndn = BER_BVNULL;
2379 struct berval setat;
2380 BerVarray bvals = NULL;
2381 const char *text;
2382 AttributeDescription *desc = NULL;
2383
2384 /* format of string is "entry/setAttrName" */
2385 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
2386 return 0;
2387 }
2388
2389 if ( acl_get_part( subj, 1, '/', &setat ) < 0 ) {
2390 setat = *default_set_attribute;
2391 }
2392
2393 /*
2394 * NOTE: dnNormalize honors the ber_len field
2395 * as the length of the dn to be normalized
2396 */
2397 if ( slap_bv2ad( &setat, &desc, &text ) == LDAP_SUCCESS ) {
2398 if ( dnNormalize( 0, NULL, NULL, &subjdn, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
2399 {
2400 backend_attribute( op, e, &ndn, desc, &bvals, ACL_NONE );
2401 if ( bvals != NULL && !BER_BVISNULL( &bvals[0] ) ) {
2402 int i;
2403
2404 set = bvals[0];
2405 BER_BVZERO( &bvals[0] );
2406 for ( i = 1; !BER_BVISNULL( &bvals[i] ); i++ )
2407 /* count */ ;
2408 bvals[0].bv_val = bvals[i-1].bv_val;
2409 BER_BVZERO( &bvals[i-1] );
2410 }
2411 ber_bvarray_free_x( bvals, op->o_tmpmemctx );
2412 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
2413 }
2414 }
2415 }
2416
2417 if ( !BER_BVISNULL( &set ) ) {
2418 cookie.asc_op = op;
2419 cookie.asc_e = e;
2420 rc = ( slap_set_filter(
2421 acl_set_gather,
2422 (SetCookie *)&cookie, &set,
2423 &op->o_ndn, &e->e_nname, NULL ) > 0 );
2424 if ( set.bv_val != subj->bv_val ) {
2425 slap_sl_free( set.bv_val, op->o_tmpmemctx );
2426 }
2427 }
2428
2429 return(rc);
2430 }
2431
2432 #ifdef SLAP_DYNACL
2433
2434 /*
2435 * dynamic ACL infrastructure
2436 */
2437 static slap_dynacl_t *da_list = NULL;
2438
2439 int
slap_dynacl_register(slap_dynacl_t * da)2440 slap_dynacl_register( slap_dynacl_t *da )
2441 {
2442 slap_dynacl_t *tmp;
2443
2444 for ( tmp = da_list; tmp; tmp = tmp->da_next ) {
2445 if ( strcasecmp( da->da_name, tmp->da_name ) == 0 ) {
2446 break;
2447 }
2448 }
2449
2450 if ( tmp != NULL ) {
2451 return -1;
2452 }
2453
2454 if ( da->da_mask == NULL ) {
2455 return -1;
2456 }
2457
2458 da->da_private = NULL;
2459 da->da_next = da_list;
2460 da_list = da;
2461
2462 return 0;
2463 }
2464
2465 static slap_dynacl_t *
slap_dynacl_next(slap_dynacl_t * da)2466 slap_dynacl_next( slap_dynacl_t *da )
2467 {
2468 if ( da ) {
2469 return da->da_next;
2470 }
2471 return da_list;
2472 }
2473
2474 slap_dynacl_t *
slap_dynacl_get(const char * name)2475 slap_dynacl_get( const char *name )
2476 {
2477 slap_dynacl_t *da;
2478
2479 for ( da = slap_dynacl_next( NULL ); da; da = slap_dynacl_next( da ) ) {
2480 if ( strcasecmp( da->da_name, name ) == 0 ) {
2481 break;
2482 }
2483 }
2484
2485 return da;
2486 }
2487 #endif /* SLAP_DYNACL */
2488
2489 /*
2490 * statically built-in dynamic ACL initialization
2491 */
2492 static int (*acl_init_func[])( void ) = {
2493 #ifdef SLAP_DYNACL
2494 /* TODO: remove when ACI will only be dynamic */
2495 #if SLAPD_ACI_ENABLED == SLAPD_MOD_STATIC
2496 dynacl_aci_init,
2497 #endif /* SLAPD_ACI_ENABLED */
2498 #endif /* SLAP_DYNACL */
2499
2500 NULL
2501 };
2502
2503 int
acl_init(void)2504 acl_init( void )
2505 {
2506 int i, rc;
2507
2508 for ( i = 0; acl_init_func[ i ] != NULL; i++ ) {
2509 rc = (*(acl_init_func[ i ]))();
2510 if ( rc != 0 ) {
2511 return rc;
2512 }
2513 }
2514
2515 return 0;
2516 }
2517
2518 int
acl_string_expand(struct berval * bv,struct berval * pat,struct berval * dn_matches,struct berval * val_matches,AclRegexMatches * matches)2519 acl_string_expand(
2520 struct berval *bv,
2521 struct berval *pat,
2522 struct berval *dn_matches,
2523 struct berval *val_matches,
2524 AclRegexMatches *matches)
2525 {
2526 ber_len_t size;
2527 char *sp;
2528 char *dp;
2529 int flag;
2530 enum { DN_FLAG, VAL_FLAG } tflag;
2531
2532 size = 0;
2533 bv->bv_val[0] = '\0';
2534 bv->bv_len--; /* leave space for lone $ */
2535
2536 flag = 0;
2537 tflag = DN_FLAG;
2538 for ( dp = bv->bv_val, sp = pat->bv_val; size < bv->bv_len &&
2539 sp < pat->bv_val + pat->bv_len ; sp++ )
2540 {
2541 /* did we previously see a $ */
2542 if ( flag ) {
2543 if ( flag == 1 && *sp == '$' ) {
2544 *dp++ = '$';
2545 size++;
2546 flag = 0;
2547 tflag = DN_FLAG;
2548
2549 } else if ( flag == 2 && *sp == 'v' /*'}'*/) {
2550 tflag = VAL_FLAG;
2551
2552 } else if ( flag == 2 && *sp == 'd' /*'}'*/) {
2553 tflag = DN_FLAG;
2554
2555 } else if ( flag == 1 && *sp == '{' /*'}'*/) {
2556 flag = 2;
2557
2558 } else if ( *sp >= '0' && *sp <= '9' ) {
2559 int nm;
2560 regmatch_t *m;
2561 char *data;
2562 int n;
2563 int i;
2564 int l;
2565
2566 n = *sp - '0';
2567
2568 if ( flag == 2 ) {
2569 for ( sp++; *sp != '\0' && *sp != /*'{'*/ '}'; sp++ ) {
2570 if ( *sp >= '0' && *sp <= '9' ) {
2571 n = 10*n + ( *sp - '0' );
2572 }
2573 }
2574
2575 if ( *sp != /*'{'*/ '}' ) {
2576 /* FIXME: error */
2577 return 1;
2578 }
2579 }
2580
2581 switch (tflag) {
2582 case DN_FLAG:
2583 nm = matches->dn_count;
2584 m = matches->dn_data;
2585 data = dn_matches ? dn_matches->bv_val : NULL;
2586 break;
2587 case VAL_FLAG:
2588 nm = matches->val_count;
2589 m = matches->val_data;
2590 data = val_matches ? val_matches->bv_val : NULL;
2591 break;
2592 default:
2593 assert( 0 );
2594 }
2595 if ( n >= nm ) {
2596 /* FIXME: error */
2597 return 1;
2598 }
2599 if ( data == NULL ) {
2600 /* FIXME: error */
2601 return 1;
2602 }
2603
2604 *dp = '\0';
2605 i = m[n].rm_so;
2606 l = m[n].rm_eo;
2607
2608 for ( ; size < bv->bv_len && i < l; size++, i++ ) {
2609 *dp++ = data[i];
2610 }
2611 *dp = '\0';
2612
2613 flag = 0;
2614 tflag = DN_FLAG;
2615 }
2616 } else {
2617 if (*sp == '$') {
2618 flag = 1;
2619 } else {
2620 *dp++ = *sp;
2621 size++;
2622 }
2623 }
2624 }
2625
2626 if ( flag ) {
2627 /* must have ended with a single $ */
2628 *dp++ = '$';
2629 size++;
2630 }
2631
2632 *dp = '\0';
2633 bv->bv_len = size;
2634
2635 Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: pattern: %.*s\n", (int)pat->bv_len, pat->bv_val, 0 );
2636 Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: expanded: %s\n", bv->bv_val, 0, 0 );
2637
2638 return 0;
2639 }
2640
2641 static int
regex_matches(struct berval * pat,char * str,struct berval * dn_matches,struct berval * val_matches,AclRegexMatches * matches)2642 regex_matches(
2643 struct berval *pat, /* pattern to expand and match against */
2644 char *str, /* string to match against pattern */
2645 struct berval *dn_matches, /* buffer with $N expansion variables from DN */
2646 struct berval *val_matches, /* buffer with $N expansion variables from val */
2647 AclRegexMatches *matches /* offsets in buffer for $N expansion variables */
2648 )
2649 {
2650 regex_t re;
2651 char newbuf[ACL_BUF_SIZE];
2652 struct berval bv;
2653 int rc;
2654
2655 bv.bv_len = sizeof( newbuf ) - 1;
2656 bv.bv_val = newbuf;
2657
2658 if (str == NULL) {
2659 str = "";
2660 };
2661
2662 if ( acl_string_expand( &bv, pat, dn_matches, val_matches, matches )) {
2663 Debug( LDAP_DEBUG_TRACE,
2664 "expand( \"%s\", \"%s\") failed\n",
2665 pat->bv_val, str, 0 );
2666 return( 0 );
2667 }
2668 rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE );
2669 if ( rc ) {
2670 char error[ACL_BUF_SIZE];
2671 regerror( rc, &re, error, sizeof( error ) );
2672
2673 Debug( LDAP_DEBUG_TRACE,
2674 "compile( \"%s\", \"%s\") failed %s\n",
2675 pat->bv_val, str, error );
2676 return( 0 );
2677 }
2678
2679 rc = regexec( &re, str, 0, NULL, 0 );
2680 regfree( &re );
2681
2682 Debug( LDAP_DEBUG_TRACE,
2683 "=> regex_matches: string: %s\n", str, 0, 0 );
2684 Debug( LDAP_DEBUG_TRACE,
2685 "=> regex_matches: rc: %d %s\n",
2686 rc, !rc ? "matches" : "no matches", 0 );
2687 return( !rc );
2688 }
2689
2690