1 /* acl.c - routines to parse and check acl's */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2014 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
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>.
16 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
17 * All rights reserved.
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.
32 #include <ac/socket.h>
33 #include <ac/string.h>
40 #define ACL_BUF_SIZE 1024 /* use most appropriate size */
42 static const struct berval acl_bv_ip_eq = BER_BVC( "IP=" );
44 static const struct berval acl_bv_ipv6_eq = BER_BVC( "IP=[" );
45 #endif /* LDAP_PF_INET6 */
47 static const struct berval acl_bv_path_eq = BER_BVC("PATH=");
48 #endif /* LDAP_PF_LOCAL */
50 static AccessControl * slap_acl_get(
51 AccessControl *ac, int *count,
52 Operation *op, Entry *e,
53 AttributeDescription *desc,
55 AclRegexMatches *matches,
57 AccessControlState *state );
59 static slap_control_t slap_acl_mask(
63 Operation *op, Entry *e,
64 AttributeDescription *desc,
66 AclRegexMatches *matches,
68 AccessControlState *state,
69 slap_access_t access );
71 static int regex_matches(
72 struct berval *pat, char *str,
73 struct berval *dn_matches, struct berval *val_matches,
74 AclRegexMatches *matches);
76 typedef struct AclSetCookie {
78 #define asc_op asc_cookie.set_op
83 SLAP_SET_GATHER acl_set_gather;
84 SLAP_SET_GATHER acl_set_gather2;
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).
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.
101 * - can be legally called with op == NULL
102 * - can be legally called with op->o_bd == NULL
106 slap_access_always_allowed(
109 AttributeDescription *desc,
111 slap_access_t access,
112 AccessControlState *state,
115 assert( maskp != NULL );
118 ACL_LVL_ASSIGN_MANAGE( *maskp );
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 */ )
138 AttributeDescription *desc,
140 slap_access_t access,
141 AccessControlState *state,
146 AccessControl *a, *prev;
149 char accessmaskbuf[ACCESSMASK_MAXLEN];
152 slap_control_t control;
153 slap_access_t access_level;
155 AclRegexMatches matches;
156 AccessControlState acl_state = ACL_STATE_INIT;
157 static AccessControlState state_init = ACL_STATE_INIT;
159 assert( op != NULL );
161 assert( desc != NULL );
162 assert( maskp != NULL );
164 access_level = ACL_LEVEL( access );
165 attr = desc->ad_cname.bv_val;
167 assert( attr != NULL );
171 /* grant database root access */
172 if ( be_isroot( op ) ) {
173 Debug( LDAP_DEBUG_ACL, "<= root access granted\n", 0, 0, 0 );
174 mask = ACL_LVL_MANAGE;
179 * no-user-modification operational attributes are ignored
180 * by ACL_WRITE checking as any found here are not provided
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.
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 )
192 Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
193 " %s access granted\n",
198 /* use backend default access if no backend acls */
199 if ( op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) {
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;
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 ) );
223 if ( state->as_desc == desc &&
224 state->as_access == access &&
225 state->as_vd_acl_present )
227 a = state->as_vd_acl;
228 count = state->as_vd_acl_count;
229 if ( state->as_fe_done )
231 ACL_PRIV_ASSIGN( mask, state->as_vd_mask );
237 ACL_PRIV_ASSIGN( mask, *maskp );
240 MATCHES_MEMSET( &matches );
243 while ( ( a = slap_acl_get( a, &count, op, e, desc, val,
244 &matches, &mask, state ) ) != NULL )
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;
253 for ( i = 0; i < dnmaxcount && dn_data[i].rm_eo > 0; i++ ) {
254 char *data = e->e_ndn;
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 ) {
261 for ( n = dn_data[i].rm_so;
262 n < dn_data[i].rm_eo; n++ ) {
263 Debug( LDAP_DEBUG_ACL, "%c",
267 Debug( LDAP_DEBUG_ACL, "\n", 0, 0, 0 );
271 for ( i = 0; i < valmaxcount && val_data[i].rm_eo > 0; i++ ) {
272 char *data = val->bv_val;
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 ) {
279 for ( n = val_data[i].rm_so;
280 n < val_data[i].rm_eo; n++ ) {
281 Debug( LDAP_DEBUG_ACL, "%c",
285 Debug( LDAP_DEBUG_ACL, "\n", 0, 0, 0 );
288 control = slap_acl_mask( a, prev, &mask, op,
289 e, desc, val, &matches, count, state, access );
291 if ( control != ACL_BREAK ) {
295 MATCHES_MEMSET( &matches );
299 if ( ACL_IS_INVALID( mask ) ) {
300 Debug( LDAP_DEBUG_ACL,
301 "=> slap_access_allowed: \"%s\" (%s) invalid!\n",
303 ACL_PRIV_ASSIGN( mask, *maskp );
305 } else if ( control == ACL_BREAK ) {
306 Debug( LDAP_DEBUG_ACL,
307 "=> slap_access_allowed: no more rules\n", 0, 0, 0 );
312 ret = ACL_GRANT( mask, access );
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 ) );
320 ACL_PRIV_ASSIGN( *maskp, mask );
328 AttributeDescription *desc,
330 slap_access_t access,
331 AccessControlState *state,
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
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;
352 rc = slap_access_allowed( op, e, desc, val, access, state, maskp );
362 AttributeDescription *desc,
364 slap_access_t access,
365 AccessControlState *state,
372 char accessmaskbuf[ACCESSMASK_MAXLEN];
375 slap_access_t access_level;
379 assert( desc != NULL );
381 access_level = ACL_LEVEL( access );
383 assert( access_level > ACL_NONE );
386 if ( maskp ) ACL_INVALIDATE( *maskp );
388 attr = desc->ad_cname.bv_val;
390 assert( attr != NULL );
393 if ( op->o_acl_priv != ACL_NONE ) {
394 access = op->o_acl_priv;
396 } else if ( op->o_is_auth_check &&
397 ( access_level == ACL_SEARCH || access_level == ACL_READ ) )
401 } else if ( get_relax( op ) && access_level == ACL_WRITE &&
402 desc == slap_schema.si_ad_entry )
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 )
414 Debug( LDAP_DEBUG_ACL,
415 "=> access_allowed: result was in cache (%s)\n",
417 return state->as_result;
419 Debug( LDAP_DEBUG_ACL,
420 "=> access_allowed: result not in cache (%s)\n",
425 Debug( LDAP_DEBUG_ACL,
426 "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
427 access2str( access ), e->e_dn, attr );
434 if ( op->o_bd == NULL ) {
435 op->o_bd = LDAP_STAILQ_FIRST( &backendDB );
438 /* FIXME: experimental; use first backend rules
439 * iff there is no global_acl (ITS#3100)
441 if ( frontendDB->be_acl != NULL ) {
442 op->o_bd = frontendDB;
445 assert( op->o_bd != NULL );
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 );
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 );
461 if ( ACL_IS_INVALID( mask ) ) {
462 Debug( LDAP_DEBUG_ACL,
463 "=> access_allowed: \"%s\" (%s) invalid!\n",
468 Debug( LDAP_DEBUG_ACL,
469 "=> access_allowed: no more rules\n", 0, 0, 0 );
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 ) );
481 if ( state != NULL ) {
482 state->as_access = access;
483 state->as_result = ret;
484 state->as_desc = desc;
486 if ( be_null ) op->o_bd = NULL;
487 if ( maskp ) ACL_PRIV_ASSIGN( *maskp, mask );
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().
498 static AccessControl *
504 AttributeDescription *desc,
506 AclRegexMatches *matches,
508 AccessControlState *state )
515 assert( count != NULL );
516 assert( desc != NULL );
517 assert( state != NULL );
519 attr = desc->ad_cname.bv_val;
521 assert( attr != NULL );
524 if( op->o_bd == NULL || op->o_bd->be_acl == NULL ) {
525 a = frontendDB->be_acl;
527 a = op->o_bd->be_acl;
532 if ( a == frontendDB->be_acl )
533 state->as_fe_done = 1;
539 dnlen = e->e_nname.bv_len;
542 for ( ; a != NULL; prev = a, a = a->acl_next ) {
545 if ( a != frontendDB->be_acl && state->as_fe_done )
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,
555 matches->dn_data, 0 ) )
561 Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n",
562 *count, a->acl_dn_pat.bv_val, 0 );
563 patlen = a->acl_dn_pat.bv_len;
564 if ( dnlen < patlen )
567 if ( a->acl_dn_style == ACL_STYLE_BASE ) {
568 /* base dn -- entire object DN must match */
569 if ( dnlen != patlen )
572 } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
573 ber_len_t rdnlen = 0;
576 if ( dnlen <= patlen )
580 if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
585 rdnlen = dn_rdnlen( NULL, &e->e_nname );
586 if ( rdnlen + patlen + sep != dnlen )
589 } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
590 if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
593 } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
594 if ( dnlen <= patlen )
596 if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
600 if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
604 Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
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;
616 /* Is this ACL only for a specific value? */
617 if ( a->acl_attrval.bv_val ) {
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 );
629 if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
630 Debug( LDAP_DEBUG_ACL,
631 "acl_get: valpat %s\n",
632 a->acl_attrval.bv_val, 0, 0 );
633 if ( regexec ( &a->acl_attrval_re,
636 matches->val_data, 0 ) )
644 Debug( LDAP_DEBUG_ACL,
646 a->acl_attrval.bv_val, 0, 0 );
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 ||
656 ber_len_t patlen, vdnlen;
658 patlen = a->acl_attrval.bv_len;
659 vdnlen = val->bv_len;
661 if ( vdnlen < patlen )
664 if ( a->acl_attrval_style == ACL_STYLE_BASE ) {
665 if ( vdnlen > patlen )
668 } else if ( a->acl_attrval_style == ACL_STYLE_ONE ) {
669 ber_len_t rdnlen = 0;
671 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
674 rdnlen = dn_rdnlen( NULL, val );
675 if ( rdnlen + patlen + 1 != vdnlen )
678 } else if ( a->acl_attrval_style == ACL_STYLE_SUBTREE ) {
679 if ( vdnlen > patlen && !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
682 } else if ( a->acl_attrval_style == ACL_STYLE_CHILDREN ) {
683 if ( vdnlen <= patlen )
686 if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
690 if ( strcmp( a->acl_attrval.bv_val, val->bv_val + vdnlen - patlen ) )
696 if ( a->acl_filter != NULL ) {
697 ber_int_t rc = test_filter( NULL, e, a->acl_filter );
698 if ( rc != LDAP_COMPARE_TRUE ) {
703 Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] attr %s\n",
708 if ( !state->as_fe_done ) {
709 state->as_fe_done = 1;
710 a = frontendDB->be_acl;
714 Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n", 0, 0, 0 );
719 * Record value-dependent access control state
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 ); \
736 AclRegexMatches *matches,
738 struct berval *opndn )
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
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.
752 if ( bdn->a_style == ACL_STYLE_ANONYMOUS ) {
753 if ( !BER_BVISEMPTY( opndn ) ) {
757 } else if ( bdn->a_style == ACL_STYLE_USERS ) {
758 if ( BER_BVISEMPTY( opndn ) ) {
762 } else if ( bdn->a_style == ACL_STYLE_SELF ) {
763 struct berval ndn, selfndn;
766 if ( BER_BVISEMPTY( opndn ) || BER_BVISNULL( &e->e_nname ) ) {
770 level = bdn->a_self_level;
778 selfndn = e->e_nname;
781 for ( ; level > 0; level-- ) {
782 if ( BER_BVISEMPTY( &ndn ) ) {
785 dnParent( &ndn, &ndn );
788 if ( BER_BVISEMPTY( &ndn ) || !dn_match( &ndn, &selfndn ) )
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;
798 regmatch_t *tmp_data;
800 MATCHES_MEMSET( &tmp_matches );
801 tmp_data = &tmp_matches.dn_data[0];
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;
811 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
814 tmp_data[0].rm_so = 0;
815 tmp_data[0].rm_eo = e->e_nname.bv_len;
816 tmp_matches.dn_count = 1;
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;
839 if ( !regex_matches( &bdn->a_pat, opndn->bv_val,
840 &e->e_nname, NULL, tmp_matchesp ) )
848 ber_len_t patlen, odnlen;
851 if ( e->e_dn == NULL )
854 if ( bdn->a_expand ) {
856 char buf[ACL_BUF_SIZE];
858 AclRegexMatches tmp_matches,
859 *tmp_matchesp = &tmp_matches;
861 regmatch_t *tmp_data;
863 MATCHES_MEMSET( &tmp_matches );
864 tmp_data = &tmp_matches.dn_data[0];
866 bv.bv_len = sizeof( buf ) - 1;
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;
878 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
881 tmp_data[0].rm_so = 0;
882 tmp_data[0].rm_eo = e->e_nname.bv_len;
883 tmp_matches.dn_count = 1;
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;
906 if ( acl_string_expand( &bv, &bdn->a_pat,
908 val, tmp_matchesp ) )
913 if ( dnNormalize(0, NULL, NULL, &bv,
914 &pat, op->o_tmpmemctx )
917 /* did not expand to a valid dn */
926 odnlen = opndn->bv_len;
927 if ( odnlen < patlen ) {
928 goto dn_match_cleanup;
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;
938 } else if ( bdn->a_style == ACL_STYLE_ONE ) {
939 ber_len_t rdnlen = 0;
941 if ( odnlen <= patlen ) {
942 goto dn_match_cleanup;
945 if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
946 goto dn_match_cleanup;
949 rdnlen = dn_rdnlen( NULL, opndn );
950 if ( rdnlen - ( odnlen - patlen - 1 ) != 0 ) {
951 goto dn_match_cleanup;
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;
959 } else if ( bdn->a_style == ACL_STYLE_CHILDREN ) {
960 if ( odnlen <= patlen ) {
961 goto dn_match_cleanup;
964 if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
965 goto dn_match_cleanup;
968 } else if ( bdn->a_style == ACL_STYLE_LEVEL ) {
969 int level = bdn->a_level;
972 if ( odnlen <= patlen ) {
973 goto dn_match_cleanup;
976 if ( level > 0 && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) )
978 goto dn_match_cleanup;
982 for ( ; level > 0; level-- ) {
983 if ( BER_BVISEMPTY( &ndn ) ) {
984 goto dn_match_cleanup;
986 dnParent( &ndn, &ndn );
987 if ( ndn.bv_len < patlen ) {
988 goto dn_match_cleanup;
992 if ( ndn.bv_len != patlen ) {
993 goto dn_match_cleanup;
997 got_match = !strcmp( pat.bv_val, &opndn->bv_val[ odnlen - patlen ] );
1000 if ( pat.bv_val != bdn->a_pat.bv_val ) {
1001 slap_sl_free( pat.bv_val, op->o_tmpmemctx );
1019 AccessControlState *state,
1021 slap_dn_access *bdn,
1022 struct berval *opndn )
1028 const char *attr = bdn->a_at->ad_cname.bv_val;
1030 assert( attr != NULL );
1032 if ( BER_BVISEMPTY( opndn ) ) {
1036 Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n", attr, 0, 0 );
1039 /* see if asker is listed in dnattr */
1040 for ( at = attrs_find( e->e_attrs, bdn->a_at );
1042 at = attrs_find( at->a_next, bdn->a_at ) )
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 )
1056 /* have a dnattr match. if this is a self clause then
1057 * the target must also match the op dn.
1059 if ( bdn->a_self ) {
1060 /* check if the target is an attribute. */
1061 if ( val == NULL ) return 1;
1063 /* target is attribute, check if the attribute value
1066 rc = value_match( &match, bdn->a_at,
1067 bdn->a_at->ad_type->sat_equality, 0,
1069 /* on match error or no match, fail the ACL clause */
1070 if ( rc != LDAP_SUCCESS || match != 0 )
1075 /* no dnattr match, check if this is a self clause */
1076 if ( ! bdn->a_self )
1079 /* this is a self clause, check if the target is an
1085 /* target is attribute, check if the attribute value
1088 rc = value_match( &match, bdn->a_at,
1089 bdn->a_at->ad_type->sat_equality, 0,
1092 /* on match error or no match, fail the ACL clause */
1093 if ( rc != LDAP_SUCCESS || match != 0 )
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).
1106 * returns 0 access NOT allowed
1110 static slap_control_t
1113 AccessControl *prev,
1117 AttributeDescription *desc,
1119 AclRegexMatches *matches,
1121 AccessControlState *state,
1122 slap_access_t access )
1127 char accessmaskbuf[ACCESSMASK_MAXLEN];
1131 slap_mask_t a2pmask = ACL_ACCESS2PRIV( access );
1132 #endif /* SLAP_DYNACL */
1134 assert( a != NULL );
1135 assert( mask != NULL );
1136 assert( desc != NULL );
1138 attr = desc->ad_cname.bv_val;
1140 assert( attr != NULL );
1142 Debug( LDAP_DEBUG_ACL,
1143 "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
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 ) );
1156 for ( ; b != NULL; b = b->a_next, i++ ) {
1157 slap_mask_t oldmask, modmask;
1159 ACL_INVALIDATE( modmask );
1161 /* check for the "self" modifier in the <access> field */
1162 if ( b->a_dn.a_self ) {
1166 ACL_RECORD_VALUE_STATE;
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;
1172 /* check if the target is an attribute. */
1173 if ( val == NULL ) continue;
1175 /* a DN must be present */
1176 if ( BER_BVISEMPTY( &op->o_ndn ) ) {
1180 /* target is attribute, check if the attribute value
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 )
1191 /* AND <who> clauses */
1192 if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
1193 Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
1194 b->a_dn_pat.bv_val, 0, 0);
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
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.
1207 if ( acl_mask_dn( op, e, val, a, matches,
1208 &b->a_dn, &op->o_ndn ) )
1214 if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
1217 Debug( LDAP_DEBUG_ACL, "<= check a_realdn_pat: %s\n",
1218 b->a_realdn_pat.bv_val, 0, 0);
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
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.
1231 if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
1233 ndn = op->o_conn->c_ndn;
1238 if ( acl_mask_dn( op, e, val, a, matches,
1239 &b->a_realdn, &ndn ) )
1245 if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
1246 if ( ! op->o_conn->c_listener ) {
1249 Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
1250 b->a_sockurl_pat.bv_val, 0, 0 );
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 ) )
1260 } else if ( b->a_sockurl_style == ACL_STYLE_EXPAND ) {
1262 char buf[ACL_BUF_SIZE];
1264 bv.bv_len = sizeof( buf ) - 1;
1266 if ( acl_string_expand( &bv, &b->a_sockurl_pat, &e->e_nname, val, matches ) )
1271 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_listener_url ) != 0 )
1277 if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &op->o_conn->c_listener_url ) != 0 )
1285 if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
1286 if ( !op->o_conn->c_peer_domain.bv_val ) {
1289 Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
1290 b->a_domain_pat.bv_val, 0, 0 );
1291 if ( !ber_bvccmp( &b->a_domain_pat, '*' ) ) {
1292 if ( b->a_domain_style == ACL_STYLE_REGEX) {
1293 if ( !regex_matches( &b->a_domain_pat, op->o_conn->c_peer_domain.bv_val,
1294 &e->e_nname, val, matches ) )
1299 char buf[ACL_BUF_SIZE];
1301 struct berval cmp = op->o_conn->c_peer_domain;
1302 struct berval pat = b->a_domain_pat;
1304 if ( b->a_domain_expand ) {
1307 bv.bv_len = sizeof(buf) - 1;
1310 if ( acl_string_expand(&bv, &b->a_domain_pat, &e->e_nname, val, matches) )
1317 if ( b->a_domain_style == ACL_STYLE_SUBTREE ) {
1318 int offset = cmp.bv_len - pat.bv_len;
1323 if ( offset == 1 || ( offset > 1 && cmp.bv_val[ offset - 1 ] != '.' ) ) {
1327 /* trim the domain */
1328 cmp.bv_val = &cmp.bv_val[ offset ];
1329 cmp.bv_len -= offset;
1332 if ( ber_bvstrcasecmp( &pat, &cmp ) != 0 ) {
1339 if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
1340 if ( !op->o_conn->c_peer_name.bv_val ) {
1343 Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
1344 b->a_peername_pat.bv_val, 0, 0 );
1345 if ( !ber_bvccmp( &b->a_peername_pat, '*' ) ) {
1346 if ( b->a_peername_style == ACL_STYLE_REGEX ) {
1347 if ( !regex_matches( &b->a_peername_pat, op->o_conn->c_peer_name.bv_val,
1348 &e->e_nname, val, matches ) )
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 ) {
1360 } else if ( b->a_peername_style == ACL_STYLE_EXPAND ) {
1362 char buf[ACL_BUF_SIZE];
1364 bv.bv_len = sizeof( buf ) - 1;
1366 if ( acl_string_expand( &bv, &b->a_peername_pat, &e->e_nname, val, matches ) )
1371 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_peer_name ) != 0 ) {
1375 /* extract IP and try exact match */
1376 } else if ( b->a_peername_style == ACL_STYLE_IP ) {
1378 char buf[STRLENOF("255.255.255.255") + 1];
1381 int port_number = -1;
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 )
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;
1391 port = strrchr( ip.bv_val, ':' );
1393 ip.bv_len = port - ip.bv_val;
1395 if ( lutil_atoi( &port_number, port ) != 0 )
1399 /* the port check can be anticipated here */
1400 if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
1403 /* address longer than expected? */
1404 if ( ip.bv_len >= sizeof(buf) )
1407 AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
1408 buf[ ip.bv_len ] = '\0';
1410 addr = inet_addr( buf );
1412 /* unable to convert? */
1413 if ( addr == (unsigned long)(-1) )
1416 if ( (addr & b->a_peername_mask) != b->a_peername_addr )
1419 #ifdef LDAP_PF_INET6
1420 /* extract IPv6 and try exact match */
1421 } else if ( b->a_peername_style == ACL_STYLE_IPV6 ) {
1423 char buf[STRLENOF("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF") + 1];
1425 struct in6_addr addr;
1426 int port_number = -1;
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 )
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;
1436 port = strrchr( ip.bv_val, ']' );
1438 ip.bv_len = port - ip.bv_val;
1440 if ( port[0] == ':' && lutil_atoi( &port_number, ++port ) != 0 )
1444 /* the port check can be anticipated here */
1445 if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
1448 /* address longer than expected? */
1449 if ( ip.bv_len >= sizeof(buf) )
1452 AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
1453 buf[ ip.bv_len ] = '\0';
1455 if ( inet_pton( AF_INET6, buf, &addr ) != 1 )
1459 if ( !slap_addr6_mask( &addr, &b->a_peername_mask6, &b->a_peername_addr6 ) )
1461 #endif /* LDAP_PF_INET6 */
1463 #ifdef LDAP_PF_LOCAL
1464 /* extract path and try exact match */
1465 } else if ( b->a_peername_style == ACL_STYLE_PATH ) {
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 )
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;
1478 if ( ber_bvcmp( &b->a_peername_pat, &path ) != 0 )
1481 #endif /* LDAP_PF_LOCAL */
1483 /* exact match (very unlikely...) */
1484 } else if ( ber_bvcmp( &op->o_conn->c_peer_name, &b->a_peername_pat ) != 0 ) {
1491 if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
1492 if ( BER_BVISNULL( &op->o_conn->c_sock_name ) ) {
1495 Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
1496 b->a_sockname_pat.bv_val, 0, 0 );
1497 if ( !ber_bvccmp( &b->a_sockname_pat, '*' ) ) {
1498 if ( b->a_sockname_style == ACL_STYLE_REGEX) {
1499 if ( !regex_matches( &b->a_sockname_pat, op->o_conn->c_sock_name.bv_val,
1500 &e->e_nname, val, matches ) )
1505 } else if ( b->a_sockname_style == ACL_STYLE_EXPAND ) {
1507 char buf[ACL_BUF_SIZE];
1509 bv.bv_len = sizeof( buf ) - 1;
1511 if ( acl_string_expand( &bv, &b->a_sockname_pat, &e->e_nname, val, matches ) )
1516 if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_sock_name ) != 0 ) {
1521 if ( ber_bvstrcasecmp( &b->a_sockname_pat, &op->o_conn->c_sock_name ) != 0 ) {
1528 if ( b->a_dn_at != NULL ) {
1529 if ( acl_mask_dnattr( op, e, val, a,
1531 &b->a_dn, &op->o_ndn ) )
1537 if ( b->a_realdn_at != NULL ) {
1540 if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
1542 ndn = op->o_conn->c_ndn;
1547 if ( acl_mask_dnattr( op, e, val, a,
1549 &b->a_realdn, &ndn ) )
1555 if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
1557 struct berval ndn = BER_BVNULL;
1560 if ( op->o_ndn.bv_len == 0 ) {
1564 Debug( LDAP_DEBUG_ACL, "<= check a_group_pat: %s\n",
1565 b->a_group_pat.bv_val, 0, 0 );
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
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;
1578 MATCHES_MEMSET( &tmp_matches );
1579 tmp_data = &tmp_matches.dn_data[0];
1581 bv.bv_len = sizeof(buf) - 1;
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;
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;
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;
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;
1623 if ( acl_string_expand( &bv, &b->a_group_pat,
1630 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn,
1631 op->o_tmpmemctx ) != LDAP_SUCCESS )
1633 /* did not expand to a valid dn */
1640 bv = b->a_group_pat;
1643 rc = backend_group( op, e, &bv, &op->o_ndn,
1644 b->a_group_oc, b->a_group_at );
1647 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
1655 if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
1657 char buf[ACL_BUF_SIZE];
1659 Debug( LDAP_DEBUG_ACL, "<= check a_set_pat: %s\n",
1660 b->a_set_pat.bv_val, 0, 0 );
1662 if ( b->a_set_style == ACL_STYLE_EXPAND ) {
1663 AclRegexMatches tmp_matches,
1664 *tmp_matchesp = &tmp_matches;
1666 regmatch_t *tmp_data;
1668 MATCHES_MEMSET( &tmp_matches );
1669 tmp_data = &tmp_matches.dn_data[0];
1671 bv.bv_len = sizeof( buf ) - 1;
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;
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;
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;
1711 if ( acl_string_expand( &bv, &b->a_set_pat,
1722 if ( acl_match_set( &bv, op, e, NULL ) == 0 ) {
1727 if ( b->a_authz.sai_ssf ) {
1728 Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
1729 b->a_authz.sai_ssf, op->o_ssf, 0 );
1730 if ( b->a_authz.sai_ssf > op->o_ssf ) {
1735 if ( b->a_authz.sai_transport_ssf ) {
1736 Debug( LDAP_DEBUG_ACL,
1737 "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
1738 b->a_authz.sai_transport_ssf, op->o_transport_ssf, 0 );
1739 if ( b->a_authz.sai_transport_ssf > op->o_transport_ssf ) {
1744 if ( b->a_authz.sai_tls_ssf ) {
1745 Debug( LDAP_DEBUG_ACL,
1746 "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
1747 b->a_authz.sai_tls_ssf, op->o_tls_ssf, 0 );
1748 if ( b->a_authz.sai_tls_ssf > op->o_tls_ssf ) {
1753 if ( b->a_authz.sai_sasl_ssf ) {
1754 Debug( LDAP_DEBUG_ACL,
1755 "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
1756 b->a_authz.sai_sasl_ssf, op->o_sasl_ssf, 0 );
1757 if ( b->a_authz.sai_sasl_ssf > op->o_sasl_ssf ) {
1763 if ( b->a_dynacl ) {
1765 slap_access_t tgrant, tdeny;
1767 Debug( LDAP_DEBUG_ACL, "<= check a_dynacl\n",
1770 /* this case works different from the others above.
1771 * since dynamic ACL's themselves give permissions, we need
1772 * to first check b->a_access_mask, the ACL's access level.
1774 /* first check if the right being requested
1775 * is allowed by the ACL clause.
1777 if ( ! ACL_PRIV_ISSET( b->a_access_mask, a2pmask ) ) {
1781 /* start out with nothing granted, nothing denied */
1782 ACL_INVALIDATE(tgrant);
1783 ACL_INVALIDATE(tdeny);
1785 for ( da = b->a_dynacl; da; da = da->da_next ) {
1786 slap_access_t grant,
1789 ACL_INVALIDATE(grant);
1790 ACL_INVALIDATE(deny);
1792 Debug( LDAP_DEBUG_ACL, " <= check a_dynacl: %s\n",
1793 da->da_name, 0, 0 );
1796 * XXXmanu Only DN matches are supplied
1797 * sending attribute values matches require
1800 (void)da->da_mask( da->da_private, op, e, desc,
1801 val, matches->dn_count, matches->dn_data,
1808 /* remove anything that the ACL clause does not allow */
1809 tgrant &= b->a_access_mask & ACL_PRIV_MASK;
1810 tdeny &= ACL_PRIV_MASK;
1812 /* see if we have anything to contribute */
1813 if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) {
1817 /* this could be improved by changing slap_acl_mask so that it can deal with
1818 * by clauses that return grant/deny pairs. Right now, it does either
1819 * additive or subtractive rights, but not both at the same time. So,
1820 * we need to combine the grant/deny pair into a single rights mask in
1821 * a smart way: if either grant or deny is "empty", then we use the
1822 * opposite as is, otherwise we remove any denied rights from the grant
1823 * rights mask and construct an additive mask.
1825 if (ACL_IS_INVALID(tdeny)) {
1826 modmask = tgrant | ACL_PRIV_ADDITIVE;
1828 } else if (ACL_IS_INVALID(tgrant)) {
1829 modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
1832 modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
1836 #endif /* SLAP_DYNACL */
1838 modmask = b->a_access_mask;
1841 Debug( LDAP_DEBUG_ACL,
1842 "<= acl_mask: [%d] applying %s (%s)\n",
1843 i, accessmask2str( modmask, accessmaskbuf, 1 ),
1844 b->a_type == ACL_CONTINUE
1846 : b->a_type == ACL_BREAK
1852 if( ACL_IS_ADDITIVE(modmask) ) {
1854 ACL_PRIV_SET( *mask, modmask );
1857 ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
1859 } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
1860 /* substract privs */
1861 ACL_PRIV_CLR( *mask, modmask );
1864 ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
1871 Debug( LDAP_DEBUG_ACL,
1872 "<= acl_mask: [%d] mask: %s\n",
1873 i, accessmask2str(*mask, accessmaskbuf, 1), 0 );
1875 if( b->a_type == ACL_CONTINUE ) {
1878 } else if ( b->a_type == ACL_BREAK ) {
1886 /* implicit "by * none" clause */
1889 Debug( LDAP_DEBUG_ACL,
1890 "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
1891 accessmask2str(*mask, accessmaskbuf, 1), 0, 0 );
1896 * acl_check_modlist - check access control on the given entry to see if
1897 * it allows the given modifications by the user associated with op.
1898 * returns 1 if mods allowed ok
1899 * 0 mods not allowed
1906 Modifications *mlist )
1909 AccessControlState state = ACL_STATE_INIT;
1912 int ret = 1; /* default is access allowed */
1916 be = LDAP_STAILQ_FIRST(&backendDB);
1920 assert( be != NULL );
1922 /* If ADD attribute checking is not enabled, just allow it */
1923 if ( op->o_tag == LDAP_REQ_ADD && !SLAP_DBACL_ADD( be ))
1926 /* short circuit root database access */
1927 if ( be_isroot( op ) ) {
1928 Debug( LDAP_DEBUG_ACL,
1929 "<= acl_access_allowed: granted to database root\n",
1934 /* use backend default access if no backend acls */
1935 if( op->o_bd != NULL && op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) {
1936 Debug( LDAP_DEBUG_ACL,
1937 "=> access_allowed: backend default %s access %s to \"%s\"\n",
1938 access2str( ACL_WRITE ),
1939 op->o_bd->be_dfltaccess >= ACL_WRITE
1940 ? "granted" : "denied",
1942 ret = (op->o_bd->be_dfltaccess >= ACL_WRITE);
1946 for ( ; mlist != NULL; mlist = mlist->sml_next ) {
1948 * Internal mods are ignored by ACL_WRITE checking
1950 if ( mlist->sml_flags & SLAP_MOD_INTERNAL ) {
1951 Debug( LDAP_DEBUG_ACL, "acl: internal mod %s:"
1952 " modify access granted\n",
1953 mlist->sml_desc->ad_cname.bv_val, 0, 0 );
1958 * no-user-modification operational attributes are ignored
1959 * by ACL_WRITE checking as any found here are not provided
1962 if ( is_at_no_user_mod( mlist->sml_desc->ad_type )
1963 && ! ( mlist->sml_flags & SLAP_MOD_MANAGING ) )
1965 Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
1966 " modify access granted\n",
1967 mlist->sml_desc->ad_cname.bv_val, 0, 0 );
1971 switch ( mlist->sml_op ) {
1972 case LDAP_MOD_REPLACE:
1973 case LDAP_MOD_INCREMENT:
1975 * We must check both permission to delete the whole
1976 * attribute and permission to add the specific attributes.
1977 * This prevents abuse from selfwriters.
1979 if ( ! access_allowed( op, e,
1980 mlist->sml_desc, NULL,
1981 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
1988 if ( mlist->sml_values == NULL ) break;
1990 /* fall thru to check value to add */
1993 case SLAP_MOD_ADD_IF_NOT_PRESENT:
1994 assert( mlist->sml_values != NULL );
1996 if ( mlist->sml_op == SLAP_MOD_ADD_IF_NOT_PRESENT
1997 && attr_find( e->e_attrs, mlist->sml_desc ) )
2002 for ( bv = mlist->sml_nvalues
2003 ? mlist->sml_nvalues : mlist->sml_values;
2004 bv->bv_val != NULL; bv++ )
2006 if ( ! access_allowed( op, e,
2007 mlist->sml_desc, bv,
2008 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WADD,
2017 case LDAP_MOD_DELETE:
2018 case SLAP_MOD_SOFTDEL:
2019 if ( mlist->sml_values == NULL ) {
2020 if ( ! access_allowed( op, e,
2021 mlist->sml_desc, NULL,
2022 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
2030 for ( bv = mlist->sml_nvalues
2031 ? mlist->sml_nvalues : mlist->sml_values;
2032 bv->bv_val != NULL; bv++ )
2034 if ( ! access_allowed( op, e,
2035 mlist->sml_desc, bv,
2036 ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
2045 case SLAP_MOD_SOFTADD:
2046 /* allow adding attribute via modrdn thru */
2058 if (be_null) op->o_bd = NULL;
2064 struct berval *list,
2077 while ( len >= 0 && --ix >= 0 ) {
2078 while ( --len >= 0 && *p++ != sep )
2081 while ( len >= 0 && *p == ' ' ) {
2094 while ( --len >= 0 && *p != sep ) {
2098 while ( bv->bv_len > 0 && *--p == ' ' ) {
2105 typedef struct acl_set_gather_t {
2111 acl_set_cb_gather( Operation *op, SlapReply *rs )
2113 acl_set_gather_t *p = (acl_set_gather_t *)op->o_callback->sc_private;
2115 if ( rs->sr_type == REP_SEARCH ) {
2116 BerValue bvals[ 2 ];
2117 BerVarray bvalsp = NULL;
2120 for ( j = 0; !BER_BVISNULL( &rs->sr_attrs[ j ].an_name ); j++ ) {
2121 AttributeDescription *desc = rs->sr_attrs[ j ].an_desc;
2123 if ( desc == NULL ) {
2127 if ( desc == slap_schema.si_ad_entryDN ) {
2129 bvals[ 0 ] = rs->sr_entry->e_nname;
2130 BER_BVZERO( &bvals[ 1 ] );
2135 a = attr_find( rs->sr_entry->e_attrs, desc );
2137 bvalsp = a->a_nvals;
2142 p->bvals = slap_set_join( p->cookie, p->bvals,
2143 ( '|' | SLAP_SET_RREF ), bvalsp );
2148 switch ( rs->sr_type ) {
2150 case REP_INTERMEDIATE:
2155 assert( rs->sr_type == REP_RESULT );
2164 acl_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2166 AclSetCookie *cp = (AclSetCookie *)cookie;
2168 LDAPURLDesc *ludp = NULL;
2169 Operation op2 = { 0 };
2170 SlapReply rs = {REP_RESULT};
2171 AttributeName anlist[ 2 ], *anlistp = NULL;
2173 slap_callback cb = { NULL, acl_set_cb_gather, NULL, NULL };
2174 acl_set_gather_t p = { 0 };
2176 /* this routine needs to return the bervals instead of
2177 * plain strings, since syntax is not known. It should
2178 * also return the syntax or some "comparison cookie".
2180 if ( strncasecmp( name->bv_val, "ldap:///", STRLENOF( "ldap:///" ) ) != 0 ) {
2181 return acl_set_gather2( cookie, name, desc );
2184 rc = ldap_url_parse( name->bv_val, &ludp );
2185 if ( rc != LDAP_URL_SUCCESS ) {
2186 Debug( LDAP_DEBUG_TRACE,
2187 "%s acl_set_gather: unable to parse URL=\"%s\"\n",
2188 cp->asc_op->o_log_prefix, name->bv_val, 0 );
2190 rc = LDAP_PROTOCOL_ERROR;
2194 if ( ( ludp->lud_host && ludp->lud_host[0] ) || ludp->lud_exts )
2196 /* host part must be empty */
2197 /* extensions parts must be empty */
2198 Debug( LDAP_DEBUG_TRACE,
2199 "%s acl_set_gather: host/exts must be absent in URL=\"%s\"\n",
2200 cp->asc_op->o_log_prefix, name->bv_val, 0 );
2202 rc = LDAP_PROTOCOL_ERROR;
2206 /* Grab the searchbase and see if an appropriate database can be found */
2207 ber_str2bv( ludp->lud_dn, 0, 0, &op2.o_req_dn );
2208 rc = dnNormalize( 0, NULL, NULL, &op2.o_req_dn,
2209 &op2.o_req_ndn, cp->asc_op->o_tmpmemctx );
2210 BER_BVZERO( &op2.o_req_dn );
2211 if ( rc != LDAP_SUCCESS ) {
2212 Debug( LDAP_DEBUG_TRACE,
2213 "%s acl_set_gather: DN=\"%s\" normalize failed\n",
2214 cp->asc_op->o_log_prefix, ludp->lud_dn, 0 );
2219 op2.o_bd = select_backend( &op2.o_req_ndn, 1 );
2220 if ( ( op2.o_bd == NULL ) || ( op2.o_bd->be_search == NULL ) ) {
2221 Debug( LDAP_DEBUG_TRACE,
2222 "%s acl_set_gather: no database could be selected for DN=\"%s\"\n",
2223 cp->asc_op->o_log_prefix, op2.o_req_ndn.bv_val, 0 );
2225 rc = LDAP_NO_SUCH_OBJECT;
2229 /* Grab the filter */
2230 if ( ludp->lud_filter ) {
2231 ber_str2bv_x( ludp->lud_filter, 0, 0, &op2.ors_filterstr,
2232 cp->asc_op->o_tmpmemctx );
2233 op2.ors_filter = str2filter_x( cp->asc_op, op2.ors_filterstr.bv_val );
2234 if ( op2.ors_filter == NULL ) {
2235 Debug( LDAP_DEBUG_TRACE,
2236 "%s acl_set_gather: unable to parse filter=\"%s\"\n",
2237 cp->asc_op->o_log_prefix, op2.ors_filterstr.bv_val, 0 );
2239 rc = LDAP_PROTOCOL_ERROR;
2244 op2.ors_filterstr = *slap_filterstr_objectClass_pres;
2245 op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
2249 /* Grab the scope */
2250 op2.ors_scope = ludp->lud_scope;
2252 /* Grap the attributes */
2253 if ( ludp->lud_attrs ) {
2256 for ( ; ludp->lud_attrs[ nattrs ]; nattrs++ )
2259 anlistp = slap_sl_calloc( sizeof( AttributeName ), nattrs + 2,
2260 cp->asc_op->o_tmpmemctx );
2262 for ( i = 0, nattrs = 0; ludp->lud_attrs[ i ]; i++ ) {
2264 AttributeDescription *desc = NULL;
2265 const char *text = NULL;
2267 ber_str2bv( ludp->lud_attrs[ i ], 0, 0, &name );
2268 rc = slap_bv2ad( &name, &desc, &text );
2269 if ( rc == LDAP_SUCCESS ) {
2270 anlistp[ nattrs ].an_name = name;
2271 anlistp[ nattrs ].an_desc = desc;
2280 anlistp[ nattrs ].an_name = desc->ad_cname;
2281 anlistp[ nattrs ].an_desc = desc;
2283 BER_BVZERO( &anlistp[ nattrs + 1 ].an_name );
2287 op2.o_hdr = cp->asc_op->o_hdr;
2288 op2.o_tag = LDAP_REQ_SEARCH;
2289 op2.o_ndn = op2.o_bd->be_rootndn;
2290 op2.o_callback = &cb;
2291 slap_op_time( &op2.o_time, &op2.o_tincr );
2292 op2.o_do_not_cache = 1;
2293 op2.o_is_auth_check = 0;
2294 ber_dupbv_x( &op2.o_req_dn, &op2.o_req_ndn, cp->asc_op->o_tmpmemctx );
2295 op2.ors_slimit = SLAP_NO_LIMIT;
2296 op2.ors_tlimit = SLAP_NO_LIMIT;
2297 op2.ors_attrs = anlistp;
2298 op2.ors_attrsonly = 0;
2299 op2.o_private = cp->asc_op->o_private;
2300 op2.o_extra = cp->asc_op->o_extra;
2304 rc = op2.o_bd->be_search( &op2, &rs );
2310 if ( op2.ors_filter && op2.ors_filter != slap_filter_objectClass_pres ) {
2311 filter_free_x( cp->asc_op, op2.ors_filter, 1 );
2313 if ( !BER_BVISNULL( &op2.o_req_ndn ) ) {
2314 slap_sl_free( op2.o_req_ndn.bv_val, cp->asc_op->o_tmpmemctx );
2316 if ( !BER_BVISNULL( &op2.o_req_dn ) ) {
2317 slap_sl_free( op2.o_req_dn.bv_val, cp->asc_op->o_tmpmemctx );
2320 ldap_free_urldesc( ludp );
2322 if ( anlistp && anlistp != anlist ) {
2323 slap_sl_free( anlistp, cp->asc_op->o_tmpmemctx );
2330 acl_set_gather2( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
2332 AclSetCookie *cp = (AclSetCookie *)cookie;
2333 BerVarray bvals = NULL;
2337 /* this routine needs to return the bervals instead of
2338 * plain strings, since syntax is not known. It should
2339 * also return the syntax or some "comparison cookie".
2341 rc = dnNormalize( 0, NULL, NULL, name, &ndn, cp->asc_op->o_tmpmemctx );
2342 if ( rc == LDAP_SUCCESS ) {
2343 if ( desc == slap_schema.si_ad_entryDN ) {
2344 bvals = (BerVarray)slap_sl_malloc( sizeof( BerValue ) * 2,
2345 cp->asc_op->o_tmpmemctx );
2347 BER_BVZERO( &bvals[ 1 ] );
2351 backend_attribute( cp->asc_op,
2352 cp->asc_e, &ndn, desc, &bvals, ACL_NONE );
2355 if ( !BER_BVISNULL( &ndn ) ) {
2356 slap_sl_free( ndn.bv_val, cp->asc_op->o_tmpmemctx );
2365 struct berval *subj,
2368 struct berval *default_set_attribute )
2370 struct berval set = BER_BVNULL;
2372 AclSetCookie cookie;
2374 if ( default_set_attribute == NULL ) {
2378 struct berval subjdn, ndn = BER_BVNULL;
2379 struct berval setat;
2380 BerVarray bvals = NULL;
2382 AttributeDescription *desc = NULL;
2384 /* format of string is "entry/setAttrName" */
2385 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
2389 if ( acl_get_part( subj, 1, '/', &setat ) < 0 ) {
2390 setat = *default_set_attribute;
2394 * NOTE: dnNormalize honors the ber_len field
2395 * as the length of the dn to be normalized
2397 if ( slap_bv2ad( &setat, &desc, &text ) == LDAP_SUCCESS ) {
2398 if ( dnNormalize( 0, NULL, NULL, &subjdn, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
2400 backend_attribute( op, e, &ndn, desc, &bvals, ACL_NONE );
2401 if ( bvals != NULL && !BER_BVISNULL( &bvals[0] ) ) {
2405 BER_BVZERO( &bvals[0] );
2406 for ( i = 1; !BER_BVISNULL( &bvals[i] ); i++ )
2408 bvals[0].bv_val = bvals[i-1].bv_val;
2409 BER_BVZERO( &bvals[i-1] );
2411 ber_bvarray_free_x( bvals, op->o_tmpmemctx );
2412 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
2417 if ( !BER_BVISNULL( &set ) ) {
2420 rc = ( slap_set_filter(
2422 (SetCookie *)&cookie, &set,
2423 &op->o_ndn, &e->e_nname, NULL ) > 0 );
2424 if ( set.bv_val != subj->bv_val ) {
2425 slap_sl_free( set.bv_val, op->o_tmpmemctx );
2435 * dynamic ACL infrastructure
2437 static slap_dynacl_t *da_list = NULL;
2440 slap_dynacl_register( slap_dynacl_t *da )
2444 for ( tmp = da_list; tmp; tmp = tmp->da_next ) {
2445 if ( strcasecmp( da->da_name, tmp->da_name ) == 0 ) {
2450 if ( tmp != NULL ) {
2454 if ( da->da_mask == NULL ) {
2458 da->da_private = NULL;
2459 da->da_next = da_list;
2465 static slap_dynacl_t *
2466 slap_dynacl_next( slap_dynacl_t *da )
2475 slap_dynacl_get( const char *name )
2479 for ( da = slap_dynacl_next( NULL ); da; da = slap_dynacl_next( da ) ) {
2480 if ( strcasecmp( da->da_name, name ) == 0 ) {
2487 #endif /* SLAP_DYNACL */
2490 * statically built-in dynamic ACL initialization
2492 static int (*acl_init_func[])( void ) = {
2494 /* TODO: remove when ACI will only be dynamic */
2495 #if SLAPD_ACI_ENABLED == SLAPD_MOD_STATIC
2497 #endif /* SLAPD_ACI_ENABLED */
2498 #endif /* SLAP_DYNACL */
2508 for ( i = 0; acl_init_func[ i ] != NULL; i++ ) {
2509 rc = (*(acl_init_func[ i ]))();
2522 struct berval *dn_matches,
2523 struct berval *val_matches,
2524 AclRegexMatches *matches)
2530 enum { DN_FLAG, VAL_FLAG } tflag;
2533 bv->bv_val[0] = '\0';
2534 bv->bv_len--; /* leave space for lone $ */
2538 for ( dp = bv->bv_val, sp = pat->bv_val; size < bv->bv_len &&
2539 sp < pat->bv_val + pat->bv_len ; sp++ )
2541 /* did we previously see a $ */
2543 if ( flag == 1 && *sp == '$' ) {
2549 } else if ( flag == 2 && *sp == 'v' /*'}'*/) {
2552 } else if ( flag == 2 && *sp == 'd' /*'}'*/) {
2555 } else if ( flag == 1 && *sp == '{' /*'}'*/) {
2558 } else if ( *sp >= '0' && *sp <= '9' ) {
2569 for ( sp++; *sp != '\0' && *sp != /*'{'*/ '}'; sp++ ) {
2570 if ( *sp >= '0' && *sp <= '9' ) {
2571 n = 10*n + ( *sp - '0' );
2575 if ( *sp != /*'{'*/ '}' ) {
2583 nm = matches->dn_count;
2584 m = matches->dn_data;
2585 data = dn_matches ? dn_matches->bv_val : NULL;
2588 nm = matches->val_count;
2589 m = matches->val_data;
2590 data = val_matches ? val_matches->bv_val : NULL;
2599 if ( data == NULL ) {
2608 for ( ; size < bv->bv_len && i < l; size++, i++ ) {
2627 /* must have ended with a single $ */
2635 Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: pattern: %.*s\n", (int)pat->bv_len, pat->bv_val, 0 );
2636 Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: expanded: %s\n", bv->bv_val, 0, 0 );
2643 struct berval *pat, /* pattern to expand and match against */
2644 char *str, /* string to match against pattern */
2645 struct berval *dn_matches, /* buffer with $N expansion variables from DN */
2646 struct berval *val_matches, /* buffer with $N expansion variables from val */
2647 AclRegexMatches *matches /* offsets in buffer for $N expansion variables */
2651 char newbuf[ACL_BUF_SIZE];
2655 bv.bv_len = sizeof( newbuf ) - 1;
2662 acl_string_expand( &bv, pat, dn_matches, val_matches, matches );
2663 rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE );
2665 char error[ACL_BUF_SIZE];
2666 regerror( rc, &re, error, sizeof( error ) );
2668 Debug( LDAP_DEBUG_TRACE,
2669 "compile( \"%s\", \"%s\") failed %s\n",
2670 pat->bv_val, str, error );
2674 rc = regexec( &re, str, 0, NULL, 0 );
2677 Debug( LDAP_DEBUG_TRACE,
2678 "=> regex_matches: string: %s\n", str, 0, 0 );
2679 Debug( LDAP_DEBUG_TRACE,
2680 "=> regex_matches: rc: %d %s\n",
2681 rc, !rc ? "matches" : "no matches", 0 );