1 /* acl.c - routines to parse and check acl's */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/acl.c,v 1.303.2.16 2008/05/20 00:08:13 quanah Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2008 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 int nmatch
, regmatch_t
*matches
,
56 AccessControlState
*state
);
58 static slap_control_t
slap_acl_mask(
59 AccessControl
*ac
, slap_mask_t
*mask
,
60 Operation
*op
, Entry
*e
,
61 AttributeDescription
*desc
,
66 AccessControlState
*state
);
68 static int regex_matches(
69 struct berval
*pat
, char *str
, char *buf
,
70 int nmatch
, regmatch_t
*matches
);
72 typedef struct AclSetCookie
{
74 #define asc_op asc_cookie.set_op
78 SLAP_SET_GATHER acl_set_gather
;
79 SLAP_SET_GATHER acl_set_gather2
;
82 * access_allowed - check whether op->o_ndn is allowed the requested access
83 * to entry e, attribute attr, value val. if val is null, access to
84 * the whole attribute is assumed (all values).
86 * This routine loops through all access controls and calls
87 * slap_acl_mask() on each applicable access control.
88 * The loop exits when a definitive answer is reached or
89 * or no more controls remain.
96 * - can be legally called with op == NULL
97 * - can be legally called with op->o_bd == NULL
101 slap_access_always_allowed(
104 AttributeDescription
*desc
,
106 slap_access_t access
,
107 AccessControlState
*state
,
110 assert( maskp
!= NULL
);
113 ACL_LVL_ASSIGN_MANAGE( *maskp
);
122 AttributeDescription
*desc
,
124 slap_access_t access
,
125 AccessControlState
*state
,
130 AccessControl
*a
= NULL
;
133 char accessmaskbuf
[ACCESSMASK_MAXLEN
];
136 slap_control_t control
;
137 slap_access_t access_level
;
139 regmatch_t matches
[MAXREMATCHES
];
141 assert( op
!= NULL
);
143 assert( desc
!= NULL
);
144 assert( maskp
!= NULL
);
146 access_level
= ACL_LEVEL( access
);
147 attr
= desc
->ad_cname
.bv_val
;
149 assert( attr
!= NULL
);
153 /* grant database root access */
154 if ( be_isroot( op
) ) {
155 Debug( LDAP_DEBUG_ACL
, "<= root access granted\n", 0, 0, 0 );
156 mask
= ACL_LVL_MANAGE
;
161 * no-user-modification operational attributes are ignored
162 * by ACL_WRITE checking as any found here are not provided
165 * NOTE: but they are not ignored for ACL_MANAGE, because
166 * if we get here it means a non-root user is trying to
167 * manage data, so we need to check its privileges.
169 if ( access_level
== ACL_WRITE
170 && is_at_no_user_mod( desc
->ad_type
)
171 && desc
!= slap_schema
.si_ad_entry
172 && desc
!= slap_schema
.si_ad_children
)
174 Debug( LDAP_DEBUG_ACL
, "NoUserMod Operational attribute:"
175 " %s access granted\n",
180 /* use backend default access if no backend acls */
181 if ( op
->o_bd
->be_acl
== NULL
) {
184 Debug( LDAP_DEBUG_ACL
,
185 "=> slap_access_allowed: backend default %s "
186 "access %s to \"%s\"\n",
187 access2str( access
),
188 op
->o_bd
->be_dfltaccess
>= access_level
? "granted" : "denied",
189 op
->o_dn
.bv_val
? op
->o_dn
.bv_val
: "(anonymous)" );
190 ret
= op
->o_bd
->be_dfltaccess
>= access_level
;
192 mask
= ACL_PRIV_LEVEL
;
193 for ( i
= ACL_NONE
; i
<= op
->o_bd
->be_dfltaccess
; i
++ ) {
194 ACL_PRIV_SET( mask
, ACL_ACCESS2PRIV( i
) );
203 if ( state
&& state
->as_vd_ad
== desc
) {
204 a
= state
->as_vd_acl
;
205 count
= state
->as_vd_acl_count
;
208 if ( state
) state
->as_vi_acl
= NULL
;
212 ACL_PRIV_ASSIGN( mask
, *maskp
);
213 memset( matches
, '\0', sizeof( matches
) );
215 while ( ( a
= slap_acl_get( a
, &count
, op
, e
, desc
, val
,
216 MAXREMATCHES
, matches
, state
) ) != NULL
)
220 for ( i
= 0; i
< MAXREMATCHES
&& matches
[i
].rm_so
> 0; i
++ ) {
221 Debug( LDAP_DEBUG_ACL
, "=> match[%d]: %d %d ", i
,
222 (int)matches
[i
].rm_so
, (int)matches
[i
].rm_eo
);
223 if ( matches
[i
].rm_so
<= matches
[0].rm_eo
) {
225 for ( n
= matches
[i
].rm_so
; n
< matches
[i
].rm_eo
; n
++ ) {
226 Debug( LDAP_DEBUG_ACL
, "%c", e
->e_ndn
[n
], 0, 0 );
229 Debug( LDAP_DEBUG_ARGS
, "\n", 0, 0, 0 );
233 if ( state
->as_vi_acl
== a
&&
234 ( state
->as_recorded
& ACL_STATE_RECORDED_NV
) )
236 Debug( LDAP_DEBUG_ACL
,
237 "=> slap_access_allowed: result was in cache (%s)\n",
239 ret
= state
->as_result
;
242 Debug( LDAP_DEBUG_ACL
,
243 "=> slap_access_allowed: result not in cache (%s)\n",
248 control
= slap_acl_mask( a
, &mask
, op
,
249 e
, desc
, val
, MAXREMATCHES
, matches
, count
, state
);
251 if ( control
!= ACL_BREAK
) {
255 memset( matches
, '\0', sizeof( matches
) );
258 if ( ACL_IS_INVALID( mask
) ) {
259 Debug( LDAP_DEBUG_ACL
,
260 "=> slap_access_allowed: \"%s\" (%s) invalid!\n",
262 ACL_PRIV_ASSIGN( mask
, *maskp
);
264 } else if ( control
== ACL_BREAK
) {
265 Debug( LDAP_DEBUG_ACL
,
266 "=> slap_access_allowed: no more rules\n", 0, 0, 0 );
271 ret
= ACL_GRANT( mask
, access
);
273 Debug( LDAP_DEBUG_ACL
,
274 "=> slap_access_allowed: %s access %s by %s\n",
275 access2str( access
), ret
? "granted" : "denied",
276 accessmask2str( mask
, accessmaskbuf
, 1 ) );
279 ACL_PRIV_ASSIGN( *maskp
, mask
);
287 AttributeDescription
*desc
,
289 slap_access_t access
,
290 AccessControlState
*state
,
297 * NOTE: control gets here if FIXME
298 * if an appropriate backend cannot be selected for the operation,
299 * we assume that the frontend should handle this
300 * FIXME: should select_backend() take care of this,
301 * and return frontendDB instead of NULL? maybe for some value
306 if ( op
->o_bd
== NULL
) {
307 op
->o_bd
= select_backend( &op
->o_req_ndn
, 0 );
308 if ( op
->o_bd
== NULL
)
309 op
->o_bd
= frontendDB
;
311 rc
= slap_access_allowed( op
, e
, desc
, val
, access
, state
, maskp
);
321 AttributeDescription
*desc
,
323 slap_access_t access
,
324 AccessControlState
*state
,
328 AccessControl
*a
= NULL
;
332 char accessmaskbuf
[ACCESSMASK_MAXLEN
];
335 slap_access_t access_level
;
337 static AccessControlState state_init
= ACL_STATE_INIT
;
340 assert( desc
!= NULL
);
342 access_level
= ACL_LEVEL( access
);
344 assert( access_level
> ACL_NONE
);
347 if ( maskp
) ACL_INVALIDATE( *maskp
);
349 attr
= desc
->ad_cname
.bv_val
;
351 assert( attr
!= NULL
);
354 if ( op
->o_acl_priv
!= ACL_NONE
) {
355 access
= op
->o_acl_priv
;
357 } else if ( op
->o_is_auth_check
&&
358 ( access_level
== ACL_SEARCH
|| access_level
== ACL_READ
) )
362 } else if ( get_relax( op
) && access_level
== ACL_WRITE
&&
363 desc
== slap_schema
.si_ad_entry
)
370 if ( state
->as_vd_ad
== desc
) {
371 if ( ( state
->as_recorded
& ACL_STATE_RECORDED_NV
) &&
374 return state
->as_result
;
382 Debug( LDAP_DEBUG_ACL
,
383 "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
384 access2str( access
), e
->e_dn
, attr
);
391 if ( op
->o_bd
== NULL
) {
392 op
->o_bd
= LDAP_STAILQ_FIRST( &backendDB
);
395 /* FIXME: experimental; use first backend rules
396 * iff there is no global_acl (ITS#3100)
398 if ( frontendDB
->be_acl
!= NULL
) {
399 op
->o_bd
= frontendDB
;
402 assert( op
->o_bd
!= NULL
);
404 /* this is enforced in backend_add() */
405 if ( op
->o_bd
->bd_info
->bi_access_allowed
) {
406 /* delegate to backend */
407 ret
= op
->o_bd
->bd_info
->bi_access_allowed( op
, e
,
408 desc
, val
, access
, state
, &mask
);
411 /* use default (but pass through frontend
412 * for global ACL overlays) */
413 ret
= frontendDB
->bd_info
->bi_access_allowed( op
, e
,
414 desc
, val
, access
, state
, &mask
);
418 if ( ACL_IS_INVALID( mask
) ) {
419 Debug( LDAP_DEBUG_ACL
,
420 "=> access_allowed: \"%s\" (%s) invalid!\n",
425 Debug( LDAP_DEBUG_ACL
,
426 "=> access_allowed: no more rules\n", 0, 0, 0 );
432 Debug( LDAP_DEBUG_ACL
,
433 "=> access_allowed: %s access %s by %s\n",
434 access2str( access
), ret
? "granted" : "denied",
435 accessmask2str( mask
, accessmaskbuf
, 1 ) );
438 if ( state
!= NULL
) {
439 /* If not value-dependent, save ACL in case of more attrs */
440 if ( !( state
->as_recorded
& ACL_STATE_RECORDED_VD
) ) {
441 state
->as_vi_acl
= a
;
442 state
->as_result
= ret
;
444 state
->as_recorded
|= ACL_STATE_RECORDED
;
445 state
->as_vd_ad
= desc
;
447 if ( be_null
) op
->o_bd
= NULL
;
448 if ( maskp
) ACL_PRIV_ASSIGN( *maskp
, mask
);
454 * slap_acl_get - return the acl applicable to entry e, attribute
455 * attr. the acl returned is suitable for use in subsequent calls to
456 * acl_access_allowed().
459 static AccessControl
*
465 AttributeDescription
*desc
,
469 AccessControlState
*state
)
476 assert( count
!= NULL
);
477 assert( desc
!= NULL
);
479 attr
= desc
->ad_cname
.bv_val
;
481 assert( attr
!= NULL
);
484 if( op
->o_bd
== NULL
) {
485 a
= frontendDB
->be_acl
;
487 a
= op
->o_bd
->be_acl
;
498 dnlen
= e
->e_nname
.bv_len
;
500 for ( ; a
!= NULL
; prev
= a
, a
= a
->acl_next
) {
503 if ( a
->acl_dn_pat
.bv_len
|| ( a
->acl_dn_style
!= ACL_STYLE_REGEX
)) {
504 if ( a
->acl_dn_style
== ACL_STYLE_REGEX
) {
505 Debug( LDAP_DEBUG_ACL
, "=> dnpat: [%d] %s nsub: %d\n",
506 *count
, a
->acl_dn_pat
.bv_val
, (int) a
->acl_dn_re
.re_nsub
);
507 if (regexec(&a
->acl_dn_re
, e
->e_ndn
, nmatch
, matches
, 0))
511 Debug( LDAP_DEBUG_ACL
, "=> dn: [%d] %s\n",
512 *count
, a
->acl_dn_pat
.bv_val
, 0 );
513 patlen
= a
->acl_dn_pat
.bv_len
;
514 if ( dnlen
< patlen
)
517 if ( a
->acl_dn_style
== ACL_STYLE_BASE
) {
518 /* base dn -- entire object DN must match */
519 if ( dnlen
!= patlen
)
522 } else if ( a
->acl_dn_style
== ACL_STYLE_ONE
) {
523 ber_len_t rdnlen
= 0;
526 if ( dnlen
<= patlen
)
530 if ( !DN_SEPARATOR( e
->e_ndn
[dnlen
- patlen
- 1] ) )
535 rdnlen
= dn_rdnlen( NULL
, &e
->e_nname
);
536 if ( rdnlen
!= dnlen
- patlen
- sep
)
539 } else if ( a
->acl_dn_style
== ACL_STYLE_SUBTREE
) {
540 if ( dnlen
> patlen
&& !DN_SEPARATOR( e
->e_ndn
[dnlen
- patlen
- 1] ) )
543 } else if ( a
->acl_dn_style
== ACL_STYLE_CHILDREN
) {
544 if ( dnlen
<= patlen
)
546 if ( !DN_SEPARATOR( e
->e_ndn
[dnlen
- patlen
- 1] ) )
550 if ( strcmp( a
->acl_dn_pat
.bv_val
, e
->e_ndn
+ dnlen
- patlen
) != 0 )
554 Debug( LDAP_DEBUG_ACL
, "=> acl_get: [%d] matched\n",
558 if ( a
->acl_attrs
&& !ad_inlist( desc
, a
->acl_attrs
) ) {
559 matches
[0].rm_so
= matches
[0].rm_eo
= -1;
563 /* Is this ACL only for a specific value? */
564 if ( a
->acl_attrval
.bv_len
) {
569 if( state
&& !( state
->as_recorded
& ACL_STATE_RECORDED_VD
)) {
570 state
->as_recorded
|= ACL_STATE_RECORDED_VD
;
571 state
->as_vd_acl
= prev
;
572 state
->as_vd_acl_count
= *count
- 1;
575 if ( a
->acl_attrval_style
== ACL_STYLE_REGEX
) {
576 Debug( LDAP_DEBUG_ACL
,
577 "acl_get: valpat %s\n",
578 a
->acl_attrval
.bv_val
, 0, 0 );
579 if ( regexec( &a
->acl_attrval_re
, val
->bv_val
, 0, NULL
, 0 ) )
587 Debug( LDAP_DEBUG_ACL
,
589 a
->acl_attrval
.bv_val
, 0, 0 );
591 if ( a
->acl_attrs
[0].an_desc
->ad_type
->sat_syntax
!= slap_schema
.si_syn_distinguishedName
) {
592 if (value_match( &match
, desc
,
593 a
->acl_attrval_mr
, 0,
594 val
, &a
->acl_attrval
, &text
) != LDAP_SUCCESS
||
601 patlen
= a
->acl_attrval
.bv_len
;
602 vdnlen
= val
->bv_len
;
604 if ( vdnlen
< patlen
)
607 if ( a
->acl_attrval_style
== ACL_STYLE_BASE
) {
608 if ( vdnlen
> patlen
)
611 } else if ( a
->acl_attrval_style
== ACL_STYLE_ONE
) {
612 ber_len_t rdnlen
= 0;
614 if ( !DN_SEPARATOR( val
->bv_val
[vdnlen
- patlen
- 1] ) )
617 rdnlen
= dn_rdnlen( NULL
, val
);
618 if ( rdnlen
!= vdnlen
- patlen
- 1 )
621 } else if ( a
->acl_attrval_style
== ACL_STYLE_SUBTREE
) {
622 if ( vdnlen
> patlen
&& !DN_SEPARATOR( val
->bv_val
[vdnlen
- patlen
- 1] ) )
625 } else if ( a
->acl_attrval_style
== ACL_STYLE_CHILDREN
) {
626 if ( vdnlen
<= patlen
)
629 if ( !DN_SEPARATOR( val
->bv_val
[vdnlen
- patlen
- 1] ) )
633 if ( strcmp( a
->acl_attrval
.bv_val
, val
->bv_val
+ vdnlen
- patlen
) )
639 if ( a
->acl_filter
!= NULL
) {
640 ber_int_t rc
= test_filter( NULL
, e
, a
->acl_filter
);
641 if ( rc
!= LDAP_COMPARE_TRUE
) {
646 Debug( LDAP_DEBUG_ACL
, "=> acl_get: [%d] attr %s\n",
651 Debug( LDAP_DEBUG_ACL
, "<= acl_get: done.\n", 0, 0, 0 );
656 * Record value-dependent access control state
658 #define ACL_RECORD_VALUE_STATE do { \
659 if( state && !( state->as_recorded & ACL_STATE_RECORDED_VD )) { \
660 state->as_recorded |= ACL_STATE_RECORDED_VD; \
661 state->as_vd_acl = a; \
662 state->as_vd_acl_count = count; \
670 AttributeDescription
*desc
,
676 struct berval
*opndn
)
679 * if access applies to the entry itself, and the
680 * user is bound as somebody in the same namespace as
681 * the entry, OR the given dn matches the dn pattern
684 * NOTE: styles "anonymous", "users" and "self"
685 * have been moved to enum slap_style_t, whose
686 * value is set in a_dn_style; however, the string
687 * is maintained in a_dn_pat.
690 if ( bdn
->a_style
== ACL_STYLE_ANONYMOUS
) {
691 if ( !BER_BVISEMPTY( opndn
) ) {
695 } else if ( bdn
->a_style
== ACL_STYLE_USERS
) {
696 if ( BER_BVISEMPTY( opndn
) ) {
700 } else if ( bdn
->a_style
== ACL_STYLE_SELF
) {
701 struct berval ndn
, selfndn
;
704 if ( BER_BVISEMPTY( opndn
) || BER_BVISNULL( &e
->e_nname
) ) {
708 level
= bdn
->a_self_level
;
716 selfndn
= e
->e_nname
;
719 for ( ; level
> 0; level
-- ) {
720 if ( BER_BVISEMPTY( &ndn
) ) {
723 dnParent( &ndn
, &ndn
);
726 if ( BER_BVISEMPTY( &ndn
) || !dn_match( &ndn
, &selfndn
) )
731 } else if ( bdn
->a_style
== ACL_STYLE_REGEX
) {
732 if ( !ber_bvccmp( &bdn
->a_pat
, '*' ) ) {
734 regmatch_t tmp_matches
[2],
735 *tmp_matchesp
= tmp_matches
;
739 switch ( a
->acl_dn_style
) {
740 case ACL_STYLE_REGEX
:
741 if ( !BER_BVISNULL( &a
->acl_dn_pat
) ) {
742 tmp_matchesp
= matches
;
746 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
749 tmp_matches
[0].rm_so
= 0;
750 tmp_matches
[0].rm_eo
= e
->e_nname
.bv_len
;
755 case ACL_STYLE_SUBTREE
:
756 case ACL_STYLE_CHILDREN
:
757 tmp_matches
[0].rm_so
= 0;
758 tmp_matches
[0].rm_eo
= e
->e_nname
.bv_len
;
759 tmp_matches
[1].rm_so
= e
->e_nname
.bv_len
- a
->acl_dn_pat
.bv_len
;
760 tmp_matches
[1].rm_eo
= e
->e_nname
.bv_len
;
774 if ( !regex_matches( &bdn
->a_pat
, opndn
->bv_val
,
775 e
->e_ndn
, tmp_nmatch
, tmp_matchesp
) )
783 ber_len_t patlen
, odnlen
;
786 if ( e
->e_dn
== NULL
)
789 if ( bdn
->a_expand
) {
791 char buf
[ACL_BUF_SIZE
];
794 regmatch_t tmp_matches
[2],
795 *tmp_matchesp
= tmp_matches
;
799 bv
.bv_len
= sizeof( buf
) - 1;
802 switch ( a
->acl_dn_style
) {
803 case ACL_STYLE_REGEX
:
804 if ( !BER_BVISNULL( &a
->acl_dn_pat
) ) {
805 tmp_matchesp
= matches
;
809 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
812 tmp_matches
[0].rm_so
= 0;
813 tmp_matches
[0].rm_eo
= e
->e_nname
.bv_len
;
818 case ACL_STYLE_SUBTREE
:
819 case ACL_STYLE_CHILDREN
:
820 tmp_matches
[0].rm_so
= 0;
821 tmp_matches
[0].rm_eo
= e
->e_nname
.bv_len
;
822 tmp_matches
[1].rm_so
= e
->e_nname
.bv_len
- a
->acl_dn_pat
.bv_len
;
823 tmp_matches
[1].rm_eo
= e
->e_nname
.bv_len
;
837 if ( acl_string_expand( &bv
, &bdn
->a_pat
,
839 tmp_nmatch
, tmp_matchesp
) )
844 if ( dnNormalize(0, NULL
, NULL
, &bv
,
845 &pat
, op
->o_tmpmemctx
)
848 /* did not expand to a valid dn */
857 odnlen
= opndn
->bv_len
;
858 if ( odnlen
< patlen
) {
859 goto dn_match_cleanup
;
863 if ( bdn
->a_style
== ACL_STYLE_BASE
) {
864 /* base dn -- entire object DN must match */
865 if ( odnlen
!= patlen
) {
866 goto dn_match_cleanup
;
869 } else if ( bdn
->a_style
== ACL_STYLE_ONE
) {
870 ber_len_t rdnlen
= 0;
872 if ( odnlen
<= patlen
) {
873 goto dn_match_cleanup
;
876 if ( !DN_SEPARATOR( opndn
->bv_val
[odnlen
- patlen
- 1] ) ) {
877 goto dn_match_cleanup
;
880 rdnlen
= dn_rdnlen( NULL
, opndn
);
881 if ( rdnlen
- ( odnlen
- patlen
- 1 ) != 0 ) {
882 goto dn_match_cleanup
;
885 } else if ( bdn
->a_style
== ACL_STYLE_SUBTREE
) {
886 if ( odnlen
> patlen
&& !DN_SEPARATOR( opndn
->bv_val
[odnlen
- patlen
- 1] ) ) {
887 goto dn_match_cleanup
;
890 } else if ( bdn
->a_style
== ACL_STYLE_CHILDREN
) {
891 if ( odnlen
<= patlen
) {
892 goto dn_match_cleanup
;
895 if ( !DN_SEPARATOR( opndn
->bv_val
[odnlen
- patlen
- 1] ) ) {
896 goto dn_match_cleanup
;
899 } else if ( bdn
->a_style
== ACL_STYLE_LEVEL
) {
900 int level
= bdn
->a_level
;
903 if ( odnlen
<= patlen
) {
904 goto dn_match_cleanup
;
907 if ( level
> 0 && !DN_SEPARATOR( opndn
->bv_val
[odnlen
- patlen
- 1] ) )
909 goto dn_match_cleanup
;
913 for ( ; level
> 0; level
-- ) {
914 if ( BER_BVISEMPTY( &ndn
) ) {
915 goto dn_match_cleanup
;
917 dnParent( &ndn
, &ndn
);
918 if ( ndn
.bv_len
< patlen
) {
919 goto dn_match_cleanup
;
923 if ( ndn
.bv_len
!= patlen
) {
924 goto dn_match_cleanup
;
928 got_match
= !strcmp( pat
.bv_val
, &opndn
->bv_val
[ odnlen
- patlen
] );
931 if ( pat
.bv_val
!= bdn
->a_pat
.bv_val
) {
932 slap_sl_free( pat
.bv_val
, op
->o_tmpmemctx
);
953 AccessControlState
*state
,
955 struct berval
*opndn
)
961 const char *attr
= bdn
->a_at
->ad_cname
.bv_val
;
963 assert( attr
!= NULL
);
965 if ( BER_BVISEMPTY( opndn
) ) {
969 Debug( LDAP_DEBUG_ACL
, "<= check a_dn_at: %s\n", attr
, 0, 0 );
972 /* see if asker is listed in dnattr */
973 for ( at
= attrs_find( e
->e_attrs
, bdn
->a_at
);
975 at
= attrs_find( at
->a_next
, bdn
->a_at
) )
977 if ( attr_valfind( at
,
978 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH
|
979 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
,
980 &bv
, NULL
, op
->o_tmpmemctx
) == 0 )
989 /* have a dnattr match. if this is a self clause then
990 * the target must also match the op dn.
993 /* check if the target is an attribute. */
994 if ( val
== NULL
) return 1;
996 /* target is attribute, check if the attribute value
999 rc
= value_match( &match
, bdn
->a_at
,
1000 bdn
->a_at
->ad_type
->sat_equality
, 0,
1002 /* on match error or no match, fail the ACL clause */
1003 if ( rc
!= LDAP_SUCCESS
|| match
!= 0 )
1008 /* no dnattr match, check if this is a self clause */
1009 if ( ! bdn
->a_self
)
1012 ACL_RECORD_VALUE_STATE
;
1014 /* this is a self clause, check if the target is an
1020 /* target is attribute, check if the attribute value
1023 rc
= value_match( &match
, bdn
->a_at
,
1024 bdn
->a_at
->ad_type
->sat_equality
, 0,
1027 /* on match error or no match, fail the ACL clause */
1028 if ( rc
!= LDAP_SUCCESS
|| match
!= 0 )
1037 * slap_acl_mask - modifies mask based upon the given acl and the
1038 * requested access to entry e, attribute attr, value val. if val
1039 * is null, access to the whole attribute is assumed (all values).
1041 * returns 0 access NOT allowed
1045 static slap_control_t
1051 AttributeDescription
*desc
,
1054 regmatch_t
*matches
,
1056 AccessControlState
*state
)
1061 char accessmaskbuf
[ACCESSMASK_MAXLEN
];
1064 slap_mask_t a2pmask
= ACL_ACCESS2PRIV( *mask
);
1066 assert( a
!= NULL
);
1067 assert( mask
!= NULL
);
1068 assert( desc
!= NULL
);
1070 attr
= desc
->ad_cname
.bv_val
;
1072 assert( attr
!= NULL
);
1074 Debug( LDAP_DEBUG_ACL
,
1075 "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
1078 Debug( LDAP_DEBUG_ACL
,
1079 "=> acl_mask: to %s by \"%s\", (%s) \n",
1080 val
? "value" : "all values",
1081 op
->o_ndn
.bv_val
? op
->o_ndn
.bv_val
: "",
1082 accessmask2str( *mask
, accessmaskbuf
, 1 ) );
1088 for ( ; b
!= NULL
; b
= b
->a_next
, i
++ ) {
1089 slap_mask_t oldmask
, modmask
;
1091 ACL_INVALIDATE( modmask
);
1093 /* AND <who> clauses */
1094 if ( !BER_BVISEMPTY( &b
->a_dn_pat
) ) {
1095 Debug( LDAP_DEBUG_ACL
, "<= check a_dn_pat: %s\n",
1096 b
->a_dn_pat
.bv_val
, 0, 0);
1098 * if access applies to the entry itself, and the
1099 * user is bound as somebody in the same namespace as
1100 * the entry, OR the given dn matches the dn pattern
1103 * NOTE: styles "anonymous", "users" and "self"
1104 * have been moved to enum slap_style_t, whose
1105 * value is set in a_dn_style; however, the string
1106 * is maintained in a_dn_pat.
1109 if ( acl_mask_dn( op
, e
, desc
, val
, a
, nmatch
, matches
,
1110 &b
->a_dn
, &op
->o_ndn
) )
1116 if ( !BER_BVISEMPTY( &b
->a_realdn_pat
) ) {
1119 Debug( LDAP_DEBUG_ACL
, "<= check a_realdn_pat: %s\n",
1120 b
->a_realdn_pat
.bv_val
, 0, 0);
1122 * if access applies to the entry itself, and the
1123 * user is bound as somebody in the same namespace as
1124 * the entry, OR the given dn matches the dn pattern
1127 * NOTE: styles "anonymous", "users" and "self"
1128 * have been moved to enum slap_style_t, whose
1129 * value is set in a_dn_style; however, the string
1130 * is maintained in a_dn_pat.
1133 if ( op
->o_conn
&& !BER_BVISNULL( &op
->o_conn
->c_ndn
) )
1135 ndn
= op
->o_conn
->c_ndn
;
1140 if ( acl_mask_dn( op
, e
, desc
, val
, a
, nmatch
, matches
,
1141 &b
->a_realdn
, &ndn
) )
1147 if ( !BER_BVISEMPTY( &b
->a_sockurl_pat
) ) {
1148 if ( ! op
->o_conn
->c_listener
) {
1151 Debug( LDAP_DEBUG_ACL
, "<= check a_sockurl_pat: %s\n",
1152 b
->a_sockurl_pat
.bv_val
, 0, 0 );
1154 if ( !ber_bvccmp( &b
->a_sockurl_pat
, '*' ) ) {
1155 if ( b
->a_sockurl_style
== ACL_STYLE_REGEX
) {
1156 if (!regex_matches( &b
->a_sockurl_pat
, op
->o_conn
->c_listener_url
.bv_val
,
1157 e
->e_ndn
, nmatch
, matches
) )
1162 } else if ( b
->a_sockurl_style
== ACL_STYLE_EXPAND
) {
1164 char buf
[ACL_BUF_SIZE
];
1166 bv
.bv_len
= sizeof( buf
) - 1;
1168 if ( acl_string_expand( &bv
, &b
->a_sockurl_pat
,
1169 e
->e_ndn
, nmatch
, matches
) )
1174 if ( ber_bvstrcasecmp( &bv
, &op
->o_conn
->c_listener_url
) != 0 )
1180 if ( ber_bvstrcasecmp( &b
->a_sockurl_pat
, &op
->o_conn
->c_listener_url
) != 0 )
1188 if ( !BER_BVISEMPTY( &b
->a_domain_pat
) ) {
1189 if ( !op
->o_conn
->c_peer_domain
.bv_val
) {
1192 Debug( LDAP_DEBUG_ACL
, "<= check a_domain_pat: %s\n",
1193 b
->a_domain_pat
.bv_val
, 0, 0 );
1194 if ( !ber_bvccmp( &b
->a_domain_pat
, '*' ) ) {
1195 if ( b
->a_domain_style
== ACL_STYLE_REGEX
) {
1196 if (!regex_matches( &b
->a_domain_pat
, op
->o_conn
->c_peer_domain
.bv_val
,
1197 e
->e_ndn
, nmatch
, matches
) )
1202 char buf
[ACL_BUF_SIZE
];
1204 struct berval cmp
= op
->o_conn
->c_peer_domain
;
1205 struct berval pat
= b
->a_domain_pat
;
1207 if ( b
->a_domain_expand
) {
1210 bv
.bv_len
= sizeof(buf
) - 1;
1213 if ( acl_string_expand(&bv
, &b
->a_domain_pat
,
1214 e
->e_ndn
, nmatch
, matches
) )
1221 if ( b
->a_domain_style
== ACL_STYLE_SUBTREE
) {
1222 int offset
= cmp
.bv_len
- pat
.bv_len
;
1227 if ( offset
== 1 || ( offset
> 1 && cmp
.bv_val
[ offset
- 1 ] != '.' ) ) {
1231 /* trim the domain */
1232 cmp
.bv_val
= &cmp
.bv_val
[ offset
];
1233 cmp
.bv_len
-= offset
;
1236 if ( ber_bvstrcasecmp( &pat
, &cmp
) != 0 ) {
1243 if ( !BER_BVISEMPTY( &b
->a_peername_pat
) ) {
1244 if ( !op
->o_conn
->c_peer_name
.bv_val
) {
1247 Debug( LDAP_DEBUG_ACL
, "<= check a_peername_path: %s\n",
1248 b
->a_peername_pat
.bv_val
, 0, 0 );
1249 if ( !ber_bvccmp( &b
->a_peername_pat
, '*' ) ) {
1250 if ( b
->a_peername_style
== ACL_STYLE_REGEX
) {
1251 if (!regex_matches( &b
->a_peername_pat
, op
->o_conn
->c_peer_name
.bv_val
,
1252 e
->e_ndn
, nmatch
, matches
) )
1258 /* try exact match */
1259 if ( b
->a_peername_style
== ACL_STYLE_BASE
) {
1260 if ( ber_bvstrcasecmp( &b
->a_peername_pat
, &op
->o_conn
->c_peer_name
) != 0 ) {
1264 } else if ( b
->a_peername_style
== ACL_STYLE_EXPAND
) {
1266 char buf
[ACL_BUF_SIZE
];
1268 bv
.bv_len
= sizeof( buf
) - 1;
1270 if ( acl_string_expand( &bv
, &b
->a_peername_pat
,
1271 e
->e_ndn
, nmatch
, matches
) )
1276 if ( ber_bvstrcasecmp( &bv
, &op
->o_conn
->c_peer_name
) != 0 ) {
1280 /* extract IP and try exact match */
1281 } else if ( b
->a_peername_style
== ACL_STYLE_IP
) {
1283 char buf
[STRLENOF("255.255.255.255") + 1];
1286 int port_number
= -1;
1288 if ( strncasecmp( op
->o_conn
->c_peer_name
.bv_val
,
1289 acl_bv_ip_eq
.bv_val
,
1290 acl_bv_ip_eq
.bv_len
) != 0 )
1293 ip
.bv_val
= op
->o_conn
->c_peer_name
.bv_val
+ acl_bv_ip_eq
.bv_len
;
1294 ip
.bv_len
= op
->o_conn
->c_peer_name
.bv_len
- acl_bv_ip_eq
.bv_len
;
1296 port
= strrchr( ip
.bv_val
, ':' );
1298 ip
.bv_len
= port
- ip
.bv_val
;
1300 if ( lutil_atoi( &port_number
, port
) != 0 )
1304 /* the port check can be anticipated here */
1305 if ( b
->a_peername_port
!= -1 && port_number
!= b
->a_peername_port
)
1308 /* address longer than expected? */
1309 if ( ip
.bv_len
>= sizeof(buf
) )
1312 AC_MEMCPY( buf
, ip
.bv_val
, ip
.bv_len
);
1313 buf
[ ip
.bv_len
] = '\0';
1315 addr
= inet_addr( buf
);
1317 /* unable to convert? */
1318 if ( addr
== (unsigned long)(-1) )
1321 if ( (addr
& b
->a_peername_mask
) != b
->a_peername_addr
)
1324 #ifdef LDAP_PF_INET6
1325 /* extract IPv6 and try exact match */
1326 } else if ( b
->a_peername_style
== ACL_STYLE_IPV6
) {
1328 char buf
[STRLENOF("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF") + 1];
1330 struct in6_addr addr
;
1331 int port_number
= -1;
1333 if ( strncasecmp( op
->o_conn
->c_peer_name
.bv_val
,
1334 acl_bv_ipv6_eq
.bv_val
,
1335 acl_bv_ipv6_eq
.bv_len
) != 0 )
1338 ip
.bv_val
= op
->o_conn
->c_peer_name
.bv_val
+ acl_bv_ipv6_eq
.bv_len
;
1339 ip
.bv_len
= op
->o_conn
->c_peer_name
.bv_len
- acl_bv_ipv6_eq
.bv_len
;
1341 port
= strrchr( ip
.bv_val
, ']' );
1343 ip
.bv_len
= port
- ip
.bv_val
;
1345 if ( port
[0] == ':' && lutil_atoi( &port_number
, ++port
) != 0 )
1349 /* the port check can be anticipated here */
1350 if ( b
->a_peername_port
!= -1 && port_number
!= b
->a_peername_port
)
1353 /* address longer than expected? */
1354 if ( ip
.bv_len
>= sizeof(buf
) )
1357 AC_MEMCPY( buf
, ip
.bv_val
, ip
.bv_len
);
1358 buf
[ ip
.bv_len
] = '\0';
1360 if ( inet_pton( AF_INET6
, buf
, &addr
) != 1 )
1364 if ( !slap_addr6_mask( &addr
, &b
->a_peername_mask6
, &b
->a_peername_addr6
) )
1366 #endif /* LDAP_PF_INET6 */
1368 #ifdef LDAP_PF_LOCAL
1369 /* extract path and try exact match */
1370 } else if ( b
->a_peername_style
== ACL_STYLE_PATH
) {
1373 if ( strncmp( op
->o_conn
->c_peer_name
.bv_val
,
1374 acl_bv_path_eq
.bv_val
,
1375 acl_bv_path_eq
.bv_len
) != 0 )
1378 path
.bv_val
= op
->o_conn
->c_peer_name
.bv_val
1379 + acl_bv_path_eq
.bv_len
;
1380 path
.bv_len
= op
->o_conn
->c_peer_name
.bv_len
1381 - acl_bv_path_eq
.bv_len
;
1383 if ( ber_bvcmp( &b
->a_peername_pat
, &path
) != 0 )
1386 #endif /* LDAP_PF_LOCAL */
1388 /* exact match (very unlikely...) */
1389 } else if ( ber_bvcmp( &op
->o_conn
->c_peer_name
, &b
->a_peername_pat
) != 0 ) {
1396 if ( !BER_BVISEMPTY( &b
->a_sockname_pat
) ) {
1397 if ( BER_BVISNULL( &op
->o_conn
->c_sock_name
) ) {
1400 Debug( LDAP_DEBUG_ACL
, "<= check a_sockname_path: %s\n",
1401 b
->a_sockname_pat
.bv_val
, 0, 0 );
1402 if ( !ber_bvccmp( &b
->a_sockname_pat
, '*' ) ) {
1403 if ( b
->a_sockname_style
== ACL_STYLE_REGEX
) {
1404 if (!regex_matches( &b
->a_sockname_pat
, op
->o_conn
->c_sock_name
.bv_val
,
1405 e
->e_ndn
, nmatch
, matches
) )
1410 } else if ( b
->a_sockname_style
== ACL_STYLE_EXPAND
) {
1412 char buf
[ACL_BUF_SIZE
];
1414 bv
.bv_len
= sizeof( buf
) - 1;
1416 if ( acl_string_expand( &bv
, &b
->a_sockname_pat
,
1417 e
->e_ndn
, nmatch
, matches
) )
1422 if ( ber_bvstrcasecmp( &bv
, &op
->o_conn
->c_sock_name
) != 0 ) {
1427 if ( ber_bvstrcasecmp( &b
->a_sockname_pat
, &op
->o_conn
->c_sock_name
) != 0 ) {
1434 if ( b
->a_dn_at
!= NULL
) {
1435 if ( acl_mask_dnattr( op
, e
, val
, a
, b
, i
,
1436 matches
, count
, state
,
1437 &b
->a_dn
, &op
->o_ndn
) )
1443 if ( b
->a_realdn_at
!= NULL
) {
1446 if ( op
->o_conn
&& !BER_BVISNULL( &op
->o_conn
->c_ndn
) )
1448 ndn
= op
->o_conn
->c_ndn
;
1453 if ( acl_mask_dnattr( op
, e
, val
, a
, b
, i
,
1454 matches
, count
, state
,
1455 &b
->a_realdn
, &ndn
) )
1461 if ( !BER_BVISEMPTY( &b
->a_group_pat
) ) {
1463 struct berval ndn
= BER_BVNULL
;
1466 if ( op
->o_ndn
.bv_len
== 0 ) {
1470 Debug( LDAP_DEBUG_ACL
, "<= check a_group_pat: %s\n",
1471 b
->a_group_pat
.bv_val
, 0, 0 );
1473 /* b->a_group is an unexpanded entry name, expanded it should be an
1474 * entry with objectclass group* and we test to see if odn is one of
1475 * the values in the attribute group
1477 /* see if asker is listed in dnattr */
1478 if ( b
->a_group_style
== ACL_STYLE_EXPAND
) {
1479 char buf
[ACL_BUF_SIZE
];
1481 regmatch_t tmp_matches
[2],
1482 *tmp_matchesp
= tmp_matches
;
1484 bv
.bv_len
= sizeof(buf
) - 1;
1489 switch ( a
->acl_dn_style
) {
1490 case ACL_STYLE_REGEX
:
1491 if ( !BER_BVISNULL( &a
->acl_dn_pat
) ) {
1492 tmp_matchesp
= matches
;
1493 tmp_nmatch
= nmatch
;
1497 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1498 case ACL_STYLE_BASE
:
1499 tmp_matches
[0].rm_so
= 0;
1500 tmp_matches
[0].rm_eo
= e
->e_nname
.bv_len
;
1505 case ACL_STYLE_SUBTREE
:
1506 case ACL_STYLE_CHILDREN
:
1507 tmp_matches
[0].rm_so
= 0;
1508 tmp_matches
[0].rm_eo
= e
->e_nname
.bv_len
;
1509 tmp_matches
[1].rm_so
= e
->e_nname
.bv_len
- a
->acl_dn_pat
.bv_len
;
1510 tmp_matches
[1].rm_eo
= e
->e_nname
.bv_len
;
1524 if ( acl_string_expand( &bv
, &b
->a_group_pat
,
1526 tmp_nmatch
, tmp_matchesp
) )
1531 if ( dnNormalize( 0, NULL
, NULL
, &bv
, &ndn
,
1532 op
->o_tmpmemctx
) != LDAP_SUCCESS
)
1534 /* did not expand to a valid dn */
1541 bv
= b
->a_group_pat
;
1544 rc
= backend_group( op
, e
, &bv
, &op
->o_ndn
,
1545 b
->a_group_oc
, b
->a_group_at
);
1548 slap_sl_free( ndn
.bv_val
, op
->o_tmpmemctx
);
1556 if ( !BER_BVISEMPTY( &b
->a_set_pat
) ) {
1558 char buf
[ACL_BUF_SIZE
];
1560 Debug( LDAP_DEBUG_ACL
, "<= check a_set_pat: %s\n",
1561 b
->a_set_pat
.bv_val
, 0, 0 );
1563 if ( b
->a_set_style
== ACL_STYLE_EXPAND
) {
1565 regmatch_t tmp_matches
[2],
1566 *tmp_matchesp
= tmp_matches
;
1569 bv
.bv_len
= sizeof( buf
) - 1;
1574 switch ( a
->acl_dn_style
) {
1575 case ACL_STYLE_REGEX
:
1576 if ( !BER_BVISNULL( &a
->acl_dn_pat
) ) {
1577 tmp_matchesp
= matches
;
1578 tmp_nmatch
= nmatch
;
1582 /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
1583 case ACL_STYLE_BASE
:
1584 tmp_matches
[0].rm_so
= 0;
1585 tmp_matches
[0].rm_eo
= e
->e_nname
.bv_len
;
1590 case ACL_STYLE_SUBTREE
:
1591 case ACL_STYLE_CHILDREN
:
1592 tmp_matches
[0].rm_so
= 0;
1593 tmp_matches
[0].rm_eo
= e
->e_nname
.bv_len
;
1594 tmp_matches
[1].rm_so
= e
->e_nname
.bv_len
- a
->acl_dn_pat
.bv_len
;
1595 tmp_matches
[1].rm_eo
= e
->e_nname
.bv_len
;
1609 if ( acl_string_expand( &bv
, &b
->a_set_pat
,
1611 tmp_nmatch
, tmp_matchesp
) )
1620 if ( acl_match_set( &bv
, op
, e
, NULL
) == 0 ) {
1625 if ( b
->a_authz
.sai_ssf
) {
1626 Debug( LDAP_DEBUG_ACL
, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
1627 b
->a_authz
.sai_ssf
, op
->o_ssf
, 0 );
1628 if ( b
->a_authz
.sai_ssf
> op
->o_ssf
) {
1633 if ( b
->a_authz
.sai_transport_ssf
) {
1634 Debug( LDAP_DEBUG_ACL
,
1635 "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
1636 b
->a_authz
.sai_transport_ssf
, op
->o_transport_ssf
, 0 );
1637 if ( b
->a_authz
.sai_transport_ssf
> op
->o_transport_ssf
) {
1642 if ( b
->a_authz
.sai_tls_ssf
) {
1643 Debug( LDAP_DEBUG_ACL
,
1644 "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
1645 b
->a_authz
.sai_tls_ssf
, op
->o_tls_ssf
, 0 );
1646 if ( b
->a_authz
.sai_tls_ssf
> op
->o_tls_ssf
) {
1651 if ( b
->a_authz
.sai_sasl_ssf
) {
1652 Debug( LDAP_DEBUG_ACL
,
1653 "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
1654 b
->a_authz
.sai_sasl_ssf
, op
->o_sasl_ssf
, 0 );
1655 if ( b
->a_authz
.sai_sasl_ssf
> op
->o_sasl_ssf
) {
1660 /* check for the "self" modifier in the <access> field */
1661 if ( b
->a_dn
.a_self
) {
1665 ACL_RECORD_VALUE_STATE
;
1667 /* must have DN syntax */
1668 if ( desc
->ad_type
->sat_syntax
!= slap_schema
.si_syn_distinguishedName
&&
1669 !is_at_syntax( desc
->ad_type
, SLAPD_NAMEUID_SYNTAX
)) continue;
1671 /* check if the target is an attribute. */
1672 if ( val
== NULL
) continue;
1674 /* a DN must be present */
1675 if ( BER_BVISEMPTY( &op
->o_ndn
) ) {
1679 /* target is attribute, check if the attribute value
1682 rc
= value_match( &match
, desc
,
1683 desc
->ad_type
->sat_equality
, 0,
1684 val
, &op
->o_ndn
, &dummy
);
1685 /* on match error or no match, fail the ACL clause */
1686 if ( rc
!= LDAP_SUCCESS
|| match
!= 0 )
1691 if ( b
->a_dynacl
) {
1693 slap_access_t tgrant
, tdeny
;
1695 Debug( LDAP_DEBUG_ACL
, "<= check a_dynacl\n",
1698 /* this case works different from the others above.
1699 * since dynamic ACL's themselves give permissions, we need
1700 * to first check b->a_access_mask, the ACL's access level.
1702 /* first check if the right being requested
1703 * is allowed by the ACL clause.
1705 if ( ! ACL_PRIV_ISSET( b
->a_access_mask
, a2pmask
) ) {
1709 /* start out with nothing granted, nothing denied */
1710 ACL_INVALIDATE(tgrant
);
1711 ACL_INVALIDATE(tdeny
);
1713 for ( da
= b
->a_dynacl
; da
; da
= da
->da_next
) {
1714 slap_access_t grant
,
1717 ACL_INVALIDATE(grant
);
1718 ACL_INVALIDATE(deny
);
1720 Debug( LDAP_DEBUG_ACL
, " <= check a_dynacl: %s\n",
1721 da
->da_name
, 0, 0 );
1723 (void)da
->da_mask( da
->da_private
, op
, e
, desc
,
1724 val
, nmatch
, matches
, &grant
, &deny
);
1730 /* remove anything that the ACL clause does not allow */
1731 tgrant
&= b
->a_access_mask
& ACL_PRIV_MASK
;
1732 tdeny
&= ACL_PRIV_MASK
;
1734 /* see if we have anything to contribute */
1735 if( ACL_IS_INVALID(tgrant
) && ACL_IS_INVALID(tdeny
) ) {
1739 /* this could be improved by changing slap_acl_mask so that it can deal with
1740 * by clauses that return grant/deny pairs. Right now, it does either
1741 * additive or subtractive rights, but not both at the same time. So,
1742 * we need to combine the grant/deny pair into a single rights mask in
1743 * a smart way: if either grant or deny is "empty", then we use the
1744 * opposite as is, otherwise we remove any denied rights from the grant
1745 * rights mask and construct an additive mask.
1747 if (ACL_IS_INVALID(tdeny
)) {
1748 modmask
= tgrant
| ACL_PRIV_ADDITIVE
;
1750 } else if (ACL_IS_INVALID(tgrant
)) {
1751 modmask
= tdeny
| ACL_PRIV_SUBSTRACTIVE
;
1754 modmask
= (tgrant
& ~tdeny
) | ACL_PRIV_ADDITIVE
;
1758 #endif /* SLAP_DYNACL */
1760 modmask
= b
->a_access_mask
;
1763 Debug( LDAP_DEBUG_ACL
,
1764 "<= acl_mask: [%d] applying %s (%s)\n",
1765 i
, accessmask2str( modmask
, accessmaskbuf
, 1 ),
1766 b
->a_type
== ACL_CONTINUE
1768 : b
->a_type
== ACL_BREAK
1774 if( ACL_IS_ADDITIVE(modmask
) ) {
1776 ACL_PRIV_SET( *mask
, modmask
);
1779 ACL_PRIV_CLR( *mask
, ~ACL_PRIV_MASK
);
1781 } else if( ACL_IS_SUBTRACTIVE(modmask
) ) {
1782 /* substract privs */
1783 ACL_PRIV_CLR( *mask
, modmask
);
1786 ACL_PRIV_CLR( *mask
, ~ACL_PRIV_MASK
);
1795 Debug( LDAP_DEBUG_ACL
,
1796 "<= acl_mask: [%d] mask: %s\n",
1797 i
, accessmask2str(*mask
, accessmaskbuf
, 1), 0 );
1799 if( b
->a_type
== ACL_CONTINUE
) {
1802 } else if ( b
->a_type
== ACL_BREAK
) {
1810 /* implicit "by * none" clause */
1813 Debug( LDAP_DEBUG_ACL
,
1814 "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
1815 accessmask2str(*mask
, accessmaskbuf
, 1), 0, 0 );
1820 * acl_check_modlist - check access control on the given entry to see if
1821 * it allows the given modifications by the user associated with op.
1822 * returns 1 if mods allowed ok
1823 * 0 mods not allowed
1830 Modifications
*mlist
)
1833 AccessControlState state
= ACL_STATE_INIT
;
1836 int ret
= 1; /* default is access allowed */
1840 be
= LDAP_STAILQ_FIRST(&backendDB
);
1844 assert( be
!= NULL
);
1846 /* short circuit root database access */
1847 if ( be_isroot( op
) ) {
1848 Debug( LDAP_DEBUG_ACL
,
1849 "<= acl_access_allowed: granted to database root\n",
1854 /* use backend default access if no backend acls */
1855 if( op
->o_bd
!= NULL
&& op
->o_bd
->be_acl
== NULL
) {
1856 Debug( LDAP_DEBUG_ACL
,
1857 "=> access_allowed: backend default %s access %s to \"%s\"\n",
1858 access2str( ACL_WRITE
),
1859 op
->o_bd
->be_dfltaccess
>= ACL_WRITE
1860 ? "granted" : "denied",
1862 ret
= (op
->o_bd
->be_dfltaccess
>= ACL_WRITE
);
1866 for ( ; mlist
!= NULL
; mlist
= mlist
->sml_next
) {
1868 * Internal mods are ignored by ACL_WRITE checking
1870 if ( mlist
->sml_flags
& SLAP_MOD_INTERNAL
) {
1871 Debug( LDAP_DEBUG_ACL
, "acl: internal mod %s:"
1872 " modify access granted\n",
1873 mlist
->sml_desc
->ad_cname
.bv_val
, 0, 0 );
1878 * no-user-modification operational attributes are ignored
1879 * by ACL_WRITE checking as any found here are not provided
1882 if ( is_at_no_user_mod( mlist
->sml_desc
->ad_type
)
1883 && ! ( mlist
->sml_flags
& SLAP_MOD_MANAGING
) )
1885 Debug( LDAP_DEBUG_ACL
, "acl: no-user-mod %s:"
1886 " modify access granted\n",
1887 mlist
->sml_desc
->ad_cname
.bv_val
, 0, 0 );
1891 switch ( mlist
->sml_op
) {
1892 case LDAP_MOD_REPLACE
:
1893 case LDAP_MOD_INCREMENT
:
1895 * We must check both permission to delete the whole
1896 * attribute and permission to add the specific attributes.
1897 * This prevents abuse from selfwriters.
1899 if ( ! access_allowed( op
, e
,
1900 mlist
->sml_desc
, NULL
,
1901 ( mlist
->sml_flags
& SLAP_MOD_MANAGING
) ? ACL_MANAGE
: ACL_WDEL
,
1908 if ( mlist
->sml_values
== NULL
) break;
1910 /* fall thru to check value to add */
1913 assert( mlist
->sml_values
!= NULL
);
1915 for ( bv
= mlist
->sml_nvalues
1916 ? mlist
->sml_nvalues
: mlist
->sml_values
;
1917 bv
->bv_val
!= NULL
; bv
++ )
1919 if ( ! access_allowed( op
, e
,
1920 mlist
->sml_desc
, bv
,
1921 ( mlist
->sml_flags
& SLAP_MOD_MANAGING
) ? ACL_MANAGE
: ACL_WADD
,
1930 case LDAP_MOD_DELETE
:
1931 if ( mlist
->sml_values
== NULL
) {
1932 if ( ! access_allowed( op
, e
,
1933 mlist
->sml_desc
, NULL
,
1934 ( mlist
->sml_flags
& SLAP_MOD_MANAGING
) ? ACL_MANAGE
: ACL_WDEL
,
1942 for ( bv
= mlist
->sml_nvalues
1943 ? mlist
->sml_nvalues
: mlist
->sml_values
;
1944 bv
->bv_val
!= NULL
; bv
++ )
1946 if ( ! access_allowed( op
, e
,
1947 mlist
->sml_desc
, bv
,
1948 ( mlist
->sml_flags
& SLAP_MOD_MANAGING
) ? ACL_MANAGE
: ACL_WDEL
,
1957 case SLAP_MOD_SOFTADD
:
1958 /* allow adding attribute via modrdn thru */
1970 if (be_null
) op
->o_bd
= NULL
;
1976 struct berval
*list
,
1989 while ( len
>= 0 && --ix
>= 0 ) {
1990 while ( --len
>= 0 && *p
++ != sep
)
1993 while ( len
>= 0 && *p
== ' ' ) {
2006 while ( --len
>= 0 && *p
!= sep
) {
2010 while ( bv
->bv_len
> 0 && *--p
== ' ' ) {
2017 typedef struct acl_set_gather_t
{
2023 acl_set_cb_gather( Operation
*op
, SlapReply
*rs
)
2025 acl_set_gather_t
*p
= (acl_set_gather_t
*)op
->o_callback
->sc_private
;
2027 if ( rs
->sr_type
== REP_SEARCH
) {
2028 BerValue bvals
[ 2 ];
2029 BerVarray bvalsp
= NULL
;
2032 for ( j
= 0; !BER_BVISNULL( &rs
->sr_attrs
[ j
].an_name
); j
++ ) {
2033 AttributeDescription
*desc
= rs
->sr_attrs
[ j
].an_desc
;
2035 if ( desc
== NULL
) {
2039 if ( desc
== slap_schema
.si_ad_entryDN
) {
2041 bvals
[ 0 ] = rs
->sr_entry
->e_nname
;
2042 BER_BVZERO( &bvals
[ 1 ] );
2047 a
= attr_find( rs
->sr_entry
->e_attrs
, desc
);
2049 bvalsp
= a
->a_nvals
;
2054 p
->bvals
= slap_set_join( p
->cookie
, p
->bvals
,
2055 ( '|' | SLAP_SET_RREF
), bvalsp
);
2060 assert( rs
->sr_type
== REP_RESULT
);
2067 acl_set_gather( SetCookie
*cookie
, struct berval
*name
, AttributeDescription
*desc
)
2069 AclSetCookie
*cp
= (AclSetCookie
*)cookie
;
2071 LDAPURLDesc
*ludp
= NULL
;
2072 Operation op2
= { 0 };
2073 SlapReply rs
= {REP_RESULT
};
2074 AttributeName anlist
[ 2 ], *anlistp
= NULL
;
2076 slap_callback cb
= { NULL
, acl_set_cb_gather
, NULL
, NULL
};
2077 acl_set_gather_t p
= { 0 };
2079 /* this routine needs to return the bervals instead of
2080 * plain strings, since syntax is not known. It should
2081 * also return the syntax or some "comparison cookie".
2083 if ( strncasecmp( name
->bv_val
, "ldap:///", STRLENOF( "ldap:///" ) ) != 0 ) {
2084 return acl_set_gather2( cookie
, name
, desc
);
2087 rc
= ldap_url_parse( name
->bv_val
, &ludp
);
2088 if ( rc
!= LDAP_URL_SUCCESS
) {
2089 Debug( LDAP_DEBUG_TRACE
,
2090 "%s acl_set_gather: unable to parse URL=\"%s\"\n",
2091 cp
->asc_op
->o_log_prefix
, name
->bv_val
, 0 );
2093 rc
= LDAP_PROTOCOL_ERROR
;
2097 if ( ( ludp
->lud_host
&& ludp
->lud_host
[0] ) || ludp
->lud_exts
)
2099 /* host part must be empty */
2100 /* extensions parts must be empty */
2101 Debug( LDAP_DEBUG_TRACE
,
2102 "%s acl_set_gather: host/exts must be absent in URL=\"%s\"\n",
2103 cp
->asc_op
->o_log_prefix
, name
->bv_val
, 0 );
2105 rc
= LDAP_PROTOCOL_ERROR
;
2109 /* Grab the searchbase and see if an appropriate database can be found */
2110 ber_str2bv( ludp
->lud_dn
, 0, 0, &op2
.o_req_dn
);
2111 rc
= dnNormalize( 0, NULL
, NULL
, &op2
.o_req_dn
,
2112 &op2
.o_req_ndn
, cp
->asc_op
->o_tmpmemctx
);
2113 BER_BVZERO( &op2
.o_req_dn
);
2114 if ( rc
!= LDAP_SUCCESS
) {
2115 Debug( LDAP_DEBUG_TRACE
,
2116 "%s acl_set_gather: DN=\"%s\" normalize failed\n",
2117 cp
->asc_op
->o_log_prefix
, op2
.o_req_dn
.bv_val
, 0 );
2122 op2
.o_bd
= select_backend( &op2
.o_req_ndn
, 1 );
2123 if ( ( op2
.o_bd
== NULL
) || ( op2
.o_bd
->be_search
== NULL
) ) {
2124 Debug( LDAP_DEBUG_TRACE
,
2125 "%s acl_set_gather: no database could be selected for DN=\"%s\"\n",
2126 cp
->asc_op
->o_log_prefix
, op2
.o_req_ndn
.bv_val
, 0 );
2128 rc
= LDAP_NO_SUCH_OBJECT
;
2132 /* Grab the filter */
2133 if ( ludp
->lud_filter
) {
2134 ber_str2bv_x( ludp
->lud_filter
, 0, 0, &op2
.ors_filterstr
,
2135 cp
->asc_op
->o_tmpmemctx
);
2136 op2
.ors_filter
= str2filter_x( cp
->asc_op
, op2
.ors_filterstr
.bv_val
);
2137 if ( op2
.ors_filter
== NULL
) {
2138 Debug( LDAP_DEBUG_TRACE
,
2139 "%s acl_set_gather: unable to parse filter=\"%s\"\n",
2140 cp
->asc_op
->o_log_prefix
, op2
.ors_filterstr
.bv_val
, 0 );
2142 rc
= LDAP_PROTOCOL_ERROR
;
2147 op2
.ors_filterstr
= *slap_filterstr_objectClass_pres
;
2148 op2
.ors_filter
= (Filter
*)slap_filter_objectClass_pres
;
2152 /* Grab the scope */
2153 op2
.ors_scope
= ludp
->lud_scope
;
2155 /* Grap the attributes */
2156 if ( ludp
->lud_attrs
) {
2159 for ( ; ludp
->lud_attrs
[ nattrs
]; nattrs
++ )
2162 anlistp
= slap_sl_calloc( sizeof( AttributeName
), nattrs
+ 2,
2163 cp
->asc_op
->o_tmpmemctx
);
2165 for ( i
= 0, nattrs
= 0; ludp
->lud_attrs
[ i
]; i
++ ) {
2167 AttributeDescription
*desc
= NULL
;
2168 const char *text
= NULL
;
2170 ber_str2bv( ludp
->lud_attrs
[ i
], 0, 0, &name
);
2171 rc
= slap_bv2ad( &name
, &desc
, &text
);
2172 if ( rc
== LDAP_SUCCESS
) {
2173 anlistp
[ nattrs
].an_name
= name
;
2174 anlistp
[ nattrs
].an_desc
= desc
;
2183 anlistp
[ nattrs
].an_name
= desc
->ad_cname
;
2184 anlistp
[ nattrs
].an_desc
= desc
;
2186 BER_BVZERO( &anlistp
[ nattrs
+ 1 ].an_name
);
2190 op2
.o_hdr
= cp
->asc_op
->o_hdr
;
2191 op2
.o_tag
= LDAP_REQ_SEARCH
;
2192 op2
.o_ndn
= op2
.o_bd
->be_rootndn
;
2193 op2
.o_callback
= &cb
;
2194 slap_op_time( &op2
.o_time
, &op2
.o_tincr
);
2195 op2
.o_do_not_cache
= 1;
2196 op2
.o_is_auth_check
= 0;
2197 ber_dupbv_x( &op2
.o_req_dn
, &op2
.o_req_ndn
, cp
->asc_op
->o_tmpmemctx
);
2198 op2
.ors_slimit
= SLAP_NO_LIMIT
;
2199 op2
.ors_tlimit
= SLAP_NO_LIMIT
;
2200 op2
.ors_attrs
= anlistp
;
2201 op2
.ors_attrsonly
= 0;
2202 op2
.o_private
= cp
->asc_op
->o_private
;
2203 op2
.o_extra
= cp
->asc_op
->o_extra
;
2207 rc
= op2
.o_bd
->be_search( &op2
, &rs
);
2213 if ( op2
.ors_filter
&& op2
.ors_filter
!= slap_filter_objectClass_pres
) {
2214 filter_free_x( cp
->asc_op
, op2
.ors_filter
);
2216 if ( !BER_BVISNULL( &op2
.o_req_ndn
) ) {
2217 slap_sl_free( op2
.o_req_ndn
.bv_val
, cp
->asc_op
->o_tmpmemctx
);
2219 if ( !BER_BVISNULL( &op2
.o_req_dn
) ) {
2220 slap_sl_free( op2
.o_req_dn
.bv_val
, cp
->asc_op
->o_tmpmemctx
);
2223 ldap_free_urldesc( ludp
);
2225 if ( anlistp
&& anlistp
!= anlist
) {
2226 slap_sl_free( anlistp
, cp
->asc_op
->o_tmpmemctx
);
2233 acl_set_gather2( SetCookie
*cookie
, struct berval
*name
, AttributeDescription
*desc
)
2235 AclSetCookie
*cp
= (AclSetCookie
*)cookie
;
2236 BerVarray bvals
= NULL
;
2240 /* this routine needs to return the bervals instead of
2241 * plain strings, since syntax is not known. It should
2242 * also return the syntax or some "comparison cookie".
2244 rc
= dnNormalize( 0, NULL
, NULL
, name
, &ndn
, cp
->asc_op
->o_tmpmemctx
);
2245 if ( rc
== LDAP_SUCCESS
) {
2246 if ( desc
== slap_schema
.si_ad_entryDN
) {
2247 bvals
= (BerVarray
)slap_sl_malloc( sizeof( BerValue
) * 2,
2248 cp
->asc_op
->o_tmpmemctx
);
2250 BER_BVZERO( &bvals
[ 1 ] );
2254 backend_attribute( cp
->asc_op
,
2255 cp
->asc_e
, &ndn
, desc
, &bvals
, ACL_NONE
);
2258 if ( !BER_BVISNULL( &ndn
) ) {
2259 slap_sl_free( ndn
.bv_val
, cp
->asc_op
->o_tmpmemctx
);
2268 struct berval
*subj
,
2271 struct berval
*default_set_attribute
)
2273 struct berval set
= BER_BVNULL
;
2275 AclSetCookie cookie
;
2277 if ( default_set_attribute
== NULL
) {
2278 ber_dupbv_x( &set
, subj
, op
->o_tmpmemctx
);
2281 struct berval subjdn
, ndn
= BER_BVNULL
;
2282 struct berval setat
;
2283 BerVarray bvals
= NULL
;
2285 AttributeDescription
*desc
= NULL
;
2287 /* format of string is "entry/setAttrName" */
2288 if ( acl_get_part( subj
, 0, '/', &subjdn
) < 0 ) {
2292 if ( acl_get_part( subj
, 1, '/', &setat
) < 0 ) {
2293 setat
= *default_set_attribute
;
2297 * NOTE: dnNormalize honors the ber_len field
2298 * as the length of the dn to be normalized
2300 if ( slap_bv2ad( &setat
, &desc
, &text
) == LDAP_SUCCESS
) {
2301 if ( dnNormalize( 0, NULL
, NULL
, &subjdn
, &ndn
, op
->o_tmpmemctx
) == LDAP_SUCCESS
)
2303 backend_attribute( op
, e
, &ndn
, desc
, &bvals
, ACL_NONE
);
2304 if ( bvals
!= NULL
&& !BER_BVISNULL( &bvals
[0] ) ) {
2308 BER_BVZERO( &bvals
[0] );
2309 for ( i
= 1; !BER_BVISNULL( &bvals
[i
] ); i
++ )
2311 bvals
[0].bv_val
= bvals
[i
-1].bv_val
;
2312 BER_BVZERO( &bvals
[i
-1] );
2314 ber_bvarray_free_x( bvals
, op
->o_tmpmemctx
);
2315 slap_sl_free( ndn
.bv_val
, op
->o_tmpmemctx
);
2320 if ( !BER_BVISNULL( &set
) ) {
2323 rc
= ( slap_set_filter(
2325 (SetCookie
*)&cookie
, &set
,
2326 &op
->o_ndn
, &e
->e_nname
, NULL
) > 0 );
2327 slap_sl_free( set
.bv_val
, op
->o_tmpmemctx
);
2336 * dynamic ACL infrastructure
2338 static slap_dynacl_t
*da_list
= NULL
;
2341 slap_dynacl_register( slap_dynacl_t
*da
)
2345 for ( tmp
= da_list
; tmp
; tmp
= tmp
->da_next
) {
2346 if ( strcasecmp( da
->da_name
, tmp
->da_name
) == 0 ) {
2351 if ( tmp
!= NULL
) {
2355 if ( da
->da_mask
== NULL
) {
2359 da
->da_private
= NULL
;
2360 da
->da_next
= da_list
;
2366 static slap_dynacl_t
*
2367 slap_dynacl_next( slap_dynacl_t
*da
)
2376 slap_dynacl_get( const char *name
)
2380 for ( da
= slap_dynacl_next( NULL
); da
; da
= slap_dynacl_next( da
) ) {
2381 if ( strcasecmp( da
->da_name
, name
) == 0 ) {
2388 #endif /* SLAP_DYNACL */
2391 * statically built-in dynamic ACL initialization
2393 static int (*acl_init_func
[])( void ) = {
2395 /* TODO: remove when ACI will only be dynamic */
2396 #if SLAPD_ACI_ENABLED == SLAPD_MOD_STATIC
2398 #endif /* SLAPD_ACI_ENABLED */
2399 #endif /* SLAP_DYNACL */
2409 for ( i
= 0; acl_init_func
[ i
] != NULL
; i
++ ) {
2410 rc
= (*(acl_init_func
[ i
]))();
2425 regmatch_t
*matches
)
2433 bv
->bv_val
[0] = '\0';
2434 bv
->bv_len
--; /* leave space for lone $ */
2437 for ( dp
= bv
->bv_val
, sp
= pat
->bv_val
; size
< bv
->bv_len
&&
2438 sp
< pat
->bv_val
+ pat
->bv_len
; sp
++ )
2440 /* did we previously see a $ */
2442 if ( flag
== 1 && *sp
== '$' ) {
2447 } else if ( flag
== 1 && *sp
== '{' /*'}'*/) {
2450 } else if ( *sp
>= '0' && *sp
<= '9' ) {
2458 for ( sp
++; *sp
!= '\0' && *sp
!= /*'{'*/ '}'; sp
++ ) {
2459 if ( *sp
>= '0' && *sp
<= '9' ) {
2460 n
= 10*n
+ ( *sp
- '0' );
2464 if ( *sp
!= /*'{'*/ '}' ) {
2470 if ( n
>= nmatch
) {
2476 i
= matches
[n
].rm_so
;
2477 l
= matches
[n
].rm_eo
;
2478 for ( ; size
< bv
->bv_len
&& i
< l
; size
++, i
++ ) {
2496 /* must have ended with a single $ */
2504 Debug( LDAP_DEBUG_TRACE
, "=> acl_string_expand: pattern: %.*s\n", (int)pat
->bv_len
, pat
->bv_val
, 0 );
2505 Debug( LDAP_DEBUG_TRACE
, "=> acl_string_expand: expanded: %s\n", bv
->bv_val
, 0, 0 );
2512 struct berval
*pat
, /* pattern to expand and match against */
2513 char *str
, /* string to match against pattern */
2514 char *buf
, /* buffer with $N expansion variables */
2515 int nmatch
, /* size of the matches array */
2516 regmatch_t
*matches
/* offsets in buffer for $N expansion variables */
2520 char newbuf
[ACL_BUF_SIZE
];
2524 bv
.bv_len
= sizeof( newbuf
) - 1;
2531 acl_string_expand( &bv
, pat
, buf
, nmatch
, matches
);
2532 rc
= regcomp( &re
, newbuf
, REG_EXTENDED
|REG_ICASE
);
2534 char error
[ACL_BUF_SIZE
];
2535 regerror( rc
, &re
, error
, sizeof( error
) );
2537 Debug( LDAP_DEBUG_TRACE
,
2538 "compile( \"%s\", \"%s\") failed %s\n",
2539 pat
->bv_val
, str
, error
);
2543 rc
= regexec( &re
, str
, 0, NULL
, 0 );
2546 Debug( LDAP_DEBUG_TRACE
,
2547 "=> regex_matches: string: %s\n", str
, 0, 0 );
2548 Debug( LDAP_DEBUG_TRACE
,
2549 "=> regex_matches: rc: %d %s\n",
2550 rc
, !rc
? "matches" : "no matches", 0 );