1 /* aci.c - routines to parse and check acl's */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 1998-2005 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.
29 #ifdef SLAPD_ACI_ENABLED
35 #include <ac/socket.h>
36 #include <ac/string.h>
37 #include <ac/unistd.h>
43 #define ACI_BUF_SIZE 1024 /* use most appropriate size */
54 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
55 if ( bv.bv_len <= 0 ) {
59 switch ( *bv.bv_val ) {
61 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
64 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
65 * the right 's' to mean "set", but in the examples states
66 * that the right 's' means "search". The latter definition
69 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
72 ACL_PRIV_SET(mask, ACL_PRIV_READ);
75 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
78 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
79 * define any equivalent to the AUTH right, so I've just used
82 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
96 const struct berval *attr,
99 struct berval bv, left, right;
102 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
103 if ( acl_get_part(&bv, 0, '=', &left ) < 0
104 || acl_get_part( &bv, 1, '=', &right ) < 0 )
106 if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
110 } else if ( val == NULL ) {
111 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
116 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
117 /* FIXME: this is also totally undocumented! */
118 /* this is experimental code that implements a
119 * simple (prefix) match of the attribute value.
120 * the ACI draft does not provide for aci's that
121 * apply to specific values, but it would be
122 * nice to have. If the <attr> part of an aci's
123 * rights list is of the form <attr>=<value>,
124 * that means the aci applies only to attrs with
125 * the given value. Furthermore, if the attr is
126 * of the form <attr>=<value>*, then <value> is
127 * treated as a prefix, and the aci applies to
128 * any value with that prefix.
130 * Ideally, this would allow r.e. matches.
132 if ( acl_get_part( &right, 0, '*', &left ) < 0
133 || right.bv_len <= left.bv_len )
135 if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
139 } else if ( val->bv_len >= left.bv_len ) {
140 if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) {
152 aci_list_get_attr_rights(
154 const struct berval *attr,
161 /* loop through each rights/attr pair, skip first part (action) */
163 for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
164 if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
168 if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
172 mask |= aci_list_map_rights( &bv );
181 const struct berval *attr,
183 slap_access_t *grant,
184 slap_access_t *deny )
186 struct berval perm, actn;
190 if ( attr == NULL || BER_BVISEMPTY( attr )
191 || ber_bvstrcasecmp( attr, &aci_bv[ ACI_BV_ENTRY ] ) == 0 )
193 attr = &aci_bv[ ACI_BV_BR_ENTRY ];
199 /* loop through each permissions clause */
200 for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
201 if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
205 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
208 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
216 *mask |= aci_list_get_attr_rights( &perm, attr, val );
217 *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
226 const struct berval *defgrpoc,
227 const struct berval *defgrpat,
234 struct berval subjdn;
237 ObjectClass *grp_oc = NULL;
238 AttributeDescription *grp_ad = NULL;
242 /* format of string is "group/objectClassValue/groupAttrName" */
243 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
247 if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
251 if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
255 rc = slap_bv2ad( &grpat, &grp_ad, &text );
256 if ( rc != LDAP_SUCCESS ) {
262 grp_oc = oc_bvfind( &grpoc );
264 if ( grp_oc != NULL && grp_ad != NULL ) {
265 char buf[ ACI_BUF_SIZE ];
266 struct berval bv, ndn;
268 bv.bv_len = sizeof( buf ) - 1;
269 bv.bv_val = (char *)&buf;
270 if ( acl_string_expand( &bv, &subjdn,
271 e->e_ndn, nmatch, matches ) )
277 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
279 rc = ( backend_group( op, e, &ndn, &op->o_ndn,
280 grp_oc, grp_ad ) == 0 );
281 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
293 AttributeDescription *desc,
298 slap_access_t *grant,
300 slap_aci_scope_t asserted_scope )
302 struct berval bv, scope, perms, type, sdn;
306 assert( !BER_BVISNULL( &desc->ad_cname ) );
308 /* parse an aci of the form:
309 oid # scope # action;rights;attr;rights;attr
310 $ action;rights;attr;rights;attr # type # subject
312 [NOTE: the following comment is very outdated,
313 as the draft version it refers to (Ando, 2004-11-20)].
315 See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
316 a full description of the format for this attribute.
317 Differences: "this" in the draft is "self" here, and
318 "self" and "public" is in the position of type.
320 <scope> = {entry|children|subtree}
321 <type> = {public|users|access-id|subtree|onelevel|children|
322 self|dnattr|group|role|set|set-ref}
324 This routine now supports scope={ENTRY,CHILDREN}
326 - ENTRY applies to "entry" and "subtree";
327 - CHILDREN aplies to "children" and "subtree"
330 /* check that the aci has all 5 components */
331 if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
335 /* check that the aci family is supported */
336 /* FIXME: the OID is ignored? */
337 if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
341 /* check that the scope matches */
342 if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
346 /* note: scope can be either ENTRY or CHILDREN;
347 * they respectively match "entry" and "children" in bv
348 * both match "subtree" */
349 switch ( asserted_scope ) {
350 case SLAP_ACI_SCOPE_ENTRY:
351 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
352 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
358 case SLAP_ACI_SCOPE_CHILDREN:
359 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
360 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
366 case SLAP_ACI_SCOPE_SUBTREE:
367 /* TODO: add assertion? */
371 /* get the list of permissions clauses, bail if empty */
372 if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
377 /* check if any permissions allow desired access */
378 if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
382 /* see if we have a DN match */
383 if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
388 /* see if we have a public (i.e. anonymous) access */
389 if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
393 /* otherwise require an identity */
394 if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
398 /* see if we have a users access */
399 if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
403 /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
404 * just grab all the berval up to its end (ITS#3303).
405 * NOTE: the problem could be solved by providing the DN with
406 * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
407 * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
409 if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
413 sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
414 sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
416 if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
417 return dn_match( &op->o_ndn, &sdn );
419 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
420 return dnIsSuffix( &op->o_ndn, &sdn );
422 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
425 dnParent( &sdn, &pdn );
427 return dn_match( &op->o_ndn, &pdn );
429 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
430 return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
432 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
433 return dn_match( &op->o_ndn, &e->e_nname );
435 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
437 AttributeDescription *ad = NULL;
440 rc = slap_bv2ad( &sdn, &ad, &text );
441 assert( rc == LDAP_SUCCESS );
444 for ( at = attrs_find( e->e_attrs, ad );
446 at = attrs_find( at->a_next, ad ) )
448 if ( value_find_ex( ad,
449 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
450 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
452 &op->o_ndn, op->o_tmpmemctx ) == 0 )
461 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
462 if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_GROUP_CLASS ],
463 &aci_bv[ ACI_BV_GROUP_ATTR ], op, e, nmatch, matches ) )
468 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
469 if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_ROLE_CLASS ],
470 &aci_bv[ ACI_BV_ROLE_ATTR ], op, e, nmatch, matches ) )
475 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
476 if ( acl_match_set( &sdn, op, e, 0 ) ) {
480 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
481 if ( acl_match_set( &sdn, op, e, 1 ) ) {
491 * FIXME: there is a silly dependence that makes it difficult
492 * to move ACIs in a run-time loadable module under the "dynacl"
493 * umbrella, because sets share some helpers with ACIs.
496 dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *right, void **privp )
498 AttributeDescription *ad = NULL;
499 const char *text = NULL;
501 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
502 fprintf( stderr, "%s: line %d: "
503 "inappropriate style \"%s\" in \"aci\" by clause\n",
504 fname, lineno, style_strings[sty] );
508 if ( right != NULL && *right != '\0' ) {
509 if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
511 "%s: line %d: aci \"%s\": %s\n",
512 fname, lineno, right, text );
517 ad = slap_schema.si_ad_aci;
520 if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
521 fprintf( stderr, "%s: line %d: "
522 "aci \"%s\": inappropriate syntax: %s\n",
523 fname, lineno, right,
524 ad->ad_type->sat_syntax_oid );
534 dynacl_aci_unparse( void *priv, struct berval *bv )
536 AttributeDescription *ad = ( AttributeDescription * )priv;
539 assert( ad != NULL );
541 bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
542 ptr = lutil_strcopy( bv->bv_val, " aci=" );
543 ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
544 bv->bv_len = ptr - bv->bv_val;
554 AttributeDescription *desc,
558 slap_access_t *grantp,
559 slap_access_t *denyp )
561 AttributeDescription *ad = ( AttributeDescription * )priv;
563 slap_access_t tgrant, tdeny, grant, deny;
565 char accessmaskbuf[ACCESSMASK_MAXLEN];
566 char accessmaskbuf1[ACCESSMASK_MAXLEN];
567 #endif /* LDAP_DEBUG */
569 /* start out with nothing granted, nothing denied */
573 /* get the aci attribute */
574 at = attr_find( e->e_attrs, ad );
578 /* the aci is an multi-valued attribute. The
579 * rights are determined by OR'ing the individual
580 * rights given by the acis.
582 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
583 if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
584 nmatch, matches, &grant, &deny,
585 SLAP_ACI_SCOPE_ENTRY ) != 0 )
592 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
593 accessmask2str( tgrant, accessmaskbuf, 1 ),
594 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
597 /* If the entry level aci didn't contain anything valid for the
598 * current operation, climb up the tree and evaluate the
599 * acis with scope set to subtree
601 if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
602 struct berval parent_ndn;
605 /* to solve the chicken'n'egg problem of accessing
606 * the OpenLDAPaci attribute, the direct access
607 * to the entry's attribute is unchecked; however,
608 * further accesses to OpenLDAPaci values in the
609 * ancestors occur through backend_attribute(), i.e.
610 * with the identity of the operation, requiring
611 * further access checking. For uniformity, this
612 * makes further requests occur as the rootdn, if
613 * any, i.e. searching for the OpenLDAPaci attribute
614 * is considered an internal search. If this is not
615 * acceptable, then the same check needs be performed
616 * when accessing the entry's attribute. */
619 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
620 op2.o_dn = op->o_bd->be_rootdn;
621 op2.o_ndn = op->o_bd->be_rootndn;
625 dnParent( &e->e_nname, &parent_ndn );
626 while ( !BER_BVISEMPTY( &parent_ndn ) ){
628 BerVarray bvals = NULL;
631 Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
632 ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
641 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
642 if ( aci_mask( op, e, desc, val,
646 SLAP_ACI_SCOPE_CHILDREN ) != 0 )
650 /* evaluation stops as soon as either a "deny" or a
651 * "grant" directive matches.
653 if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
657 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
658 accessmask2str( tgrant, accessmaskbuf, 1 ),
659 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
663 case LDAP_NO_SUCH_ATTRIBUTE:
664 /* just go on if the aci-Attribute is not present in
667 Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
671 case LDAP_NO_SUCH_OBJECT:
672 /* We have reached the base object */
673 Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
685 dnParent( &parent_ndn, &parent_ndn );
695 /* need to register this at some point */
696 static slap_dynacl_t dynacl_aci = {
707 dynacl_aci_init( void )
709 return slap_dynacl_register( &dynacl_aci );
712 #endif /* SLAP_DYNACL */
714 /* ACI syntax validation */
717 * Matches given berval to array of bervals
719 * >=0 if one if the array elements equals to this berval
720 * -1 if string was not found in array
725 const struct berval *arr[] )
729 if ( BER_BVISEMPTY( bv ) ) {
733 for ( i = 0; arr[ i ] != NULL ; i++ ) {
734 if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
743 /* Returns what have left in input berval after current sub */
748 struct berval *tail )
752 tail->bv_val = sub->bv_val + sub->bv_len;
753 head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
754 tail->bv_len = val->bv_len - head_len;
759 * aci is accepted in following form:
760 * oid#scope#rights#type#subject
763 * scope := entry|children
764 * rights := right[[$right]...]
765 * right := (grant|deny);action
766 * action := perms;attr[[;perms;attr]...]
767 * perms := perm[[,perm]...]
769 * attr := attributeType|[all]
770 * type := public|users|self|dnattr|group|role|set|set-ref|
771 * access_id|subtree|onelevel|children
774 OpenLDAPaciValidatePerms(
775 struct berval *perms )
779 for ( i = 0; i < perms->bv_len; ) {
780 switch ( perms->bv_val[ i ] ) {
789 return LDAP_INVALID_SYNTAX;
792 if ( ++i == perms->bv_len ) {
796 while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
799 assert( i != perms->bv_len );
801 if ( perms->bv_val[ i ] != ',' ) {
802 return LDAP_INVALID_SYNTAX;
807 } while ( perms->bv_val[ i ] == ' ' );
813 static const struct berval *ACIgrantdeny[] = {
814 &aci_bv[ ACI_BV_GRANT ],
815 &aci_bv[ ACI_BV_DENY ],
820 OpenLDAPaciValidateRight(
821 struct berval *action )
823 struct berval bv = BER_BVNULL;
827 if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
828 bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
830 return LDAP_INVALID_SYNTAX;
833 for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
836 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
838 return LDAP_INVALID_SYNTAX;
843 AttributeDescription *ad = NULL;
844 const char *text = NULL;
846 /* could be "[all]" or an attribute description */
847 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
851 if ( slap_bv2ad( &bv, &ad, &text ) != LDAP_SUCCESS ) {
852 return LDAP_INVALID_SYNTAX;
857 /* "perms;attr" go in pairs */
858 if ( i > 0 && ( i & 1 ) == 0 ) {
862 return LDAP_INVALID_SYNTAX;
869 OpenLDAPaciNormalizeRight(
870 struct berval *action,
871 struct berval *naction,
874 struct berval grantdeny,
881 if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
882 return LDAP_INVALID_SYNTAX;
884 idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
886 return LDAP_INVALID_SYNTAX;
889 ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
891 for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
894 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
896 return LDAP_INVALID_SYNTAX;
904 /* could be "[all]" or an attribute description */
905 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
906 bv = aci_bv[ ACI_BV_BR_ALL ];
909 AttributeDescription *ad = NULL;
910 const char *text = NULL;
913 rc = slap_bv2ad( &bv, &ad, &text );
914 if ( rc != LDAP_SUCCESS ) {
915 return LDAP_INVALID_SYNTAX;
921 naction->bv_val = ber_memrealloc_x( naction->bv_val,
922 naction->bv_len + STRLENOF( ";" )
923 + perms.bv_len + STRLENOF( ";" )
927 ptr = &naction->bv_val[ naction->bv_len ];
930 ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
933 ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
935 naction->bv_len += STRLENOF( ";" ) + perms.bv_len
936 + STRLENOF( ";" ) + bv.bv_len;
940 /* perms;attr go in pairs */
941 if ( i > 1 && ( i & 1 ) ) {
945 return LDAP_INVALID_SYNTAX;
950 OpenLDAPaciValidateRights(
951 struct berval *actions )
954 struct berval bv = BER_BVNULL;
957 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
958 if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
959 return LDAP_INVALID_SYNTAX;
967 OpenLDAPaciNormalizeRights(
968 struct berval *actions,
969 struct berval *nactions,
973 struct berval bv = BER_BVNULL;
976 BER_BVZERO( nactions );
977 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
981 rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
982 if ( rc != LDAP_SUCCESS ) {
983 ber_memfree_x( nactions->bv_val, ctx );
984 BER_BVZERO( nactions );
985 return LDAP_INVALID_SYNTAX;
992 nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
993 nactions->bv_len + STRLENOF( "$" )
996 nactions->bv_val[ nactions->bv_len ] = '$';
997 AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
998 nbv.bv_val, nbv.bv_len + 1 );
999 ber_memfree_x( nbv.bv_val, ctx );
1000 nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
1005 return LDAP_SUCCESS;
1008 static const struct berval *OpenLDAPaciscopes[] = {
1009 &aci_bv[ ACI_BV_ENTRY ],
1010 &aci_bv[ ACI_BV_CHILDREN ],
1011 &aci_bv[ ACI_BV_SUBTREE ],
1016 static const struct berval *OpenLDAPacitypes[] = {
1018 &aci_bv[ ACI_BV_GROUP ],
1019 &aci_bv[ ACI_BV_ROLE ],
1021 /* set to one past the last DN-valued type with options (/) */
1022 #define LAST_OPTIONAL 2
1024 &aci_bv[ ACI_BV_ACCESS_ID ],
1025 &aci_bv[ ACI_BV_SUBTREE ],
1026 &aci_bv[ ACI_BV_ONELEVEL ],
1027 &aci_bv[ ACI_BV_CHILDREN ],
1029 /* set to one past the last DN-valued type */
1030 #define LAST_DNVALUED 6
1033 &aci_bv[ ACI_BV_DNATTR ],
1034 &aci_bv[ ACI_BV_PUBLIC ],
1035 &aci_bv[ ACI_BV_USERS ],
1036 &aci_bv[ ACI_BV_SELF ],
1037 &aci_bv[ ACI_BV_SET ],
1038 &aci_bv[ ACI_BV_SET_REF ],
1044 OpenLDAPaciValidate(
1046 struct berval *val )
1048 struct berval oid = BER_BVNULL,
1050 rights = BER_BVNULL,
1052 subject = BER_BVNULL;
1055 if ( BER_BVISEMPTY( val ) ) {
1056 return LDAP_INVALID_SYNTAX;
1060 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1061 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1063 /* NOTE: the numericoidValidate() is rather pedantic;
1064 * I'd replace it with X-ORDERED VALUES so that
1065 * it's guaranteed values are maintained and used
1066 * in the desired order */
1067 return LDAP_INVALID_SYNTAX;
1071 if ( acl_get_part( val, 1, '#', &scope ) < 0 ||
1072 bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
1074 return LDAP_INVALID_SYNTAX;
1078 if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
1079 OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS )
1081 return LDAP_INVALID_SYNTAX;
1085 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1086 return LDAP_INVALID_SYNTAX;
1088 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1092 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1093 return LDAP_INVALID_SYNTAX;
1096 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1097 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1098 return LDAP_INVALID_SYNTAX;
1103 bv_get_tail( val, &type, &subject );
1104 if ( subject.bv_val[ 0 ] != '#' ) {
1105 return LDAP_INVALID_SYNTAX;
1108 if ( idx >= LAST_DNVALUED ) {
1109 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1110 AttributeDescription *ad = NULL;
1111 const char *text = NULL;
1114 rc = slap_bv2ad( &subject, &ad, &text );
1115 if ( rc != LDAP_SUCCESS ) {
1116 return LDAP_INVALID_SYNTAX;
1119 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1120 /* FIXME: allow nameAndOptionalUID? */
1121 return LDAP_INVALID_SYNTAX;
1126 return LDAP_SUCCESS;
1128 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1129 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1131 /* do {group|role}/oc/at check */
1132 struct berval ocbv = BER_BVNULL,
1135 ocbv.bv_val = strchr( type.bv_val, '/' );
1136 if ( ocbv.bv_val != NULL ) {
1139 atbv.bv_val = strchr( ocbv.bv_val, '/' );
1140 if ( atbv.bv_val != NULL ) {
1141 AttributeDescription *ad = NULL;
1142 const char *text = NULL;
1146 atbv.bv_len = type.bv_len
1147 - ( atbv.bv_val - type.bv_val );
1148 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1150 rc = slap_bv2ad( &atbv, &ad, &text );
1151 if ( rc != LDAP_SUCCESS ) {
1152 return LDAP_INVALID_SYNTAX;
1156 ocbv.bv_len = type.bv_len
1157 - ( ocbv.bv_val - type.bv_val );
1160 if ( oc_bvfind( &ocbv ) == NULL ) {
1161 return LDAP_INVALID_SYNTAX;
1166 if ( BER_BVISEMPTY( &subject ) ) {
1167 /* empty DN invalid */
1168 return LDAP_INVALID_SYNTAX;
1174 /* FIXME: pass DN syntax? */
1175 return dnValidate( NULL, &subject );
1179 OpenLDAPaciPrettyNormal(
1185 struct berval oid = BER_BVNULL,
1187 rights = BER_BVNULL,
1188 nrights = BER_BVNULL,
1191 subject = BER_BVNULL,
1192 nsubject = BER_BVNULL;
1199 if ( BER_BVISEMPTY( val ) ) {
1200 return LDAP_INVALID_SYNTAX;
1203 /* oid: if valid, it's already normalized */
1204 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1205 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1207 return LDAP_INVALID_SYNTAX;
1210 /* scope: normalize by replacing with OpenLDAPaciscopes */
1211 if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
1212 return LDAP_INVALID_SYNTAX;
1214 idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
1216 return LDAP_INVALID_SYNTAX;
1218 scope = *OpenLDAPaciscopes[ idx ];
1221 if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
1222 return LDAP_INVALID_SYNTAX;
1224 if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
1227 return LDAP_INVALID_SYNTAX;
1231 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1232 rc = LDAP_INVALID_SYNTAX;
1235 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1239 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1240 rc = LDAP_INVALID_SYNTAX;
1244 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1245 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1246 rc = LDAP_INVALID_SYNTAX;
1250 ntype = *OpenLDAPacitypes[ idx ];
1253 bv_get_tail( val, &type, &subject );
1255 if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
1256 rc = LDAP_INVALID_SYNTAX;
1263 if ( idx < LAST_DNVALUED ) {
1264 /* FIXME: pass DN syntax? */
1266 rc = dnNormalize( 0, NULL, NULL,
1267 &subject, &nsubject, ctx );
1269 rc = dnPretty( NULL, &subject, &nsubject, ctx );
1272 if ( rc == LDAP_SUCCESS ) {
1279 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1280 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1282 /* do {group|role}/oc/at check */
1283 struct berval ocbv = BER_BVNULL,
1286 ocbv.bv_val = strchr( type.bv_val, '/' );
1287 if ( ocbv.bv_val != NULL ) {
1288 ObjectClass *oc = NULL;
1289 AttributeDescription *ad = NULL;
1290 const char *text = NULL;
1294 bv.bv_len = ntype.bv_len;
1298 atbv.bv_val = strchr( ocbv.bv_val, '/' );
1299 if ( atbv.bv_val != NULL ) {
1301 atbv.bv_len = type.bv_len
1302 - ( atbv.bv_val - type.bv_val );
1303 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1305 rc = slap_bv2ad( &atbv, &ad, &text );
1306 if ( rc != LDAP_SUCCESS ) {
1307 rc = LDAP_INVALID_SYNTAX;
1311 bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
1314 ocbv.bv_len = type.bv_len
1315 - ( ocbv.bv_val - type.bv_val );
1318 if ( oc_bvfind( &ocbv ) == NULL ) {
1319 rc = LDAP_INVALID_SYNTAX;
1323 bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
1324 bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
1327 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1330 ptr = lutil_strncopy( ptr,
1331 oc->soc_cname.bv_val,
1332 oc->soc_cname.bv_len );
1336 ptr = lutil_strncopy( ptr,
1337 ad->ad_cname.bv_val,
1338 ad->ad_cname.bv_len );
1347 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1348 AttributeDescription *ad = NULL;
1349 const char *text = NULL;
1352 rc = slap_bv2ad( &subject, &ad, &text );
1353 if ( rc != LDAP_SUCCESS ) {
1354 rc = LDAP_INVALID_SYNTAX;
1358 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1359 /* FIXME: allow nameAndOptionalUID? */
1360 rc = LDAP_INVALID_SYNTAX;
1364 nsubject = ad->ad_cname;
1369 oid.bv_len + STRLENOF( "#" )
1370 + scope.bv_len + STRLENOF( "#" )
1371 + rights.bv_len + STRLENOF( "#" )
1372 + ntype.bv_len + STRLENOF( "#" )
1375 out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
1376 ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
1379 ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
1382 ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
1385 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1388 if ( !BER_BVISNULL( &nsubject ) ) {
1389 ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
1394 if ( freesubject ) {
1395 ber_memfree_x( nsubject.bv_val, ctx );
1399 ber_memfree_x( ntype.bv_val, ctx );
1402 if ( !BER_BVISNULL( &nrights ) ) {
1403 ber_memfree_x( nrights.bv_val, ctx );
1416 return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
1420 OpenLDAPaciNormalize(
1428 return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
1431 #endif /* SLAPD_ACI_ENABLED */