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 /* TODO: use ber_bvcmp */
352 if ( ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
353 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
359 case SLAP_ACI_SCOPE_CHILDREN:
360 /* TODO: use ber_bvcmp */
361 if ( ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
362 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
369 /* TODO: add assertion */
373 /* get the list of permissions clauses, bail if empty */
374 if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
375 /* TODO: add assertion */
379 /* check if any permissions allow desired access */
380 if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
384 /* see if we have a DN match */
385 if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
386 /* TODO: add assertion */
390 /* see if we have a public (i.e. anonymous) access */
391 /* TODO: use ber_bvcmp */
392 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
396 /* otherwise require an identity */
397 if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
401 /* see if we have a users access */
402 /* TODO: use ber_bvcmp */
403 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
407 /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
408 * just grab all the berval up to its end (ITS#3303).
409 * NOTE: the problem could be solved by providing the DN with
410 * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
411 * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
413 if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
417 sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
418 sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
420 /* TODO: use ber_bvcmp */
421 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
424 /* TODO: don't normalize */
425 rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
426 if ( rc != LDAP_SUCCESS ) {
430 if ( dn_match( &op->o_ndn, &ndn ) ) {
433 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
437 /* TODO: use ber_bvcmp */
438 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
441 /* TODO: don't normalize */
442 rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
443 if ( rc != LDAP_SUCCESS ) {
447 if ( dnIsSuffix( &op->o_ndn, &ndn ) ) {
450 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
454 /* TODO: use ber_bvcmp */
455 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
456 struct berval ndn, pndn;
458 /* TODO: don't normalize */
459 rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
460 if ( rc != LDAP_SUCCESS ) {
464 dnParent( &ndn, &pndn );
466 if ( dn_match( &op->o_ndn, &pndn ) ) {
469 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
473 /* TODO: use ber_bvcmp */
474 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
477 /* TODO: don't normalize */
478 rc = dnNormalize( 0, NULL, NULL, &sdn, &ndn, op->o_tmpmemctx );
479 if ( rc != LDAP_SUCCESS ) {
483 if ( !dn_match( &op->o_ndn, &ndn )
484 && dnIsSuffix( &op->o_ndn, &ndn ) )
488 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
492 /* TODO: use ber_bvcmp */
493 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
494 if ( dn_match( &op->o_ndn, &e->e_nname ) ) {
498 /* TODO: use ber_bvcmp */
499 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
501 AttributeDescription *ad = NULL;
504 rc = slap_bv2ad( &sdn, &ad, &text );
505 if ( rc != LDAP_SUCCESS ) {
506 /* TODO: add assertion */
511 for ( at = attrs_find( e->e_attrs, ad );
513 at = attrs_find( at->a_next, ad ) )
515 if ( value_find_ex( ad,
516 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
517 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
519 &op->o_ndn, op->o_tmpmemctx ) == 0 )
528 /* TODO: use ber_bvcmp */
529 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
530 if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_GROUP_CLASS ],
531 &aci_bv[ ACI_BV_GROUP_ATTR ], op, e, nmatch, matches ) )
536 /* TODO: use ber_bvcmp */
537 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
538 if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_ROLE_CLASS ],
539 &aci_bv[ ACI_BV_ROLE_ATTR ], op, e, nmatch, matches ) )
544 /* TODO: use ber_bvcmp */
545 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
546 if ( acl_match_set( &sdn, op, e, 0 ) ) {
550 /* TODO: use ber_bvcmp */
551 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
552 if ( acl_match_set( &sdn, op, e, 1 ) ) {
562 * FIXME: there is a silly dependence that makes it difficult
563 * to move ACIs in a run-time loadable module under the "dynacl"
564 * umbrella, because sets share some helpers with ACIs.
567 dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *right, void **privp )
569 AttributeDescription *ad = NULL;
570 const char *text = NULL;
572 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
573 fprintf( stderr, "%s: line %d: "
574 "inappropriate style \"%s\" in \"aci\" by clause\n",
575 fname, lineno, style_strings[sty] );
579 if ( right != NULL && *right != '\0' ) {
580 if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
582 "%s: line %d: aci \"%s\": %s\n",
583 fname, lineno, right, text );
588 ad = slap_schema.si_ad_aci;
591 if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
592 fprintf( stderr, "%s: line %d: "
593 "aci \"%s\": inappropriate syntax: %s\n",
594 fname, lineno, right,
595 ad->ad_type->sat_syntax_oid );
605 dynacl_aci_unparse( void *priv, struct berval *bv )
607 AttributeDescription *ad = ( AttributeDescription * )priv;
610 assert( ad != NULL );
612 bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
613 ptr = lutil_strcopy( bv->bv_val, " aci=" );
614 ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
615 bv->bv_len = ptr - bv->bv_val;
625 AttributeDescription *desc,
629 slap_access_t *grantp,
630 slap_access_t *denyp )
632 AttributeDescription *ad = ( AttributeDescription * )priv;
634 slap_access_t tgrant, tdeny, grant, deny;
636 char accessmaskbuf[ACCESSMASK_MAXLEN];
637 char accessmaskbuf1[ACCESSMASK_MAXLEN];
638 #endif /* LDAP_DEBUG */
640 /* start out with nothing granted, nothing denied */
644 /* get the aci attribute */
645 at = attr_find( e->e_attrs, ad );
649 /* the aci is an multi-valued attribute. The
650 * rights are determined by OR'ing the individual
651 * rights given by the acis.
653 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
654 if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
655 nmatch, matches, &grant, &deny,
656 SLAP_ACI_SCOPE_ENTRY ) != 0 )
663 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
664 accessmask2str( tgrant, accessmaskbuf, 1 ),
665 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
668 /* If the entry level aci didn't contain anything valid for the
669 * current operation, climb up the tree and evaluate the
670 * acis with scope set to subtree
672 if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
673 struct berval parent_ndn;
676 /* to solve the chicken'n'egg problem of accessing
677 * the OpenLDAPaci attribute, the direct access
678 * to the entry's attribute is unchecked; however,
679 * further accesses to OpenLDAPaci values in the
680 * ancestors occur through backend_attribute(), i.e.
681 * with the identity of the operation, requiring
682 * further access checking. For uniformity, this
683 * makes further requests occur as the rootdn, if
684 * any, i.e. searching for the OpenLDAPaci attribute
685 * is considered an internal search. If this is not
686 * acceptable, then the same check needs be performed
687 * when accessing the entry's attribute. */
690 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
691 op2.o_dn = op->o_bd->be_rootdn;
692 op2.o_ndn = op->o_bd->be_rootndn;
696 dnParent( &e->e_nname, &parent_ndn );
697 while ( !BER_BVISEMPTY( &parent_ndn ) ){
699 BerVarray bvals = NULL;
702 Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
703 ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
712 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
713 if ( aci_mask( op, e, desc, val,
717 SLAP_ACI_SCOPE_CHILDREN ) != 0 )
721 /* evaluation stops as soon as either a "deny" or a
722 * "grant" directive matches.
724 if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
728 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
729 accessmask2str( tgrant, accessmaskbuf, 1 ),
730 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
734 case LDAP_NO_SUCH_ATTRIBUTE:
735 /* just go on if the aci-Attribute is not present in
738 Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
742 case LDAP_NO_SUCH_OBJECT:
743 /* We have reached the base object */
744 Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
756 dnParent( &parent_ndn, &parent_ndn );
766 /* need to register this at some point */
767 static slap_dynacl_t dynacl_aci = {
778 dynacl_aci_init( void )
780 return slap_dynacl_register( &dynacl_aci );
783 #endif /* SLAP_DYNACL */
785 /* ACI syntax validation */
788 * Matches given berval to array of bervals
790 * >=0 if one if the array elements equals to this berval
791 * -1 if string was not found in array
796 const struct berval *arr[] )
800 if ( BER_BVISEMPTY( bv ) ) {
804 for ( i = 0; arr[ i ] != NULL ; i++ ) {
805 if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
814 /* Returns what have left in input berval after current sub */
819 struct berval *tail )
823 tail->bv_val = sub->bv_val + sub->bv_len;
824 head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
825 tail->bv_len = val->bv_len - head_len;
830 * aci is accepted in following form:
831 * oid#scope#rights#type#subject
834 * scope := entry|children
835 * rights := right[[$right]...]
836 * right := (grant|deny);action
837 * action := perms;attr[[;perms;attr]...]
838 * perms := perm[[,perm]...]
840 * attr := attributeType|[all]
841 * type := public|users|self|dnattr|group|role|set|set-ref|
842 * access_id|subtree|onelevel|children
845 OpenLDAPaciValidatePerms(
846 struct berval *perms )
850 for ( i = 0; i < perms->bv_len; ) {
851 switch ( perms->bv_val[ i ] ) {
860 return LDAP_INVALID_SYNTAX;
863 if ( ++i == perms->bv_len ) {
867 while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
870 assert( i != perms->bv_len );
872 if ( perms->bv_val[ i ] != ',' ) {
873 return LDAP_INVALID_SYNTAX;
878 } while ( perms->bv_val[ i ] == ' ' );
884 static const struct berval *ACIgrantdeny[] = {
885 &aci_bv[ ACI_BV_GRANT ],
886 &aci_bv[ ACI_BV_DENY ],
891 OpenLDAPaciValidateRight(
892 struct berval *action )
894 struct berval bv = BER_BVNULL;
898 if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
899 bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
901 return LDAP_INVALID_SYNTAX;
904 for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
907 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
909 return LDAP_INVALID_SYNTAX;
914 AttributeDescription *ad = NULL;
915 const char *text = NULL;
917 /* could be "[all]" or an attribute description */
918 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
922 if ( slap_bv2ad( &bv, &ad, &text ) != LDAP_SUCCESS ) {
923 return LDAP_INVALID_SYNTAX;
928 /* "perms;attr" go in pairs */
929 if ( i > 0 && ( i & 1 ) == 0 ) {
933 return LDAP_INVALID_SYNTAX;
940 OpenLDAPaciNormalizeRight(
941 struct berval *action,
942 struct berval *naction,
945 struct berval grantdeny,
952 if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
953 return LDAP_INVALID_SYNTAX;
955 idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
957 return LDAP_INVALID_SYNTAX;
960 ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
962 for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
965 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
967 return LDAP_INVALID_SYNTAX;
975 /* could be "[all]" or an attribute description */
976 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
977 bv = aci_bv[ ACI_BV_BR_ALL ];
980 AttributeDescription *ad = NULL;
981 const char *text = NULL;
984 rc = slap_bv2ad( &bv, &ad, &text );
985 if ( rc != LDAP_SUCCESS ) {
986 return LDAP_INVALID_SYNTAX;
992 naction->bv_val = ber_memrealloc_x( naction->bv_val,
993 naction->bv_len + STRLENOF( ";" )
994 + perms.bv_len + STRLENOF( ";" )
998 ptr = &naction->bv_val[ naction->bv_len ];
1001 ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
1004 ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
1006 naction->bv_len += STRLENOF( ";" ) + perms.bv_len
1007 + STRLENOF( ";" ) + bv.bv_len;
1011 /* perms;attr go in pairs */
1012 if ( i > 1 && ( i & 1 ) ) {
1013 return LDAP_SUCCESS;
1016 return LDAP_INVALID_SYNTAX;
1021 OpenLDAPaciValidateRights(
1022 struct berval *actions )
1025 struct berval bv = BER_BVNULL;
1028 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1029 if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
1030 return LDAP_INVALID_SYNTAX;
1034 return LDAP_SUCCESS;
1038 OpenLDAPaciNormalizeRights(
1039 struct berval *actions,
1040 struct berval *nactions,
1044 struct berval bv = BER_BVNULL;
1047 BER_BVZERO( nactions );
1048 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1052 rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
1053 if ( rc != LDAP_SUCCESS ) {
1054 ber_memfree_x( nactions->bv_val, ctx );
1055 BER_BVZERO( nactions );
1056 return LDAP_INVALID_SYNTAX;
1063 nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
1064 nactions->bv_len + STRLENOF( "$" )
1067 nactions->bv_val[ nactions->bv_len ] = '$';
1068 AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
1069 nbv.bv_val, nbv.bv_len + 1 );
1070 ber_memfree_x( nbv.bv_val, ctx );
1071 nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
1076 return LDAP_SUCCESS;
1079 static const struct berval *OpenLDAPaciscopes[] = {
1080 &aci_bv[ ACI_BV_ENTRY ],
1081 &aci_bv[ ACI_BV_CHILDREN ],
1086 static const struct berval *OpenLDAPacitypes[] = {
1088 &aci_bv[ ACI_BV_GROUP ],
1089 &aci_bv[ ACI_BV_ROLE ],
1091 /* set to one past the last DN-valued type with options (/) */
1092 #define LAST_OPTIONAL 2
1094 &aci_bv[ ACI_BV_ACCESS_ID ],
1095 &aci_bv[ ACI_BV_SUBTREE ],
1096 &aci_bv[ ACI_BV_ONELEVEL ],
1097 &aci_bv[ ACI_BV_CHILDREN ],
1099 /* set to one past the last DN-valued type */
1100 #define LAST_DNVALUED 6
1103 &aci_bv[ ACI_BV_DNATTR ],
1104 &aci_bv[ ACI_BV_PUBLIC ],
1105 &aci_bv[ ACI_BV_USERS ],
1106 &aci_bv[ ACI_BV_SELF ],
1107 &aci_bv[ ACI_BV_SET ],
1108 &aci_bv[ ACI_BV_SET_REF ],
1114 OpenLDAPaciValidate(
1116 struct berval *val )
1118 struct berval oid = BER_BVNULL,
1120 rights = BER_BVNULL,
1122 subject = BER_BVNULL;
1125 if ( BER_BVISEMPTY( val ) ) {
1126 return LDAP_INVALID_SYNTAX;
1130 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1131 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1133 /* NOTE: the numericoidValidate() is rather pedantic;
1134 * I'd replace it with X-ORDERED VALUES so that
1135 * it's guaranteed values are maintained and used
1136 * in the desired order */
1137 return LDAP_INVALID_SYNTAX;
1141 if ( acl_get_part( val, 1, '#', &scope ) < 0 ||
1142 bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
1144 return LDAP_INVALID_SYNTAX;
1148 if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
1149 OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS )
1151 return LDAP_INVALID_SYNTAX;
1155 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1156 return LDAP_INVALID_SYNTAX;
1158 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1162 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1163 return LDAP_INVALID_SYNTAX;
1166 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1167 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1168 return LDAP_INVALID_SYNTAX;
1173 bv_get_tail( val, &type, &subject );
1174 if ( subject.bv_val[ 0 ] != '#' ) {
1175 return LDAP_INVALID_SYNTAX;
1178 if ( idx >= LAST_DNVALUED ) {
1179 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1180 AttributeDescription *ad = NULL;
1181 const char *text = NULL;
1184 rc = slap_bv2ad( &subject, &ad, &text );
1185 if ( rc != LDAP_SUCCESS ) {
1186 return LDAP_INVALID_SYNTAX;
1189 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1190 /* FIXME: allow nameAndOptionalUID? */
1191 return LDAP_INVALID_SYNTAX;
1196 return LDAP_SUCCESS;
1198 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1199 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1201 /* do {group|role}/oc/at check */
1202 struct berval ocbv = BER_BVNULL,
1205 ocbv.bv_val = strchr( type.bv_val, '/' );
1206 if ( ocbv.bv_val != NULL ) {
1209 atbv.bv_val = strchr( ocbv.bv_val, '/' );
1210 if ( atbv.bv_val != NULL ) {
1211 AttributeDescription *ad = NULL;
1212 const char *text = NULL;
1216 atbv.bv_len = type.bv_len
1217 - ( atbv.bv_val - type.bv_val );
1218 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1220 rc = slap_bv2ad( &atbv, &ad, &text );
1221 if ( rc != LDAP_SUCCESS ) {
1222 return LDAP_INVALID_SYNTAX;
1226 ocbv.bv_len = type.bv_len
1227 - ( ocbv.bv_val - type.bv_val );
1230 if ( oc_bvfind( &ocbv ) == NULL ) {
1231 return LDAP_INVALID_SYNTAX;
1236 if ( BER_BVISEMPTY( &subject ) ) {
1237 /* empty DN invalid */
1238 return LDAP_INVALID_SYNTAX;
1244 /* FIXME: pass DN syntax? */
1245 return dnValidate( NULL, &subject );
1249 OpenLDAPaciPrettyNormal(
1255 struct berval oid = BER_BVNULL,
1257 rights = BER_BVNULL,
1258 nrights = BER_BVNULL,
1261 subject = BER_BVNULL,
1262 nsubject = BER_BVNULL;
1269 if ( BER_BVISEMPTY( val ) ) {
1270 return LDAP_INVALID_SYNTAX;
1273 /* oid: if valid, it's already normalized */
1274 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1275 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1277 return LDAP_INVALID_SYNTAX;
1280 /* scope: normalize by replacing with OpenLDAPaciscopes */
1281 if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
1282 return LDAP_INVALID_SYNTAX;
1284 idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
1286 return LDAP_INVALID_SYNTAX;
1288 scope = *OpenLDAPaciscopes[ idx ];
1291 if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
1292 return LDAP_INVALID_SYNTAX;
1294 if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
1297 return LDAP_INVALID_SYNTAX;
1301 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1302 rc = LDAP_INVALID_SYNTAX;
1305 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1309 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1310 rc = LDAP_INVALID_SYNTAX;
1314 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1315 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1316 rc = LDAP_INVALID_SYNTAX;
1320 ntype = *OpenLDAPacitypes[ idx ];
1323 bv_get_tail( val, &type, &subject );
1325 if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
1326 rc = LDAP_INVALID_SYNTAX;
1333 if ( idx < LAST_DNVALUED ) {
1334 /* FIXME: pass DN syntax? */
1336 rc = dnNormalize( 0, NULL, NULL,
1337 &subject, &nsubject, ctx );
1339 rc = dnPretty( NULL, &subject, &nsubject, ctx );
1342 if ( rc == LDAP_SUCCESS ) {
1349 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1350 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1352 /* do {group|role}/oc/at check */
1353 struct berval ocbv = BER_BVNULL,
1356 ocbv.bv_val = strchr( type.bv_val, '/' );
1357 if ( ocbv.bv_val != NULL ) {
1358 ObjectClass *oc = NULL;
1359 AttributeDescription *ad = NULL;
1360 const char *text = NULL;
1364 bv.bv_len = ntype.bv_len;
1368 atbv.bv_val = strchr( ocbv.bv_val, '/' );
1369 if ( atbv.bv_val != NULL ) {
1371 atbv.bv_len = type.bv_len
1372 - ( atbv.bv_val - type.bv_val );
1373 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1375 rc = slap_bv2ad( &atbv, &ad, &text );
1376 if ( rc != LDAP_SUCCESS ) {
1377 rc = LDAP_INVALID_SYNTAX;
1381 bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
1384 ocbv.bv_len = type.bv_len
1385 - ( ocbv.bv_val - type.bv_val );
1388 if ( oc_bvfind( &ocbv ) == NULL ) {
1389 rc = LDAP_INVALID_SYNTAX;
1393 bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
1394 bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
1397 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1400 ptr = lutil_strncopy( ptr,
1401 oc->soc_cname.bv_val,
1402 oc->soc_cname.bv_len );
1406 ptr = lutil_strncopy( ptr,
1407 ad->ad_cname.bv_val,
1408 ad->ad_cname.bv_len );
1417 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1418 AttributeDescription *ad = NULL;
1419 const char *text = NULL;
1422 rc = slap_bv2ad( &subject, &ad, &text );
1423 if ( rc != LDAP_SUCCESS ) {
1424 rc = LDAP_INVALID_SYNTAX;
1428 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1429 /* FIXME: allow nameAndOptionalUID? */
1430 rc = LDAP_INVALID_SYNTAX;
1434 nsubject = ad->ad_cname;
1439 oid.bv_len + STRLENOF( "#" )
1440 + scope.bv_len + STRLENOF( "#" )
1441 + rights.bv_len + STRLENOF( "#" )
1442 + ntype.bv_len + STRLENOF( "#" )
1445 out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
1446 ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
1449 ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
1452 ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
1455 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1458 if ( !BER_BVISNULL( &nsubject ) ) {
1459 ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
1464 if ( freesubject ) {
1465 ber_memfree_x( nsubject.bv_val, ctx );
1469 ber_memfree_x( ntype.bv_val, ctx );
1472 if ( !BER_BVISNULL( &nrights ) ) {
1473 ber_memfree_x( nrights.bv_val, ctx );
1486 return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
1490 OpenLDAPaciNormalize(
1498 return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
1501 #endif /* SLAPD_ACI_ENABLED */