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 */
47 #endif /* SLAP_DYNACL */
48 AttributeDescription *slap_ad_aci;
71 #define OpenLDAPaciMatch octetStringMatch
82 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
83 if ( bv.bv_len <= 0 ) {
87 switch ( *bv.bv_val ) {
89 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
90 * define any equivalent to the AUTH right, so I've just used
93 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
96 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
97 * the right 'd' to mean "delete"; we hijack it to mean
98 * "disclose" for consistency wuith the rest of slapd.
100 ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
103 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
106 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
107 * the right 's' to mean "set", but in the examples states
108 * that the right 's' means "search". The latter definition
111 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
114 ACL_PRIV_SET(mask, ACL_PRIV_READ);
117 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
131 const struct berval *attr,
134 struct berval bv, left, right;
137 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
138 if ( acl_get_part(&bv, 0, '=', &left ) < 0
139 || acl_get_part( &bv, 1, '=', &right ) < 0 )
141 if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
145 } else if ( val == NULL ) {
146 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
151 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
152 /* FIXME: this is also totally undocumented! */
153 /* this is experimental code that implements a
154 * simple (prefix) match of the attribute value.
155 * the ACI draft does not provide for aci's that
156 * apply to specific values, but it would be
157 * nice to have. If the <attr> part of an aci's
158 * rights list is of the form <attr>=<value>,
159 * that means the aci applies only to attrs with
160 * the given value. Furthermore, if the attr is
161 * of the form <attr>=<value>*, then <value> is
162 * treated as a prefix, and the aci applies to
163 * any value with that prefix.
165 * Ideally, this would allow r.e. matches.
167 if ( acl_get_part( &right, 0, '*', &left ) < 0
168 || right.bv_len <= left.bv_len )
170 if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
174 } else if ( val->bv_len >= left.bv_len ) {
175 if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) {
187 aci_list_get_attr_rights(
189 const struct berval *attr,
196 /* loop through each rights/attr pair, skip first part (action) */
198 for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
199 if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
203 if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
207 mask |= aci_list_map_rights( &bv );
216 const struct berval *attr,
218 slap_access_t *grant,
219 slap_access_t *deny )
221 struct berval perm, actn;
225 if ( attr == NULL || BER_BVISEMPTY( attr )
226 || ber_bvstrcasecmp( attr, &aci_bv[ ACI_BV_ENTRY ] ) == 0 )
228 attr = &aci_bv[ ACI_BV_BR_ENTRY ];
234 /* loop through each permissions clause */
235 for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
236 if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
240 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
243 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
251 *mask |= aci_list_get_attr_rights( &perm, attr, val );
252 *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
261 const struct berval *defgrpoc,
262 const struct berval *defgrpat,
269 struct berval subjdn;
272 ObjectClass *grp_oc = NULL;
273 AttributeDescription *grp_ad = NULL;
277 /* format of string is "{group|role}/objectClassValue/groupAttrName" */
278 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
282 if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
286 if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
290 rc = slap_bv2ad( &grpat, &grp_ad, &text );
291 if ( rc != LDAP_SUCCESS ) {
297 grp_oc = oc_bvfind( &grpoc );
299 if ( grp_oc != NULL && grp_ad != NULL ) {
300 char buf[ ACI_BUF_SIZE ];
301 struct berval bv, ndn;
303 bv.bv_len = sizeof( buf ) - 1;
304 bv.bv_val = (char *)&buf;
305 if ( acl_string_expand( &bv, &subjdn,
306 e->e_ndn, nmatch, matches ) )
312 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
314 rc = ( backend_group( op, e, &ndn, &op->o_ndn,
315 grp_oc, grp_ad ) == 0 );
316 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
328 AttributeDescription *desc,
333 slap_access_t *grant,
335 slap_aci_scope_t asserted_scope )
346 assert( !BER_BVISNULL( &desc->ad_cname ) );
348 /* parse an aci of the form:
349 oid # scope # action;rights;attr;rights;attr
350 $ action;rights;attr;rights;attr # type # subject
352 [NOTE: the following comment is very outdated,
353 as the draft version it refers to (Ando, 2004-11-20)].
355 See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
356 a full description of the format for this attribute.
357 Differences: "this" in the draft is "self" here, and
358 "self" and "public" is in the position of type.
360 <scope> = {entry|children|subtree}
361 <type> = {public|users|access-id|subtree|onelevel|children|
362 self|dnattr|group|role|set|set-ref}
364 This routine now supports scope={ENTRY,CHILDREN}
366 - ENTRY applies to "entry" and "subtree";
367 - CHILDREN aplies to "children" and "subtree"
370 /* check that the aci has all 5 components */
371 if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
375 /* check that the aci family is supported */
376 /* FIXME: the OID is ignored? */
377 if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
381 /* check that the scope matches */
382 if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
386 /* note: scope can be either ENTRY or CHILDREN;
387 * they respectively match "entry" and "children" in bv
388 * both match "subtree" */
389 switch ( asserted_scope ) {
390 case SLAP_ACI_SCOPE_ENTRY:
391 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
392 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
398 case SLAP_ACI_SCOPE_CHILDREN:
399 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
400 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
406 case SLAP_ACI_SCOPE_SUBTREE:
407 /* TODO: add assertion? */
411 /* get the list of permissions clauses, bail if empty */
412 if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
417 /* check if any permissions allow desired access */
418 if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
422 /* see if we have a DN match */
423 if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
428 /* see if we have a public (i.e. anonymous) access */
429 if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
433 /* otherwise require an identity */
434 if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
438 /* see if we have a users access */
439 if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
443 /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
444 * just grab all the berval up to its end (ITS#3303).
445 * NOTE: the problem could be solved by providing the DN with
446 * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
447 * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
449 if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
453 sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
454 sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
456 /* get the type options, if any */
457 if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) {
458 opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val );
459 type.bv_len = opts.bv_val - type.bv_val - 1;
465 if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
466 return dn_match( &op->o_ndn, &sdn );
468 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
469 return dnIsSuffix( &op->o_ndn, &sdn );
471 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
474 dnParent( &sdn, &pdn );
476 return dn_match( &op->o_ndn, &pdn );
478 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
479 return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
481 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
482 return dn_match( &op->o_ndn, &e->e_nname );
484 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
486 AttributeDescription *ad = NULL;
489 rc = slap_bv2ad( &sdn, &ad, &text );
490 assert( rc == LDAP_SUCCESS );
493 for ( at = attrs_find( e->e_attrs, ad );
495 at = attrs_find( at->a_next, ad ) )
497 if ( value_find_ex( ad,
498 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
499 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
501 &op->o_ndn, op->o_tmpmemctx ) == 0 )
510 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
514 if ( BER_BVISNULL( &opts ) ) {
515 oc = aci_bv[ ACI_BV_GROUP_CLASS ];
516 at = aci_bv[ ACI_BV_GROUP_ATTR ];
519 if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
523 if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
524 at = aci_bv[ ACI_BV_GROUP_ATTR ];
528 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
533 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
537 if ( BER_BVISNULL( &opts ) ) {
538 oc = aci_bv[ ACI_BV_ROLE_CLASS ];
539 at = aci_bv[ ACI_BV_ROLE_ATTR ];
542 if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
546 if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
547 at = aci_bv[ ACI_BV_ROLE_ATTR ];
551 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
556 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
557 if ( acl_match_set( &sdn, op, e, 0 ) ) {
561 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
562 if ( acl_match_set( &sdn, op, e, 1 ) ) {
567 /* it passed normalization! */
577 /* OpenLDAP Experimental Syntax */
578 static slap_syntax_defs_rec aci_syntax_def = {
579 "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
584 static slap_mrule_defs_rec aci_mr_def = {
585 "( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
586 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
587 SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
588 NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
596 AttributeDescription **ad;
598 "OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 "
599 "NAME 'OpenLDAPaci' "
600 "DESC 'OpenLDAP access control information (experimental)' "
601 "EQUALITY OpenLDAPaciMatch "
602 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 "
603 "USAGE directoryOperation )",
608 LDAPAttributeType *at;
614 rc = register_syntax( &aci_syntax_def );
619 /* ACI equality rule */
620 rc = register_matching_rule( &aci_mr_def );
626 at = ldap_str2attributetype( aci_at.desc,
627 &rc, &text, LDAP_SCHEMA_ALLOW_ALL );
629 Debug( LDAP_DEBUG_ANY,
630 "%s AttributeType load failed: %s %s\n",
631 aci_at.name, ldap_scherr2str( rc ), text );
635 rc = at_add( at, 0, &sat, &text );
636 if ( rc != LDAP_SUCCESS ) {
637 ldap_attributetype_free( at );
638 fprintf( stderr, "iMUX_monitor_schema_init: "
639 "AttributeType load failed: %s %s\n",
640 scherr2str( rc ), text );
645 rc = slap_str2ad( aci_at.name,
647 if ( rc != LDAP_SUCCESS ) {
648 Debug( LDAP_DEBUG_ANY,
649 "unable to find AttributeDescription "
651 aci_at.name, rc, text );
656 sat->sat_flags |= aci_at.flags;
663 * FIXME: there is a silly dependence that makes it difficult
664 * to move ACIs in a run-time loadable module under the "dynacl"
665 * umbrella, because sets share some helpers with ACIs.
676 AttributeDescription *ad = NULL;
677 const char *text = NULL;
679 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
680 fprintf( stderr, "%s: line %d: "
681 "inappropriate style \"%s\" in \"aci\" by clause\n",
682 fname, lineno, style_strings[sty] );
686 if ( right != NULL && *right != '\0' ) {
687 if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
689 "%s: line %d: aci \"%s\": %s\n",
690 fname, lineno, right, text );
698 if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
699 fprintf( stderr, "%s: line %d: "
700 "aci \"%s\": inappropriate syntax: %s\n",
701 fname, lineno, right,
702 ad->ad_type->sat_syntax_oid );
712 dynacl_aci_unparse( void *priv, struct berval *bv )
714 AttributeDescription *ad = ( AttributeDescription * )priv;
717 assert( ad != NULL );
719 bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
720 ptr = lutil_strcopy( bv->bv_val, " aci=" );
721 ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
722 bv->bv_len = ptr - bv->bv_val;
732 AttributeDescription *desc,
736 slap_access_t *grantp,
737 slap_access_t *denyp )
739 AttributeDescription *ad = ( AttributeDescription * )priv;
741 slap_access_t tgrant, tdeny, grant, deny;
743 char accessmaskbuf[ACCESSMASK_MAXLEN];
744 char accessmaskbuf1[ACCESSMASK_MAXLEN];
745 #endif /* LDAP_DEBUG */
747 /* start out with nothing granted, nothing denied */
751 /* get the aci attribute */
752 at = attr_find( e->e_attrs, ad );
756 /* the aci is an multi-valued attribute. The
757 * rights are determined by OR'ing the individual
758 * rights given by the acis.
760 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
761 if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
762 nmatch, matches, &grant, &deny,
763 SLAP_ACI_SCOPE_ENTRY ) != 0 )
770 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
771 accessmask2str( tgrant, accessmaskbuf, 1 ),
772 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
775 /* If the entry level aci didn't contain anything valid for the
776 * current operation, climb up the tree and evaluate the
777 * acis with scope set to subtree
779 if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
780 struct berval parent_ndn;
783 /* to solve the chicken'n'egg problem of accessing
784 * the OpenLDAPaci attribute, the direct access
785 * to the entry's attribute is unchecked; however,
786 * further accesses to OpenLDAPaci values in the
787 * ancestors occur through backend_attribute(), i.e.
788 * with the identity of the operation, requiring
789 * further access checking. For uniformity, this
790 * makes further requests occur as the rootdn, if
791 * any, i.e. searching for the OpenLDAPaci attribute
792 * is considered an internal search. If this is not
793 * acceptable, then the same check needs be performed
794 * when accessing the entry's attribute. */
797 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
798 op2.o_dn = op->o_bd->be_rootdn;
799 op2.o_ndn = op->o_bd->be_rootndn;
803 dnParent( &e->e_nname, &parent_ndn );
804 while ( !BER_BVISEMPTY( &parent_ndn ) ){
806 BerVarray bvals = NULL;
809 Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
810 ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
819 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
820 if ( aci_mask( op, e, desc, val,
824 SLAP_ACI_SCOPE_CHILDREN ) != 0 )
828 /* evaluation stops as soon as either a "deny" or a
829 * "grant" directive matches.
831 if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
835 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
836 accessmask2str( tgrant, accessmaskbuf, 1 ),
837 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
841 case LDAP_NO_SUCH_ATTRIBUTE:
842 /* just go on if the aci-Attribute is not present in
845 Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
849 case LDAP_NO_SUCH_OBJECT:
850 /* We have reached the base object */
851 Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
863 dnParent( &parent_ndn, &parent_ndn );
873 /* need to register this at some point */
874 static slap_dynacl_t dynacl_aci = {
885 dynacl_aci_init( void )
892 rc = slap_dynacl_register( &dynacl_aci );
898 #endif /* SLAP_DYNACL */
900 /* ACI syntax validation */
903 * Matches given berval to array of bervals
905 * >=0 if one if the array elements equals to this berval
906 * -1 if string was not found in array
911 const struct berval *arr[] )
915 if ( BER_BVISEMPTY( bv ) ) {
919 for ( i = 0; arr[ i ] != NULL ; i++ ) {
920 if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
929 /* Returns what have left in input berval after current sub */
934 struct berval *tail )
938 tail->bv_val = sub->bv_val + sub->bv_len;
939 head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
940 tail->bv_len = val->bv_len - head_len;
945 * aci is accepted in following form:
946 * oid#scope#rights#type#subject
949 * scope := entry|children
950 * rights := right[[$right]...]
951 * right := (grant|deny);action
952 * action := perms;attr[[;perms;attr]...]
953 * perms := perm[[,perm]...]
955 * attr := attributeType|[all]
956 * type := public|users|self|dnattr|group|role|set|set-ref|
957 * access_id|subtree|onelevel|children
960 OpenLDAPaciValidatePerms(
961 struct berval *perms )
965 for ( i = 0; i < perms->bv_len; ) {
966 switch ( perms->bv_val[ i ] ) {
976 return LDAP_INVALID_SYNTAX;
979 if ( ++i == perms->bv_len ) {
983 while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
986 assert( i != perms->bv_len );
988 if ( perms->bv_val[ i ] != ',' ) {
989 return LDAP_INVALID_SYNTAX;
994 } while ( perms->bv_val[ i ] == ' ' );
1000 static const struct berval *ACIgrantdeny[] = {
1001 &aci_bv[ ACI_BV_GRANT ],
1002 &aci_bv[ ACI_BV_DENY ],
1007 OpenLDAPaciValidateRight(
1008 struct berval *action )
1010 struct berval bv = BER_BVNULL;
1014 if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
1015 bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
1017 return LDAP_INVALID_SYNTAX;
1020 for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
1023 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1025 return LDAP_INVALID_SYNTAX;
1030 AttributeDescription *ad = NULL;
1031 const char *text = NULL;
1033 /* could be "[all]" or an attribute description */
1034 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1038 if ( slap_bv2ad( &bv, &ad, &text ) != LDAP_SUCCESS ) {
1039 return LDAP_INVALID_SYNTAX;
1044 /* "perms;attr" go in pairs */
1045 if ( i > 0 && ( i & 1 ) == 0 ) {
1046 return LDAP_SUCCESS;
1049 return LDAP_INVALID_SYNTAX;
1052 return LDAP_SUCCESS;
1056 OpenLDAPaciNormalizeRight(
1057 struct berval *action,
1058 struct berval *naction,
1061 struct berval grantdeny,
1068 if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
1069 return LDAP_INVALID_SYNTAX;
1071 idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
1073 return LDAP_INVALID_SYNTAX;
1076 ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
1078 for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
1081 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1083 return LDAP_INVALID_SYNTAX;
1091 /* could be "[all]" or an attribute description */
1092 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1093 bv = aci_bv[ ACI_BV_BR_ALL ];
1096 AttributeDescription *ad = NULL;
1097 const char *text = NULL;
1100 rc = slap_bv2ad( &bv, &ad, &text );
1101 if ( rc != LDAP_SUCCESS ) {
1102 return LDAP_INVALID_SYNTAX;
1108 naction->bv_val = ber_memrealloc_x( naction->bv_val,
1109 naction->bv_len + STRLENOF( ";" )
1110 + perms.bv_len + STRLENOF( ";" )
1114 ptr = &naction->bv_val[ naction->bv_len ];
1117 ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
1120 ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
1122 naction->bv_len += STRLENOF( ";" ) + perms.bv_len
1123 + STRLENOF( ";" ) + bv.bv_len;
1127 /* perms;attr go in pairs */
1128 if ( i > 1 && ( i & 1 ) ) {
1129 return LDAP_SUCCESS;
1132 return LDAP_INVALID_SYNTAX;
1137 OpenLDAPaciValidateRights(
1138 struct berval *actions )
1141 struct berval bv = BER_BVNULL;
1144 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1145 if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
1146 return LDAP_INVALID_SYNTAX;
1150 return LDAP_SUCCESS;
1154 OpenLDAPaciNormalizeRights(
1155 struct berval *actions,
1156 struct berval *nactions,
1160 struct berval bv = BER_BVNULL;
1163 BER_BVZERO( nactions );
1164 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1168 rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
1169 if ( rc != LDAP_SUCCESS ) {
1170 ber_memfree_x( nactions->bv_val, ctx );
1171 BER_BVZERO( nactions );
1172 return LDAP_INVALID_SYNTAX;
1179 nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
1180 nactions->bv_len + STRLENOF( "$" )
1183 nactions->bv_val[ nactions->bv_len ] = '$';
1184 AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
1185 nbv.bv_val, nbv.bv_len + 1 );
1186 ber_memfree_x( nbv.bv_val, ctx );
1187 nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
1192 return LDAP_SUCCESS;
1195 static const struct berval *OpenLDAPaciscopes[] = {
1196 &aci_bv[ ACI_BV_ENTRY ],
1197 &aci_bv[ ACI_BV_CHILDREN ],
1198 &aci_bv[ ACI_BV_SUBTREE ],
1203 static const struct berval *OpenLDAPacitypes[] = {
1205 &aci_bv[ ACI_BV_GROUP ],
1206 &aci_bv[ ACI_BV_ROLE ],
1208 /* set to one past the last DN-valued type with options (/) */
1209 #define LAST_OPTIONAL 2
1211 &aci_bv[ ACI_BV_ACCESS_ID ],
1212 &aci_bv[ ACI_BV_SUBTREE ],
1213 &aci_bv[ ACI_BV_ONELEVEL ],
1214 &aci_bv[ ACI_BV_CHILDREN ],
1216 /* set to one past the last DN-valued type */
1217 #define LAST_DNVALUED 6
1220 &aci_bv[ ACI_BV_DNATTR ],
1221 &aci_bv[ ACI_BV_PUBLIC ],
1222 &aci_bv[ ACI_BV_USERS ],
1223 &aci_bv[ ACI_BV_SELF ],
1224 &aci_bv[ ACI_BV_SET ],
1225 &aci_bv[ ACI_BV_SET_REF ],
1231 OpenLDAPaciValidate(
1233 struct berval *val )
1235 struct berval oid = BER_BVNULL,
1237 rights = BER_BVNULL,
1239 subject = BER_BVNULL;
1242 if ( BER_BVISEMPTY( val ) ) {
1243 return LDAP_INVALID_SYNTAX;
1247 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1248 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1250 /* NOTE: the numericoidValidate() is rather pedantic;
1251 * I'd replace it with X-ORDERED VALUES so that
1252 * it's guaranteed values are maintained and used
1253 * in the desired order */
1254 return LDAP_INVALID_SYNTAX;
1258 if ( acl_get_part( val, 1, '#', &scope ) < 0 ||
1259 bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
1261 return LDAP_INVALID_SYNTAX;
1265 if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
1266 OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS )
1268 return LDAP_INVALID_SYNTAX;
1272 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1273 return LDAP_INVALID_SYNTAX;
1275 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1279 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1280 return LDAP_INVALID_SYNTAX;
1283 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1284 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1285 return LDAP_INVALID_SYNTAX;
1290 bv_get_tail( val, &type, &subject );
1291 if ( subject.bv_val[ 0 ] != '#' ) {
1292 return LDAP_INVALID_SYNTAX;
1295 if ( idx >= LAST_DNVALUED ) {
1296 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1297 AttributeDescription *ad = NULL;
1298 const char *text = NULL;
1301 rc = slap_bv2ad( &subject, &ad, &text );
1302 if ( rc != LDAP_SUCCESS ) {
1303 return LDAP_INVALID_SYNTAX;
1306 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1307 /* FIXME: allow nameAndOptionalUID? */
1308 return LDAP_INVALID_SYNTAX;
1313 return LDAP_SUCCESS;
1315 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1316 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1318 /* do {group|role}/oc/at check */
1319 struct berval ocbv = BER_BVNULL,
1322 ocbv.bv_val = strchr( type.bv_val, '/' );
1323 if ( ocbv.bv_val != NULL
1324 && ( ocbv.bv_val - type.bv_val ) < type.bv_len )
1328 atbv.bv_val = strchr( ocbv.bv_val, '/' );
1329 if ( atbv.bv_val != NULL
1330 && ( atbv.bv_val - ocbv.bv_val ) < ocbv.bv_len )
1332 AttributeDescription *ad = NULL;
1333 const char *text = NULL;
1337 atbv.bv_len = type.bv_len
1338 - ( atbv.bv_val - type.bv_val );
1339 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1341 rc = slap_bv2ad( &atbv, &ad, &text );
1342 if ( rc != LDAP_SUCCESS ) {
1343 return LDAP_INVALID_SYNTAX;
1347 ocbv.bv_len = type.bv_len
1348 - ( ocbv.bv_val - type.bv_val );
1351 if ( oc_bvfind( &ocbv ) == NULL ) {
1352 return LDAP_INVALID_SYNTAX;
1357 if ( BER_BVISEMPTY( &subject ) ) {
1358 /* empty DN invalid */
1359 return LDAP_INVALID_SYNTAX;
1365 /* FIXME: pass DN syntax? */
1366 return dnValidate( NULL, &subject );
1370 OpenLDAPaciPrettyNormal(
1376 struct berval oid = BER_BVNULL,
1378 rights = BER_BVNULL,
1379 nrights = BER_BVNULL,
1382 subject = BER_BVNULL,
1383 nsubject = BER_BVNULL;
1390 if ( BER_BVISEMPTY( val ) ) {
1391 return LDAP_INVALID_SYNTAX;
1394 /* oid: if valid, it's already normalized */
1395 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1396 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1398 return LDAP_INVALID_SYNTAX;
1401 /* scope: normalize by replacing with OpenLDAPaciscopes */
1402 if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
1403 return LDAP_INVALID_SYNTAX;
1405 idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
1407 return LDAP_INVALID_SYNTAX;
1409 scope = *OpenLDAPaciscopes[ idx ];
1412 if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
1413 return LDAP_INVALID_SYNTAX;
1415 if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
1418 return LDAP_INVALID_SYNTAX;
1422 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1423 rc = LDAP_INVALID_SYNTAX;
1426 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1430 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1431 rc = LDAP_INVALID_SYNTAX;
1435 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1436 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1437 rc = LDAP_INVALID_SYNTAX;
1441 ntype = *OpenLDAPacitypes[ idx ];
1444 bv_get_tail( val, &type, &subject );
1446 if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
1447 rc = LDAP_INVALID_SYNTAX;
1454 if ( idx < LAST_DNVALUED ) {
1455 /* FIXME: pass DN syntax? */
1457 rc = dnNormalize( 0, NULL, NULL,
1458 &subject, &nsubject, ctx );
1460 rc = dnPretty( NULL, &subject, &nsubject, ctx );
1463 if ( rc == LDAP_SUCCESS ) {
1470 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1471 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1473 /* do {group|role}/oc/at check */
1474 struct berval ocbv = BER_BVNULL,
1477 ocbv.bv_val = strchr( type.bv_val, '/' );
1478 if ( ocbv.bv_val != NULL
1479 && ( ocbv.bv_val - type.bv_val ) < type.bv_len )
1481 ObjectClass *oc = NULL;
1482 AttributeDescription *ad = NULL;
1483 const char *text = NULL;
1487 bv.bv_len = ntype.bv_len;
1491 atbv.bv_val = strchr( ocbv.bv_val, '/' );
1492 if ( atbv.bv_val != NULL
1493 && ( atbv.bv_val - ocbv.bv_val ) < ocbv.bv_len )
1496 atbv.bv_len = type.bv_len
1497 - ( atbv.bv_val - type.bv_val );
1498 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1500 rc = slap_bv2ad( &atbv, &ad, &text );
1501 if ( rc != LDAP_SUCCESS ) {
1502 rc = LDAP_INVALID_SYNTAX;
1506 bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
1509 ocbv.bv_len = type.bv_len
1510 - ( ocbv.bv_val - type.bv_val );
1513 oc = oc_bvfind( &ocbv );
1515 rc = LDAP_INVALID_SYNTAX;
1519 bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
1520 bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
1523 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1526 ptr = lutil_strncopy( ptr,
1527 oc->soc_cname.bv_val,
1528 oc->soc_cname.bv_len );
1532 ptr = lutil_strncopy( ptr,
1533 ad->ad_cname.bv_val,
1534 ad->ad_cname.bv_len );
1543 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1544 AttributeDescription *ad = NULL;
1545 const char *text = NULL;
1548 rc = slap_bv2ad( &subject, &ad, &text );
1549 if ( rc != LDAP_SUCCESS ) {
1550 rc = LDAP_INVALID_SYNTAX;
1554 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1555 /* FIXME: allow nameAndOptionalUID? */
1556 rc = LDAP_INVALID_SYNTAX;
1560 nsubject = ad->ad_cname;
1565 oid.bv_len + STRLENOF( "#" )
1566 + scope.bv_len + STRLENOF( "#" )
1567 + rights.bv_len + STRLENOF( "#" )
1568 + ntype.bv_len + STRLENOF( "#" )
1571 out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
1572 ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
1575 ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
1578 ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
1581 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1584 if ( !BER_BVISNULL( &nsubject ) ) {
1585 ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
1590 if ( freesubject ) {
1591 ber_memfree_x( nsubject.bv_val, ctx );
1595 ber_memfree_x( ntype.bv_val, ctx );
1598 if ( !BER_BVISNULL( &nrights ) ) {
1599 ber_memfree_x( nrights.bv_val, ctx );
1612 return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
1616 OpenLDAPaciNormalize(
1624 return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
1627 #if SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC
1629 * FIXME: need config and Makefile.am code to ease building
1633 init_module( int argc, char *argv[] )
1635 return slap_dynacl_register();
1637 #endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */
1639 #endif /* SLAPD_ACI_ENABLED */