/* nestgroup.c - nested group overlay */ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * * 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 * . */ /* ACKNOWLEDGEMENTS: * This work was initially developed by Howard Chu for inclusion in * OpenLDAP Software. */ #include "portable.h" #ifdef SLAPD_OVER_NESTGROUP #include #include #include #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; igi_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; ia_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; ia_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; if_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; ia_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; ia_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; if_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; if_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) */