static int aci_list_has_attr( struct berval *list, const struct berval *attr, struct berval *val ) { struct berval bv, left, right; int i; for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) { if ( acl_get_part(&bv, 0, '=', &left ) < 0 || acl_get_part( &bv, 1, '=', &right ) < 0 ) { if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) { return(1); } } else if ( val == NULL ) { if ( ber_bvstrcasecmp( attr, &left ) == 0 ) { return(1); } } else { if ( ber_bvstrcasecmp( attr, &left ) == 0 ) { /* FIXME: this is also totally undocumented! */ /* this is experimental code that implements a * simple (prefix) match of the attribute value. * the ACI draft does not provide for aci's that * apply to specific values, but it would be * nice to have. If the <attr> part of an aci's * rights list is of the form <attr>=<value>, * that means the aci applies only to attrs with * the given value. Furthermore, if the attr is * of the form <attr>=<value>*, then <value> is * treated as a prefix, and the aci applies to * any value with that prefix. * * Ideally, this would allow r.e. matches. */ if ( acl_get_part( &right, 0, '*', &left ) < 0 || right.bv_len <= left.bv_len ) { if ( ber_bvstrcasecmp( val, &right ) == 0 ) { return 1; } } else if ( val->bv_len >= left.bv_len ) { if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) { return(1); } } } } } return 0; }
static int aci_list_get_rights( struct berval *list, struct berval *attr, struct berval *val, slap_access_t *grant, slap_access_t *deny ) { struct berval perm, actn, baseattr; slap_access_t *mask; int i, found; if ( attr == NULL || BER_BVISEMPTY( attr ) ) { attr = (struct berval *)&aci_bv[ ACI_BV_ENTRY ]; } else if ( acl_get_part( attr, 0, ';', &baseattr ) > 0 ) { attr = &baseattr; } found = 0; ACL_INIT(*grant); ACL_INIT(*deny); /* loop through each permissions clause */ for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) { if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) { continue; } if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) { mask = grant; } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) { mask = deny; } else { continue; } *mask |= aci_list_get_attr_rights( &perm, attr, val ); *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL ); if ( *mask != ACL_PRIV_NONE ) { found = 1; } } return found; }
static slap_access_t aci_list_get_attr_rights( struct berval *list, const struct berval *attr, struct berval *val ) { struct berval bv; slap_access_t mask; int i; /* loop through each rights/attr pair, skip first part (action) */ ACL_INIT(mask); for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) { if ( aci_list_has_attr( &bv, attr, val ) == 0 ) { Debug( LDAP_DEBUG_ACL, " <= aci_list_get_attr_rights " "test %s for %s -> failed\n", bv.bv_val, attr->bv_val, 0 ); continue; } Debug( LDAP_DEBUG_ACL, " <= aci_list_get_attr_rights " "test %s for %s -> ok\n", bv.bv_val, attr->bv_val, 0 ); if ( acl_get_part( list, i, ';', &bv ) < 0 ) { Debug( LDAP_DEBUG_ACL, " <= aci_list_get_attr_rights " "test no rights\n", 0, 0, 0 ); continue; } mask |= aci_list_map_rights( &bv ); Debug( LDAP_DEBUG_ACL, " <= aci_list_get_attr_rights " "rights %s to mask 0x%x\n", bv.bv_val, mask, 0 ); } return mask; }
static int aci_list_map_rights( struct berval *list ) { struct berval bv; slap_access_t mask; int i; ACL_INIT( mask ); for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) { if ( bv.bv_len <= 0 ) { continue; } switch ( *bv.bv_val ) { case 'x': /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not * define any equivalent to the AUTH right, so I've just used * 'x' for now. */ ACL_PRIV_SET(mask, ACL_PRIV_AUTH); break; case 'd': /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines * the right 'd' to mean "delete"; we hijack it to mean * "disclose" for consistency wuith the rest of slapd. */ ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE); break; case 'c': ACL_PRIV_SET(mask, ACL_PRIV_COMPARE); break; case 's': /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines * the right 's' to mean "set", but in the examples states * that the right 's' means "search". The latter definition * is used here. */ ACL_PRIV_SET(mask, ACL_PRIV_SEARCH); break; case 'r': ACL_PRIV_SET(mask, ACL_PRIV_READ); break; case 'w': ACL_PRIV_SET(mask, ACL_PRIV_WRITE); break; default: break; } } return mask; }
static int OpenLDAPaciValidateRights( struct berval *actions ) { struct berval bv = BER_BVNULL; int i; for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) { if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) { return LDAP_INVALID_SYNTAX; } } return LDAP_SUCCESS; }
static int OpenLDAPaciNormalizeRights( struct berval *actions, struct berval *nactions, void *ctx ) { struct berval bv = BER_BVNULL; int i; BER_BVZERO( nactions ); for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) { int rc; struct berval nbv; rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx ); if ( rc != LDAP_SUCCESS ) { ber_memfree_x( nactions->bv_val, ctx ); BER_BVZERO( nactions ); return LDAP_INVALID_SYNTAX; } if ( i == 0 ) { *nactions = nbv; } else { nactions->bv_val = ber_memrealloc_x( nactions->bv_val, nactions->bv_len + STRLENOF( "$" ) + nbv.bv_len + 1, ctx ); nactions->bv_val[ nactions->bv_len ] = '$'; memcpy( &nactions->bv_val[ nactions->bv_len + 1 ], nbv.bv_val, nbv.bv_len + 1 ); ber_memfree_x( nbv.bv_val, ctx ); nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len; } BER_BVZERO( &nbv ); } return LDAP_SUCCESS; }
static int aci_mask( Operation *op, Entry *e, AttributeDescription *desc, struct berval *val, struct berval *aci, int nmatch, regmatch_t *matches, slap_access_t *grant, slap_access_t *deny, slap_aci_scope_t asserted_scope ) { struct berval bv, scope, perms, type, opts, sdn; int rc; ACL_INIT( *grant ); ACL_INIT( *deny ); assert( !BER_BVISNULL( &desc->ad_cname ) ); /* parse an aci of the form: oid # scope # action;rights;attr;rights;attr $ action;rights;attr;rights;attr # type # subject [NOTE: the following comment is very outdated, as the draft version it refers to (Ando, 2004-11-20)]. See draft-ietf-ldapext-aci-model-04.txt section 9.1 for a full description of the format for this attribute. Differences: "this" in the draft is "self" here, and "self" and "public" is in the position of type. <scope> = {entry|children|subtree} <type> = {public|users|access-id|subtree|onelevel|children| self|dnattr|group|role|set|set-ref} This routine now supports scope={ENTRY,CHILDREN} with the semantics: - ENTRY applies to "entry" and "subtree"; - CHILDREN applies to "children" and "subtree" */ /* check that the aci has all 5 components */ if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) { return 0; } /* check that the aci family is supported */ /* FIXME: the OID is ignored? */ if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) { return 0; } /* check that the scope matches */ if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) { return 0; } /* note: scope can be either ENTRY or CHILDREN; * they respectively match "entry" and "children" in bv * both match "subtree" */ switch ( asserted_scope ) { case SLAP_ACI_SCOPE_ENTRY: if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 ) { return 0; } break; case SLAP_ACI_SCOPE_CHILDREN: if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0 && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 ) { return 0; } break; case SLAP_ACI_SCOPE_SUBTREE: /* TODO: add assertion? */ return 0; } /* get the list of permissions clauses, bail if empty */ if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) { LDAP_BUG(); return 0; } /* check if any permissions allow desired access */ if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) { return 0; } /* see if we have a DN match */ if ( acl_get_part( aci, 3, '#', &type ) < 0 ) { LDAP_BUG(); return 0; } /* see if we have a public (i.e. anonymous) access */ if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) { return 1; } /* otherwise require an identity */ if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) { return 0; } /* see if we have a users access */ if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) { return 1; } /* NOTE: this may fail if a DN contains a valid '#' (unescaped); * just grab all the berval up to its end (ITS#3303). * NOTE: the problem could be solved by providing the DN with * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would * become "cn=Foo\23Bar" and be safely used by aci_mask(). */ #if 0 if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) { return 0; } #endif sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" ); sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val ); /* get the type options, if any */ if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) { opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val ); type.bv_len = opts.bv_val - type.bv_val - 1; } else { BER_BVZERO( &opts ); } if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) { return dn_match( &op->o_ndn, &sdn ); } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) { return dnIsSuffix( &op->o_ndn, &sdn ); } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) { struct berval pdn; dnParent( &sdn, &pdn ); return dn_match( &op->o_ndn, &pdn ); } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) { return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) ); } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) { return dn_match( &op->o_ndn, &e->e_nname ); } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) { Attribute *at; AttributeDescription *ad = NULL; const char *text; rc = slap_bv2ad( &sdn, &ad, &text ); assert( rc == LDAP_SUCCESS ); rc = 0; for ( at = attrs_find( e->e_attrs, ad ); at != NULL; at = attrs_find( at->a_next, ad ) ) { if ( attr_valfind( at, SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, &op->o_ndn, NULL, op->o_tmpmemctx ) == 0 ) { rc = 1; break; } } return rc; } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) { struct berval oc, at; if ( BER_BVISNULL( &opts ) ) { oc = aci_bv[ ACI_BV_GROUP_CLASS ]; at = aci_bv[ ACI_BV_GROUP_ATTR ]; } else { if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) { LDAP_BUG(); } if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) { at = aci_bv[ ACI_BV_GROUP_ATTR ]; } } if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) ) { return 1; } } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) { struct berval oc, at; if ( BER_BVISNULL( &opts ) ) { oc = aci_bv[ ACI_BV_ROLE_CLASS ]; at = aci_bv[ ACI_BV_ROLE_ATTR ]; } else { if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) { LDAP_BUG(); } if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) { at = aci_bv[ ACI_BV_ROLE_ATTR ]; } } if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) ) { return 1; } } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) { if ( acl_match_set( &sdn, op, e, NULL ) ) { return 1; } } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) { if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) { return 1; } } else { /* it passed normalization! */ LDAP_BUG(); } return 0; }
static int aci_group_member ( struct berval *subj, const struct berval *defgrpoc, const struct berval *defgrpat, Operation *op, Entry *e, int nmatch, regmatch_t *matches ) { struct berval subjdn; struct berval grpoc; struct berval grpat; ObjectClass *grp_oc = NULL; AttributeDescription *grp_ad = NULL; const char *text; int rc; /* format of string is "{group|role}/objectClassValue/groupAttrName" */ if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) { return 0; } if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) { grpoc = *defgrpoc; } if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) { grpat = *defgrpat; } rc = slap_bv2ad( &grpat, &grp_ad, &text ); if ( rc != LDAP_SUCCESS ) { rc = 0; goto done; } rc = 0; grp_oc = oc_bvfind( &grpoc ); if ( grp_oc != NULL && grp_ad != NULL ) { char buf[ ACI_BUF_SIZE ]; struct berval bv, ndn; AclRegexMatches amatches = { 0 }; amatches.dn_count = nmatch; memcpy( amatches.dn_data, matches, sizeof( amatches.dn_data ) ); bv.bv_len = sizeof( buf ) - 1; bv.bv_val = (char *)&buf; if ( acl_string_expand( &bv, &subjdn, &e->e_nname, NULL, &amatches ) ) { rc = LDAP_OTHER; goto done; } if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS ) { rc = ( backend_group( op, e, &ndn, &op->o_ndn, grp_oc, grp_ad ) == 0 ); slap_sl_free( ndn.bv_val, op->o_tmpmemctx ); } } done: return rc; }
static int OpenLDAPaciPrettyNormal( struct berval *val, struct berval *out, void *ctx, int normalize ) { struct berval oid = BER_BVNULL, scope = BER_BVNULL, rights = BER_BVNULL, nrights = BER_BVNULL, type = BER_BVNULL, ntype = BER_BVNULL, subject = BER_BVNULL, nsubject = BER_BVNULL; int idx, rc = LDAP_SUCCESS, freesubject = 0, freetype = 0; char *ptr; BER_BVZERO( out ); if ( BER_BVISEMPTY( val ) ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: value is empty\n" ); return LDAP_INVALID_SYNTAX; } /* oid: if valid, it's already normalized */ if ( acl_get_part( val, 0, '#', &oid ) < 0 || numericoidValidate( NULL, &oid ) != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid oid '%s'\n", oid.bv_val ); return LDAP_INVALID_SYNTAX; } /* scope: normalize by replacing with OpenLDAPaciscopes */ if ( acl_get_part( val, 1, '#', &scope ) < 0 ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing scope in '%s'\n", val->bv_val ); return LDAP_INVALID_SYNTAX; } idx = bv_getcaseidx( &scope, OpenLDAPaciscopes ); if ( idx == -1 ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid scope '%s'\n", scope.bv_val ); return LDAP_INVALID_SYNTAX; } scope = *OpenLDAPaciscopes[ idx ]; /* rights */ if ( acl_get_part( val, 2, '#', &rights ) < 0 ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing rights in '%s'\n", val->bv_val ); return LDAP_INVALID_SYNTAX; } if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx ) != LDAP_SUCCESS ) { return LDAP_INVALID_SYNTAX; } /* type */ if ( acl_get_part( val, 3, '#', &type ) < 0 ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing type in '%s'\n", val->bv_val ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } idx = bv_getcaseidx( &type, OpenLDAPacitypes ); if ( idx == -1 ) { struct berval isgr; if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", type.bv_val ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } idx = bv_getcaseidx( &isgr, OpenLDAPacitypes ); if ( idx == -1 || idx >= LAST_OPTIONAL ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", isgr.bv_val ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } } ntype = *OpenLDAPacitypes[ idx ]; /* subject */ bv_get_tail( val, &type, &subject ); if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing subject in '%s'\n", val->bv_val ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } subject.bv_val++; subject.bv_len--; if ( idx < LAST_DNVALUED ) { /* FIXME: pass DN syntax? */ if ( normalize ) { rc = dnNormalize( 0, NULL, NULL, &subject, &nsubject, ctx ); } else { rc = dnPretty( NULL, &subject, &nsubject, ctx ); } if ( rc == LDAP_SUCCESS ) { freesubject = 1; } else { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid subject dn '%s'\n", subject.bv_val ); goto cleanup; } if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ] || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] ) { /* do {group|role}/oc/at check */ struct berval ocbv = BER_BVNULL, atbv = BER_BVNULL; ocbv.bv_val = ber_bvchr( &type, '/' ); if ( ocbv.bv_val != NULL ) { ObjectClass *oc = NULL; AttributeDescription *ad = NULL; const char *text = NULL; int rc; struct berval bv; bv.bv_len = ntype.bv_len; ocbv.bv_val++; ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val ); atbv.bv_val = ber_bvchr( &ocbv, '/' ); if ( atbv.bv_val != NULL ) { atbv.bv_val++; atbv.bv_len = type.bv_len - ( atbv.bv_val - type.bv_val ); ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1; rc = slap_bv2ad( &atbv, &ad, &text ); if ( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown group attribute '%s'\n", atbv.bv_val ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len; } oc = oc_bvfind( &ocbv ); if ( oc == NULL ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid group '%s'\n", ocbv.bv_val ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len; bv.bv_val = ber_memalloc_x( bv.bv_len + 1, ctx ); ptr = bv.bv_val; ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len ); ptr[ 0 ] = '/'; ptr++; ptr = lutil_strncopy( ptr, oc->soc_cname.bv_val, oc->soc_cname.bv_len ); if ( ad != NULL ) { ptr[ 0 ] = '/'; ptr++; ptr = lutil_strncopy( ptr, ad->ad_cname.bv_val, ad->ad_cname.bv_len ); } ptr[ 0 ] = '\0'; ntype = bv; freetype = 1; } } } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) { AttributeDescription *ad = NULL; const char *text = NULL; int rc; rc = slap_bv2ad( &subject, &ad, &text ); if ( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown dn attribute '%s'\n", subject.bv_val ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) { /* FIXME: allow nameAndOptionalUID? */ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: wrong syntax for dn attribute '%s'\n", subject.bv_val ); rc = LDAP_INVALID_SYNTAX; goto cleanup; } nsubject = ad->ad_cname; } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET ] || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET_REF ] ) { /* NOTE: dunno how to normalize it... */ nsubject = subject; } out->bv_len = oid.bv_len + STRLENOF( "#" ) + scope.bv_len + STRLENOF( "#" ) + nrights.bv_len + STRLENOF( "#" ) + ntype.bv_len + STRLENOF( "#" ) + nsubject.bv_len; out->bv_val = ber_memalloc_x( out->bv_len + 1, ctx ); ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len ); ptr[ 0 ] = '#'; ptr++; ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len ); ptr[ 0 ] = '#'; ptr++; ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len ); ptr[ 0 ] = '#'; ptr++; ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len ); ptr[ 0 ] = '#'; ptr++; if ( !BER_BVISNULL( &nsubject ) ) { ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len ); } ptr[ 0 ] = '\0'; cleanup:; if ( freesubject ) { ber_memfree_x( nsubject.bv_val, ctx ); } if ( freetype ) { ber_memfree_x( ntype.bv_val, ctx ); } if ( !BER_BVISNULL( &nrights ) ) { ber_memfree_x( nrights.bv_val, ctx ); } return rc; }
static int OpenLDAPaciValidate( Syntax *syntax, struct berval *val ) { struct berval oid = BER_BVNULL, scope = BER_BVNULL, rights = BER_BVNULL, type = BER_BVNULL, subject = BER_BVNULL; int idx; int rc; if ( BER_BVISEMPTY( val ) ) { Debug( LDAP_DEBUG_ACL, "aciValidatet: value is empty\n" ); return LDAP_INVALID_SYNTAX; } /* oid */ if ( acl_get_part( val, 0, '#', &oid ) < 0 || numericoidValidate( NULL, &oid ) != LDAP_SUCCESS ) { /* NOTE: the numericoidValidate() is rather pedantic; * I'd replace it with X-ORDERED VALUES so that * it's guaranteed values are maintained and used * in the desired order */ Debug( LDAP_DEBUG_ACL, "aciValidate: invalid oid '%s'\n", oid.bv_val ); return LDAP_INVALID_SYNTAX; } /* scope */ if ( acl_get_part( val, 1, '#', &scope ) < 0 || bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 ) { Debug( LDAP_DEBUG_ACL, "aciValidate: invalid scope '%s'\n", scope.bv_val ); return LDAP_INVALID_SYNTAX; } /* rights */ if ( acl_get_part( val, 2, '#', &rights ) < 0 || OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS ) { return LDAP_INVALID_SYNTAX; } /* type */ if ( acl_get_part( val, 3, '#', &type ) < 0 ) { Debug( LDAP_DEBUG_ACL, "aciValidate: missing type in '%s'\n", val->bv_val ); return LDAP_INVALID_SYNTAX; } idx = bv_getcaseidx( &type, OpenLDAPacitypes ); if ( idx == -1 ) { struct berval isgr; if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) { Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", type.bv_val ); return LDAP_INVALID_SYNTAX; } idx = bv_getcaseidx( &isgr, OpenLDAPacitypes ); if ( idx == -1 || idx >= LAST_OPTIONAL ) { Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", isgr.bv_val ); return LDAP_INVALID_SYNTAX; } } /* subject */ bv_get_tail( val, &type, &subject ); if ( subject.bv_val[ 0 ] != '#' ) { Debug( LDAP_DEBUG_ACL, "aciValidate: missing subject in '%s'\n", val->bv_val ); return LDAP_INVALID_SYNTAX; } if ( idx >= LAST_DNVALUED ) { if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) { AttributeDescription *ad = NULL; const char *text = NULL; rc = slap_bv2ad( &subject, &ad, &text ); if ( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ACL, "aciValidate: unknown dn attribute '%s'\n", subject.bv_val ); return LDAP_INVALID_SYNTAX; } if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) { /* FIXME: allow nameAndOptionalUID? */ Debug( LDAP_DEBUG_ACL, "aciValidate: wrong syntax for dn attribute '%s'\n", subject.bv_val ); return LDAP_INVALID_SYNTAX; } } /* not a DN */ return LDAP_SUCCESS; } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ] || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] ) { /* do {group|role}/oc/at check */ struct berval ocbv = BER_BVNULL, atbv = BER_BVNULL; ocbv.bv_val = ber_bvchr( &type, '/' ); if ( ocbv.bv_val != NULL ) { ocbv.bv_val++; ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val ); atbv.bv_val = ber_bvchr( &ocbv, '/' ); if ( atbv.bv_val != NULL ) { AttributeDescription *ad = NULL; const char *text = NULL; int rc; atbv.bv_val++; atbv.bv_len = type.bv_len - ( atbv.bv_val - type.bv_val ); ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1; rc = slap_bv2ad( &atbv, &ad, &text ); if ( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group attribute '%s'\n", atbv.bv_val ); return LDAP_INVALID_SYNTAX; } } if ( oc_bvfind( &ocbv ) == NULL ) { Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group '%s'\n", ocbv.bv_val ); return LDAP_INVALID_SYNTAX; } } } if ( BER_BVISEMPTY( &subject ) ) { /* empty DN invalid */ Debug( LDAP_DEBUG_ACL, "aciValidate: missing dn in '%s'\n", val->bv_val ); return LDAP_INVALID_SYNTAX; } subject.bv_val++; subject.bv_len--; /* FIXME: pass DN syntax? */ rc = dnValidate( NULL, &subject ); if ( rc != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ACL, "aciValidate: invalid dn '%s'\n", subject.bv_val ); } return rc; }
static int OpenLDAPaciNormalizeRight( struct berval *action, struct berval *naction, void *ctx ) { struct berval grantdeny, perms = BER_BVNULL, bv = BER_BVNULL; int idx, i; /* grant|deny */ if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) { Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: missing ';' in '%s'\n", action->bv_val ); return LDAP_INVALID_SYNTAX; } idx = bv_getcaseidx( &grantdeny, ACIgrantdeny ); if ( idx == -1 ) { Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: '%s' must be grant or deny\n", grantdeny.bv_val ); return LDAP_INVALID_SYNTAX; } ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx ); for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) { struct berval nattrs = BER_BVNULL; int freenattrs = 1; if ( i & 1 ) { /* perms */ if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS ) { return LDAP_INVALID_SYNTAX; } perms = bv; } else { /* attr */ char *ptr; /* could be "[all]" or an attribute description */ if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) { nattrs = aci_bv[ ACI_BV_BR_ALL ]; freenattrs = 0; } else { AttributeDescription *ad = NULL; AttributeDescription adstatic= { 0 }; const char *text = NULL; struct berval attr, left, right; int j; int len; for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ ) { ad = NULL; text = NULL; /* openldap 2.1 aci compabitibility [entry] -> entry */ if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ENTRY ] ) == 0 ) { ad = &adstatic; adstatic.ad_cname = aci_bv[ ACI_BV_ENTRY ]; /* openldap 2.1 aci compabitibility [children] -> children */ } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_CHILDREN ] ) == 0 ) { ad = &adstatic; adstatic.ad_cname = aci_bv[ ACI_BV_CHILDREN ]; /* openldap 2.1 aci compabitibility [all] -> only [all] */ } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) { ber_memfree_x( nattrs.bv_val, ctx ); nattrs = aci_bv[ ACI_BV_BR_ALL ]; freenattrs = 0; break; } else if ( acl_get_part( &attr, 0, '=', &left ) < 0 || acl_get_part( &attr, 1, '=', &right ) < 0 ) { if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) { ber_memfree_x( nattrs.bv_val, ctx ); Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", attr.bv_val ); return LDAP_INVALID_SYNTAX; } } else { if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS ) { ber_memfree_x( nattrs.bv_val, ctx ); Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", left.bv_val ); return LDAP_INVALID_SYNTAX; } } len = nattrs.bv_len + ( !BER_BVISEMPTY( &nattrs ) ? STRLENOF( "," ) : 0 ) + ad->ad_cname.bv_len; nattrs.bv_val = ber_memrealloc_x( nattrs.bv_val, len + 1, ctx ); ptr = &nattrs.bv_val[ nattrs.bv_len ]; if ( !BER_BVISEMPTY( &nattrs ) ) { *ptr++ = ','; } ptr = lutil_strncopy( ptr, ad->ad_cname.bv_val, ad->ad_cname.bv_len ); ptr[ 0 ] = '\0'; nattrs.bv_len = len; } } naction->bv_val = ber_memrealloc_x( naction->bv_val, naction->bv_len + STRLENOF( ";" ) + perms.bv_len + STRLENOF( ";" ) + nattrs.bv_len + 1, ctx ); ptr = &naction->bv_val[ naction->bv_len ]; ptr[ 0 ] = ';'; ptr++; ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len ); ptr[ 0 ] = ';'; ptr++; ptr = lutil_strncopy( ptr, nattrs.bv_val, nattrs.bv_len ); ptr[ 0 ] = '\0'; naction->bv_len += STRLENOF( ";" ) + perms.bv_len + STRLENOF( ";" ) + nattrs.bv_len; if ( freenattrs ) { ber_memfree_x( nattrs.bv_val, ctx ); } } } /* perms;attr go in pairs */ if ( i > 1 && ( i & 1 ) ) { return LDAP_SUCCESS; } else { Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: perms:attr need to be pairs in '%s'\n", action->bv_val ); return LDAP_INVALID_SYNTAX; } }
static int OpenLDAPaciValidateRight( struct berval *action ) { struct berval bv = BER_BVNULL; int i; /* grant|deny */ if ( acl_get_part( action, 0, ';', &bv ) < 0 || bv_getcaseidx( &bv, ACIgrantdeny ) == -1 ) { Debug( LDAP_DEBUG_ACL, "aciValidateRight: '%s' must be either 'grant' or 'deny'\n", bv.bv_val ); return LDAP_INVALID_SYNTAX; } for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) { if ( i & 1 ) { /* perms */ if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS ) { return LDAP_INVALID_SYNTAX; } } else { /* attr */ AttributeDescription *ad; const char *text; struct berval attr, left, right; int j; /* could be "[all]" or an attribute description */ if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) { continue; } for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ ) { ad = NULL; text = NULL; if ( acl_get_part( &attr, 0, '=', &left ) < 0 || acl_get_part( &attr, 1, '=', &right ) < 0 ) { if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", attr.bv_val ); return LDAP_INVALID_SYNTAX; } } else { if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS ) { Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", left.bv_val ); return LDAP_INVALID_SYNTAX; } } } } } /* "perms;attr" go in pairs */ if ( i > 0 && ( i & 1 ) == 0 ) { return LDAP_SUCCESS; } else { Debug( LDAP_DEBUG_ACL, "aciValidateRight: perms:attr need to be pairs in '%s'\n", action->bv_val ); return LDAP_INVALID_SYNTAX; } return LDAP_SUCCESS; }