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" );
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 );
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] );
265 }
266 }
267 Debug( LDAP_DEBUG_ACL, "\n" );
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] );
283 }
284 }
285 Debug( LDAP_DEBUG_ACL, "\n" );
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 );
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" );
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 );
417 return state->as_result;
418 } else {
419 Debug( LDAP_DEBUG_ACL,
420 "=> access_allowed: result not in cache (%s)\n",
421 attr );
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 );
465 ACL_INIT( mask );
466
467 } else {
468 Debug( LDAP_DEBUG_ACL,
469 "=> access_allowed: no more rules\n" );
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 );
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 );
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 );
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 );
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 );
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" );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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 );
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
1769 /* this case works different from the others above.
1770 * since dynamic ACL's themselves give permissions, we need
1771 * to first check b->a_access_mask, the ACL's access level.
1772 */
1773 /* first check if the right being requested
1774 * is allowed by the ACL clause.
1775 */
1776 if ( ! ACL_PRIV_ISSET( b->a_access_mask, a2pmask ) ) {
1777 continue;
1778 }
1779
1780 /* start out with nothing granted, nothing denied */
1781 ACL_INVALIDATE(tgrant);
1782 ACL_INVALIDATE(tdeny);
1783
1784 for ( da = b->a_dynacl; da; da = da->da_next ) {
1785 slap_access_t grant,
1786 deny;
1787
1788 ACL_INVALIDATE(grant);
1789 ACL_INVALIDATE(deny);
1790
1791 Debug( LDAP_DEBUG_ACL, " <= check a_dynacl: %s\n",
1792 da->da_name );
1793
1794 /*
1795 * XXXmanu Only DN matches are supplied
1796 * sending attribute values matches require
1797 * an API update
1798 */
1799 (void)da->da_mask( da->da_private, op, e, desc,
1800 val, matches->dn_count, matches->dn_data,
1801 &grant, &deny );
1802
1803 tgrant |= grant;
1804 tdeny |= deny;
1805 }
1806
1807 /* remove anything that the ACL clause does not allow */
1808 tgrant &= b->a_access_mask & ACL_PRIV_MASK;
1809 tdeny &= ACL_PRIV_MASK;
1810
1811 /* see if we have anything to contribute */
1812 if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) {
1813 continue;
1814 }
1815
1816 /* this could be improved by changing slap_acl_mask so that it can deal with
1817 * by clauses that return grant/deny pairs. Right now, it does either
1818 * additive or subtractive rights, but not both at the same time. So,
1819 * we need to combine the grant/deny pair into a single rights mask in
1820 * a smart way: if either grant or deny is "empty", then we use the
1821 * opposite as is, otherwise we remove any denied rights from the grant
1822 * rights mask and construct an additive mask.
1823 */
1824 if (ACL_IS_INVALID(tdeny)) {
1825 modmask = tgrant | ACL_PRIV_ADDITIVE;
1826
1827 } else if (ACL_IS_INVALID(tgrant)) {
1828 modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
1829
1830 } else {
1831 modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
1832 }
1833
1834 } else
1835 #endif /* SLAP_DYNACL */
1836 {
1837 modmask = b->a_access_mask;
1838 }
1839
1840 Debug( LDAP_DEBUG_ACL,
1841 "<= acl_mask: [%d] applying %s (%s)\n",
1842 i, accessmask2str( modmask, accessmaskbuf, 1 ),
1843 b->a_type == ACL_CONTINUE
1844 ? "continue"
1845 : b->a_type == ACL_BREAK
1846 ? "break"
1847 : "stop" );
1848 /* save old mask */
1849 oldmask = *mask;
1850
1851 if( ACL_IS_ADDITIVE(modmask) ) {
1852 /* add privs */
1853 ACL_PRIV_SET( *mask, modmask );
1854
1855 /* cleanup */
1856 ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
1857
1858 } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
1859 /* subtract privs */
1860 ACL_PRIV_CLR( *mask, modmask );
1861
1862 /* cleanup */
1863 ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
1864
1865 } else {
1866 /* assign privs */
1867 *mask = modmask;
1868 }
1869
1870 Debug( LDAP_DEBUG_ACL,
1871 "<= acl_mask: [%d] mask: %s\n",
1872 i, accessmask2str(*mask, accessmaskbuf, 1) );
1873
1874 if( b->a_type == ACL_CONTINUE ) {
1875 continue;
1876
1877 } else if ( b->a_type == ACL_BREAK ) {
1878 return ACL_BREAK;
1879
1880 } else {
1881 return ACL_STOP;
1882 }
1883 }
1884
1885 /* implicit "by * none" clause */
1886 ACL_INIT(*mask);
1887
1888 Debug( LDAP_DEBUG_ACL,
1889 "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
1890 accessmask2str(*mask, accessmaskbuf, 1) );
1891 return ACL_STOP;
1892 }
1893
1894 /*
1895 * acl_check_modlist - check access control on the given entry to see if
1896 * it allows the given modifications by the user associated with op.
1897 * returns 1 if mods allowed ok
1898 * 0 mods not allowed
1899 */
1900
1901 int
acl_check_modlist(Operation * op,Entry * e,Modifications * mlist)1902 acl_check_modlist(
1903 Operation *op,
1904 Entry *e,
1905 Modifications *mlist )
1906 {
1907 struct berval *bv;
1908 AccessControlState state = ACL_STATE_INIT;
1909 Backend *be;
1910 int be_null = 0;
1911 int ret = 1; /* default is access allowed */
1912
1913 be = op->o_bd;
1914 if ( be == NULL ) {
1915 be = LDAP_STAILQ_FIRST(&backendDB);
1916 be_null = 1;
1917 op->o_bd = be;
1918 }
1919 assert( be != NULL );
1920
1921 /* If ADD attribute checking is not enabled, just allow it */
1922 if ( op->o_tag == LDAP_REQ_ADD && !SLAP_DBACL_ADD( be ))
1923 return 1;
1924
1925 /* short circuit root database access */
1926 if ( be_isroot( op ) ) {
1927 Debug( LDAP_DEBUG_ACL,
1928 "<= acl_access_allowed: granted to database root\n" );
1929 goto done;
1930 }
1931
1932 /* use backend default access if no backend acls */
1933 if( op->o_bd != NULL && op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) {
1934 Debug( LDAP_DEBUG_ACL,
1935 "=> access_allowed: backend default %s access %s to \"%s\"\n",
1936 access2str( ACL_WRITE ),
1937 op->o_bd->be_dfltaccess >= ACL_WRITE
1938 ? "granted" : "denied",
1939 op->o_dn.bv_val );
1940 ret = (op->o_bd->be_dfltaccess >= ACL_WRITE);
1941 goto done;
1942 }
1943
1944 for ( ; mlist != NULL; mlist = mlist->sml_next ) {
1945 /*
1946 * Internal mods are ignored by ACL_WRITE checking
1947 */
1948 if ( mlist->sml_flags & SLAP_MOD_INTERNAL ) {
1949 Debug( LDAP_DEBUG_ACL, "acl: internal mod %s:"
1950 " modify access granted\n",
1951 mlist->sml_desc->ad_cname.bv_val );
1952 continue;
1953 }
1954
1955 /*
1956 * no-user-modification operational attributes are ignored
1957 * by ACL_WRITE checking as any found here are not provided
1958 * by the user
1959 */
1960 if ( is_at_no_user_mod( mlist->sml_desc->ad_type )
1961 && ! ( mlist->sml_flags & SLAP_MOD_MANAGING ) )
1962 {
1963 Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
1964 " modify access granted\n",
1965 mlist->sml_desc->ad_cname.bv_val );
1966 continue;
1967 }
1968
1969 switch ( mlist->sml_op ) {
1970 case LDAP_MOD_REPLACE:
1971 case LDAP_MOD_INCREMENT:
1972 /*
1973 * We must check both permission to delete the whole
1974 * attribute and permission to add the specific attributes.
1975 * This prevents abuse from selfwriters.
1976 */
1977 if ( ! access_allowed( op, e,
1978 mlist->sml_desc, NULL,
1979 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
1980 &state ) )
1981 {
1982 ret = 0;
1983 goto done;
1984 }
1985
1986 if ( mlist->sml_values == NULL ) break;
1987
1988 /* fall thru to check value to add */
1989
1990 case LDAP_MOD_ADD:
1991 case SLAP_MOD_ADD_IF_NOT_PRESENT:
1992 assert( mlist->sml_values != NULL );
1993
1994 if ( mlist->sml_op == SLAP_MOD_ADD_IF_NOT_PRESENT
1995 && attr_find( e->e_attrs, mlist->sml_desc ) )
1996 {
1997 break;
1998 }
1999
2000 for ( bv = mlist->sml_nvalues
2001 ? mlist->sml_nvalues : mlist->sml_values;
2002 bv->bv_val != NULL; bv++ )
2003 {
2004 if ( ! access_allowed( op, e,
2005 mlist->sml_desc, bv,
2006 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WADD,
2007 &state ) )
2008 {
2009 ret = 0;
2010 goto done;
2011 }
2012 }
2013 break;
2014
2015 case LDAP_MOD_DELETE:
2016 case SLAP_MOD_SOFTDEL:
2017 if ( mlist->sml_values == NULL ) {
2018 if ( ! access_allowed( op, e,
2019 mlist->sml_desc, NULL,
2020 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
2021 &state ) )
2022 {
2023 ret = 0;
2024 goto done;
2025 }
2026 break;
2027 }
2028 for ( bv = mlist->sml_nvalues
2029 ? mlist->sml_nvalues : mlist->sml_values;
2030 bv->bv_val != NULL; bv++ )
2031 {
2032 if ( ! access_allowed( op, e,
2033 mlist->sml_desc, bv,
2034 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
2035 &state ) )
2036 {
2037 ret = 0;
2038 goto done;
2039 }
2040 }
2041 break;
2042
2043 case SLAP_MOD_SOFTADD:
2044 /* allow adding attribute via modrdn thru */
2045 break;
2046
2047 default:
2048 assert( 0 );
2049 /* not reached */
2050 ret = 0;
2051 break;
2052 }
2053 }
2054
2055 done:
2056 if (be_null) op->o_bd = NULL;
2057 return( ret );
2058 }
2059
2060 int
acl_get_part(struct berval * list,int ix,char sep,struct berval * bv)2061 acl_get_part(
2062 struct berval *list,
2063 int ix,
2064 char sep,
2065 struct berval *bv )
2066 {
2067 int len;
2068 char *p;
2069
2070 if ( bv ) {
2071 BER_BVZERO( bv );
2072 }
2073 len = list->bv_len;
2074 p = list->bv_val;
2075 while ( len >= 0 && --ix >= 0 ) {
2076 while ( --len >= 0 && *p++ != sep )
2077 ;
2078 }
2079 while ( len >= 0 && *p == ' ' ) {
2080 len--;
2081 p++;
2082 }
2083 if ( len < 0 ) {
2084 return -1;
2085 }
2086
2087 if ( !bv ) {
2088 return 0;
2089 }
2090
2091 bv->bv_val = p;
2092 while ( --len >= 0 && *p != sep ) {
2093 bv->bv_len++;
2094 p++;
2095 }
2096 while ( bv->bv_len > 0 && *--p == ' ' ) {
2097 bv->bv_len--;
2098 }
2099
2100 return bv->bv_len;
2101 }
2102
2103 typedef struct acl_set_gather_t {
2104 SetCookie *cookie;
2105 BerVarray bvals;
2106 } acl_set_gather_t;
2107
2108 static int
acl_set_cb_gather(Operation * op,SlapReply * rs)2109 acl_set_cb_gather( Operation *op, SlapReply *rs )
2110 {
2111 acl_set_gather_t *p = (acl_set_gather_t *)op->o_callback->sc_private;
2112
2113 if ( rs->sr_type == REP_SEARCH ) {
2114 BerValue bvals[ 2 ];
2115 BerVarray bvalsp = NULL;
2116 int j;
2117
2118 for ( j = 0; !BER_BVISNULL( &rs->sr_attrs[ j ].an_name ); j++ ) {
2119 AttributeDescription *desc = rs->sr_attrs[ j ].an_desc;
2120
2121 if ( desc == NULL ) {
2122 continue;
2123 }
2124
2125 if ( desc == slap_schema.si_ad_entryDN ) {
2126 bvalsp = bvals;
2127 bvals[ 0 ] = rs->sr_entry->e_nname;
2128 BER_BVZERO( &bvals[ 1 ] );
2129
2130 } else {
2131 Attribute *a;
2132
2133 a = attr_find( rs->sr_entry->e_attrs, desc );
2134 if ( a != NULL ) {
2135 bvalsp = a->a_nvals;
2136 }
2137 }
2138
2139 if ( bvalsp ) {
2140 p->bvals = slap_set_join( p->cookie, p->bvals,
2141 ( '|' | SLAP_SET_RREF ), bvalsp );
2142 }
2143 }
2144
2145 } else {
2146 switch ( rs->sr_type ) {
2147 case REP_SEARCHREF:
2148 case REP_INTERMEDIATE:
2149 /* ignore */
2150 break;
2151
2152 default:
2153 assert( rs->sr_type == REP_RESULT );
2154 break;
2155 }
2156 }
2157
2158 return 0;
2159 }
2160
2161 BerVarray
acl_set_gather(SetCookie * cookie,struct berval * name,AttributeDescription * desc)2162 acl_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2163 {
2164 AclSetCookie *cp = (AclSetCookie *)cookie;
2165 int rc = 0;
2166 LDAPURLDesc *ludp = NULL;
2167 Operation op2 = { 0 };
2168 SlapReply rs = {REP_RESULT};
2169 AttributeName anlist[ 2 ], *anlistp = NULL;
2170 int nattrs = 0;
2171 slap_callback cb = { NULL, acl_set_cb_gather, NULL, NULL };
2172 acl_set_gather_t p = { 0 };
2173
2174 /* this routine needs to return the bervals instead of
2175 * plain strings, since syntax is not known. It should
2176 * also return the syntax or some "comparison cookie".
2177 */
2178 if ( strncasecmp( name->bv_val, "ldap:///", STRLENOF( "ldap:///" ) ) != 0 ) {
2179 return acl_set_gather2( cookie, name, desc );
2180 }
2181
2182 rc = ldap_url_parse( name->bv_val, &ludp );
2183 if ( rc != LDAP_URL_SUCCESS ) {
2184 Debug( LDAP_DEBUG_TRACE,
2185 "%s acl_set_gather: unable to parse URL=\"%s\"\n",
2186 cp->asc_op->o_log_prefix, name->bv_val );
2187
2188 rc = LDAP_PROTOCOL_ERROR;
2189 goto url_done;
2190 }
2191
2192 if ( ( ludp->lud_host && ludp->lud_host[0] ) || ludp->lud_exts )
2193 {
2194 /* host part must be empty */
2195 /* extensions parts must be empty */
2196 Debug( LDAP_DEBUG_TRACE,
2197 "%s acl_set_gather: host/exts must be absent in URL=\"%s\"\n",
2198 cp->asc_op->o_log_prefix, name->bv_val );
2199
2200 rc = LDAP_PROTOCOL_ERROR;
2201 goto url_done;
2202 }
2203
2204 /* Grab the searchbase and see if an appropriate database can be found */
2205 ber_str2bv( ludp->lud_dn, 0, 0, &op2.o_req_dn );
2206 rc = dnNormalize( 0, NULL, NULL, &op2.o_req_dn,
2207 &op2.o_req_ndn, cp->asc_op->o_tmpmemctx );
2208 BER_BVZERO( &op2.o_req_dn );
2209 if ( rc != LDAP_SUCCESS ) {
2210 Debug( LDAP_DEBUG_TRACE,
2211 "%s acl_set_gather: DN=\"%s\" normalize failed\n",
2212 cp->asc_op->o_log_prefix, ludp->lud_dn );
2213
2214 goto url_done;
2215 }
2216
2217 op2.o_bd = select_backend( &op2.o_req_ndn, 1 );
2218 if ( ( op2.o_bd == NULL ) || ( op2.o_bd->be_search == NULL ) ) {
2219 Debug( LDAP_DEBUG_TRACE,
2220 "%s acl_set_gather: no database could be selected for DN=\"%s\"\n",
2221 cp->asc_op->o_log_prefix, op2.o_req_ndn.bv_val );
2222
2223 rc = LDAP_NO_SUCH_OBJECT;
2224 goto url_done;
2225 }
2226
2227 /* Grab the filter */
2228 if ( ludp->lud_filter ) {
2229 ber_str2bv_x( ludp->lud_filter, 0, 0, &op2.ors_filterstr,
2230 cp->asc_op->o_tmpmemctx );
2231 op2.ors_filter = str2filter_x( cp->asc_op, op2.ors_filterstr.bv_val );
2232 if ( op2.ors_filter == NULL ) {
2233 Debug( LDAP_DEBUG_TRACE,
2234 "%s acl_set_gather: unable to parse filter=\"%s\"\n",
2235 cp->asc_op->o_log_prefix, op2.ors_filterstr.bv_val );
2236
2237 rc = LDAP_PROTOCOL_ERROR;
2238 goto url_done;
2239 }
2240
2241 } else {
2242 op2.ors_filterstr = *slap_filterstr_objectClass_pres;
2243 op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
2244 }
2245
2246
2247 /* Grab the scope */
2248 op2.ors_scope = ludp->lud_scope;
2249
2250 /* Grap the attributes */
2251 if ( ludp->lud_attrs ) {
2252 int i;
2253
2254 for ( ; ludp->lud_attrs[ nattrs ]; nattrs++ )
2255 ;
2256
2257 anlistp = slap_sl_calloc( sizeof( AttributeName ), nattrs + 2,
2258 cp->asc_op->o_tmpmemctx );
2259
2260 for ( i = 0, nattrs = 0; ludp->lud_attrs[ i ]; i++ ) {
2261 struct berval name;
2262 AttributeDescription *desc = NULL;
2263 const char *text = NULL;
2264
2265 ber_str2bv( ludp->lud_attrs[ i ], 0, 0, &name );
2266 rc = slap_bv2ad( &name, &desc, &text );
2267 if ( rc == LDAP_SUCCESS ) {
2268 anlistp[ nattrs ].an_name = name;
2269 anlistp[ nattrs ].an_desc = desc;
2270 nattrs++;
2271 }
2272 }
2273
2274 } else {
2275 anlistp = anlist;
2276 }
2277
2278 anlistp[ nattrs ].an_name = desc->ad_cname;
2279 anlistp[ nattrs ].an_desc = desc;
2280
2281 BER_BVZERO( &anlistp[ nattrs + 1 ].an_name );
2282
2283 p.cookie = cookie;
2284
2285 op2.o_hdr = cp->asc_op->o_hdr;
2286 op2.o_tag = LDAP_REQ_SEARCH;
2287 op2.o_ndn = op2.o_bd->be_rootndn;
2288 op2.o_callback = &cb;
2289 slap_op_time( &op2.o_time, &op2.o_tincr );
2290 op2.o_do_not_cache = 1;
2291 op2.o_is_auth_check = 0;
2292 ber_dupbv_x( &op2.o_req_dn, &op2.o_req_ndn, cp->asc_op->o_tmpmemctx );
2293 op2.ors_slimit = SLAP_NO_LIMIT;
2294 op2.ors_tlimit = SLAP_NO_LIMIT;
2295 op2.ors_attrs = anlistp;
2296 op2.ors_attrsonly = 0;
2297 op2.o_private = cp->asc_op->o_private;
2298 op2.o_extra = cp->asc_op->o_extra;
2299
2300 cb.sc_private = &p;
2301
2302 rc = op2.o_bd->be_search( &op2, &rs );
2303 if ( rc != 0 ) {
2304 goto url_done;
2305 }
2306
2307 url_done:;
2308 if ( op2.ors_filter && op2.ors_filter != slap_filter_objectClass_pres ) {
2309 filter_free_x( cp->asc_op, op2.ors_filter, 1 );
2310 }
2311 if ( !BER_BVISNULL( &op2.o_req_ndn ) ) {
2312 slap_sl_free( op2.o_req_ndn.bv_val, cp->asc_op->o_tmpmemctx );
2313 }
2314 if ( !BER_BVISNULL( &op2.o_req_dn ) ) {
2315 slap_sl_free( op2.o_req_dn.bv_val, cp->asc_op->o_tmpmemctx );
2316 }
2317 if ( ludp ) {
2318 ldap_free_urldesc( ludp );
2319 }
2320 if ( anlistp && anlistp != anlist ) {
2321 slap_sl_free( anlistp, cp->asc_op->o_tmpmemctx );
2322 }
2323
2324 return p.bvals;
2325 }
2326
2327 BerVarray
acl_set_gather2(SetCookie * cookie,struct berval * name,AttributeDescription * desc)2328 acl_set_gather2( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2329 {
2330 AclSetCookie *cp = (AclSetCookie *)cookie;
2331 BerVarray bvals = NULL;
2332 struct berval ndn;
2333 int rc = 0;
2334
2335 /* this routine needs to return the bervals instead of
2336 * plain strings, since syntax is not known. It should
2337 * also return the syntax or some "comparison cookie".
2338 */
2339 rc = dnNormalize( 0, NULL, NULL, name, &ndn, cp->asc_op->o_tmpmemctx );
2340 if ( rc == LDAP_SUCCESS ) {
2341 if ( desc == slap_schema.si_ad_entryDN ) {
2342 bvals = (BerVarray)slap_sl_malloc( sizeof( BerValue ) * 2,
2343 cp->asc_op->o_tmpmemctx );
2344 bvals[ 0 ] = ndn;
2345 BER_BVZERO( &bvals[ 1 ] );
2346 BER_BVZERO( &ndn );
2347
2348 } else {
2349 backend_attribute( cp->asc_op,
2350 cp->asc_e, &ndn, desc, &bvals, ACL_NONE );
2351 }
2352
2353 if ( !BER_BVISNULL( &ndn ) ) {
2354 slap_sl_free( ndn.bv_val, cp->asc_op->o_tmpmemctx );
2355 }
2356 }
2357
2358 return bvals;
2359 }
2360
2361 int
acl_match_set(struct berval * subj,Operation * op,Entry * e,struct berval * default_set_attribute)2362 acl_match_set (
2363 struct berval *subj,
2364 Operation *op,
2365 Entry *e,
2366 struct berval *default_set_attribute )
2367 {
2368 struct berval set = BER_BVNULL;
2369 int rc = 0;
2370 AclSetCookie cookie;
2371
2372 if ( default_set_attribute == NULL ) {
2373 set = *subj;
2374
2375 } else {
2376 struct berval subjdn, ndn = BER_BVNULL;
2377 struct berval setat;
2378 BerVarray bvals = NULL;
2379 const char *text;
2380 AttributeDescription *desc = NULL;
2381
2382 /* format of string is "entry/setAttrName" */
2383 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
2384 return 0;
2385 }
2386
2387 if ( acl_get_part( subj, 1, '/', &setat ) < 0 ) {
2388 setat = *default_set_attribute;
2389 }
2390
2391 /*
2392 * NOTE: dnNormalize honors the ber_len field
2393 * as the length of the dn to be normalized
2394 */
2395 if ( slap_bv2ad( &setat, &desc, &text ) == LDAP_SUCCESS ) {
2396 if ( dnNormalize( 0, NULL, NULL, &subjdn, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
2397 {
2398 backend_attribute( op, e, &ndn, desc, &bvals, ACL_NONE );
2399 if ( bvals != NULL && !BER_BVISNULL( &bvals[0] ) ) {
2400 int i;
2401
2402 set = bvals[0];
2403 BER_BVZERO( &bvals[0] );
2404 for ( i = 1; !BER_BVISNULL( &bvals[i] ); i++ )
2405 /* count */ ;
2406 bvals[0].bv_val = bvals[i-1].bv_val;
2407 BER_BVZERO( &bvals[i-1] );
2408 }
2409 ber_bvarray_free_x( bvals, op->o_tmpmemctx );
2410 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
2411 }
2412 }
2413 }
2414
2415 if ( !BER_BVISNULL( &set ) ) {
2416 cookie.asc_op = op;
2417 cookie.asc_e = e;
2418 rc = ( slap_set_filter(
2419 acl_set_gather,
2420 (SetCookie *)&cookie, &set,
2421 &op->o_ndn, &e->e_nname, NULL ) > 0 );
2422 if ( set.bv_val != subj->bv_val ) {
2423 slap_sl_free( set.bv_val, op->o_tmpmemctx );
2424 }
2425 }
2426
2427 return(rc);
2428 }
2429
2430 #ifdef SLAP_DYNACL
2431
2432 /*
2433 * dynamic ACL infrastructure
2434 */
2435 static slap_dynacl_t *da_list = NULL;
2436
2437 int
slap_dynacl_register(slap_dynacl_t * da)2438 slap_dynacl_register( slap_dynacl_t *da )
2439 {
2440 slap_dynacl_t *tmp;
2441
2442 for ( tmp = da_list; tmp; tmp = tmp->da_next ) {
2443 if ( strcasecmp( da->da_name, tmp->da_name ) == 0 ) {
2444 break;
2445 }
2446 }
2447
2448 if ( tmp != NULL ) {
2449 return -1;
2450 }
2451
2452 if ( da->da_mask == NULL ) {
2453 return -1;
2454 }
2455
2456 da->da_private = NULL;
2457 da->da_next = da_list;
2458 da_list = da;
2459
2460 return 0;
2461 }
2462
2463 static slap_dynacl_t *
slap_dynacl_next(slap_dynacl_t * da)2464 slap_dynacl_next( slap_dynacl_t *da )
2465 {
2466 if ( da ) {
2467 return da->da_next;
2468 }
2469 return da_list;
2470 }
2471
2472 slap_dynacl_t *
slap_dynacl_get(const char * name)2473 slap_dynacl_get( const char *name )
2474 {
2475 slap_dynacl_t *da;
2476
2477 for ( da = slap_dynacl_next( NULL ); da; da = slap_dynacl_next( da ) ) {
2478 if ( strcasecmp( da->da_name, name ) == 0 ) {
2479 break;
2480 }
2481 }
2482
2483 return da;
2484 }
2485 #endif /* SLAP_DYNACL */
2486
2487 /*
2488 * statically built-in dynamic ACL initialization
2489 */
2490 static int (*acl_init_func[])( void ) = {
2491 #ifdef SLAP_DYNACL
2492 /* TODO: remove when ACI will only be dynamic */
2493 #if SLAPD_ACI_ENABLED == SLAPD_MOD_STATIC
2494 dynacl_aci_init,
2495 #endif /* SLAPD_ACI_ENABLED */
2496 #endif /* SLAP_DYNACL */
2497
2498 NULL
2499 };
2500
2501 int
acl_init(void)2502 acl_init( void )
2503 {
2504 int i, rc;
2505
2506 for ( i = 0; acl_init_func[ i ] != NULL; i++ ) {
2507 rc = (*(acl_init_func[ i ]))();
2508 if ( rc != 0 ) {
2509 return rc;
2510 }
2511 }
2512
2513 return 0;
2514 }
2515
2516 int
acl_string_expand(struct berval * bv,struct berval * pat,struct berval * dn_matches,struct berval * val_matches,AclRegexMatches * matches)2517 acl_string_expand(
2518 struct berval *bv,
2519 struct berval *pat,
2520 struct berval *dn_matches,
2521 struct berval *val_matches,
2522 AclRegexMatches *matches)
2523 {
2524 ber_len_t size;
2525 char *sp;
2526 char *dp;
2527 int flag;
2528 enum { DN_FLAG, VAL_FLAG } tflag;
2529
2530 size = 0;
2531 bv->bv_val[0] = '\0';
2532 bv->bv_len--; /* leave space for lone $ */
2533
2534 flag = 0;
2535 tflag = DN_FLAG;
2536 for ( dp = bv->bv_val, sp = pat->bv_val; size < bv->bv_len &&
2537 sp < pat->bv_val + pat->bv_len ; sp++ )
2538 {
2539 /* did we previously see a $ */
2540 if ( flag ) {
2541 if ( flag == 1 && *sp == '$' ) {
2542 *dp++ = '$';
2543 size++;
2544 flag = 0;
2545 tflag = DN_FLAG;
2546
2547 } else if ( flag == 2 && *sp == 'v' /*'}'*/) {
2548 tflag = VAL_FLAG;
2549
2550 } else if ( flag == 2 && *sp == 'd' /*'}'*/) {
2551 tflag = DN_FLAG;
2552
2553 } else if ( flag == 1 && *sp == '{' /*'}'*/) {
2554 flag = 2;
2555
2556 } else if ( *sp >= '0' && *sp <= '9' ) {
2557 int nm;
2558 regmatch_t *m;
2559 char *data;
2560 int n;
2561 int i;
2562 int l;
2563
2564 n = *sp - '0';
2565
2566 if ( flag == 2 ) {
2567 for ( sp++; *sp != '\0' && *sp != /*'{'*/ '}'; sp++ ) {
2568 if ( *sp >= '0' && *sp <= '9' ) {
2569 n = 10*n + ( *sp - '0' );
2570 }
2571 }
2572
2573 if ( *sp != /*'{'*/ '}' ) {
2574 /* FIXME: error */
2575 return 1;
2576 }
2577 }
2578
2579 switch (tflag) {
2580 case DN_FLAG:
2581 nm = matches->dn_count;
2582 m = matches->dn_data;
2583 data = dn_matches ? dn_matches->bv_val : NULL;
2584 break;
2585 case VAL_FLAG:
2586 nm = matches->val_count;
2587 m = matches->val_data;
2588 data = val_matches ? val_matches->bv_val : NULL;
2589 break;
2590 default:
2591 assert( 0 );
2592 }
2593 if ( n >= nm ) {
2594 /* FIXME: error */
2595 return 1;
2596 }
2597 if ( data == NULL ) {
2598 /* FIXME: error */
2599 return 1;
2600 }
2601
2602 *dp = '\0';
2603 i = m[n].rm_so;
2604 l = m[n].rm_eo;
2605
2606 for ( ; size < bv->bv_len && i < l; size++, i++ ) {
2607 *dp++ = data[i];
2608 }
2609 *dp = '\0';
2610
2611 flag = 0;
2612 tflag = DN_FLAG;
2613 }
2614 } else {
2615 if (*sp == '$') {
2616 flag = 1;
2617 } else {
2618 *dp++ = *sp;
2619 size++;
2620 }
2621 }
2622 }
2623
2624 if ( flag ) {
2625 /* must have ended with a single $ */
2626 *dp++ = '$';
2627 size++;
2628 }
2629
2630 *dp = '\0';
2631 bv->bv_len = size;
2632
2633 Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: pattern: %.*s\n", (int)pat->bv_len, pat->bv_val );
2634 Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: expanded: %s\n", bv->bv_val );
2635
2636 return 0;
2637 }
2638
2639 static int
regex_matches(struct berval * pat,char * str,struct berval * dn_matches,struct berval * val_matches,AclRegexMatches * matches)2640 regex_matches(
2641 struct berval *pat, /* pattern to expand and match against */
2642 char *str, /* string to match against pattern */
2643 struct berval *dn_matches, /* buffer with $N expansion variables from DN */
2644 struct berval *val_matches, /* buffer with $N expansion variables from val */
2645 AclRegexMatches *matches /* offsets in buffer for $N expansion variables */
2646 )
2647 {
2648 regex_t re;
2649 char newbuf[ACL_BUF_SIZE];
2650 struct berval bv;
2651 int rc;
2652
2653 bv.bv_len = sizeof( newbuf ) - 1;
2654 bv.bv_val = newbuf;
2655
2656 if (str == NULL) {
2657 str = "";
2658 };
2659
2660 if ( acl_string_expand( &bv, pat, dn_matches, val_matches, matches )) {
2661 Debug( LDAP_DEBUG_TRACE,
2662 "expand( \"%s\", \"%s\") failed\n",
2663 pat->bv_val, str );
2664 return( 0 );
2665 }
2666 rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE );
2667 if ( rc ) {
2668 char error[ACL_BUF_SIZE];
2669 regerror( rc, &re, error, sizeof( error ) );
2670
2671 Debug( LDAP_DEBUG_TRACE,
2672 "compile( \"%s\", \"%s\") failed %s\n",
2673 pat->bv_val, str, error );
2674 return( 0 );
2675 }
2676
2677 rc = regexec( &re, str, 0, NULL, 0 );
2678 regfree( &re );
2679
2680 Debug( LDAP_DEBUG_TRACE,
2681 "=> regex_matches: string: %s\n", str );
2682 Debug( LDAP_DEBUG_TRACE,
2683 "=> regex_matches: rc: %d %s\n",
2684 rc, !rc ? "matches" : "no matches" );
2685 return( !rc );
2686 }
2687
2688