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-2006 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 /* use most appropriate size */
44 #define ACI_BUF_SIZE 1024
46 /* move to "stable" when no longer experimental */
47 #define SLAPD_ACI_SYNTAX "1.3.6.1.4.1.4203.666.2.1"
49 /* change this to "OpenLDAPset" */
50 #define SLAPD_ACI_SET_ATTR "template"
52 typedef enum slap_aci_scope_t {
53 SLAP_ACI_SCOPE_ENTRY = 0x1,
54 SLAP_ACI_SCOPE_CHILDREN = 0x2,
55 SLAP_ACI_SCOPE_SUBTREE = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
90 static const struct berval aci_bv[] = {
102 BER_BVC("access-id"),
117 BER_BVC(SLAPD_GROUP_CLASS),
118 BER_BVC(SLAPD_GROUP_ATTR),
119 BER_BVC(SLAPD_ROLE_CLASS),
120 BER_BVC(SLAPD_ROLE_ATTR),
122 BER_BVC(SLAPD_ACI_SET_ATTR),
127 static AttributeDescription *slap_ad_aci;
132 struct berval *val );
142 OpenLDAPaciNormalize(
150 #define OpenLDAPaciMatch octetStringMatch
154 struct berval *list )
161 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
162 if ( bv.bv_len <= 0 ) {
166 switch ( *bv.bv_val ) {
168 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
169 * define any equivalent to the AUTH right, so I've just used
172 ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
175 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
176 * the right 'd' to mean "delete"; we hijack it to mean
177 * "disclose" for consistency wuith the rest of slapd.
179 ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
182 ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
185 /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
186 * the right 's' to mean "set", but in the examples states
187 * that the right 's' means "search". The latter definition
190 ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
193 ACL_PRIV_SET(mask, ACL_PRIV_READ);
196 ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
210 const struct berval *attr,
213 struct berval bv, left, right;
216 for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
217 if ( acl_get_part(&bv, 0, '=', &left ) < 0
218 || acl_get_part( &bv, 1, '=', &right ) < 0 )
220 if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
224 } else if ( val == NULL ) {
225 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
230 if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
231 /* FIXME: this is also totally undocumented! */
232 /* this is experimental code that implements a
233 * simple (prefix) match of the attribute value.
234 * the ACI draft does not provide for aci's that
235 * apply to specific values, but it would be
236 * nice to have. If the <attr> part of an aci's
237 * rights list is of the form <attr>=<value>,
238 * that means the aci applies only to attrs with
239 * the given value. Furthermore, if the attr is
240 * of the form <attr>=<value>*, then <value> is
241 * treated as a prefix, and the aci applies to
242 * any value with that prefix.
244 * Ideally, this would allow r.e. matches.
246 if ( acl_get_part( &right, 0, '*', &left ) < 0
247 || right.bv_len <= left.bv_len )
249 if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
253 } else if ( val->bv_len >= left.bv_len ) {
254 if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) {
266 aci_list_get_attr_rights(
268 const struct berval *attr,
275 /* loop through each rights/attr pair, skip first part (action) */
277 for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
278 if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
282 if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
286 mask |= aci_list_map_rights( &bv );
295 const struct berval *attr,
297 slap_access_t *grant,
298 slap_access_t *deny )
300 struct berval perm, actn;
304 if ( attr == NULL || BER_BVISEMPTY( attr )
305 || ber_bvstrcasecmp( attr, &aci_bv[ ACI_BV_ENTRY ] ) == 0 )
307 attr = &aci_bv[ ACI_BV_BR_ENTRY ];
313 /* loop through each permissions clause */
314 for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
315 if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
319 if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
322 } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
330 *mask |= aci_list_get_attr_rights( &perm, attr, val );
331 *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
340 const struct berval *defgrpoc,
341 const struct berval *defgrpat,
348 struct berval subjdn;
351 ObjectClass *grp_oc = NULL;
352 AttributeDescription *grp_ad = NULL;
356 /* format of string is "{group|role}/objectClassValue/groupAttrName" */
357 if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
361 if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
365 if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
369 rc = slap_bv2ad( &grpat, &grp_ad, &text );
370 if ( rc != LDAP_SUCCESS ) {
376 grp_oc = oc_bvfind( &grpoc );
378 if ( grp_oc != NULL && grp_ad != NULL ) {
379 char buf[ ACI_BUF_SIZE ];
380 struct berval bv, ndn;
382 bv.bv_len = sizeof( buf ) - 1;
383 bv.bv_val = (char *)&buf;
384 if ( acl_string_expand( &bv, &subjdn,
385 e->e_ndn, nmatch, matches ) )
391 if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
393 rc = ( backend_group( op, e, &ndn, &op->o_ndn,
394 grp_oc, grp_ad ) == 0 );
395 slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
407 AttributeDescription *desc,
412 slap_access_t *grant,
414 slap_aci_scope_t asserted_scope )
425 assert( !BER_BVISNULL( &desc->ad_cname ) );
427 /* parse an aci of the form:
428 oid # scope # action;rights;attr;rights;attr
429 $ action;rights;attr;rights;attr # type # subject
431 [NOTE: the following comment is very outdated,
432 as the draft version it refers to (Ando, 2004-11-20)].
434 See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
435 a full description of the format for this attribute.
436 Differences: "this" in the draft is "self" here, and
437 "self" and "public" is in the position of type.
439 <scope> = {entry|children|subtree}
440 <type> = {public|users|access-id|subtree|onelevel|children|
441 self|dnattr|group|role|set|set-ref}
443 This routine now supports scope={ENTRY,CHILDREN}
445 - ENTRY applies to "entry" and "subtree";
446 - CHILDREN aplies to "children" and "subtree"
449 /* check that the aci has all 5 components */
450 if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
454 /* check that the aci family is supported */
455 /* FIXME: the OID is ignored? */
456 if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
460 /* check that the scope matches */
461 if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
465 /* note: scope can be either ENTRY or CHILDREN;
466 * they respectively match "entry" and "children" in bv
467 * both match "subtree" */
468 switch ( asserted_scope ) {
469 case SLAP_ACI_SCOPE_ENTRY:
470 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
471 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
477 case SLAP_ACI_SCOPE_CHILDREN:
478 if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
479 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
485 case SLAP_ACI_SCOPE_SUBTREE:
486 /* TODO: add assertion? */
490 /* get the list of permissions clauses, bail if empty */
491 if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
496 /* check if any permissions allow desired access */
497 if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
501 /* see if we have a DN match */
502 if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
507 /* see if we have a public (i.e. anonymous) access */
508 if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
512 /* otherwise require an identity */
513 if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
517 /* see if we have a users access */
518 if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
522 /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
523 * just grab all the berval up to its end (ITS#3303).
524 * NOTE: the problem could be solved by providing the DN with
525 * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
526 * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
528 if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
532 sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
533 sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
535 /* get the type options, if any */
536 if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) {
537 opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val );
538 type.bv_len = opts.bv_val - type.bv_val - 1;
544 if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
545 return dn_match( &op->o_ndn, &sdn );
547 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
548 return dnIsSuffix( &op->o_ndn, &sdn );
550 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
553 dnParent( &sdn, &pdn );
555 return dn_match( &op->o_ndn, &pdn );
557 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
558 return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
560 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
561 return dn_match( &op->o_ndn, &e->e_nname );
563 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
565 AttributeDescription *ad = NULL;
568 rc = slap_bv2ad( &sdn, &ad, &text );
569 assert( rc == LDAP_SUCCESS );
572 for ( at = attrs_find( e->e_attrs, ad );
574 at = attrs_find( at->a_next, ad ) )
576 if ( value_find_ex( ad,
577 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
578 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
580 &op->o_ndn, op->o_tmpmemctx ) == 0 )
589 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
593 if ( BER_BVISNULL( &opts ) ) {
594 oc = aci_bv[ ACI_BV_GROUP_CLASS ];
595 at = aci_bv[ ACI_BV_GROUP_ATTR ];
598 if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
602 if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
603 at = aci_bv[ ACI_BV_GROUP_ATTR ];
607 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
612 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
616 if ( BER_BVISNULL( &opts ) ) {
617 oc = aci_bv[ ACI_BV_ROLE_CLASS ];
618 at = aci_bv[ ACI_BV_ROLE_ATTR ];
621 if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
625 if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
626 at = aci_bv[ ACI_BV_ROLE_ATTR ];
630 if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
635 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
636 if ( acl_match_set( &sdn, op, e, NULL ) ) {
640 } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
641 if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) {
646 /* it passed normalization! */
656 /* OpenLDAP eXperimental Syntax */
657 static slap_syntax_defs_rec aci_syntax_def = {
658 "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
663 static slap_mrule_defs_rec aci_mr_def = {
664 "( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
665 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
666 SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
667 NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
675 AttributeDescription **ad;
677 "OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 "
678 "NAME 'OpenLDAPaci' "
679 "DESC 'OpenLDAP access control information (experimental)' "
680 "EQUALITY OpenLDAPaciMatch "
681 "SYNTAX 1.3.6.1.4.1.4203.666.2.1 "
682 "USAGE directoryOperation )",
687 LDAPAttributeType *at;
693 rc = register_syntax( &aci_syntax_def );
698 /* ACI equality rule */
699 rc = register_matching_rule( &aci_mr_def );
705 rc = register_at( aci_at.desc, aci_at.ad, 0 );
706 if ( rc != LDAP_SUCCESS ) {
707 Debug( LDAP_DEBUG_ANY,
708 "aci_init: at_register failed\n", 0, 0, 0 );
713 (*aci_at.ad)->ad_type->sat_flags |= aci_at.flags;
727 AttributeDescription *ad = NULL;
728 const char *text = NULL;
730 if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
731 fprintf( stderr, "%s: line %d: "
732 "inappropriate style \"%s\" in \"aci\" by clause\n",
733 fname, lineno, style_strings[sty] );
737 if ( right != NULL && *right != '\0' ) {
738 if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
740 "%s: line %d: aci \"%s\": %s\n",
741 fname, lineno, right, text );
749 if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
750 fprintf( stderr, "%s: line %d: "
751 "aci \"%s\": inappropriate syntax: %s\n",
752 fname, lineno, right,
753 ad->ad_type->sat_syntax_oid );
763 dynacl_aci_unparse( void *priv, struct berval *bv )
765 AttributeDescription *ad = ( AttributeDescription * )priv;
768 assert( ad != NULL );
770 bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
771 ptr = lutil_strcopy( bv->bv_val, " aci=" );
772 ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
773 bv->bv_len = ptr - bv->bv_val;
783 AttributeDescription *desc,
787 slap_access_t *grantp,
788 slap_access_t *denyp )
790 AttributeDescription *ad = ( AttributeDescription * )priv;
792 slap_access_t tgrant, tdeny, grant, deny;
794 char accessmaskbuf[ACCESSMASK_MAXLEN];
795 char accessmaskbuf1[ACCESSMASK_MAXLEN];
796 #endif /* LDAP_DEBUG */
798 if ( BER_BVISEMPTY( &e->e_nname ) ) {
799 /* no ACIs in the root DSE */
803 /* start out with nothing granted, nothing denied */
807 /* get the aci attribute */
808 at = attr_find( e->e_attrs, ad );
812 /* the aci is an multi-valued attribute. The
813 * rights are determined by OR'ing the individual
814 * rights given by the acis.
816 for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
817 if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
818 nmatch, matches, &grant, &deny,
819 SLAP_ACI_SCOPE_ENTRY ) != 0 )
826 Debug( LDAP_DEBUG_ACL, " <= aci_mask grant %s deny %s\n",
827 accessmask2str( tgrant, accessmaskbuf, 1 ),
828 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
831 /* If the entry level aci didn't contain anything valid for the
832 * current operation, climb up the tree and evaluate the
833 * acis with scope set to subtree
835 if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
836 struct berval parent_ndn;
838 dnParent( &e->e_nname, &parent_ndn );
839 while ( !BER_BVISEMPTY( &parent_ndn ) ){
841 BerVarray bvals = NULL;
844 /* to solve the chicken'n'egg problem of accessing
845 * the OpenLDAPaci attribute, the direct access
846 * to the entry's attribute is unchecked; however,
847 * further accesses to OpenLDAPaci values in the
848 * ancestors occur through backend_attribute(), i.e.
849 * with the identity of the operation, requiring
850 * further access checking. For uniformity, this
851 * makes further requests occur as the rootdn, if
852 * any, i.e. searching for the OpenLDAPaci attribute
853 * is considered an internal search. If this is not
854 * acceptable, then the same check needs be performed
855 * when accessing the entry's attribute. */
856 struct berval save_o_dn, save_o_ndn;
858 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
859 save_o_dn = op->o_dn;
860 save_o_ndn = op->o_ndn;
862 op->o_dn = op->o_bd->be_rootdn;
863 op->o_ndn = op->o_bd->be_rootndn;
866 Debug( LDAP_DEBUG_ACL, " checking ACI of \"%s\"\n", parent_ndn.bv_val, 0, 0 );
867 ret = backend_attribute( op, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
869 if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
870 op->o_dn = save_o_dn;
871 op->o_ndn = save_o_ndn;
881 for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
882 if ( aci_mask( op, e, desc, val,
886 SLAP_ACI_SCOPE_CHILDREN ) != 0 )
890 /* evaluation stops as soon as either a "deny" or a
891 * "grant" directive matches.
893 if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
897 Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
898 accessmask2str( tgrant, accessmaskbuf, 1 ),
899 accessmask2str( tdeny, accessmaskbuf1, 1 ), 0 );
903 case LDAP_NO_SUCH_ATTRIBUTE:
904 /* just go on if the aci-Attribute is not present in
907 Debug( LDAP_DEBUG_ACL, "no such attribute\n", 0, 0, 0 );
911 case LDAP_NO_SUCH_OBJECT:
912 /* We have reached the base object */
913 Debug( LDAP_DEBUG_ACL, "no such object\n", 0, 0, 0 );
925 dnParent( &parent_ndn, &parent_ndn );
935 /* need to register this at some point */
936 static slap_dynacl_t dynacl_aci = {
947 dynacl_aci_init( void )
954 rc = slap_dynacl_register( &dynacl_aci );
961 /* ACI syntax validation */
964 * Matches given berval to array of bervals
966 * >=0 if one if the array elements equals to this berval
967 * -1 if string was not found in array
972 const struct berval *arr[] )
976 if ( BER_BVISEMPTY( bv ) ) {
980 for ( i = 0; arr[ i ] != NULL ; i++ ) {
981 if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
990 /* Returns what have left in input berval after current sub */
995 struct berval *tail )
999 tail->bv_val = sub->bv_val + sub->bv_len;
1000 head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
1001 tail->bv_len = val->bv_len - head_len;
1006 * aci is accepted in following form:
1007 * oid#scope#rights#type#subject
1009 * oid := numeric OID
1010 * scope := entry|children
1011 * rights := right[[$right]...]
1012 * right := (grant|deny);action
1013 * action := perms;attr[[;perms;attr]...]
1014 * perms := perm[[,perm]...]
1016 * attr := attributeType|[all]
1017 * type := public|users|self|dnattr|group|role|set|set-ref|
1018 * access_id|subtree|onelevel|children
1021 OpenLDAPaciValidatePerms(
1022 struct berval *perms )
1026 for ( i = 0; i < perms->bv_len; ) {
1027 switch ( perms->bv_val[ i ] ) {
1037 return LDAP_INVALID_SYNTAX;
1040 if ( ++i == perms->bv_len ) {
1041 return LDAP_SUCCESS;
1044 while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
1047 assert( i != perms->bv_len );
1049 if ( perms->bv_val[ i ] != ',' ) {
1050 return LDAP_INVALID_SYNTAX;
1055 } while ( perms->bv_val[ i ] == ' ' );
1058 return LDAP_SUCCESS;
1061 static const struct berval *ACIgrantdeny[] = {
1062 &aci_bv[ ACI_BV_GRANT ],
1063 &aci_bv[ ACI_BV_DENY ],
1068 OpenLDAPaciValidateRight(
1069 struct berval *action )
1071 struct berval bv = BER_BVNULL;
1075 if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
1076 bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
1078 return LDAP_INVALID_SYNTAX;
1081 for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
1084 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1086 return LDAP_INVALID_SYNTAX;
1091 AttributeDescription *ad = NULL;
1092 const char *text = NULL;
1094 /* could be "[all]" or an attribute description */
1095 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1099 if ( slap_bv2ad( &bv, &ad, &text ) != LDAP_SUCCESS ) {
1100 return LDAP_INVALID_SYNTAX;
1105 /* "perms;attr" go in pairs */
1106 if ( i > 0 && ( i & 1 ) == 0 ) {
1107 return LDAP_SUCCESS;
1110 return LDAP_INVALID_SYNTAX;
1113 return LDAP_SUCCESS;
1117 OpenLDAPaciNormalizeRight(
1118 struct berval *action,
1119 struct berval *naction,
1122 struct berval grantdeny,
1129 if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
1130 return LDAP_INVALID_SYNTAX;
1132 idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
1134 return LDAP_INVALID_SYNTAX;
1137 ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
1139 for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
1142 if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
1144 return LDAP_INVALID_SYNTAX;
1152 /* could be "[all]" or an attribute description */
1153 if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
1154 bv = aci_bv[ ACI_BV_BR_ALL ];
1157 AttributeDescription *ad = NULL;
1158 const char *text = NULL;
1161 rc = slap_bv2ad( &bv, &ad, &text );
1162 if ( rc != LDAP_SUCCESS ) {
1163 return LDAP_INVALID_SYNTAX;
1169 naction->bv_val = ber_memrealloc_x( naction->bv_val,
1170 naction->bv_len + STRLENOF( ";" )
1171 + perms.bv_len + STRLENOF( ";" )
1175 ptr = &naction->bv_val[ naction->bv_len ];
1178 ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
1181 ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
1183 naction->bv_len += STRLENOF( ";" ) + perms.bv_len
1184 + STRLENOF( ";" ) + bv.bv_len;
1188 /* perms;attr go in pairs */
1189 if ( i > 1 && ( i & 1 ) ) {
1190 return LDAP_SUCCESS;
1193 return LDAP_INVALID_SYNTAX;
1198 OpenLDAPaciValidateRights(
1199 struct berval *actions )
1202 struct berval bv = BER_BVNULL;
1205 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1206 if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
1207 return LDAP_INVALID_SYNTAX;
1211 return LDAP_SUCCESS;
1215 OpenLDAPaciNormalizeRights(
1216 struct berval *actions,
1217 struct berval *nactions,
1221 struct berval bv = BER_BVNULL;
1224 BER_BVZERO( nactions );
1225 for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
1229 rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
1230 if ( rc != LDAP_SUCCESS ) {
1231 ber_memfree_x( nactions->bv_val, ctx );
1232 BER_BVZERO( nactions );
1233 return LDAP_INVALID_SYNTAX;
1240 nactions->bv_val = ber_memrealloc_x( nactions->bv_val,
1241 nactions->bv_len + STRLENOF( "$" )
1244 nactions->bv_val[ nactions->bv_len ] = '$';
1245 AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
1246 nbv.bv_val, nbv.bv_len + 1 );
1247 ber_memfree_x( nbv.bv_val, ctx );
1248 nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
1253 return LDAP_SUCCESS;
1256 static const struct berval *OpenLDAPaciscopes[] = {
1257 &aci_bv[ ACI_BV_ENTRY ],
1258 &aci_bv[ ACI_BV_CHILDREN ],
1259 &aci_bv[ ACI_BV_SUBTREE ],
1264 static const struct berval *OpenLDAPacitypes[] = {
1266 &aci_bv[ ACI_BV_GROUP ],
1267 &aci_bv[ ACI_BV_ROLE ],
1269 /* set to one past the last DN-valued type with options (/) */
1270 #define LAST_OPTIONAL 2
1272 &aci_bv[ ACI_BV_ACCESS_ID ],
1273 &aci_bv[ ACI_BV_SUBTREE ],
1274 &aci_bv[ ACI_BV_ONELEVEL ],
1275 &aci_bv[ ACI_BV_CHILDREN ],
1277 /* set to one past the last DN-valued type */
1278 #define LAST_DNVALUED 6
1281 &aci_bv[ ACI_BV_DNATTR ],
1282 &aci_bv[ ACI_BV_PUBLIC ],
1283 &aci_bv[ ACI_BV_USERS ],
1284 &aci_bv[ ACI_BV_SELF ],
1285 &aci_bv[ ACI_BV_SET ],
1286 &aci_bv[ ACI_BV_SET_REF ],
1292 OpenLDAPaciValidate(
1294 struct berval *val )
1296 struct berval oid = BER_BVNULL,
1298 rights = BER_BVNULL,
1300 subject = BER_BVNULL;
1303 if ( BER_BVISEMPTY( val ) ) {
1304 return LDAP_INVALID_SYNTAX;
1308 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1309 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1311 /* NOTE: the numericoidValidate() is rather pedantic;
1312 * I'd replace it with X-ORDERED VALUES so that
1313 * it's guaranteed values are maintained and used
1314 * in the desired order */
1315 return LDAP_INVALID_SYNTAX;
1319 if ( acl_get_part( val, 1, '#', &scope ) < 0 ||
1320 bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
1322 return LDAP_INVALID_SYNTAX;
1326 if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
1327 OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS )
1329 return LDAP_INVALID_SYNTAX;
1333 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1334 return LDAP_INVALID_SYNTAX;
1336 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1340 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1341 return LDAP_INVALID_SYNTAX;
1344 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1345 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1346 return LDAP_INVALID_SYNTAX;
1351 bv_get_tail( val, &type, &subject );
1352 if ( subject.bv_val[ 0 ] != '#' ) {
1353 return LDAP_INVALID_SYNTAX;
1356 if ( idx >= LAST_DNVALUED ) {
1357 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1358 AttributeDescription *ad = NULL;
1359 const char *text = NULL;
1362 rc = slap_bv2ad( &subject, &ad, &text );
1363 if ( rc != LDAP_SUCCESS ) {
1364 return LDAP_INVALID_SYNTAX;
1367 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1368 /* FIXME: allow nameAndOptionalUID? */
1369 return LDAP_INVALID_SYNTAX;
1374 return LDAP_SUCCESS;
1376 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1377 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1379 /* do {group|role}/oc/at check */
1380 struct berval ocbv = BER_BVNULL,
1383 ocbv.bv_val = ber_bvchr( &type, '/' );
1384 if ( ocbv.bv_val != NULL ) {
1386 ocbv.bv_len = type.bv_len
1387 - ( ocbv.bv_val - type.bv_val );
1389 atbv.bv_val = ber_bvchr( &ocbv, '/' );
1390 if ( atbv.bv_val != NULL ) {
1391 AttributeDescription *ad = NULL;
1392 const char *text = NULL;
1396 atbv.bv_len = type.bv_len
1397 - ( atbv.bv_val - type.bv_val );
1398 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1400 rc = slap_bv2ad( &atbv, &ad, &text );
1401 if ( rc != LDAP_SUCCESS ) {
1402 return LDAP_INVALID_SYNTAX;
1406 if ( oc_bvfind( &ocbv ) == NULL ) {
1407 return LDAP_INVALID_SYNTAX;
1412 if ( BER_BVISEMPTY( &subject ) ) {
1413 /* empty DN invalid */
1414 return LDAP_INVALID_SYNTAX;
1420 /* FIXME: pass DN syntax? */
1421 return dnValidate( NULL, &subject );
1425 OpenLDAPaciPrettyNormal(
1431 struct berval oid = BER_BVNULL,
1433 rights = BER_BVNULL,
1434 nrights = BER_BVNULL,
1437 subject = BER_BVNULL,
1438 nsubject = BER_BVNULL;
1445 if ( BER_BVISEMPTY( val ) ) {
1446 return LDAP_INVALID_SYNTAX;
1449 /* oid: if valid, it's already normalized */
1450 if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
1451 numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
1453 return LDAP_INVALID_SYNTAX;
1456 /* scope: normalize by replacing with OpenLDAPaciscopes */
1457 if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
1458 return LDAP_INVALID_SYNTAX;
1460 idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
1462 return LDAP_INVALID_SYNTAX;
1464 scope = *OpenLDAPaciscopes[ idx ];
1467 if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
1468 return LDAP_INVALID_SYNTAX;
1470 if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
1473 return LDAP_INVALID_SYNTAX;
1477 if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
1478 rc = LDAP_INVALID_SYNTAX;
1481 idx = bv_getcaseidx( &type, OpenLDAPacitypes );
1485 if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
1486 rc = LDAP_INVALID_SYNTAX;
1490 idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
1491 if ( idx == -1 || idx >= LAST_OPTIONAL ) {
1492 rc = LDAP_INVALID_SYNTAX;
1496 ntype = *OpenLDAPacitypes[ idx ];
1499 bv_get_tail( val, &type, &subject );
1501 if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
1502 rc = LDAP_INVALID_SYNTAX;
1509 if ( idx < LAST_DNVALUED ) {
1510 /* FIXME: pass DN syntax? */
1512 rc = dnNormalize( 0, NULL, NULL,
1513 &subject, &nsubject, ctx );
1515 rc = dnPretty( NULL, &subject, &nsubject, ctx );
1518 if ( rc == LDAP_SUCCESS ) {
1525 if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
1526 || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
1528 /* do {group|role}/oc/at check */
1529 struct berval ocbv = BER_BVNULL,
1532 ocbv.bv_val = ber_bvchr( &type, '/' );
1533 if ( ocbv.bv_val != NULL ) {
1534 ObjectClass *oc = NULL;
1535 AttributeDescription *ad = NULL;
1536 const char *text = NULL;
1540 bv.bv_len = ntype.bv_len;
1543 ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val );
1545 atbv.bv_val = ber_bvchr( &ocbv, '/' );
1546 if ( atbv.bv_val != NULL ) {
1548 atbv.bv_len = type.bv_len
1549 - ( atbv.bv_val - type.bv_val );
1550 ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
1552 rc = slap_bv2ad( &atbv, &ad, &text );
1553 if ( rc != LDAP_SUCCESS ) {
1554 rc = LDAP_INVALID_SYNTAX;
1558 bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
1561 oc = oc_bvfind( &ocbv );
1563 rc = LDAP_INVALID_SYNTAX;
1567 bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
1568 bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx );
1571 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1574 ptr = lutil_strncopy( ptr,
1575 oc->soc_cname.bv_val,
1576 oc->soc_cname.bv_len );
1580 ptr = lutil_strncopy( ptr,
1581 ad->ad_cname.bv_val,
1582 ad->ad_cname.bv_len );
1591 } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
1592 AttributeDescription *ad = NULL;
1593 const char *text = NULL;
1596 rc = slap_bv2ad( &subject, &ad, &text );
1597 if ( rc != LDAP_SUCCESS ) {
1598 rc = LDAP_INVALID_SYNTAX;
1602 if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
1603 /* FIXME: allow nameAndOptionalUID? */
1604 rc = LDAP_INVALID_SYNTAX;
1608 nsubject = ad->ad_cname;
1613 oid.bv_len + STRLENOF( "#" )
1614 + scope.bv_len + STRLENOF( "#" )
1615 + rights.bv_len + STRLENOF( "#" )
1616 + ntype.bv_len + STRLENOF( "#" )
1619 out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx );
1620 ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
1623 ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
1626 ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
1629 ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
1632 if ( !BER_BVISNULL( &nsubject ) ) {
1633 ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
1638 if ( freesubject ) {
1639 ber_memfree_x( nsubject.bv_val, ctx );
1643 ber_memfree_x( ntype.bv_val, ctx );
1646 if ( !BER_BVISNULL( &nrights ) ) {
1647 ber_memfree_x( nrights.bv_val, ctx );
1660 return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
1664 OpenLDAPaciNormalize(
1672 return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
1675 #if SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC
1677 * FIXME: need config and Makefile.am code to ease building
1681 init_module( int argc, char *argv[] )
1683 return dynacl_aci_init();
1685 #endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */
1687 #endif /* SLAPD_ACI_ENABLED */