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