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 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
92 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
93 * the right 's' to mean "set", but in the examples states
94 * that the right 's' means "search". The latter definition
97 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
100 ACL_PRIV_SET(mask, ACL_PRIV_READ);
103 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
106 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
107 * define any equivalent to the AUTH right, so I've just used
110 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
124 const struct berval *attr,
127 struct berval bv, left, right;
130 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
131 if ( acl_get_part(&bv, 0, '=', &left ) < 0
132 || acl_get_part( &bv, 1, '=', &right ) < 0 )
134 if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
138 } else if ( val == NULL ) {
139 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
144 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
145 /* FIXME: this is also totally undocumented! */
146 /* this is experimental code that implements a
147 * simple (prefix) match of the attribute value.
148 * the ACI draft does not provide for aci's that
149 * apply to specific values, but it would be
150 * nice to have. If the <attr> part of an aci's
151 * rights list is of the form <attr>=<value>,
152 * that means the aci applies only to attrs with
153 * the given value. Furthermore, if the attr is
154 * of the form <attr>=<value>*, then <value> is
155 * treated as a prefix, and the aci applies to
156 * any value with that prefix.
158 * Ideally, this would allow r.e. matches.
160 if ( acl_get_part( &right, 0, '*', &left ) < 0
161 || right.bv_len <= left.bv_len )
163 if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
167 } else if ( val->bv_len >= left.bv_len ) {
168 if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) {
180 aci_list_get_attr_rights(
182 const struct berval *attr,
189 /* loop through each rights/attr pair, skip first part (action) */
191 for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
192 if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
196 if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
200 mask |= aci_list_map_rights( &bv );
209 const struct berval *attr,
211 slap_access_t *grant,
212 slap_access_t *deny )
214 struct berval perm, actn;
218 if ( attr == NULL || BER_BVISEMPTY( attr )
219 || ber_bvstrcasecmp( attr, &aci_bv[ ACI_BV_ENTRY ] ) == 0 )
221 attr = &aci_bv[ ACI_BV_BR_ENTRY ];
227 /* loop through each permissions clause */
228 for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
229 if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
233 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
236 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
244 *mask |= aci_list_get_attr_rights( &perm, attr, val );
245 *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
254 const struct berval *defgrpoc,
255 const struct berval *defgrpat,
262 struct berval subjdn;
265 ObjectClass *grp_oc = NULL;
266 AttributeDescription *grp_ad = NULL;
270 /* format of string is "group/objectClassValue/groupAttrName" */
271 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
275 if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
279 if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
283 rc = slap_bv2ad( &grpat, &grp_ad, &text );
284 if ( rc != LDAP_SUCCESS ) {
290 grp_oc = oc_bvfind( &grpoc );
292 if ( grp_oc != NULL && grp_ad != NULL ) {
293 char buf[ ACI_BUF_SIZE ];
294 struct berval bv, ndn;
296 bv.bv_len = sizeof( buf ) - 1;
297 bv.bv_val = (char *)&buf;
298 if ( acl_string_expand( &bv, &subjdn,
299 e->e_ndn, nmatch, matches ) )
305 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
307 rc = ( backend_group( op, e, &ndn, &op->o_ndn,
308 grp_oc, grp_ad ) == 0 );
309 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
321 AttributeDescription *desc,
326 slap_access_t *grant,
328 slap_aci_scope_t asserted_scope )
330 struct berval bv, scope, perms, type, sdn;
334 assert( !BER_BVISNULL( &desc->ad_cname ) );
336 /* parse an aci of the form:
337 oid # scope # action;rights;attr;rights;attr
338 $ action;rights;attr;rights;attr # type # subject
340 [NOTE: the following comment is very outdated,
341 as the draft version it refers to (Ando, 2004-11-20)].
343 See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
344 a full description of the format for this attribute.
345 Differences: "this" in the draft is "self" here, and
346 "self" and "public" is in the position of type.
348 <scope> = {entry|children|subtree}
349 <type> = {public|users|access-id|subtree|onelevel|children|
350 self|dnattr|group|role|set|set-ref}
352 This routine now supports scope={ENTRY,CHILDREN}
354 - ENTRY applies to "entry" and "subtree";
355 - CHILDREN aplies to "children" and "subtree"
358 /* check that the aci has all 5 components */
359 if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
363 /* check that the aci family is supported */
364 /* FIXME: the OID is ignored? */
365 if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
369 /* check that the scope matches */
370 if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
374 /* note: scope can be either ENTRY or CHILDREN;
375 * they respectively match "entry" and "children" in bv
376 * both match "subtree" */
377 switch ( asserted_scope ) {
378 case SLAP_ACI_SCOPE_ENTRY:
379 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
380 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
386 case SLAP_ACI_SCOPE_CHILDREN:
387 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
388 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
394 case SLAP_ACI_SCOPE_SUBTREE:
395 /* TODO: add assertion? */
399 /* get the list of permissions clauses, bail if empty */
400 if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
405 /* check if any permissions allow desired access */
406 if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
410 /* see if we have a DN match */
411 if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
416 /* see if we have a public (i.e. anonymous) access */
417 if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
421 /* otherwise require an identity */
422 if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
426 /* see if we have a users access */
427 if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
431 /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
432 * just grab all the berval up to its end (ITS#3303).
433 * NOTE: the problem could be solved by providing the DN with
434 * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
435 * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
437 if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
441 sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
442 sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
444 if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
445 return dn_match( &op->o_ndn, &sdn );
447 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
448 return dnIsSuffix( &op->o_ndn, &sdn );
450 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
453 dnParent( &sdn, &pdn );
455 return dn_match( &op->o_ndn, &pdn );
457 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
458 return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
460 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
461 return dn_match( &op->o_ndn, &e->e_nname );
463 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
465 AttributeDescription *ad = NULL;
468 rc = slap_bv2ad( &sdn, &ad, &text );
469 assert( rc == LDAP_SUCCESS );
472 for ( at = attrs_find( e->e_attrs, ad );
474 at = attrs_find( at->a_next, ad ) )
476 if ( value_find_ex( ad,
477 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
478 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
480 &op->o_ndn, op->o_tmpmemctx ) == 0 )
489 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
490 if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_GROUP_CLASS ],
491 &aci_bv[ ACI_BV_GROUP_ATTR ], op, e, nmatch, matches ) )
496 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
497 if ( aci_group_member( &sdn, &aci_bv[ ACI_BV_ROLE_CLASS ],
498 &aci_bv[ ACI_BV_ROLE_ATTR ], op, e, nmatch, matches ) )
503 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
504 if ( acl_match_set( &sdn, op, e, 0 ) ) {
508 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
509 if ( acl_match_set( &sdn, op, e, 1 ) ) {
520 /* OpenLDAP Experimental Syntax */
521 static slap_syntax_defs_rec aci_syntax_def = {
522 "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
527 static slap_mrule_defs_rec aci_mr_def = {
528 "( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
529 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
530 SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
531 NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
539 AttributeDescription **ad;
541 "OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 "
542 "NAME 'OpenLDAPaci' "
543 "DESC 'OpenLDAP access control information (experimental)' "
544 "EQUALITY OpenLDAPaciMatch "
545 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 "
546 "USAGE directoryOperation )",
551 LDAPAttributeType *at;
557 rc = register_syntax( &aci_syntax_def );
562 /* ACI equality rule */
563 rc = register_matching_rule( &aci_mr_def );
569 at = ldap_str2attributetype( aci_at.desc,
570 &rc, &text, LDAP_SCHEMA_ALLOW_ALL );
572 Debug( LDAP_DEBUG_ANY,
573 "%s AttributeType load failed: %s %s\n",
574 aci_at.name, ldap_scherr2str( rc ), text );
578 rc = at_add( at, 0, &sat, &text );
579 if ( rc != LDAP_SUCCESS ) {
580 ldap_attributetype_free( at );
581 fprintf( stderr, "iMUX_monitor_schema_init: "
582 "AttributeType load failed: %s %s\n",
583 scherr2str( rc ), text );
588 rc = slap_str2ad( aci_at.name,
590 if ( rc != LDAP_SUCCESS ) {
591 Debug( LDAP_DEBUG_ANY,
592 "unable to find AttributeDescription "
594 aci_at.name, rc, text );
599 sat->sat_flags |= aci_at.flags;
606 * FIXME: there is a silly dependence that makes it difficult
607 * to move ACIs in a run-time loadable module under the "dynacl"
608 * umbrella, because sets share some helpers with ACIs.
611 dynacl_aci_parse( const char *fname, int lineno, slap_style_t sty, const char *right, void **privp )
613 AttributeDescription *ad = NULL;
614 const char *text = NULL;
616 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
617 fprintf( stderr, "%s: line %d: "
618 "inappropriate style \"%s\" in \"aci\" by clause\n",
619 fname, lineno, style_strings[sty] );
623 if ( right != NULL && *right != '\0' ) {
624 if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
626 "%s: line %d: aci \"%s\": %s\n",
627 fname, lineno, right, text );
635 if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
636 fprintf( stderr, "%s: line %d: "
637 "aci \"%s\": inappropriate syntax: %s\n",
638 fname, lineno, right,
639 ad->ad_type->sat_syntax_oid );
649 dynacl_aci_unparse( void *priv, struct berval *bv )
651 AttributeDescription *ad = ( AttributeDescription * )priv;
654 assert( ad != NULL );
656 bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
657 ptr = lutil_strcopy( bv->bv_val, " aci=" );
658 ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
659 bv->bv_len = ptr - bv->bv_val;
669 AttributeDescription *desc,
673 slap_access_t *grantp,
674 slap_access_t *denyp )
676 AttributeDescription *ad = ( AttributeDescription * )priv;
678 slap_access_t tgrant, tdeny, grant, deny;
680 char accessmaskbuf[ACCESSMASK_MAXLEN];
681 char accessmaskbuf1[ACCESSMASK_MAXLEN];
682 #endif /* LDAP_DEBUG */
684 /* start out with nothing granted, nothing denied */
688 /* get the aci attribute */
689 at = attr_find( e->e_attrs, ad );
693 /* the aci is an multi-valued attribute. The
694 * rights are determined by OR'ing the individual
695 * rights given by the acis.
697 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
698 if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
699 nmatch, matches, &grant, &deny,
700 SLAP_ACI_SCOPE_ENTRY ) != 0 )
707 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
708 accessmask2str( tgrant, accessmaskbuf, 1 ),
709 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
712 /* If the entry level aci didn't contain anything valid for the
713 * current operation, climb up the tree and evaluate the
714 * acis with scope set to subtree
716 if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
717 struct berval parent_ndn;
720 /* to solve the chicken'n'egg problem of accessing
721 * the OpenLDAPaci attribute, the direct access
722 * to the entry's attribute is unchecked; however,
723 * further accesses to OpenLDAPaci values in the
724 * ancestors occur through backend_attribute(), i.e.
725 * with the identity of the operation, requiring
726 * further access checking. For uniformity, this
727 * makes further requests occur as the rootdn, if
728 * any, i.e. searching for the OpenLDAPaci attribute
729 * is considered an internal search. If this is not
730 * acceptable, then the same check needs be performed
731 * when accessing the entry's attribute. */
734 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
735 op2.o_dn = op->o_bd->be_rootdn;
736 op2.o_ndn = op->o_bd->be_rootndn;
740 dnParent( &e->e_nname, &parent_ndn );
741 while ( !BER_BVISEMPTY( &parent_ndn ) ){
743 BerVarray bvals = NULL;
746 Debug( LDAP_DEBUG_ACL, "checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
747 ret = backend_attribute( &op2, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
756 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
757 if ( aci_mask( op, e, desc, val,
761 SLAP_ACI_SCOPE_CHILDREN ) != 0 )
765 /* evaluation stops as soon as either a "deny" or a
766 * "grant" directive matches.
768 if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
772 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
773 accessmask2str( tgrant, accessmaskbuf, 1 ),
774 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
778 case LDAP_NO_SUCH_ATTRIBUTE:
779 /* just go on if the aci-Attribute is not present in
782 Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
786 case LDAP_NO_SUCH_OBJECT:
787 /* We have reached the base object */
788 Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
800 dnParent( &parent_ndn, &parent_ndn );
810 /* need to register this at some point */
811 static slap_dynacl_t dynacl_aci = {
822 dynacl_aci_init( void )
829 rc = slap_dynacl_register( &dynacl_aci );
835 #endif /* SLAP_DYNACL */
837 /* ACI syntax validation */
840 * Matches given berval to array of bervals
842 * >=0 if one if the array elements equals to this berval
843 * -1 if string was not found in array
848 const struct berval *arr[] )
852 if ( BER_BVISEMPTY( bv ) ) {
856 for ( i = 0; arr[ i ] != NULL ; i++ ) {
857 if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
866 /* Returns what have left in input berval after current sub */
871 struct berval *tail )
875 tail->bv_val = sub->bv_val + sub->bv_len;
876 head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
877 tail->bv_len = val->bv_len - head_len;
882 * aci is accepted in following form:
883 * oid#scope#rights#type#subject
886 * scope := entry|children
887 * rights := right[[$right]...]
888 * right := (grant|deny);action
889 * action := perms;attr[[;perms;attr]...]
890 * perms := perm[[,perm]...]
892 * attr := attributeType|[all]
893 * type := public|users|self|dnattr|group|role|set|set-ref|
894 * access_id|subtree|onelevel|children
897 OpenLDAPaciValidatePerms(
898 struct berval *perms )
902 for ( i = 0; i < perms->bv_len; ) {
903 switch ( perms->bv_val[ i ] ) {
912 return LDAP_INVALID_SYNTAX;
915 if ( ++i == perms->bv_len ) {
919 while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
922 assert( i != perms->bv_len );
924 if ( perms->bv_val[ i ] != ',' ) {
925 return LDAP_INVALID_SYNTAX;
930 } while ( perms->bv_val[ i ] == ' ' );
936 static const struct berval *ACIgrantdeny[] = {
937 &aci_bv[ ACI_BV_GRANT ],
938 &aci_bv[ ACI_BV_DENY ],
943 OpenLDAPaciValidateRight(
944 struct berval *action )
946 struct berval bv = BER_BVNULL;
950 if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
951 bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
953 return LDAP_INVALID_SYNTAX;
956 for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
959 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
961 return LDAP_INVALID_SYNTAX;
966 AttributeDescription *ad = NULL;
967 const char *text = NULL;
969 /* could be "[all]" or an attribute description */
970 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
974 if ( slap_bv2ad( &bv, &ad, &text ) != LDAP_SUCCESS ) {
975 return LDAP_INVALID_SYNTAX;
980 /* "perms;attr" go in pairs */
981 if ( i > 0 && ( i & 1 ) == 0 ) {
985 return LDAP_INVALID_SYNTAX;
992 OpenLDAPaciNormalizeRight(
993 struct berval *action,
994 struct berval *naction,
997 struct berval grantdeny,
1004 if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
1005 return LDAP_INVALID_SYNTAX;
1007 idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
1009 return LDAP_INVALID_SYNTAX;
1012 ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
1014 for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
1017 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1019 return LDAP_INVALID_SYNTAX;
1027 /* could be "[all]" or an attribute description */
1028 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1029 bv = aci_bv[ ACI_BV_BR_ALL ];
1032 AttributeDescription *ad = NULL;
1033 const char *text = NULL;
1036 rc = slap_bv2ad( &bv, &ad, &text );
1037 if ( rc != LDAP_SUCCESS ) {
1038 return LDAP_INVALID_SYNTAX;
1044 naction->bv_val = ber_memrealloc_x( naction->bv_val,
1045 naction->bv_len + STRLENOF( ";" )
1046 + perms.bv_len + STRLENOF( ";" )
1050 ptr = &naction->bv_val[ naction->bv_len ];
1053 ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
1056 ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
1058 naction->bv_len += STRLENOF( ";" ) + perms.bv_len
1059 + STRLENOF( ";" ) + bv.bv_len;
1063 /* perms;attr go in pairs */
1064 if ( i > 1 && ( i & 1 ) ) {
1065 return LDAP_SUCCESS;
1068 return LDAP_INVALID_SYNTAX;
1073 OpenLDAPaciValidateRights(
1074 struct berval *actions )
1077 struct berval bv = BER_BVNULL;
1080 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1081 if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
1082 return LDAP_INVALID_SYNTAX;
1086 return LDAP_SUCCESS;
1090 OpenLDAPaciNormalizeRights(
1091 struct berval *actions,
1092 struct berval *nactions,
1096 struct berval bv = BER_BVNULL;
1099 BER_BVZERO( nactions );
1100 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1104 rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
1105 if ( rc != LDAP_SUCCESS ) {
1106 ber_memfree_x( nactions->bv_val, ctx );
1107 BER_BVZERO( nactions );
1108 return LDAP_INVALID_SYNTAX;
1115 nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
1116 nactions->bv_len + STRLENOF( "$" )
1119 nactions->bv_val[ nactions->bv_len ] = '$';
1120 AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
1121 nbv.bv_val, nbv.bv_len + 1 );
1122 ber_memfree_x( nbv.bv_val, ctx );
1123 nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
1128 return LDAP_SUCCESS;
1131 static const struct berval *OpenLDAPaciscopes[] = {
1132 &aci_bv[ ACI_BV_ENTRY ],
1133 &aci_bv[ ACI_BV_CHILDREN ],
1134 &aci_bv[ ACI_BV_SUBTREE ],
1139 static const struct berval *OpenLDAPacitypes[] = {
1141 &aci_bv[ ACI_BV_GROUP ],
1142 &aci_bv[ ACI_BV_ROLE ],
1144 /* set to one past the last DN-valued type with options (/) */
1145 #define LAST_OPTIONAL 2
1147 &aci_bv[ ACI_BV_ACCESS_ID ],
1148 &aci_bv[ ACI_BV_SUBTREE ],
1149 &aci_bv[ ACI_BV_ONELEVEL ],
1150 &aci_bv[ ACI_BV_CHILDREN ],
1152 /* set to one past the last DN-valued type */
1153 #define LAST_DNVALUED 6
1156 &aci_bv[ ACI_BV_DNATTR ],
1157 &aci_bv[ ACI_BV_PUBLIC ],
1158 &aci_bv[ ACI_BV_USERS ],
1159 &aci_bv[ ACI_BV_SELF ],
1160 &aci_bv[ ACI_BV_SET ],
1161 &aci_bv[ ACI_BV_SET_REF ],
1167 OpenLDAPaciValidate(
1169 struct berval *val )
1171 struct berval oid = BER_BVNULL,
1173 rights = BER_BVNULL,
1175 subject = BER_BVNULL;
1178 if ( BER_BVISEMPTY( val ) ) {
1179 return LDAP_INVALID_SYNTAX;
1183 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1184 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1186 /* NOTE: the numericoidValidate() is rather pedantic;
1187 * I'd replace it with X-ORDERED VALUES so that
1188 * it's guaranteed values are maintained and used
1189 * in the desired order */
1190 return LDAP_INVALID_SYNTAX;
1194 if ( acl_get_part( val, 1, '#', &scope ) < 0 ||
1195 bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
1197 return LDAP_INVALID_SYNTAX;
1201 if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
1202 OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS )
1204 return LDAP_INVALID_SYNTAX;
1208 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1209 return LDAP_INVALID_SYNTAX;
1211 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1215 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1216 return LDAP_INVALID_SYNTAX;
1219 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1220 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1221 return LDAP_INVALID_SYNTAX;
1226 bv_get_tail( val, &type, &subject );
1227 if ( subject.bv_val[ 0 ] != '#' ) {
1228 return LDAP_INVALID_SYNTAX;
1231 if ( idx >= LAST_DNVALUED ) {
1232 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1233 AttributeDescription *ad = NULL;
1234 const char *text = NULL;
1237 rc = slap_bv2ad( &subject, &ad, &text );
1238 if ( rc != LDAP_SUCCESS ) {
1239 return LDAP_INVALID_SYNTAX;
1242 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1243 /* FIXME: allow nameAndOptionalUID? */
1244 return LDAP_INVALID_SYNTAX;
1249 return LDAP_SUCCESS;
1251 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1252 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1254 /* do {group|role}/oc/at check */
1255 struct berval ocbv = BER_BVNULL,
1258 ocbv.bv_val = strchr( type.bv_val, '/' );
1259 if ( ocbv.bv_val != NULL ) {
1262 atbv.bv_val = strchr( ocbv.bv_val, '/' );
1263 if ( atbv.bv_val != NULL ) {
1264 AttributeDescription *ad = NULL;
1265 const char *text = NULL;
1269 atbv.bv_len = type.bv_len
1270 - ( atbv.bv_val - type.bv_val );
1271 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1273 rc = slap_bv2ad( &atbv, &ad, &text );
1274 if ( rc != LDAP_SUCCESS ) {
1275 return LDAP_INVALID_SYNTAX;
1279 ocbv.bv_len = type.bv_len
1280 - ( ocbv.bv_val - type.bv_val );
1283 if ( oc_bvfind( &ocbv ) == NULL ) {
1284 return LDAP_INVALID_SYNTAX;
1289 if ( BER_BVISEMPTY( &subject ) ) {
1290 /* empty DN invalid */
1291 return LDAP_INVALID_SYNTAX;
1297 /* FIXME: pass DN syntax? */
1298 return dnValidate( NULL, &subject );
1302 OpenLDAPaciPrettyNormal(
1308 struct berval oid = BER_BVNULL,
1310 rights = BER_BVNULL,
1311 nrights = BER_BVNULL,
1314 subject = BER_BVNULL,
1315 nsubject = BER_BVNULL;
1322 if ( BER_BVISEMPTY( val ) ) {
1323 return LDAP_INVALID_SYNTAX;
1326 /* oid: if valid, it's already normalized */
1327 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1328 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1330 return LDAP_INVALID_SYNTAX;
1333 /* scope: normalize by replacing with OpenLDAPaciscopes */
1334 if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
1335 return LDAP_INVALID_SYNTAX;
1337 idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
1339 return LDAP_INVALID_SYNTAX;
1341 scope = *OpenLDAPaciscopes[ idx ];
1344 if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
1345 return LDAP_INVALID_SYNTAX;
1347 if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
1350 return LDAP_INVALID_SYNTAX;
1354 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1355 rc = LDAP_INVALID_SYNTAX;
1358 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1362 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1363 rc = LDAP_INVALID_SYNTAX;
1367 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1368 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1369 rc = LDAP_INVALID_SYNTAX;
1373 ntype = *OpenLDAPacitypes[ idx ];
1376 bv_get_tail( val, &type, &subject );
1378 if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
1379 rc = LDAP_INVALID_SYNTAX;
1386 if ( idx < LAST_DNVALUED ) {
1387 /* FIXME: pass DN syntax? */
1389 rc = dnNormalize( 0, NULL, NULL,
1390 &subject, &nsubject, ctx );
1392 rc = dnPretty( NULL, &subject, &nsubject, ctx );
1395 if ( rc == LDAP_SUCCESS ) {
1402 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1403 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1405 /* do {group|role}/oc/at check */
1406 struct berval ocbv = BER_BVNULL,
1409 ocbv.bv_val = strchr( type.bv_val, '/' );
1410 if ( ocbv.bv_val != NULL ) {
1411 ObjectClass *oc = NULL;
1412 AttributeDescription *ad = NULL;
1413 const char *text = NULL;
1417 bv.bv_len = ntype.bv_len;
1421 atbv.bv_val = strchr( ocbv.bv_val, '/' );
1422 if ( atbv.bv_val != NULL ) {
1424 atbv.bv_len = type.bv_len
1425 - ( atbv.bv_val - type.bv_val );
1426 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1428 rc = slap_bv2ad( &atbv, &ad, &text );
1429 if ( rc != LDAP_SUCCESS ) {
1430 rc = LDAP_INVALID_SYNTAX;
1434 bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
1437 ocbv.bv_len = type.bv_len
1438 - ( ocbv.bv_val - type.bv_val );
1441 if ( oc_bvfind( &ocbv ) == NULL ) {
1442 rc = LDAP_INVALID_SYNTAX;
1446 bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
1447 bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
1450 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1453 ptr = lutil_strncopy( ptr,
1454 oc->soc_cname.bv_val,
1455 oc->soc_cname.bv_len );
1459 ptr = lutil_strncopy( ptr,
1460 ad->ad_cname.bv_val,
1461 ad->ad_cname.bv_len );
1470 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1471 AttributeDescription *ad = NULL;
1472 const char *text = NULL;
1475 rc = slap_bv2ad( &subject, &ad, &text );
1476 if ( rc != LDAP_SUCCESS ) {
1477 rc = LDAP_INVALID_SYNTAX;
1481 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1482 /* FIXME: allow nameAndOptionalUID? */
1483 rc = LDAP_INVALID_SYNTAX;
1487 nsubject = ad->ad_cname;
1492 oid.bv_len + STRLENOF( "#" )
1493 + scope.bv_len + STRLENOF( "#" )
1494 + rights.bv_len + STRLENOF( "#" )
1495 + ntype.bv_len + STRLENOF( "#" )
1498 out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
1499 ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
1502 ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
1505 ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
1508 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1511 if ( !BER_BVISNULL( &nsubject ) ) {
1512 ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
1517 if ( freesubject ) {
1518 ber_memfree_x( nsubject.bv_val, ctx );
1522 ber_memfree_x( ntype.bv_val, ctx );
1525 if ( !BER_BVISNULL( &nrights ) ) {
1526 ber_memfree_x( nrights.bv_val, ctx );
1539 return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
1543 OpenLDAPaciNormalize(
1551 return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
1554 #endif /* SLAPD_ACI_ENABLED */