diff options
Diffstat (limited to 'servers/slapd/overlays/nestgroup.c')
-rw-r--r-- | servers/slapd/overlays/nestgroup.c | 909 |
1 files changed, 909 insertions, 0 deletions
diff --git a/servers/slapd/overlays/nestgroup.c b/servers/slapd/overlays/nestgroup.c new file mode 100644 index 0000000..ca5c928 --- /dev/null +++ b/servers/slapd/overlays/nestgroup.c @@ -0,0 +1,909 @@ +/* nestgroup.c - nested group overlay */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2024 The OpenLDAP Foundation. + * Copyright 2024 by Howard Chu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#ifdef SLAPD_OVER_NESTGROUP + +#include <stdio.h> + +#include <ac/string.h> +#include <ac/socket.h> + +#include "lutil.h" +#include "slap.h" +#include "slap-config.h" + +/* This overlay dynamically constructs member and memberOf attributes + * for nested groups. + */ + +#define SLAPD_MEMBEROF_ATTR "memberOf" + +#define NG_MBR_VALUES 0x01 +#define NG_MBR_FILTER 0x02 +#define NG_MOF_VALUES 0x04 +#define NG_MOF_FILTER 0x08 +#define NG_NEGATED 0x10 + +static AttributeDescription *ad_member; +static AttributeDescription *ad_memberOf; + +static slap_verbmasks nestgroup_flags[] = { + { BER_BVC("member-values"), NG_MBR_VALUES }, + { BER_BVC("member-filter"), NG_MBR_FILTER }, + { BER_BVC("memberof-values"), NG_MOF_VALUES }, + { BER_BVC("memberof-filter"), NG_MOF_FILTER }, + { BER_BVNULL, 0 } +}; + +enum { + NG_MEMBER = 1, + NG_MEMBEROF, + NG_GROUPBASE, + NG_FLAGS +}; + +typedef struct nestgroup_info_t { + AttributeDescription *ngi_member; + AttributeDescription *ngi_memberOf; + BerVarray ngi_groupBase; + BerVarray ngi_ngroupBase; + int ngi_flags; +} nestgroup_info_t; + +static int ngroup_cf( ConfigArgs *c ) +{ + slap_overinst *on = (slap_overinst *)c->bi; + nestgroup_info_t *ngi = (nestgroup_info_t *)on->on_bi.bi_private; + int rc = 1; + + if ( c->op == SLAP_CONFIG_EMIT ) { + switch( c->type ) { + case NG_MEMBER: + if ( ngi->ngi_member ) { + value_add_one( &c->rvalue_vals, &ngi->ngi_member->ad_cname ); + rc = 0; + } + break; + case NG_MEMBEROF: + if ( ngi->ngi_memberOf ) { + value_add_one( &c->rvalue_vals, &ngi->ngi_memberOf->ad_cname ); + rc = 0; + } + break; + case NG_GROUPBASE: + if ( ngi->ngi_groupBase ) { + value_add( &c->rvalue_vals, ngi->ngi_groupBase ); + value_add( &c->rvalue_nvals, ngi->ngi_ngroupBase ); + rc = 0; + } + break; + case NG_FLAGS: + return mask_to_verbs( nestgroup_flags, ngi->ngi_flags, &c->rvalue_vals ); + default: + break; + } + return rc; + } else if ( c->op == LDAP_MOD_DELETE ) { + switch( c->type ) { + case NG_MEMBER: + ngi->ngi_member = ad_member; + rc = 0; + break; + case NG_MEMBEROF: + ngi->ngi_memberOf = ad_memberOf; + rc = 0; + break; + case NG_GROUPBASE: + if ( c->valx < 0 ) { + ber_bvarray_free( ngi->ngi_groupBase ); + ber_bvarray_free( ngi->ngi_ngroupBase ); + ngi->ngi_groupBase = NULL; + ngi->ngi_ngroupBase = NULL; + } else { + int i = c->valx; + ch_free( ngi->ngi_groupBase[i].bv_val ); + ch_free( ngi->ngi_ngroupBase[i].bv_val ); + do { + ngi->ngi_groupBase[i] = ngi->ngi_groupBase[i+1]; + ngi->ngi_ngroupBase[i] = ngi->ngi_ngroupBase[i+1]; + i++; + } while ( !BER_BVISNULL( &ngi->ngi_groupBase[i] )); + } + rc = 0; + break; + case NG_FLAGS: + if ( !c->line ) { + ngi->ngi_flags = 0; + } else { + int i = verb_to_mask( c->line, nestgroup_flags ); + ngi->ngi_flags &= ~nestgroup_flags[i].mask; + } + rc = 0; + break; + default: + break; + } + return rc; + } + + switch( c->type ) { + case NG_MEMBER: + if ( !is_at_syntax( c->value_ad->ad_type, SLAPD_DN_SYNTAX ) && + !is_at_syntax( c->value_ad->ad_type, SLAPD_NAMEUID_SYNTAX )) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "member attribute=\"%s\" must use DN (%s) or NAMEUID (%s) syntax", + c->argv[1], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX ); + Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, + "%s: %s\n", c->log, c->cr_msg ); + return ARG_BAD_CONF; + } + ngi->ngi_member = c->value_ad; + rc = 0; + break; + case NG_MEMBEROF: + if ( !is_at_syntax( c->value_ad->ad_type, SLAPD_DN_SYNTAX ) && + !is_at_syntax( c->value_ad->ad_type, SLAPD_NAMEUID_SYNTAX )) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "memberOf attribute=\"%s\" must use DN (%s) or NAMEUID (%s) syntax", + c->argv[1], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX ); + Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, + "%s: %s\n", c->log, c->cr_msg ); + return ARG_BAD_CONF; + } + ngi->ngi_memberOf = c->value_ad; + rc = 0; + break; + case NG_GROUPBASE: + ber_bvarray_add( &ngi->ngi_groupBase, &c->value_dn ); + ber_bvarray_add( &ngi->ngi_ngroupBase, &c->value_ndn ); + rc = 0; + break; + case NG_FLAGS: { + slap_mask_t flags = 0; + int i; + if ( c->op != SLAP_CONFIG_ADD && c->argc > 2 ) { + /* We wouldn't know how to delete these values later */ + snprintf( c->cr_msg, sizeof( c->cr_msg ), + "Please insert multiple names as separate %s values", + c->argv[0] ); + Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, + "%s: %s\n", c->log, c->cr_msg ); + rc = LDAP_INVALID_SYNTAX; + break; + } + i = verbs_to_mask( c->argc, c->argv, nestgroup_flags, &flags ); + if ( i ) { + snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown option", c->argv[0] ); + Debug(LDAP_DEBUG_ANY, "%s: %s %s\n", + c->log, c->cr_msg, c->argv[i]); + return(1); + } + ngi->ngi_flags |= flags; + rc = 0; + break; } + default: + break; + } + + return rc; +} + +static ConfigTable ngroupcfg[] = { + { "nestgroup-member", "member-ad", 2, 2, 0, + ARG_MAGIC|ARG_ATDESC|NG_MEMBER, ngroup_cf, + "( OLcfgOvAt:25.1 NAME 'olcNestGroupMember' " + "EQUALITY caseIgnoreMatch " + "DESC 'Member attribute' " + "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL }, + { "nestgroup-memberof", "memberOf-ad", 2, 2, 0, + ARG_MAGIC|ARG_ATDESC|NG_MEMBEROF, ngroup_cf, + "( OLcfgOvAt:25.2 NAME 'olcNestGroupMemberOf' " + "EQUALITY caseIgnoreMatch " + "DESC 'MemberOf attribute' " + "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL }, + { "nestgroup-base", "dn", 2, 2, 0, + ARG_DN|ARG_QUOTE|ARG_MAGIC|NG_GROUPBASE, ngroup_cf, + "( OLcfgOvAt:25.3 NAME 'olcNestGroupBase' " + "EQUALITY distinguishedNameMatch " + "DESC 'Base[s] of group subtree[s]' " + "SYNTAX OMsDN )", NULL, NULL }, + { "nestgroup-flags", "options", 2, 0, 0, + ARG_MAGIC|NG_FLAGS, ngroup_cf, + "( OLcfgOvAt:25.4 NAME 'olcNestGroupFlags' " + "EQUALITY caseIgnoreMatch " + "DESC 'Features to use' " + "SYNTAX OMsDirectoryString )", NULL, NULL }, + { NULL, NULL, 0, 0, 0, ARG_IGNORED } +}; + +static ConfigOCs ngroupocs[] = { + { "( OLcfgOvOc:25.1 " + "NAME 'olcNestGroupConfig' " + "DESC 'Nested Group configuration' " + "SUP olcOverlayConfig " + "MAY ( olcNestGroupMember $ olcNestGroupMemberOf $ " + " olcNestGroupBase $ olcNestGroupFlags ) ) ", + Cft_Overlay, ngroupcfg }, + { NULL, 0, NULL } +}; + +typedef struct nestgroup_filterinst_t { + Filter *nf_f; + Filter *nf_new; + Entry *nf_e; +} nestgroup_filterinst_t; + +/* Record occurrences of ad in filter. Ignore in negated filters. */ +static void +nestgroup_filter_instances( Operation *op, AttributeDescription *ad, Filter *f, int not, + int *nfn, nestgroup_filterinst_t **nfp, int *negated ) +{ + if ( !f ) + return; + + switch( f->f_choice & SLAPD_FILTER_MASK ) { + case LDAP_FILTER_EQUALITY: + if ( f->f_av_desc == ad ) { + if ( not ) { + *negated = 1; + } else { + nestgroup_filterinst_t *nf = *nfp; + int n = *nfn; + nf = op->o_tmprealloc( nf, (n + 1) * sizeof(nestgroup_filterinst_t), op->o_tmpmemctx ); + nf[n].nf_f = f; + nf[n].nf_new = NULL; + nf[n++].nf_e = NULL; + *nfp = nf; + *nfn = n; + } + } + break; + case SLAPD_FILTER_COMPUTED: + case LDAP_FILTER_PRESENT: + case LDAP_FILTER_GE: + case LDAP_FILTER_LE: + case LDAP_FILTER_APPROX: + case LDAP_FILTER_SUBSTRINGS: + case LDAP_FILTER_EXT: + break; + case LDAP_FILTER_NOT: not ^= 1; + /* FALLTHRU */ + case LDAP_FILTER_AND: + case LDAP_FILTER_OR: + for ( f = f->f_list; f; f = f->f_next ) + nestgroup_filter_instances( op, ad, f, not, nfn, nfp, negated ); + } +} + +static int +nestgroup_check_needed( Operation *op, int attrflags, AttributeDescription *ad ) +{ + if ( is_at_operational( ad->ad_type )) { + if ( SLAP_OPATTRS( attrflags )) + return 1; + } else { + if ( SLAP_USERATTRS( attrflags )) + return 1; + } + return ( ad_inlist( ad, op->ors_attrs )); +} + +typedef struct DNpair { + struct berval dp_ndn; + struct berval dp_dn; + struct DNpair *dp_next; + int dp_flag; +} DNpair; + +typedef struct gdn_info { + TAvlnode *gi_DNs; + DNpair *gi_DNlist; + nestgroup_info_t *gi_ngi; + int gi_numDNs; + int gi_saveDN; + Attribute *gi_merge; +} gdn_info; + +static int +nestgroup_dncmp( const void *v1, const void *v2 ) +{ + return ber_bvcmp((const struct berval *)v1, (const struct berval *)v2); +} + +static int +nestgroup_gotDNresp( Operation *op, SlapReply *rs ) +{ + if ( rs->sr_type == REP_SEARCH ) { + gdn_info *gi = (gdn_info *)(op->o_callback+1); + DNpair *dp = op->o_tmpalloc( sizeof(DNpair), op->o_tmpmemctx ); + dp->dp_ndn = rs->sr_entry->e_nname; + if ( ldap_tavl_insert( &gi->gi_DNs, dp, nestgroup_dncmp, ldap_avl_dup_error )) { + op->o_tmpfree( dp, op->o_tmpmemctx ); + } else { + ber_dupbv_x( &dp->dp_ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx ); + if ( gi->gi_saveDN ) + ber_dupbv_x( &dp->dp_dn, &rs->sr_entry->e_name, op->o_tmpmemctx ); + gi->gi_numDNs++; + dp->dp_next = gi->gi_DNlist; + dp->dp_flag = 0; + gi->gi_DNlist = dp; + } + } + return 0; +} + +static void +nestgroup_get_parentDNs( Operation *op, struct berval *ndn ) +{ + SlapReply r = { REP_SEARCH }; + gdn_info *gi = (gdn_info *)(op->o_callback+1); + nestgroup_info_t *ngi = gi->gi_ngi; + int i; + + op->ors_filter->f_av_value = *ndn; + for ( i=0; !BER_BVISEMPTY( &ngi->ngi_ngroupBase[i] ); i++ ) { + op->o_req_dn = ngi->ngi_groupBase[i]; + op->o_req_ndn = ngi->ngi_ngroupBase[i]; + op->o_bd->be_search( op, &r ); + } + gi->gi_numDNs = 0; /* ignore first count, that's just the original member= result set */ + + while ( gi->gi_DNlist ) { + int prevnum; + DNpair *dp = gi->gi_DNlist; + gi->gi_DNlist = NULL; + for ( ; dp; dp=dp->dp_next ) { + op->ors_filter->f_av_value = dp->dp_ndn; + prevnum = gi->gi_numDNs; + for ( i=0; !BER_BVISEMPTY( &ngi->ngi_ngroupBase[i] ); i++ ) { + op->o_req_dn = ngi->ngi_groupBase[i]; + op->o_req_ndn = ngi->ngi_ngroupBase[i]; + op->o_bd->be_search( op, &r ); + } + if ( gi->gi_numDNs > prevnum ) + dp->dp_flag = 1; /* this group had a parent */ + } + } +} + +static void +nestgroup_memberFilter( Operation *op, int mbr_nf, nestgroup_filterinst_t *mbr_f ) +{ + slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; + nestgroup_info_t *ngi = on->on_bi.bi_private; + AttributeDescription *ad = mbr_f[0].nf_f->f_av_desc; + slap_callback *sc; + gdn_info *gi; + Filter mf; + AttributeAssertion mava; + Operation o = *op; + int i; + + o.o_managedsait = SLAP_CONTROL_CRITICAL; + sc = op->o_tmpcalloc( 1, sizeof(slap_callback) + sizeof(gdn_info), op->o_tmpmemctx); + gi = (gdn_info *)(sc+1); + gi->gi_ngi = ngi; + o.o_callback = sc; + sc->sc_response = nestgroup_gotDNresp; + o.ors_attrs = slap_anlist_no_attrs; + + mf.f_choice = LDAP_FILTER_EQUALITY; + mf.f_ava = &mava; + mf.f_av_desc = ad; + mf.f_next = NULL; + + o.ors_scope = LDAP_SCOPE_SUBTREE; + o.ors_deref = LDAP_DEREF_NEVER; + o.ors_limit = NULL; + o.ors_tlimit = SLAP_NO_LIMIT; + o.ors_slimit = SLAP_NO_LIMIT; + o.ors_filter = &mf; + o.o_bd->bd_info = (BackendInfo *)on->on_info; + + for ( i=0; i<mbr_nf; i++ ) { + gi->gi_DNs = NULL; + gi->gi_numDNs = 0; + nestgroup_get_parentDNs( &o, &mbr_f[i].nf_f->f_av_value ); + if ( gi->gi_numDNs ) { + int j; + Filter *f, *nf; + TAvlnode *t; + DNpair *dp; + + f = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); + f->f_next = NULL; + t = ldap_tavl_end( gi->gi_DNs, TAVL_DIR_RIGHT ); + do { + dp = t->avl_data; + if ( dp->dp_flag ) { + nf = f; + nf->f_ava = op->o_tmpcalloc( 1, sizeof( AttributeAssertion ), op->o_tmpmemctx ); + nf->f_choice = LDAP_FILTER_EQUALITY; + nf->f_av_desc = ad; + nf->f_av_value = dp->dp_ndn; + f = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); + f->f_next = nf; + } + t = ldap_tavl_next( t, TAVL_DIR_LEFT ); + op->o_tmpfree( dp, op->o_tmpmemctx ); + } while ( t ); + ldap_tavl_free( gi->gi_DNs, NULL ); + f->f_choice = LDAP_FILTER_EQUALITY; + f->f_ava = mbr_f[i].nf_f->f_ava; + mbr_f[i].nf_new = f; + } + } + o.o_bd->bd_info = (BackendInfo *)on->on_info; + op->o_tmpfree( sc, op->o_tmpmemctx ); +} + +static void +nestgroup_addUnique( Operation *op, Attribute *old, Attribute *new ) +{ + /* strip out any duplicates from new before adding */ + struct berval *bv, *nbv; + int i, j, flags; + + bv = op->o_tmpalloc( (new->a_numvals + 1) * 2 * sizeof(struct berval), op->o_tmpmemctx ); + nbv = bv + new->a_numvals+1; + + flags = SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH|SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH; + for (i=0,j=0; i<new->a_numvals; i++) { + int rc = attr_valfind( old, flags, &new->a_nvals[i], NULL, NULL ); + if ( rc ) { + bv[j] = new->a_vals[i]; + nbv[j++] = new->a_nvals[i]; + } + } + BER_BVZERO( &bv[j] ); + BER_BVZERO( &nbv[j] ); + attr_valadd( old, bv, nbv, j ); + op->o_tmpfree( bv, op->o_tmpmemctx ); +} + +static void +nestgroup_get_childDNs( Operation *op, slap_overinst *on, gdn_info *gi, struct berval *ndn ) +{ + nestgroup_info_t *ngi = on->on_bi.bi_private; + Entry *e; + Attribute *a; + + if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) != LDAP_SUCCESS || e == NULL ) + return; + + a = attr_find( e->e_attrs, ngi->ngi_member ); + if ( a ) { + int i, j; + for (i = 0; i<a->a_numvals; i++ ) { + /* record all group entries */ + for (j = 0; !BER_BVISEMPTY( &ngi->ngi_groupBase[j] ); j++) { + if ( dnIsSuffix( &a->a_nvals[i], &ngi->ngi_ngroupBase[j] )) { + DNpair *dp = op->o_tmpalloc( sizeof(DNpair), op->o_tmpmemctx ); + dp->dp_ndn = a->a_nvals[i]; + if ( ldap_tavl_insert( &gi->gi_DNs, dp, nestgroup_dncmp, ldap_avl_dup_error )) { + op->o_tmpfree( dp, op->o_tmpmemctx ); + } else { + ber_dupbv_x( &dp->dp_ndn, &a->a_nvals[i], op->o_tmpmemctx ); + gi->gi_numDNs++; + dp->dp_next = gi->gi_DNlist; + gi->gi_DNlist = dp; + } + break; + } + } + } + if ( gi->gi_merge ) { + nestgroup_addUnique( op, gi->gi_merge, a ); + } + } + overlay_entry_release_ov( op, e, 0, on ); +} + +static void +nestgroup_memberOfFilter( Operation *op, int mof_nf, nestgroup_filterinst_t *mof_f ) +{ + slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; + AttributeDescription *ad = mof_f[0].nf_f->f_av_desc; + gdn_info gi = {0}; + int i; + + for ( i=0; i<mof_nf; i++ ) { + gi.gi_DNs = NULL; + gi.gi_numDNs = 0; + nestgroup_get_childDNs( op, on, &gi, &mof_f[i].nf_f->f_av_value ); + + while ( gi.gi_DNlist ) { + DNpair *dp = gi.gi_DNlist; + gi.gi_DNlist = NULL; + for ( ; dp; dp=dp->dp_next ) { + nestgroup_get_childDNs( op, on, &gi, &dp->dp_ndn ); + } + } + + if ( gi.gi_numDNs ) { + int j; + Filter *f, *nf; + TAvlnode *t; + DNpair *dp; + + f = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); + f->f_next = NULL; + t = ldap_tavl_end( gi.gi_DNs, TAVL_DIR_RIGHT ); + do { + dp = t->avl_data; + nf = f; + nf->f_ava = op->o_tmpcalloc( 1, sizeof( AttributeAssertion ), op->o_tmpmemctx ); + nf->f_choice = LDAP_FILTER_EQUALITY; + nf->f_av_desc = ad; + nf->f_av_value = dp->dp_ndn; + f = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx ); + f->f_next = nf; + t = ldap_tavl_next( t, TAVL_DIR_LEFT ); + op->o_tmpfree( dp, op->o_tmpmemctx ); + } while ( t ); + ldap_tavl_free( gi.gi_DNs, NULL ); + f->f_choice = LDAP_FILTER_EQUALITY; + f->f_ava = mof_f[i].nf_f->f_ava; + mof_f[i].nf_new = f; + } + } +} + +static void +nestgroup_memberOfVals( Operation *op, slap_overinst *on, Attribute *a ) +{ + nestgroup_info_t *ngi = on->on_bi.bi_private; + Operation o = *op; + slap_callback *sc; + gdn_info *gi; + Filter mf; + AttributeAssertion mava; + int i; + + o.o_managedsait = SLAP_CONTROL_CRITICAL; + sc = op->o_tmpcalloc( 1, sizeof(slap_callback) + sizeof(gdn_info), op->o_tmpmemctx); + gi = (gdn_info *)(sc+1); + gi->gi_ngi = ngi; + o.o_callback = sc; + sc->sc_response = nestgroup_gotDNresp; + o.ors_attrs = slap_anlist_no_attrs; + + mf.f_choice = LDAP_FILTER_EQUALITY; + mf.f_ava = &mava; + mf.f_av_desc = ngi->ngi_member; + mf.f_next = NULL; + + o.ors_filter = &mf; + o.ors_scope = LDAP_SCOPE_SUBTREE; + o.ors_deref = LDAP_DEREF_NEVER; + o.ors_limit = NULL; + o.ors_tlimit = SLAP_NO_LIMIT; + o.ors_slimit = SLAP_NO_LIMIT; + o.o_bd->bd_info = (BackendInfo *)on->on_info; + gi->gi_saveDN = 1; + + for ( i=0; i<a->a_numvals; i++ ) { + nestgroup_get_parentDNs( &o, &a->a_nvals[i] ); + + while ( gi->gi_DNlist ) { + DNpair *dp = gi->gi_DNlist; + gi->gi_DNlist = NULL; + for ( ; dp; dp=dp->dp_next ) { + nestgroup_get_parentDNs( &o, &dp->dp_ndn ); + } + } + } + if ( gi->gi_DNs ) { + TAvlnode *p = ldap_tavl_end( gi->gi_DNs, TAVL_DIR_LEFT ); + int flags = SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH|SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH; + do { + DNpair *dp = p->avl_data; + int rc = attr_valfind( a, flags, &dp->dp_ndn, NULL, NULL ); + if ( rc ) + attr_valadd( a, &dp->dp_dn, &dp->dp_ndn, 1 ); + op->o_tmpfree( dp->dp_dn.bv_val, op->o_tmpmemctx ); + op->o_tmpfree( dp->dp_ndn.bv_val, op->o_tmpmemctx ); + op->o_tmpfree( dp, op->o_tmpmemctx ); + p = ldap_tavl_next( p, TAVL_DIR_RIGHT ); + } while ( p ); + ldap_tavl_free( gi->gi_DNs, NULL ); + } + o.o_bd->bd_info = (BackendInfo *)on->on_info; + op->o_tmpfree( sc, op->o_tmpmemctx ); +} + +typedef struct nestgroup_cbinfo { + slap_overinst *nc_on; + int nc_needed; +} nestgroup_cbinfo; + +static int +nestgroup_searchresp( Operation *op, SlapReply *rs ) +{ + if (rs->sr_type == REP_SEARCH ) { + nestgroup_cbinfo *nc = op->o_callback->sc_private; + slap_overinst *on = nc->nc_on; + nestgroup_info_t *ngi = on->on_bi.bi_private; + Attribute *a; + + if ( nc->nc_needed & NG_MBR_VALUES ) { + a = attr_find( rs->sr_entry->e_attrs, ngi->ngi_member ); + if ( a ) { + gdn_info gi = {0}; + int i, j; + if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE )) { + Entry *e = entry_dup( rs->sr_entry ); + rs_replace_entry( op, rs, on, e ); + rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED; + a = attr_find( e->e_attrs, ngi->ngi_member ); + } + gi.gi_merge = a; + + for ( i=0; i<a->a_numvals; i++ ) { + for ( j=0; !BER_BVISEMPTY( &ngi->ngi_ngroupBase[j] ); j++ ) { + if ( dnIsSuffix( &a->a_nvals[i], &ngi->ngi_ngroupBase[j] )) { + nestgroup_get_childDNs( op, on, &gi, &a->a_nvals[i] ); + + while ( gi.gi_DNlist ) { + DNpair *dp = gi.gi_DNlist; + gi.gi_DNlist = NULL; + for ( ; dp; dp=dp->dp_next ) { + nestgroup_get_childDNs( op, on, &gi, &dp->dp_ndn ); + } + } + break; + } + } + } + if ( gi.gi_numDNs ) { + TAvlnode *p = ldap_tavl_end( gi.gi_DNs, TAVL_DIR_LEFT ); + do { + DNpair *dp = p->avl_data; + op->o_tmpfree( dp->dp_ndn.bv_val, op->o_tmpmemctx ); + op->o_tmpfree( dp, op->o_tmpmemctx ); + p = ldap_tavl_next( p, TAVL_DIR_RIGHT ); + } while ( p ); + ldap_tavl_free( gi.gi_DNs, NULL ); + } + } + } + + if ( nc->nc_needed & NG_MOF_VALUES ) { + a = attr_find( rs->sr_entry->e_attrs, ngi->ngi_memberOf ); + if ( a ) { + if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE )) { + Entry *e = entry_dup( rs->sr_entry ); + rs_replace_entry( op, rs, on, e ); + rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED; + a = attr_find( e->e_attrs, ngi->ngi_memberOf ); + } + nestgroup_memberOfVals( op, on, a ); + } + } + if (( nc->nc_needed & NG_NEGATED ) && + test_filter( op, rs->sr_entry, op->ors_filter ) != LDAP_COMPARE_TRUE ) + return 0; + } + return SLAP_CB_CONTINUE; +} + +static int +nestgroup_op_search( Operation *op, SlapReply *rs ) +{ + slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; + nestgroup_info_t *ngi = on->on_bi.bi_private; + int mbr_nf = 0, mof_nf = 0, negated = 0; + nestgroup_filterinst_t *mbr_f = NULL, *mof_f = NULL; + + if ( get_manageDSAit( op )) + return SLAP_CB_CONTINUE; + + /* groupBase must be explicitly configured */ + if ( !ngi->ngi_ngroupBase ) + return SLAP_CB_CONTINUE; + + /* handle attrs in filter */ + if ( ngi->ngi_flags & NG_MBR_FILTER ) { + nestgroup_filter_instances( op, ngi->ngi_member, op->ors_filter, 0, &mbr_nf, &mbr_f, &negated ); + if ( mbr_nf ) { + /* find member=(parent groups) */ + nestgroup_memberFilter( op, mbr_nf, mbr_f ); + } + } + if ( ngi->ngi_flags & NG_MOF_FILTER ) { + nestgroup_filter_instances( op, ngi->ngi_memberOf, op->ors_filter, 0, &mof_nf, &mof_f, &negated ); + if ( mof_nf ) { + /* find memberOf=(child groups) */ + nestgroup_memberOfFilter( op, mof_nf, mof_f ); + } + } + if ( mbr_nf ) { + int i; + for ( i=0; i<mbr_nf; i++ ) { + if ( mbr_f[i].nf_new ) { + mbr_f[i].nf_f->f_choice = LDAP_FILTER_OR; + mbr_f[i].nf_f->f_list = mbr_f[i].nf_new; + } + } + op->o_tmpfree( mbr_f, op->o_tmpmemctx ); + } + if ( mof_nf ) { + int i; + for ( i=0; i<mof_nf; i++ ) { + if ( mof_f[i].nf_new ) { + mof_f[i].nf_f->f_choice = LDAP_FILTER_OR; + mof_f[i].nf_f->f_list = mof_f[i].nf_new; + } + } + op->o_tmpfree( mof_f, op->o_tmpmemctx ); + } + + if ( ngi->ngi_flags & ( NG_MBR_VALUES|NG_MOF_VALUES )) { + /* check for attrs in attrlist */ + int attrflags = slap_attr_flags( op->ors_attrs ); + int needed = 0; + if (( ngi->ngi_flags & NG_MBR_VALUES ) && + nestgroup_check_needed( op, attrflags, ngi->ngi_member )) { + /* collect all members from child groups */ + needed |= NG_MBR_VALUES; + } + if (( ngi->ngi_flags & NG_MOF_VALUES ) && + nestgroup_check_needed( op, attrflags, ngi->ngi_memberOf )) { + /* collect DNs of all parent groups */ + needed |= NG_MOF_VALUES; + } + if ( needed ) { + nestgroup_cbinfo *nc; + slap_callback *sc = op->o_tmpcalloc( 1, sizeof(slap_callback)+sizeof(nestgroup_cbinfo), op->o_tmpmemctx ); + nc = (nestgroup_cbinfo *)(sc+1); + sc->sc_private = nc; + nc->nc_needed = needed; + nc->nc_on = on; + sc->sc_response = nestgroup_searchresp; + sc->sc_next = op->o_callback; + op->o_callback = sc; + if ( negated ) nc->nc_needed |= NG_NEGATED; + } + } + return SLAP_CB_CONTINUE; +} + +static int +nestgroup_db_init( + BackendDB *be, + ConfigReply *cr) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + nestgroup_info_t *ngi; + int rc; + const char *text = NULL; + + ngi = (nestgroup_info_t *)ch_calloc( 1, sizeof( *ngi )); + on->on_bi.bi_private = ngi; + + if ( !ad_memberOf ) { + rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &ad_memberOf, &text ); + if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, "nestgroup_db_init: " + "unable to find attribute=\"%s\": %s (%d)\n", + SLAPD_MEMBEROF_ATTR, text, rc ); + return rc; + } + } + + if ( !ad_member ) { + rc = slap_str2ad( SLAPD_GROUP_ATTR, &ad_member, &text ); + if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, "nestgroup_db_init: " + "unable to find attribute=\"%s\": %s (%d)\n", + SLAPD_GROUP_ATTR, text, rc ); + return rc; + } + } + + return 0; +} + +static int +nestgroup_db_open( + BackendDB *be, + ConfigReply *cr) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + nestgroup_info_t *ngi = on->on_bi.bi_private; + + if ( !ngi->ngi_member ) + ngi->ngi_member = ad_member; + + if ( !ngi->ngi_memberOf ) + ngi->ngi_memberOf = ad_memberOf; + + return 0; +} + +static int +nestgroup_db_destroy( + BackendDB *be, + ConfigReply *cr +) +{ + slap_overinst *on = (slap_overinst *) be->bd_info; + nestgroup_info_t *ngi = on->on_bi.bi_private; + + ber_bvarray_free( ngi->ngi_groupBase ); + ber_bvarray_free( ngi->ngi_ngroupBase ); + ch_free( ngi ); + + return 0; +} + +static slap_overinst nestgroup; + +/* This overlay is set up for dynamic loading via moduleload. For static + * configuration, you'll need to arrange for the slap_overinst to be + * initialized and registered by some other function inside slapd. + */ + +int nestgroup_initialize() { + int code; + + code = register_at( + "( 1.2.840.113556.1.2.102 " + "NAME 'memberOf' " + "DESC 'Group that the entry belongs to' " + "SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' " + "EQUALITY distinguishedNameMatch " /* added */ + "USAGE dSAOperation " /* added; questioned */ + "NO-USER-MODIFICATION " /* added */ + "X-ORIGIN 'iPlanet Delegated Administrator' )", + &ad_memberOf, 0 ); + if ( code && code != SLAP_SCHERR_ATTR_DUP ) { + Debug( LDAP_DEBUG_ANY, + "nestgroup_initialize: register_at (memberOf) failed\n" ); + return code; + } + + nestgroup.on_bi.bi_type = "nestgroup"; + nestgroup.on_bi.bi_db_init = nestgroup_db_init; + nestgroup.on_bi.bi_db_open = nestgroup_db_open; + nestgroup.on_bi.bi_db_destroy = nestgroup_db_destroy; + + nestgroup.on_bi.bi_op_search = nestgroup_op_search; +/* nestgroup.on_bi.bi_op_compare = nestgroup_op_compare; */ + + nestgroup.on_bi.bi_cf_ocs = ngroupocs; + code = config_register_schema( ngroupcfg, ngroupocs ); + if ( code ) return code; + + return overlay_register( &nestgroup ); +} + +#if SLAPD_OVER_NESTGROUP == SLAPD_MOD_DYNAMIC +int +init_module( int argc, char *argv[] ) +{ + return nestgroup_initialize(); +} +#endif + +#endif /* defined(SLAPD_OVER_NESTGROUP) */ |