mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-27 03:20:22 +08:00
2210 lines
55 KiB
C
2210 lines
55 KiB
C
/* memberof.c - back-reference for group membership */
|
|
/* $OpenLDAP$ */
|
|
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
|
*
|
|
* Copyright 2005-2007 Pierangelo Masarati <ando@sys-net.it>
|
|
* 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>.
|
|
*/
|
|
/* ACKNOWLEDGMENTS:
|
|
* This work was initially developed by Pierangelo Masarati for inclusion
|
|
* in OpenLDAP Software, sponsored by SysNet s.r.l.
|
|
*/
|
|
|
|
#include "portable.h"
|
|
|
|
#ifdef SLAPD_OVER_MEMBEROF
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "ac/string.h"
|
|
#include "ac/socket.h"
|
|
|
|
#include "slap.h"
|
|
#include "slap-config.h"
|
|
#include "lutil.h"
|
|
|
|
/*
|
|
* Glossary:
|
|
*
|
|
* GROUP a group object (an entry with GROUP_OC
|
|
* objectClass)
|
|
* MEMBER a member object (an entry whose DN is
|
|
* listed as MEMBER_AT value of a GROUP)
|
|
* GROUP_OC the objectClass of the group object
|
|
* (default: groupOfNames)
|
|
* MEMBER_AT the membership attribute, DN-valued;
|
|
* note: nameAndOptionalUID is tolerated
|
|
* as soon as the optionalUID is absent
|
|
* (default: member)
|
|
* MEMBER_OF reverse membership attribute
|
|
* (default: memberOf)
|
|
*
|
|
* - add:
|
|
* - if the entry that is being added is a GROUP,
|
|
* the MEMBER_AT defined as values of the add operation
|
|
* get the MEMBER_OF value directly from the request.
|
|
*
|
|
* if configured to do so, the MEMBER objects do not exist,
|
|
* and no relax control is issued, either:
|
|
* - fail
|
|
* - drop non-existing members
|
|
* (by default: don't muck with values)
|
|
*
|
|
* - if (configured to do so,) the referenced GROUP exists,
|
|
* the relax control is set and the user has
|
|
* "manage" privileges, allow to add MEMBER_OF values to
|
|
* generic entries.
|
|
*
|
|
* - modify:
|
|
* - if the entry being modified is a GROUP_OC and the
|
|
* MEMBER_AT attribute is modified, the MEMBER_OF value
|
|
* of the (existing) MEMBER_AT entries that are affected
|
|
* is modified according to the request:
|
|
* - if a MEMBER is removed from the group,
|
|
* delete the corresponding MEMBER_OF
|
|
* - if a MEMBER is added to a group,
|
|
* add the corresponding MEMBER_OF
|
|
*
|
|
* We need to determine, from the database, if it is
|
|
* a GROUP_OC, and we need to check, from the
|
|
* modification list, if the MEMBER_AT attribute is being
|
|
* affected, and what MEMBER_AT values are affected.
|
|
*
|
|
* if configured to do so, the entries corresponding to
|
|
* the MEMBER_AT values do not exist, and no relax control
|
|
* is issued, either:
|
|
* - fail
|
|
* - drop non-existing members
|
|
* (by default: don't muck with values)
|
|
*
|
|
* - if configured to do so, the referenced GROUP exists,
|
|
* (the relax control is set) and the user has
|
|
* "manage" privileges, allow to add MEMBER_OF values to
|
|
* generic entries; the change is NOT automatically reflected
|
|
* in the MEMBER attribute of the GROUP referenced
|
|
* by the value of MEMBER_OF; a separate modification,
|
|
* with or without relax control, needs to be performed.
|
|
*
|
|
* - modrdn:
|
|
* - if the entry being renamed is a GROUP, the MEMBER_OF
|
|
* value of the (existing) MEMBER objects is modified
|
|
* accordingly based on the newDN of the GROUP.
|
|
*
|
|
* We need to determine, from the database, if it is
|
|
* a GROUP; the list of MEMBER objects is obtained from
|
|
* the database.
|
|
*
|
|
* Non-existing MEMBER objects are ignored, since the
|
|
* MEMBER_AT is not being addressed by the operation.
|
|
*
|
|
* - if the entry being renamed has the MEMBER_OF attribute,
|
|
* the corresponding MEMBER value must be modified in the
|
|
* respective group entries.
|
|
*
|
|
*
|
|
* - delete:
|
|
* - if the entry being deleted is a GROUP, the (existing)
|
|
* MEMBER objects are modified accordingly; a copy of the
|
|
* values of the MEMBER_AT is saved and, if the delete
|
|
* succeeds, the MEMBER_OF value of the (existing) MEMBER
|
|
* objects is deleted.
|
|
*
|
|
* We need to determine, from the database, if it is
|
|
* a GROUP.
|
|
*
|
|
* Non-existing MEMBER objects are ignored, since the entry
|
|
* is being deleted.
|
|
*
|
|
* - if the entry being deleted has the MEMBER_OF attribute,
|
|
* the corresponding value of the MEMBER_AT must be deleted
|
|
* from the respective GROUP entries.
|
|
*/
|
|
|
|
#define SLAPD_MEMBEROF_ATTR "memberOf"
|
|
|
|
static AttributeDescription *ad_member;
|
|
static AttributeDescription *ad_memberOf;
|
|
|
|
static ObjectClass *oc_group;
|
|
|
|
static slap_overinst memberof;
|
|
|
|
typedef struct memberof_t {
|
|
struct berval mo_dn;
|
|
struct berval mo_ndn;
|
|
|
|
ObjectClass *mo_oc_group;
|
|
AttributeDescription *mo_ad_member;
|
|
AttributeDescription *mo_ad_memberof;
|
|
|
|
struct berval mo_groupFilterstr;
|
|
AttributeAssertion mo_groupAVA;
|
|
Filter mo_groupFilter;
|
|
|
|
struct berval mo_memberFilterstr;
|
|
Filter mo_memberFilter;
|
|
|
|
unsigned mo_flags;
|
|
#define MEMBEROF_NONE 0x00U
|
|
#define MEMBEROF_FDANGLING_DROP 0x01U
|
|
#define MEMBEROF_FDANGLING_ERROR 0x02U
|
|
#define MEMBEROF_FDANGLING_MASK (MEMBEROF_FDANGLING_DROP|MEMBEROF_FDANGLING_ERROR)
|
|
#define MEMBEROF_FREFINT 0x04U
|
|
#define MEMBEROF_FREVERSE 0x08U
|
|
|
|
ber_int_t mo_dangling_err;
|
|
|
|
#define MEMBEROF_CHK(mo,f) \
|
|
(((mo)->mo_flags & (f)) == (f))
|
|
#define MEMBEROF_DANGLING_CHECK(mo) \
|
|
((mo)->mo_flags & MEMBEROF_FDANGLING_MASK)
|
|
#define MEMBEROF_DANGLING_DROP(mo) \
|
|
MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_DROP)
|
|
#define MEMBEROF_DANGLING_ERROR(mo) \
|
|
MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_ERROR)
|
|
#define MEMBEROF_REFINT(mo) \
|
|
MEMBEROF_CHK((mo),MEMBEROF_FREFINT)
|
|
#define MEMBEROF_REVERSE(mo) \
|
|
MEMBEROF_CHK((mo),MEMBEROF_FREVERSE)
|
|
} memberof_t;
|
|
|
|
typedef enum memberof_is_t {
|
|
MEMBEROF_IS_NONE = 0x00,
|
|
MEMBEROF_IS_GROUP = 0x01,
|
|
MEMBEROF_IS_MEMBER = 0x02,
|
|
MEMBEROF_IS_BOTH = (MEMBEROF_IS_GROUP|MEMBEROF_IS_MEMBER)
|
|
} memberof_is_t;
|
|
|
|
typedef struct memberof_cookie_t {
|
|
AttributeDescription *ad;
|
|
BerVarray vals;
|
|
int foundit;
|
|
} memberof_cookie_t;
|
|
|
|
typedef struct memberof_cbinfo_t {
|
|
slap_overinst *on;
|
|
BerVarray member;
|
|
BerVarray memberof;
|
|
memberof_is_t what;
|
|
} memberof_cbinfo_t;
|
|
|
|
static void
|
|
memberof_set_backend( Operation *op_target, Operation *op, slap_overinst *on )
|
|
{
|
|
BackendInfo *bi = op->o_bd->bd_info;
|
|
|
|
if ( bi->bi_type == memberof.on_bi.bi_type )
|
|
op_target->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
}
|
|
|
|
static int
|
|
memberof_isGroupOrMember_cb( Operation *op, SlapReply *rs )
|
|
{
|
|
if ( rs->sr_type == REP_SEARCH ) {
|
|
memberof_cookie_t *mc;
|
|
|
|
mc = (memberof_cookie_t *)op->o_callback->sc_private;
|
|
mc->foundit = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* callback for internal search that saves the member attribute values
|
|
* of groups being deleted.
|
|
*/
|
|
static int
|
|
memberof_saveMember_cb( Operation *op, SlapReply *rs )
|
|
{
|
|
if ( rs->sr_type == REP_SEARCH ) {
|
|
memberof_cookie_t *mc;
|
|
Attribute *a;
|
|
|
|
mc = (memberof_cookie_t *)op->o_callback->sc_private;
|
|
mc->foundit = 1;
|
|
|
|
assert( rs->sr_entry != NULL );
|
|
assert( rs->sr_entry->e_attrs != NULL );
|
|
|
|
a = attr_find( rs->sr_entry->e_attrs, mc->ad );
|
|
if ( a != NULL ) {
|
|
ber_bvarray_dup_x( &mc->vals, a->a_nvals, op->o_tmpmemctx );
|
|
|
|
assert( attr_find( a->a_next, mc->ad ) == NULL );
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* the delete hook performs an internal search that saves the member
|
|
* attribute values of groups being deleted.
|
|
*/
|
|
static int
|
|
memberof_isGroupOrMember( Operation *op, memberof_cbinfo_t *mci )
|
|
{
|
|
slap_overinst *on = mci->on;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
Operation op2 = *op;
|
|
slap_callback cb = { 0 };
|
|
BackendInfo *bi = op->o_bd->bd_info;
|
|
AttributeName an[ 2 ];
|
|
|
|
memberof_is_t iswhat = MEMBEROF_IS_NONE;
|
|
memberof_cookie_t mc;
|
|
|
|
assert( mci->what != MEMBEROF_IS_NONE );
|
|
|
|
cb.sc_private = &mc;
|
|
if ( op->o_tag == LDAP_REQ_DELETE ) {
|
|
cb.sc_response = memberof_saveMember_cb;
|
|
|
|
} else {
|
|
cb.sc_response = memberof_isGroupOrMember_cb;
|
|
}
|
|
|
|
op2.o_tag = LDAP_REQ_SEARCH;
|
|
op2.o_callback = &cb;
|
|
op2.o_dn = op->o_bd->be_rootdn;
|
|
op2.o_ndn = op->o_bd->be_rootndn;
|
|
|
|
op2.ors_scope = LDAP_SCOPE_BASE;
|
|
op2.ors_deref = LDAP_DEREF_NEVER;
|
|
BER_BVZERO( &an[ 1 ].an_name );
|
|
op2.ors_attrs = an;
|
|
op2.ors_attrsonly = 0;
|
|
op2.ors_limit = NULL;
|
|
op2.ors_slimit = 1;
|
|
op2.ors_tlimit = SLAP_NO_LIMIT;
|
|
|
|
if ( mci->what & MEMBEROF_IS_GROUP ) {
|
|
SlapReply rs2 = { REP_RESULT };
|
|
|
|
mc.ad = mo->mo_ad_member;
|
|
mc.foundit = 0;
|
|
mc.vals = NULL;
|
|
an[ 0 ].an_desc = mo->mo_ad_member;
|
|
an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
|
|
op2.ors_filterstr = mo->mo_groupFilterstr;
|
|
op2.ors_filter = &mo->mo_groupFilter;
|
|
op2.o_do_not_cache = 1; /* internal search, don't log */
|
|
|
|
memberof_set_backend( &op2, op, on );
|
|
(void)op->o_bd->be_search( &op2, &rs2 );
|
|
op2.o_bd->bd_info = bi;
|
|
|
|
if ( mc.foundit ) {
|
|
iswhat |= MEMBEROF_IS_GROUP;
|
|
if ( mc.vals ) mci->member = mc.vals;
|
|
|
|
}
|
|
}
|
|
|
|
if ( mci->what & MEMBEROF_IS_MEMBER ) {
|
|
SlapReply rs2 = { REP_RESULT };
|
|
|
|
mc.ad = mo->mo_ad_memberof;
|
|
mc.foundit = 0;
|
|
mc.vals = NULL;
|
|
an[ 0 ].an_desc = mo->mo_ad_memberof;
|
|
an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
|
|
op2.ors_filterstr = mo->mo_memberFilterstr;
|
|
op2.ors_filter = &mo->mo_memberFilter;
|
|
op2.o_do_not_cache = 1; /* internal search, don't log */
|
|
|
|
memberof_set_backend( &op2, op, on );
|
|
(void)op->o_bd->be_search( &op2, &rs2 );
|
|
op2.o_bd->bd_info = bi;
|
|
|
|
if ( mc.foundit ) {
|
|
iswhat |= MEMBEROF_IS_MEMBER;
|
|
if ( mc.vals ) mci->memberof = mc.vals;
|
|
|
|
}
|
|
}
|
|
|
|
mci->what = iswhat;
|
|
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* response callback that adds memberof values when a group is modified.
|
|
*/
|
|
static void
|
|
memberof_value_modify(
|
|
Operation *op,
|
|
struct berval *ndn,
|
|
AttributeDescription *ad,
|
|
struct berval *old_dn,
|
|
struct berval *old_ndn,
|
|
struct berval *new_dn,
|
|
struct berval *new_ndn )
|
|
{
|
|
memberof_cbinfo_t *mci = op->o_callback->sc_private;
|
|
slap_overinst *on = mci->on;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
Operation op2 = *op;
|
|
unsigned long opid = op->o_opid;
|
|
SlapReply rs2 = { REP_RESULT };
|
|
slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
|
|
Modifications mod[ 2 ] = { { { 0 } } }, *ml;
|
|
struct berval values[ 4 ], nvalues[ 4 ];
|
|
int mcnt = 0;
|
|
|
|
if ( old_ndn != NULL && new_ndn != NULL &&
|
|
ber_bvcmp( old_ndn, new_ndn ) == 0 ) {
|
|
/* DNs compare equal, it's a noop */
|
|
return;
|
|
}
|
|
|
|
op2.o_tag = LDAP_REQ_MODIFY;
|
|
|
|
op2.o_req_dn = *ndn;
|
|
op2.o_req_ndn = *ndn;
|
|
|
|
op2.o_callback = &cb;
|
|
op2.o_dn = op->o_bd->be_rootdn;
|
|
op2.o_ndn = op->o_bd->be_rootndn;
|
|
op2.orm_modlist = NULL;
|
|
|
|
/* Internal ops, never replicate these */
|
|
op2.o_opid = 0; /* shared with op, saved above */
|
|
op2.orm_no_opattrs = 1;
|
|
op2.o_dont_replicate = 1;
|
|
|
|
if ( !BER_BVISNULL( &mo->mo_ndn ) ) {
|
|
ml = &mod[ mcnt ];
|
|
ml->sml_numvals = 1;
|
|
ml->sml_values = &values[ 0 ];
|
|
ml->sml_values[ 0 ] = mo->mo_dn;
|
|
BER_BVZERO( &ml->sml_values[ 1 ] );
|
|
ml->sml_nvalues = &nvalues[ 0 ];
|
|
ml->sml_nvalues[ 0 ] = mo->mo_ndn;
|
|
BER_BVZERO( &ml->sml_nvalues[ 1 ] );
|
|
ml->sml_desc = slap_schema.si_ad_modifiersName;
|
|
ml->sml_type = ml->sml_desc->ad_cname;
|
|
ml->sml_op = LDAP_MOD_REPLACE;
|
|
ml->sml_flags = SLAP_MOD_INTERNAL;
|
|
ml->sml_next = op2.orm_modlist;
|
|
op2.orm_modlist = ml;
|
|
|
|
mcnt++;
|
|
}
|
|
|
|
ml = &mod[ mcnt ];
|
|
ml->sml_numvals = 1;
|
|
ml->sml_values = &values[ 2 ];
|
|
BER_BVZERO( &ml->sml_values[ 1 ] );
|
|
ml->sml_nvalues = &nvalues[ 2 ];
|
|
BER_BVZERO( &ml->sml_nvalues[ 1 ] );
|
|
ml->sml_desc = ad;
|
|
ml->sml_type = ml->sml_desc->ad_cname;
|
|
ml->sml_flags = SLAP_MOD_INTERNAL;
|
|
ml->sml_next = op2.orm_modlist;
|
|
op2.orm_modlist = ml;
|
|
|
|
if ( new_ndn != NULL ) {
|
|
BackendInfo *bi = op2.o_bd->bd_info;
|
|
OpExtra oex;
|
|
|
|
assert( !BER_BVISNULL( new_dn ) );
|
|
assert( !BER_BVISNULL( new_ndn ) );
|
|
|
|
ml = &mod[ mcnt ];
|
|
ml->sml_op = LDAP_MOD_ADD;
|
|
|
|
ml->sml_values[ 0 ] = *new_dn;
|
|
ml->sml_nvalues[ 0 ] = *new_ndn;
|
|
|
|
oex.oe_key = (void *)&memberof;
|
|
LDAP_SLIST_INSERT_HEAD(&op2.o_extra, &oex, oe_next);
|
|
memberof_set_backend( &op2, op, on );
|
|
(void)op->o_bd->be_modify( &op2, &rs2 );
|
|
op2.o_bd->bd_info = bi;
|
|
LDAP_SLIST_REMOVE(&op2.o_extra, &oex, OpExtra, oe_next);
|
|
if ( rs2.sr_err != LDAP_SUCCESS ) {
|
|
Debug(LDAP_DEBUG_ANY,
|
|
"%s: memberof_value_modify DN=\"%s\" add %s=\"%s\" failed err=%d\n",
|
|
op->o_log_prefix, op2.o_req_dn.bv_val,
|
|
ad->ad_cname.bv_val, new_dn->bv_val, rs2.sr_err );
|
|
}
|
|
|
|
assert( op2.orm_modlist == &mod[ mcnt ] );
|
|
assert( mcnt == 0 || op2.orm_modlist->sml_next == &mod[ 0 ] );
|
|
ml = op2.orm_modlist->sml_next;
|
|
if ( mcnt == 1 ) {
|
|
assert( ml == &mod[ 0 ] );
|
|
ml = ml->sml_next;
|
|
}
|
|
if ( ml != NULL ) {
|
|
slap_mods_free( ml, 1 );
|
|
}
|
|
|
|
mod[ 0 ].sml_next = NULL;
|
|
}
|
|
|
|
if ( old_ndn != NULL ) {
|
|
BackendInfo *bi = op2.o_bd->bd_info;
|
|
OpExtra oex;
|
|
|
|
assert( !BER_BVISNULL( old_dn ) );
|
|
assert( !BER_BVISNULL( old_ndn ) );
|
|
|
|
ml = &mod[ mcnt ];
|
|
ml->sml_op = LDAP_MOD_DELETE;
|
|
|
|
ml->sml_values[ 0 ] = *old_dn;
|
|
ml->sml_nvalues[ 0 ] = *old_ndn;
|
|
|
|
oex.oe_key = (void *)&memberof;
|
|
LDAP_SLIST_INSERT_HEAD(&op2.o_extra, &oex, oe_next);
|
|
memberof_set_backend( &op2, op, on );
|
|
(void)op->o_bd->be_modify( &op2, &rs2 );
|
|
op2.o_bd->bd_info = bi;
|
|
LDAP_SLIST_REMOVE(&op2.o_extra, &oex, OpExtra, oe_next);
|
|
if ( rs2.sr_err != LDAP_SUCCESS ) {
|
|
Debug(LDAP_DEBUG_ANY,
|
|
"%s: memberof_value_modify DN=\"%s\" delete %s=\"%s\" failed err=%d\n",
|
|
op->o_log_prefix, op2.o_req_dn.bv_val,
|
|
ad->ad_cname.bv_val, old_dn->bv_val, rs2.sr_err );
|
|
}
|
|
|
|
assert( op2.orm_modlist == &mod[ mcnt ] );
|
|
ml = op2.orm_modlist->sml_next;
|
|
if ( mcnt == 1 ) {
|
|
assert( ml == &mod[ 0 ] );
|
|
ml = ml->sml_next;
|
|
}
|
|
if ( ml != NULL ) {
|
|
slap_mods_free( ml, 1 );
|
|
}
|
|
}
|
|
/* restore original opid */
|
|
op->o_opid = opid;
|
|
|
|
/* FIXME: if old_group_ndn doesn't exist, both delete __and__
|
|
* add will fail; better split in two operations, although
|
|
* not optimal in terms of performance. At least it would
|
|
* move towards self-repairing capabilities. */
|
|
}
|
|
|
|
static int
|
|
memberof_cleanup( Operation *op, SlapReply *rs )
|
|
{
|
|
slap_callback *sc = op->o_callback;
|
|
memberof_cbinfo_t *mci = sc->sc_private;
|
|
|
|
op->o_callback = sc->sc_next;
|
|
if ( mci->memberof )
|
|
ber_bvarray_free_x( mci->memberof, op->o_tmpmemctx );
|
|
if ( mci->member )
|
|
ber_bvarray_free_x( mci->member, op->o_tmpmemctx );
|
|
op->o_tmpfree( sc, op->o_tmpmemctx );
|
|
return 0;
|
|
}
|
|
|
|
static int memberof_res_add( Operation *op, SlapReply *rs );
|
|
static int memberof_res_delete( Operation *op, SlapReply *rs );
|
|
static int memberof_res_modify( Operation *op, SlapReply *rs );
|
|
static int memberof_res_modrdn( Operation *op, SlapReply *rs );
|
|
|
|
static int
|
|
memberof_op_add( Operation *op, SlapReply *rs )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
Attribute **ap, **map = NULL;
|
|
int rc = SLAP_CB_CONTINUE;
|
|
int i;
|
|
struct berval save_dn, save_ndn;
|
|
slap_callback *sc;
|
|
memberof_cbinfo_t *mci;
|
|
OpExtra *oex;
|
|
|
|
LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
|
|
if ( oex->oe_key == (void *)&memberof )
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
if ( op->ora_e->e_attrs == NULL ) {
|
|
/* FIXME: global overlay; need to deal with */
|
|
Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
|
|
"consistency checks not implemented when overlay "
|
|
"is instantiated as global.\n",
|
|
op->o_log_prefix, op->o_req_dn.bv_val );
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
if ( MEMBEROF_REVERSE( mo ) ) {
|
|
for ( ap = &op->ora_e->e_attrs; *ap; ap = &(*ap)->a_next ) {
|
|
Attribute *a = *ap;
|
|
|
|
if ( a->a_desc == mo->mo_ad_memberof ) {
|
|
map = ap;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
save_dn = op->o_dn;
|
|
save_ndn = op->o_ndn;
|
|
|
|
if ( MEMBEROF_DANGLING_CHECK( mo )
|
|
&& !get_relax( op )
|
|
&& is_entry_objectclass_or_sub( op->ora_e, mo->mo_oc_group ) )
|
|
{
|
|
op->o_dn = op->o_bd->be_rootdn;
|
|
op->o_ndn = op->o_bd->be_rootndn;
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
|
|
for ( ap = &op->ora_e->e_attrs; *ap; ) {
|
|
Attribute *a = *ap;
|
|
|
|
if ( !is_ad_subtype( a->a_desc, mo->mo_ad_member ) ) {
|
|
ap = &a->a_next;
|
|
continue;
|
|
}
|
|
|
|
assert( a->a_nvals != NULL );
|
|
|
|
for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
|
|
Entry *e = NULL;
|
|
|
|
/* ITS#6670 Ignore member pointing to this entry */
|
|
if ( dn_match( &a->a_nvals[i], &save_ndn ))
|
|
continue;
|
|
|
|
rc = be_entry_get_rw( op, &a->a_nvals[ i ],
|
|
NULL, NULL, 0, &e );
|
|
if ( rc == LDAP_SUCCESS ) {
|
|
be_entry_release_r( op, e );
|
|
continue;
|
|
}
|
|
|
|
if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
|
|
rc = rs->sr_err = mo->mo_dangling_err;
|
|
rs->sr_text = "adding non-existing object "
|
|
"as group member";
|
|
send_ldap_result( op, rs );
|
|
goto done;
|
|
}
|
|
|
|
if ( MEMBEROF_DANGLING_DROP( mo ) ) {
|
|
int j;
|
|
|
|
Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
|
|
"member=\"%s\" does not exist (stripping...)\n",
|
|
op->o_log_prefix, op->ora_e->e_name.bv_val,
|
|
a->a_vals[ i ].bv_val );
|
|
|
|
for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
|
|
ber_memfree( a->a_vals[ i ].bv_val );
|
|
BER_BVZERO( &a->a_vals[ i ] );
|
|
if ( a->a_nvals != a->a_vals ) {
|
|
ber_memfree( a->a_nvals[ i ].bv_val );
|
|
BER_BVZERO( &a->a_nvals[ i ] );
|
|
}
|
|
a->a_numvals--;
|
|
if ( j - i == 1 ) {
|
|
break;
|
|
}
|
|
|
|
AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
|
|
sizeof( struct berval ) * ( j - i ) );
|
|
if ( a->a_nvals != a->a_vals ) {
|
|
AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
|
|
sizeof( struct berval ) * ( j - i ) );
|
|
}
|
|
i--;
|
|
}
|
|
}
|
|
|
|
/* If all values have been removed,
|
|
* remove the attribute itself. */
|
|
if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
|
|
*ap = a->a_next;
|
|
attr_free( a );
|
|
|
|
} else {
|
|
ap = &a->a_next;
|
|
}
|
|
}
|
|
op->o_dn = save_dn;
|
|
op->o_ndn = save_ndn;
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
}
|
|
|
|
if ( map != NULL ) {
|
|
Attribute *a = *map;
|
|
AccessControlState acl_state = ACL_STATE_INIT;
|
|
|
|
for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
|
|
Entry *e;
|
|
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
/* access is checked with the original identity */
|
|
rc = access_allowed( op, op->ora_e, mo->mo_ad_memberof,
|
|
&a->a_nvals[ i ], ACL_WADD,
|
|
&acl_state );
|
|
if ( rc == 0 ) {
|
|
rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
|
|
rs->sr_text = NULL;
|
|
send_ldap_result( op, rs );
|
|
goto done;
|
|
}
|
|
/* ITS#6670 Ignore member pointing to this entry */
|
|
if ( dn_match( &a->a_nvals[i], &save_ndn ))
|
|
continue;
|
|
|
|
rc = be_entry_get_rw( op, &a->a_nvals[ i ],
|
|
NULL, NULL, 0, &e );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
if ( get_relax( op ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
|
|
rc = rs->sr_err = mo->mo_dangling_err;
|
|
rs->sr_text = "adding non-existing object "
|
|
"as memberof";
|
|
send_ldap_result( op, rs );
|
|
goto done;
|
|
}
|
|
|
|
if ( MEMBEROF_DANGLING_DROP( mo ) ) {
|
|
int j;
|
|
|
|
Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
|
|
"memberof=\"%s\" does not exist (stripping...)\n",
|
|
op->o_log_prefix, op->ora_e->e_name.bv_val,
|
|
a->a_nvals[ i ].bv_val );
|
|
|
|
for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
|
|
ber_memfree( a->a_vals[ i ].bv_val );
|
|
BER_BVZERO( &a->a_vals[ i ] );
|
|
if ( a->a_nvals != a->a_vals ) {
|
|
ber_memfree( a->a_nvals[ i ].bv_val );
|
|
BER_BVZERO( &a->a_nvals[ i ] );
|
|
}
|
|
if ( j - i == 1 ) {
|
|
break;
|
|
}
|
|
|
|
AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
|
|
sizeof( struct berval ) * ( j - i ) );
|
|
if ( a->a_nvals != a->a_vals ) {
|
|
AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
|
|
sizeof( struct berval ) * ( j - i ) );
|
|
}
|
|
i--;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
/* access is checked with the original identity */
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
rc = access_allowed( op, e, mo->mo_ad_member,
|
|
&op->o_req_ndn, ACL_WADD, NULL );
|
|
be_entry_release_r( op, e );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
|
|
if ( !rc ) {
|
|
rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
|
|
rs->sr_text = "insufficient access to object referenced by memberof";
|
|
send_ldap_result( op, rs );
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
|
|
*map = a->a_next;
|
|
attr_free( a );
|
|
}
|
|
}
|
|
|
|
rc = SLAP_CB_CONTINUE;
|
|
|
|
sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
|
|
sc->sc_private = sc+1;
|
|
sc->sc_response = memberof_res_add;
|
|
sc->sc_cleanup = memberof_cleanup;
|
|
sc->sc_writewait = 0;
|
|
mci = sc->sc_private;
|
|
mci->on = on;
|
|
mci->member = NULL;
|
|
mci->memberof = NULL;
|
|
sc->sc_next = op->o_callback;
|
|
op->o_callback = sc;
|
|
|
|
done:;
|
|
op->o_dn = save_dn;
|
|
op->o_ndn = save_ndn;
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
memberof_op_delete( Operation *op, SlapReply *rs )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
slap_callback *sc;
|
|
memberof_cbinfo_t *mci;
|
|
OpExtra *oex;
|
|
|
|
LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
|
|
if ( oex->oe_key == (void *)&memberof )
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
|
|
sc->sc_private = sc+1;
|
|
sc->sc_response = memberof_res_delete;
|
|
sc->sc_cleanup = memberof_cleanup;
|
|
sc->sc_writewait = 0;
|
|
mci = sc->sc_private;
|
|
mci->on = on;
|
|
mci->member = NULL;
|
|
mci->memberof = NULL;
|
|
mci->what = MEMBEROF_IS_GROUP;
|
|
if ( MEMBEROF_REFINT( mo ) ) {
|
|
mci->what = MEMBEROF_IS_BOTH;
|
|
}
|
|
|
|
memberof_isGroupOrMember( op, mci );
|
|
|
|
sc->sc_next = op->o_callback;
|
|
op->o_callback = sc;
|
|
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
static int
|
|
memberof_op_modify( Operation *op, SlapReply *rs )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
Modifications **mlp, **mmlp = NULL;
|
|
int rc = SLAP_CB_CONTINUE, save_member = 0;
|
|
struct berval save_dn, save_ndn;
|
|
slap_callback *sc;
|
|
memberof_cbinfo_t *mci, mcis;
|
|
OpExtra *oex;
|
|
|
|
LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
|
|
if ( oex->oe_key == (void *)&memberof )
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
if ( MEMBEROF_REVERSE( mo ) ) {
|
|
for ( mlp = &op->orm_modlist; *mlp; mlp = &(*mlp)->sml_next ) {
|
|
Modifications *ml = *mlp;
|
|
|
|
if ( ml->sml_desc == mo->mo_ad_memberof ) {
|
|
mmlp = mlp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
save_dn = op->o_dn;
|
|
save_ndn = op->o_ndn;
|
|
mcis.on = on;
|
|
mcis.what = MEMBEROF_IS_GROUP;
|
|
|
|
if ( memberof_isGroupOrMember( op, &mcis ) == LDAP_SUCCESS
|
|
&& ( mcis.what & MEMBEROF_IS_GROUP ) )
|
|
{
|
|
Modifications *ml;
|
|
|
|
for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
|
|
if ( ml->sml_desc == mo->mo_ad_member ) {
|
|
switch ( ml->sml_op ) {
|
|
case LDAP_MOD_DELETE:
|
|
case LDAP_MOD_REPLACE:
|
|
case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
|
|
save_member = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if ( MEMBEROF_DANGLING_CHECK( mo )
|
|
&& !get_relax( op ) )
|
|
{
|
|
op->o_dn = op->o_bd->be_rootdn;
|
|
op->o_ndn = op->o_bd->be_rootndn;
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
|
|
assert( op->orm_modlist != NULL );
|
|
|
|
for ( mlp = &op->orm_modlist; *mlp; ) {
|
|
Modifications *ml = *mlp;
|
|
int i;
|
|
|
|
if ( !is_ad_subtype( ml->sml_desc, mo->mo_ad_member ) ) {
|
|
mlp = &ml->sml_next;
|
|
continue;
|
|
}
|
|
|
|
switch ( ml->sml_op ) {
|
|
case LDAP_MOD_DELETE:
|
|
case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
|
|
/* we don't care about cancellations: if the value
|
|
* exists, fine; if it doesn't, we let the underlying
|
|
* database fail as appropriate; */
|
|
mlp = &ml->sml_next;
|
|
break;
|
|
|
|
case LDAP_MOD_REPLACE:
|
|
/* Handle this just like a delete (see above) */
|
|
if ( !ml->sml_values ) {
|
|
mlp = &ml->sml_next;
|
|
break;
|
|
}
|
|
|
|
case LDAP_MOD_ADD:
|
|
case SLAP_MOD_SOFTADD: /* ITS#7487 */
|
|
case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
|
|
/* NOTE: right now, the attributeType we use
|
|
* for member must have a normalized value */
|
|
assert( ml->sml_nvalues != NULL );
|
|
|
|
for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
|
|
Entry *e;
|
|
|
|
/* ITS#6670 Ignore member pointing to this entry */
|
|
if ( dn_match( &ml->sml_nvalues[i], &save_ndn ))
|
|
continue;
|
|
|
|
if ( be_entry_get_rw( op, &ml->sml_nvalues[ i ],
|
|
NULL, NULL, 0, &e ) == LDAP_SUCCESS )
|
|
{
|
|
be_entry_release_r( op, e );
|
|
continue;
|
|
}
|
|
|
|
if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
|
|
rc = rs->sr_err = mo->mo_dangling_err;
|
|
rs->sr_text = "adding non-existing object "
|
|
"as group member";
|
|
send_ldap_result( op, rs );
|
|
goto done;
|
|
}
|
|
|
|
if ( MEMBEROF_DANGLING_DROP( mo ) ) {
|
|
int j;
|
|
|
|
Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
|
|
"member=\"%s\" does not exist (stripping...)\n",
|
|
op->o_log_prefix, op->o_req_dn.bv_val,
|
|
ml->sml_nvalues[ i ].bv_val );
|
|
|
|
for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
|
|
ber_memfree( ml->sml_values[ i ].bv_val );
|
|
BER_BVZERO( &ml->sml_values[ i ] );
|
|
ber_memfree( ml->sml_nvalues[ i ].bv_val );
|
|
BER_BVZERO( &ml->sml_nvalues[ i ] );
|
|
ml->sml_numvals--;
|
|
if ( j - i == 1 ) {
|
|
break;
|
|
}
|
|
|
|
AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
|
|
sizeof( struct berval ) * ( j - i ) );
|
|
AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
|
|
sizeof( struct berval ) * ( j - i ) );
|
|
i--;
|
|
}
|
|
}
|
|
|
|
if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
|
|
*mlp = ml->sml_next;
|
|
slap_mod_free( &ml->sml_mod, 0 );
|
|
free( ml );
|
|
|
|
} else {
|
|
mlp = &ml->sml_next;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( mmlp != NULL ) {
|
|
Modifications *ml = *mmlp;
|
|
int i;
|
|
Entry *target;
|
|
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
rc = be_entry_get_rw( op, &op->o_req_ndn,
|
|
NULL, NULL, 0, &target );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
|
|
send_ldap_result( op, rs );
|
|
goto done;
|
|
}
|
|
|
|
switch ( ml->sml_op ) {
|
|
case LDAP_MOD_DELETE:
|
|
case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
|
|
if ( ml->sml_nvalues != NULL ) {
|
|
AccessControlState acl_state = ACL_STATE_INIT;
|
|
|
|
for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
|
|
Entry *e;
|
|
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
/* access is checked with the original identity */
|
|
rc = access_allowed( op, target,
|
|
mo->mo_ad_memberof,
|
|
&ml->sml_nvalues[ i ],
|
|
ACL_WDEL,
|
|
&acl_state );
|
|
if ( rc == 0 ) {
|
|
rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
|
|
rs->sr_text = NULL;
|
|
send_ldap_result( op, rs );
|
|
goto done2;
|
|
}
|
|
|
|
rc = be_entry_get_rw( op, &ml->sml_nvalues[ i ],
|
|
NULL, NULL, 0, &e );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
if ( get_relax( op ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
|
|
rc = rs->sr_err = mo->mo_dangling_err;
|
|
rs->sr_text = "deleting non-existing object "
|
|
"as memberof";
|
|
send_ldap_result( op, rs );
|
|
goto done2;
|
|
}
|
|
|
|
if ( MEMBEROF_DANGLING_DROP( mo ) ) {
|
|
int j;
|
|
|
|
Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
|
|
"memberof=\"%s\" does not exist (stripping...)\n",
|
|
op->o_log_prefix, op->o_req_ndn.bv_val,
|
|
ml->sml_nvalues[ i ].bv_val );
|
|
|
|
for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
|
|
ber_memfree( ml->sml_values[ i ].bv_val );
|
|
BER_BVZERO( &ml->sml_values[ i ] );
|
|
if ( ml->sml_nvalues != ml->sml_values ) {
|
|
ber_memfree( ml->sml_nvalues[ i ].bv_val );
|
|
BER_BVZERO( &ml->sml_nvalues[ i ] );
|
|
}
|
|
ml->sml_numvals--;
|
|
if ( j - i == 1 ) {
|
|
break;
|
|
}
|
|
|
|
AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
|
|
sizeof( struct berval ) * ( j - i ) );
|
|
if ( ml->sml_nvalues != ml->sml_values ) {
|
|
AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
|
|
sizeof( struct berval ) * ( j - i ) );
|
|
}
|
|
i--;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
/* access is checked with the original identity */
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
rc = access_allowed( op, e, mo->mo_ad_member,
|
|
&op->o_req_ndn,
|
|
ACL_WDEL, NULL );
|
|
be_entry_release_r( op, e );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
|
|
if ( !rc ) {
|
|
rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
|
|
rs->sr_text = "insufficient access to object referenced by memberof";
|
|
send_ldap_result( op, rs );
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
|
|
*mmlp = ml->sml_next;
|
|
slap_mod_free( &ml->sml_mod, 0 );
|
|
free( ml );
|
|
}
|
|
|
|
break;
|
|
}
|
|
/* fall thru */
|
|
|
|
case LDAP_MOD_REPLACE:
|
|
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
/* access is checked with the original identity */
|
|
rc = access_allowed( op, target,
|
|
mo->mo_ad_memberof,
|
|
NULL,
|
|
ACL_WDEL, NULL );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
if ( rc == 0 ) {
|
|
rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
|
|
rs->sr_text = NULL;
|
|
send_ldap_result( op, rs );
|
|
goto done2;
|
|
}
|
|
|
|
if ( ml->sml_op == LDAP_MOD_DELETE || ml->sml_op == SLAP_MOD_SOFTDEL || !ml->sml_values ) {
|
|
break;
|
|
}
|
|
/* fall thru */
|
|
|
|
case LDAP_MOD_ADD:
|
|
case SLAP_MOD_SOFTADD: /* ITS#7487 */
|
|
case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
|
|
{
|
|
AccessControlState acl_state = ACL_STATE_INIT;
|
|
|
|
for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
|
|
Entry *e;
|
|
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
/* access is checked with the original identity */
|
|
rc = access_allowed( op, target,
|
|
mo->mo_ad_memberof,
|
|
&ml->sml_nvalues[ i ],
|
|
ACL_WADD,
|
|
&acl_state );
|
|
if ( rc == 0 ) {
|
|
rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
|
|
rs->sr_text = NULL;
|
|
send_ldap_result( op, rs );
|
|
goto done2;
|
|
}
|
|
|
|
/* ITS#6670 Ignore member pointing to this entry */
|
|
if ( dn_match( &ml->sml_nvalues[i], &save_ndn ))
|
|
continue;
|
|
|
|
rc = be_entry_get_rw( op, &ml->sml_nvalues[ i ],
|
|
NULL, NULL, 0, &e );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
|
|
rc = rs->sr_err = mo->mo_dangling_err;
|
|
rs->sr_text = "adding non-existing object "
|
|
"as memberof";
|
|
send_ldap_result( op, rs );
|
|
goto done2;
|
|
}
|
|
|
|
if ( MEMBEROF_DANGLING_DROP( mo ) ) {
|
|
int j;
|
|
|
|
Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
|
|
"memberof=\"%s\" does not exist (stripping...)\n",
|
|
op->o_log_prefix, op->o_req_ndn.bv_val,
|
|
ml->sml_nvalues[ i ].bv_val );
|
|
|
|
for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
|
|
ber_memfree( ml->sml_values[ i ].bv_val );
|
|
BER_BVZERO( &ml->sml_values[ i ] );
|
|
if ( ml->sml_nvalues != ml->sml_values ) {
|
|
ber_memfree( ml->sml_nvalues[ i ].bv_val );
|
|
BER_BVZERO( &ml->sml_nvalues[ i ] );
|
|
}
|
|
ml->sml_numvals--;
|
|
if ( j - i == 1 ) {
|
|
break;
|
|
}
|
|
|
|
AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
|
|
sizeof( struct berval ) * ( j - i ) );
|
|
if ( ml->sml_nvalues != ml->sml_values ) {
|
|
AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
|
|
sizeof( struct berval ) * ( j - i ) );
|
|
}
|
|
i--;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
/* access is checked with the original identity */
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
rc = access_allowed( op, e, mo->mo_ad_member,
|
|
&op->o_req_ndn,
|
|
ACL_WDEL, NULL );
|
|
be_entry_release_r( op, e );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
|
|
if ( !rc ) {
|
|
rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
|
|
rs->sr_text = "insufficient access to object referenced by memberof";
|
|
send_ldap_result( op, rs );
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
|
|
*mmlp = ml->sml_next;
|
|
slap_mod_free( &ml->sml_mod, 0 );
|
|
free( ml );
|
|
}
|
|
|
|
} break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
}
|
|
|
|
done2:;
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
be_entry_release_r( op, target );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
}
|
|
|
|
sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
|
|
sc->sc_private = sc+1;
|
|
sc->sc_response = memberof_res_modify;
|
|
sc->sc_cleanup = memberof_cleanup;
|
|
sc->sc_writewait = 0;
|
|
mci = sc->sc_private;
|
|
mci->on = on;
|
|
mci->member = NULL;
|
|
mci->memberof = NULL;
|
|
mci->what = mcis.what;
|
|
|
|
if ( save_member ) {
|
|
op->o_dn = op->o_bd->be_rootdn;
|
|
op->o_ndn = op->o_bd->be_rootndn;
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
rc = backend_attribute( op, NULL, &op->o_req_ndn,
|
|
mo->mo_ad_member, &mci->member, ACL_READ );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
}
|
|
|
|
sc->sc_next = op->o_callback;
|
|
op->o_callback = sc;
|
|
|
|
rc = SLAP_CB_CONTINUE;
|
|
|
|
done:;
|
|
op->o_dn = save_dn;
|
|
op->o_ndn = save_ndn;
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
memberof_op_modrdn( Operation *op, SlapReply *rs )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
|
|
slap_callback *sc;
|
|
memberof_cbinfo_t *mci;
|
|
OpExtra *oex;
|
|
|
|
LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
|
|
if ( oex->oe_key == (void *)&memberof )
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
|
|
sc->sc_private = sc+1;
|
|
sc->sc_response = memberof_res_modrdn;
|
|
sc->sc_cleanup = memberof_cleanup;
|
|
sc->sc_writewait = 0;
|
|
mci = sc->sc_private;
|
|
mci->on = on;
|
|
mci->member = NULL;
|
|
mci->memberof = NULL;
|
|
|
|
sc->sc_next = op->o_callback;
|
|
op->o_callback = sc;
|
|
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
/*
|
|
* response callback that adds memberof values when a group is added.
|
|
*/
|
|
static int
|
|
memberof_res_add( Operation *op, SlapReply *rs )
|
|
{
|
|
memberof_cbinfo_t *mci = op->o_callback->sc_private;
|
|
slap_overinst *on = mci->on;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
int i;
|
|
|
|
if ( rs->sr_err != LDAP_SUCCESS ) {
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
if ( MEMBEROF_REVERSE( mo ) ) {
|
|
Attribute *ma;
|
|
|
|
ma = attr_find( op->ora_e->e_attrs, mo->mo_ad_memberof );
|
|
if ( ma != NULL ) {
|
|
/* relax is required to allow to add
|
|
* a non-existing member */
|
|
op->o_relax = SLAP_CONTROL_CRITICAL;
|
|
|
|
for ( i = 0; !BER_BVISNULL( &ma->a_nvals[ i ] ); i++ ) {
|
|
|
|
/* ITS#6670 Ignore member pointing to this entry */
|
|
if ( dn_match( &ma->a_nvals[i], &op->o_req_ndn ))
|
|
continue;
|
|
|
|
/* the modification is attempted
|
|
* with the original identity */
|
|
memberof_value_modify( op,
|
|
&ma->a_nvals[ i ], mo->mo_ad_member,
|
|
NULL, NULL, &op->o_req_dn, &op->o_req_ndn );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( is_entry_objectclass_or_sub( op->ora_e, mo->mo_oc_group ) ) {
|
|
Attribute *a;
|
|
|
|
for ( a = attrs_find( op->ora_e->e_attrs, mo->mo_ad_member );
|
|
a != NULL;
|
|
a = attrs_find( a->a_next, mo->mo_ad_member ) )
|
|
{
|
|
for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
|
|
/* ITS#6670 Ignore member pointing to this entry */
|
|
if ( dn_match( &a->a_nvals[i], &op->o_req_ndn ))
|
|
continue;
|
|
|
|
memberof_value_modify( op,
|
|
&a->a_nvals[ i ],
|
|
mo->mo_ad_memberof,
|
|
NULL, NULL,
|
|
&op->o_req_dn,
|
|
&op->o_req_ndn );
|
|
}
|
|
}
|
|
}
|
|
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
/*
|
|
* response callback that deletes memberof values when a group is deleted.
|
|
*/
|
|
static int
|
|
memberof_res_delete( Operation *op, SlapReply *rs )
|
|
{
|
|
memberof_cbinfo_t *mci = op->o_callback->sc_private;
|
|
slap_overinst *on = mci->on;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
BerVarray vals;
|
|
int i;
|
|
|
|
if ( rs->sr_err != LDAP_SUCCESS ) {
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
vals = mci->member;
|
|
if ( vals != NULL ) {
|
|
for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
|
|
memberof_value_modify( op,
|
|
&vals[ i ], mo->mo_ad_memberof,
|
|
&op->o_req_dn, &op->o_req_ndn,
|
|
NULL, NULL );
|
|
}
|
|
}
|
|
|
|
if ( MEMBEROF_REFINT( mo ) ) {
|
|
vals = mci->memberof;
|
|
if ( vals != NULL ) {
|
|
for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
|
|
memberof_value_modify( op,
|
|
&vals[ i ], mo->mo_ad_member,
|
|
&op->o_req_dn, &op->o_req_ndn,
|
|
NULL, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
/*
|
|
* response callback that adds/deletes memberof values when a group
|
|
* is modified.
|
|
*/
|
|
static int
|
|
memberof_res_modify( Operation *op, SlapReply *rs )
|
|
{
|
|
memberof_cbinfo_t *mci = op->o_callback->sc_private;
|
|
slap_overinst *on = mci->on;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
int i, rc;
|
|
Modifications *ml, *mml = NULL;
|
|
BerVarray vals;
|
|
|
|
if ( rs->sr_err != LDAP_SUCCESS ) {
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
if ( MEMBEROF_REVERSE( mo ) ) {
|
|
for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
|
|
if ( ml->sml_desc == mo->mo_ad_memberof ) {
|
|
mml = ml;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( mml != NULL ) {
|
|
BerVarray vals = mml->sml_nvalues;
|
|
|
|
switch ( mml->sml_op ) {
|
|
case LDAP_MOD_DELETE:
|
|
case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
|
|
if ( vals != NULL ) {
|
|
for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
|
|
memberof_value_modify( op,
|
|
&vals[ i ], mo->mo_ad_member,
|
|
&op->o_req_dn, &op->o_req_ndn,
|
|
NULL, NULL );
|
|
}
|
|
break;
|
|
}
|
|
/* fall thru */
|
|
|
|
case LDAP_MOD_REPLACE:
|
|
/* delete all ... */
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
rc = backend_attribute( op, NULL, &op->o_req_ndn,
|
|
mo->mo_ad_memberof, &vals, ACL_READ );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
if ( rc == LDAP_SUCCESS ) {
|
|
for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
|
|
memberof_value_modify( op,
|
|
&vals[ i ], mo->mo_ad_member,
|
|
&op->o_req_dn, &op->o_req_ndn,
|
|
NULL, NULL );
|
|
}
|
|
ber_bvarray_free_x( vals, op->o_tmpmemctx );
|
|
}
|
|
|
|
if ( ml->sml_op == LDAP_MOD_DELETE || !mml->sml_values ) {
|
|
break;
|
|
}
|
|
/* fall thru */
|
|
|
|
case LDAP_MOD_ADD:
|
|
case SLAP_MOD_SOFTADD: /* ITS#7487 */
|
|
case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
|
|
assert( vals != NULL );
|
|
|
|
for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
|
|
memberof_value_modify( op,
|
|
&vals[ i ], mo->mo_ad_member,
|
|
NULL, NULL,
|
|
&op->o_req_dn, &op->o_req_ndn );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
}
|
|
}
|
|
|
|
if ( mci->what & MEMBEROF_IS_GROUP )
|
|
{
|
|
for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
|
|
if ( ml->sml_desc != mo->mo_ad_member ) {
|
|
continue;
|
|
}
|
|
|
|
switch ( ml->sml_op ) {
|
|
case LDAP_MOD_DELETE:
|
|
case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
|
|
vals = ml->sml_nvalues;
|
|
if ( vals != NULL ) {
|
|
for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
|
|
memberof_value_modify( op,
|
|
&vals[ i ], mo->mo_ad_memberof,
|
|
&op->o_req_dn, &op->o_req_ndn,
|
|
NULL, NULL );
|
|
}
|
|
break;
|
|
}
|
|
/* fall thru */
|
|
|
|
case LDAP_MOD_REPLACE:
|
|
vals = mci->member;
|
|
|
|
/* delete all ... */
|
|
if ( vals != NULL ) {
|
|
for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
|
|
memberof_value_modify( op,
|
|
&vals[ i ], mo->mo_ad_memberof,
|
|
&op->o_req_dn, &op->o_req_ndn,
|
|
NULL, NULL );
|
|
}
|
|
}
|
|
|
|
if ( ml->sml_op == LDAP_MOD_DELETE || ml->sml_op == SLAP_MOD_SOFTDEL || !ml->sml_values ) {
|
|
break;
|
|
}
|
|
/* fall thru */
|
|
|
|
case LDAP_MOD_ADD:
|
|
case SLAP_MOD_SOFTADD: /* ITS#7487 */
|
|
case SLAP_MOD_ADD_IF_NOT_PRESENT : /* ITS#7487 */
|
|
assert( ml->sml_nvalues != NULL );
|
|
vals = ml->sml_nvalues;
|
|
for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
|
|
memberof_value_modify( op,
|
|
&vals[ i ], mo->mo_ad_memberof,
|
|
NULL, NULL,
|
|
&op->o_req_dn, &op->o_req_ndn );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
/*
|
|
* response callback that adds/deletes member values when a group member
|
|
* is renamed.
|
|
*/
|
|
static int
|
|
memberof_res_modrdn( Operation *op, SlapReply *rs )
|
|
{
|
|
memberof_cbinfo_t *mci = op->o_callback->sc_private;
|
|
slap_overinst *on = mci->on;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
struct berval newPDN, newDN = BER_BVNULL, newPNDN, newNDN;
|
|
int i, rc;
|
|
BerVarray vals;
|
|
|
|
struct berval save_dn, save_ndn;
|
|
|
|
if ( rs->sr_err != LDAP_SUCCESS ) {
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
mci->what = MEMBEROF_IS_GROUP;
|
|
if ( MEMBEROF_REFINT( mo ) ) {
|
|
mci->what |= MEMBEROF_IS_MEMBER;
|
|
}
|
|
|
|
if ( op->orr_nnewSup ) {
|
|
newPNDN = *op->orr_nnewSup;
|
|
|
|
} else {
|
|
dnParent( &op->o_req_ndn, &newPNDN );
|
|
}
|
|
|
|
build_new_dn( &newNDN, &newPNDN, &op->orr_nnewrdn, op->o_tmpmemctx );
|
|
|
|
save_dn = op->o_req_dn;
|
|
save_ndn = op->o_req_ndn;
|
|
|
|
op->o_req_dn = newNDN;
|
|
op->o_req_ndn = newNDN;
|
|
rc = memberof_isGroupOrMember( op, mci );
|
|
op->o_req_dn = save_dn;
|
|
op->o_req_ndn = save_ndn;
|
|
|
|
if ( rc != LDAP_SUCCESS || mci->what == MEMBEROF_IS_NONE ) {
|
|
goto done;
|
|
}
|
|
|
|
if ( op->orr_newSup ) {
|
|
newPDN = *op->orr_newSup;
|
|
|
|
} else {
|
|
dnParent( &op->o_req_dn, &newPDN );
|
|
}
|
|
|
|
build_new_dn( &newDN, &newPDN, &op->orr_newrdn, op->o_tmpmemctx );
|
|
|
|
if ( mci->what & MEMBEROF_IS_GROUP ) {
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
rc = backend_attribute( op, NULL, &newNDN,
|
|
mo->mo_ad_member, &vals, ACL_READ );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
|
|
if ( rc == LDAP_SUCCESS ) {
|
|
for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
|
|
memberof_value_modify( op,
|
|
&vals[ i ], mo->mo_ad_memberof,
|
|
&op->o_req_dn, &op->o_req_ndn,
|
|
&newDN, &newNDN );
|
|
}
|
|
ber_bvarray_free_x( vals, op->o_tmpmemctx );
|
|
}
|
|
}
|
|
|
|
if ( MEMBEROF_REFINT( mo ) && ( mci->what & MEMBEROF_IS_MEMBER ) ) {
|
|
op->o_bd->bd_info = (BackendInfo *)on->on_info;
|
|
rc = backend_attribute( op, NULL, &newNDN,
|
|
mo->mo_ad_memberof, &vals, ACL_READ );
|
|
op->o_bd->bd_info = (BackendInfo *)on;
|
|
|
|
if ( rc == LDAP_SUCCESS ) {
|
|
for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
|
|
memberof_value_modify( op,
|
|
&vals[ i ], mo->mo_ad_member,
|
|
&op->o_req_dn, &op->o_req_ndn,
|
|
&newDN, &newNDN );
|
|
}
|
|
ber_bvarray_free_x( vals, op->o_tmpmemctx );
|
|
}
|
|
}
|
|
|
|
done:;
|
|
if ( !BER_BVISNULL( &newDN ) ) {
|
|
op->o_tmpfree( newDN.bv_val, op->o_tmpmemctx );
|
|
}
|
|
op->o_tmpfree( newNDN.bv_val, op->o_tmpmemctx );
|
|
|
|
return SLAP_CB_CONTINUE;
|
|
}
|
|
|
|
|
|
static int
|
|
memberof_db_init(
|
|
BackendDB *be,
|
|
ConfigReply *cr )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)be->bd_info;
|
|
memberof_t *mo;
|
|
const char *text = NULL;
|
|
int rc;
|
|
|
|
mo = (memberof_t *)ch_calloc( 1, sizeof( memberof_t ) );
|
|
|
|
/* safe default */
|
|
mo->mo_dangling_err = LDAP_CONSTRAINT_VIOLATION;
|
|
|
|
if ( !ad_memberOf ) {
|
|
rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &ad_memberOf, &text );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
Debug( LDAP_DEBUG_ANY, "memberof_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, "memberof_db_init: "
|
|
"unable to find attribute=\"%s\": %s (%d)\n",
|
|
SLAPD_GROUP_ATTR, text, rc );
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
if ( !oc_group ) {
|
|
oc_group = oc_find( SLAPD_GROUP_CLASS );
|
|
if ( oc_group == NULL ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"memberof_db_init: "
|
|
"unable to find objectClass=\"%s\"\n",
|
|
SLAPD_GROUP_CLASS );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
on->on_bi.bi_private = (void *)mo;
|
|
|
|
return 0;
|
|
}
|
|
|
|
enum {
|
|
MO_DN = 1,
|
|
MO_DANGLING,
|
|
MO_REFINT,
|
|
MO_GROUP_OC,
|
|
MO_MEMBER_AD,
|
|
MO_MEMBER_OF_AD,
|
|
#if 0
|
|
MO_REVERSE,
|
|
#endif
|
|
|
|
MO_DANGLING_ERROR,
|
|
|
|
MO_LAST
|
|
};
|
|
|
|
static ConfigDriver mo_cf_gen;
|
|
|
|
#define OID "1.3.6.1.4.1.7136.2.666.4"
|
|
#define OIDAT OID ".1.1"
|
|
#define OIDCFGAT OID ".1.2"
|
|
#define OIDOC OID ".2.1"
|
|
#define OIDCFGOC OID ".2.2"
|
|
|
|
|
|
static ConfigTable mo_cfg[] = {
|
|
{ "memberof-dn", "modifiersName",
|
|
2, 2, 0, ARG_MAGIC|ARG_QUOTE|ARG_DN|MO_DN, mo_cf_gen,
|
|
"( OLcfgOvAt:18.0 NAME 'olcMemberOfDN' "
|
|
"DESC 'DN to be used as modifiersName' "
|
|
"EQUALITY distinguishedNameMatch "
|
|
"SYNTAX OMsDN SINGLE-VALUE )",
|
|
NULL, NULL },
|
|
|
|
{ "memberof-dangling", "ignore|drop|error",
|
|
2, 2, 0, ARG_MAGIC|MO_DANGLING, mo_cf_gen,
|
|
"( OLcfgOvAt:18.1 NAME 'olcMemberOfDangling' "
|
|
"DESC 'Behavior with respect to dangling members, "
|
|
"constrained to ignore, drop, error' "
|
|
"EQUALITY caseIgnoreMatch "
|
|
"SYNTAX OMsDirectoryString SINGLE-VALUE )",
|
|
NULL, NULL },
|
|
|
|
{ "memberof-refint", "true|FALSE",
|
|
2, 2, 0, ARG_MAGIC|ARG_ON_OFF|MO_REFINT, mo_cf_gen,
|
|
"( OLcfgOvAt:18.2 NAME 'olcMemberOfRefInt' "
|
|
"DESC 'Take care of referential integrity' "
|
|
"EQUALITY booleanMatch "
|
|
"SYNTAX OMsBoolean SINGLE-VALUE )",
|
|
NULL, NULL },
|
|
|
|
{ "memberof-group-oc", "objectClass",
|
|
2, 2, 0, ARG_MAGIC|MO_GROUP_OC, mo_cf_gen,
|
|
"( OLcfgOvAt:18.3 NAME 'olcMemberOfGroupOC' "
|
|
"DESC 'Group objectClass' "
|
|
"EQUALITY caseIgnoreMatch "
|
|
"SYNTAX OMsDirectoryString SINGLE-VALUE )",
|
|
NULL, NULL },
|
|
|
|
{ "memberof-member-ad", "member attribute",
|
|
2, 2, 0, ARG_MAGIC|ARG_ATDESC|MO_MEMBER_AD, mo_cf_gen,
|
|
"( OLcfgOvAt:18.4 NAME 'olcMemberOfMemberAD' "
|
|
"DESC 'member attribute' "
|
|
"EQUALITY caseIgnoreMatch "
|
|
"SYNTAX OMsDirectoryString SINGLE-VALUE )",
|
|
NULL, NULL },
|
|
|
|
{ "memberof-memberof-ad", "memberOf attribute",
|
|
2, 2, 0, ARG_MAGIC|ARG_ATDESC|MO_MEMBER_OF_AD, mo_cf_gen,
|
|
"( OLcfgOvAt:18.5 NAME 'olcMemberOfMemberOfAD' "
|
|
"DESC 'memberOf attribute' "
|
|
"EQUALITY caseIgnoreMatch "
|
|
"SYNTAX OMsDirectoryString SINGLE-VALUE )",
|
|
NULL, NULL },
|
|
|
|
#if 0
|
|
{ "memberof-reverse", "true|FALSE",
|
|
2, 2, 0, ARG_MAGIC|ARG_ON_OFF|MO_REVERSE, mo_cf_gen,
|
|
"( OLcfgOvAt:18.6 NAME 'olcMemberOfReverse' "
|
|
"DESC 'Take care of referential integrity "
|
|
"also when directly modifying memberOf' "
|
|
"SYNTAX OMsBoolean SINGLE-VALUE )",
|
|
NULL, NULL },
|
|
#endif
|
|
|
|
{ "memberof-dangling-error", "error code",
|
|
2, 2, 0, ARG_MAGIC|MO_DANGLING_ERROR, mo_cf_gen,
|
|
"( OLcfgOvAt:18.7 NAME 'olcMemberOfDanglingError' "
|
|
"DESC 'Error code returned in case of dangling back reference' "
|
|
"EQUALITY caseIgnoreMatch "
|
|
"SYNTAX OMsDirectoryString SINGLE-VALUE )",
|
|
NULL, NULL },
|
|
|
|
{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
|
|
};
|
|
|
|
static ConfigOCs mo_ocs[] = {
|
|
{ "( OLcfgOvOc:18.1 "
|
|
"NAME ( 'olcMemberOfConfig' 'olcMemberOf' ) "
|
|
"DESC 'Member-of configuration' "
|
|
"SUP olcOverlayConfig "
|
|
"MAY ( "
|
|
"olcMemberOfDN "
|
|
"$ olcMemberOfDangling "
|
|
"$ olcMemberOfDanglingError"
|
|
"$ olcMemberOfRefInt "
|
|
"$ olcMemberOfGroupOC "
|
|
"$ olcMemberOfMemberAD "
|
|
"$ olcMemberOfMemberOfAD "
|
|
#if 0
|
|
"$ olcMemberOfReverse "
|
|
#endif
|
|
") "
|
|
")",
|
|
Cft_Overlay, mo_cfg, NULL, NULL },
|
|
{ NULL, 0, NULL }
|
|
};
|
|
|
|
static slap_verbmasks dangling_mode[] = {
|
|
{ BER_BVC( "ignore" ), MEMBEROF_NONE },
|
|
{ BER_BVC( "drop" ), MEMBEROF_FDANGLING_DROP },
|
|
{ BER_BVC( "error" ), MEMBEROF_FDANGLING_ERROR },
|
|
{ BER_BVNULL, 0 }
|
|
};
|
|
|
|
static int
|
|
memberof_make_group_filter( memberof_t *mo )
|
|
{
|
|
char *ptr;
|
|
|
|
if ( !BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
|
|
ch_free( mo->mo_groupFilterstr.bv_val );
|
|
}
|
|
|
|
mo->mo_groupFilter.f_choice = LDAP_FILTER_EQUALITY;
|
|
mo->mo_groupFilter.f_ava = &mo->mo_groupAVA;
|
|
|
|
mo->mo_groupFilter.f_av_desc = slap_schema.si_ad_objectClass;
|
|
mo->mo_groupFilter.f_av_value = mo->mo_oc_group->soc_cname;
|
|
|
|
mo->mo_groupFilterstr.bv_len = STRLENOF( "(=)" )
|
|
+ slap_schema.si_ad_objectClass->ad_cname.bv_len
|
|
+ mo->mo_oc_group->soc_cname.bv_len;
|
|
ptr = mo->mo_groupFilterstr.bv_val = ch_malloc( mo->mo_groupFilterstr.bv_len + 1 );
|
|
*ptr++ = '(';
|
|
ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
|
|
*ptr++ = '=';
|
|
ptr = lutil_strcopy( ptr, mo->mo_oc_group->soc_cname.bv_val );
|
|
*ptr++ = ')';
|
|
*ptr = '\0';
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
memberof_make_member_filter( memberof_t *mo )
|
|
{
|
|
char *ptr;
|
|
|
|
if ( !BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
|
|
ch_free( mo->mo_memberFilterstr.bv_val );
|
|
}
|
|
|
|
mo->mo_memberFilter.f_choice = LDAP_FILTER_PRESENT;
|
|
mo->mo_memberFilter.f_desc = mo->mo_ad_memberof;
|
|
|
|
mo->mo_memberFilterstr.bv_len = STRLENOF( "(=*)" )
|
|
+ mo->mo_ad_memberof->ad_cname.bv_len;
|
|
ptr = mo->mo_memberFilterstr.bv_val = ch_malloc( mo->mo_memberFilterstr.bv_len + 1 );
|
|
*ptr++ = '(';
|
|
ptr = lutil_strcopy( ptr, mo->mo_ad_memberof->ad_cname.bv_val );
|
|
ptr = lutil_strcopy( ptr, "=*)" );
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
mo_cf_gen( ConfigArgs *c )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)c->bi;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
int i, rc = 0;
|
|
|
|
if ( c->op == SLAP_CONFIG_EMIT ) {
|
|
struct berval bv = BER_BVNULL;
|
|
|
|
switch( c->type ) {
|
|
case MO_DN:
|
|
if ( mo->mo_dn.bv_val != NULL) {
|
|
value_add_one( &c->rvalue_vals, &mo->mo_dn );
|
|
value_add_one( &c->rvalue_nvals, &mo->mo_ndn );
|
|
}
|
|
break;
|
|
|
|
case MO_DANGLING:
|
|
enum_to_verb( dangling_mode, (mo->mo_flags & MEMBEROF_FDANGLING_MASK), &bv );
|
|
if ( BER_BVISNULL( &bv ) ) {
|
|
/* there's something wrong... */
|
|
assert( 0 );
|
|
rc = 1;
|
|
|
|
} else {
|
|
value_add_one( &c->rvalue_vals, &bv );
|
|
}
|
|
break;
|
|
|
|
case MO_DANGLING_ERROR:
|
|
if ( mo->mo_flags & MEMBEROF_FDANGLING_ERROR ) {
|
|
char buf[ SLAP_TEXT_BUFLEN ];
|
|
enum_to_verb( slap_ldap_response_code, mo->mo_dangling_err, &bv );
|
|
if ( BER_BVISNULL( &bv ) ) {
|
|
bv.bv_len = snprintf( buf, sizeof( buf ), "0x%x", mo->mo_dangling_err );
|
|
if ( bv.bv_len < sizeof( buf ) ) {
|
|
bv.bv_val = buf;
|
|
} else {
|
|
rc = 1;
|
|
break;
|
|
}
|
|
}
|
|
value_add_one( &c->rvalue_vals, &bv );
|
|
} else {
|
|
rc = 1;
|
|
}
|
|
break;
|
|
|
|
case MO_REFINT:
|
|
c->value_int = MEMBEROF_REFINT( mo );
|
|
break;
|
|
|
|
#if 0
|
|
case MO_REVERSE:
|
|
c->value_int = MEMBEROF_REVERSE( mo );
|
|
break;
|
|
#endif
|
|
|
|
case MO_GROUP_OC:
|
|
if ( mo->mo_oc_group != NULL ){
|
|
value_add_one( &c->rvalue_vals, &mo->mo_oc_group->soc_cname );
|
|
}
|
|
break;
|
|
|
|
case MO_MEMBER_AD:
|
|
c->value_ad = mo->mo_ad_member;
|
|
break;
|
|
|
|
case MO_MEMBER_OF_AD:
|
|
c->value_ad = mo->mo_ad_memberof;
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
return 1;
|
|
}
|
|
|
|
return rc;
|
|
|
|
} else if ( c->op == LDAP_MOD_DELETE ) {
|
|
switch( c->type ) {
|
|
case MO_DN:
|
|
if ( !BER_BVISNULL( &mo->mo_dn ) ) {
|
|
ber_memfree( mo->mo_dn.bv_val );
|
|
ber_memfree( mo->mo_ndn.bv_val );
|
|
BER_BVZERO( &mo->mo_dn );
|
|
BER_BVZERO( &mo->mo_ndn );
|
|
}
|
|
break;
|
|
|
|
case MO_DANGLING:
|
|
mo->mo_flags &= ~MEMBEROF_FDANGLING_MASK;
|
|
break;
|
|
|
|
case MO_DANGLING_ERROR:
|
|
mo->mo_dangling_err = LDAP_CONSTRAINT_VIOLATION;
|
|
break;
|
|
|
|
case MO_REFINT:
|
|
mo->mo_flags &= ~MEMBEROF_FREFINT;
|
|
break;
|
|
|
|
#if 0
|
|
case MO_REVERSE:
|
|
mo->mo_flags &= ~MEMBEROF_FREVERSE;
|
|
break;
|
|
#endif
|
|
|
|
case MO_GROUP_OC:
|
|
mo->mo_oc_group = oc_group;
|
|
memberof_make_group_filter( mo );
|
|
break;
|
|
|
|
case MO_MEMBER_AD:
|
|
mo->mo_ad_member = ad_member;
|
|
break;
|
|
|
|
case MO_MEMBER_OF_AD:
|
|
mo->mo_ad_memberof = ad_memberOf;
|
|
memberof_make_member_filter( mo );
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
return 1;
|
|
}
|
|
|
|
} else {
|
|
switch( c->type ) {
|
|
case MO_DN:
|
|
if ( !BER_BVISNULL( &mo->mo_dn ) ) {
|
|
ber_memfree( mo->mo_dn.bv_val );
|
|
ber_memfree( mo->mo_ndn.bv_val );
|
|
}
|
|
mo->mo_dn = c->value_dn;
|
|
mo->mo_ndn = c->value_ndn;
|
|
break;
|
|
|
|
case MO_DANGLING:
|
|
i = verb_to_mask( c->argv[ 1 ], dangling_mode );
|
|
if ( BER_BVISNULL( &dangling_mode[ i ].word ) ) {
|
|
return 1;
|
|
}
|
|
|
|
mo->mo_flags &= ~MEMBEROF_FDANGLING_MASK;
|
|
mo->mo_flags |= dangling_mode[ i ].mask;
|
|
break;
|
|
|
|
case MO_DANGLING_ERROR:
|
|
i = verb_to_mask( c->argv[ 1 ], slap_ldap_response_code );
|
|
if ( !BER_BVISNULL( &slap_ldap_response_code[ i ].word ) ) {
|
|
mo->mo_dangling_err = slap_ldap_response_code[ i ].mask;
|
|
} else if ( lutil_atoix( &mo->mo_dangling_err, c->argv[ 1 ], 0 ) ) {
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case MO_REFINT:
|
|
if ( c->value_int ) {
|
|
mo->mo_flags |= MEMBEROF_FREFINT;
|
|
|
|
} else {
|
|
mo->mo_flags &= ~MEMBEROF_FREFINT;
|
|
}
|
|
break;
|
|
|
|
#if 0
|
|
case MO_REVERSE:
|
|
if ( c->value_int ) {
|
|
mo->mo_flags |= MEMBEROF_FREVERSE;
|
|
|
|
} else {
|
|
mo->mo_flags &= ~MEMBEROF_FREVERSE;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case MO_GROUP_OC: {
|
|
ObjectClass *oc = NULL;
|
|
|
|
oc = oc_find( c->argv[ 1 ] );
|
|
if ( oc == NULL ) {
|
|
snprintf( c->cr_msg, sizeof( c->cr_msg ),
|
|
"unable to find group objectClass=\"%s\"",
|
|
c->argv[ 1 ] );
|
|
Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
|
|
c->log, c->cr_msg );
|
|
return 1;
|
|
}
|
|
|
|
mo->mo_oc_group = oc;
|
|
memberof_make_group_filter( mo );
|
|
} break;
|
|
|
|
case MO_MEMBER_AD: {
|
|
AttributeDescription *ad = c->value_ad;
|
|
|
|
if ( !is_at_syntax( ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
|
|
&& !is_at_syntax( ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
|
|
{
|
|
snprintf( c->cr_msg, sizeof( c->cr_msg ),
|
|
"member attribute=\"%s\" must either "
|
|
"have DN (%s) or nameUID (%s) syntax",
|
|
c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
|
|
Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
|
|
c->log, c->cr_msg );
|
|
return 1;
|
|
}
|
|
|
|
mo->mo_ad_member = ad;
|
|
} break;
|
|
|
|
case MO_MEMBER_OF_AD: {
|
|
AttributeDescription *ad = c->value_ad;
|
|
|
|
if ( !is_at_syntax( ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
|
|
&& !is_at_syntax( ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
|
|
{
|
|
snprintf( c->cr_msg, sizeof( c->cr_msg ),
|
|
"memberof attribute=\"%s\" must either "
|
|
"have DN (%s) or nameUID (%s) syntax",
|
|
c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
|
|
Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
|
|
c->log, c->cr_msg );
|
|
return 1;
|
|
}
|
|
|
|
mo->mo_ad_memberof = ad;
|
|
memberof_make_member_filter( mo );
|
|
} break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
memberof_db_open(
|
|
BackendDB *be,
|
|
ConfigReply *cr )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)be->bd_info;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
int rc;
|
|
|
|
if ( !mo->mo_ad_memberof ) {
|
|
mo->mo_ad_memberof = ad_memberOf;
|
|
}
|
|
|
|
if ( ! mo->mo_ad_member ) {
|
|
mo->mo_ad_member = ad_member;
|
|
}
|
|
|
|
if ( ! mo->mo_oc_group ) {
|
|
mo->mo_oc_group = oc_group;
|
|
}
|
|
|
|
if ( BER_BVISNULL( &mo->mo_dn ) && !BER_BVISNULL( &be->be_rootdn ) ) {
|
|
ber_dupbv( &mo->mo_dn, &be->be_rootdn );
|
|
ber_dupbv( &mo->mo_ndn, &be->be_rootndn );
|
|
}
|
|
|
|
if ( BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
|
|
memberof_make_group_filter( mo );
|
|
}
|
|
|
|
if ( BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
|
|
memberof_make_member_filter( mo );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
memberof_db_destroy(
|
|
BackendDB *be,
|
|
ConfigReply *cr )
|
|
{
|
|
slap_overinst *on = (slap_overinst *)be->bd_info;
|
|
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
|
|
|
|
if ( mo ) {
|
|
if ( !BER_BVISNULL( &mo->mo_dn ) ) {
|
|
ber_memfree( mo->mo_dn.bv_val );
|
|
ber_memfree( mo->mo_ndn.bv_val );
|
|
}
|
|
|
|
if ( !BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
|
|
ber_memfree( mo->mo_groupFilterstr.bv_val );
|
|
}
|
|
|
|
if ( !BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
|
|
ber_memfree( mo->mo_memberFilterstr.bv_val );
|
|
}
|
|
|
|
ber_memfree( mo );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct {
|
|
char *desc;
|
|
AttributeDescription **adp;
|
|
} as[] = {
|
|
{ "( 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 },
|
|
{ NULL }
|
|
};
|
|
|
|
#if SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC
|
|
static
|
|
#endif /* SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC */
|
|
int
|
|
memberof_initialize( void )
|
|
{
|
|
int code, i;
|
|
|
|
for ( i = 0; as[ i ].desc != NULL; i++ ) {
|
|
code = register_at( as[ i ].desc, as[ i ].adp, 1 );
|
|
if ( code && code != SLAP_SCHERR_ATTR_DUP ) {
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"memberof_initialize: register_at #%d failed\n",
|
|
i );
|
|
return code;
|
|
}
|
|
}
|
|
|
|
memberof.on_bi.bi_type = "memberof";
|
|
|
|
memberof.on_bi.bi_db_init = memberof_db_init;
|
|
memberof.on_bi.bi_db_open = memberof_db_open;
|
|
memberof.on_bi.bi_db_destroy = memberof_db_destroy;
|
|
|
|
memberof.on_bi.bi_op_add = memberof_op_add;
|
|
memberof.on_bi.bi_op_delete = memberof_op_delete;
|
|
memberof.on_bi.bi_op_modify = memberof_op_modify;
|
|
memberof.on_bi.bi_op_modrdn = memberof_op_modrdn;
|
|
|
|
memberof.on_bi.bi_cf_ocs = mo_ocs;
|
|
|
|
code = config_register_schema( mo_cfg, mo_ocs );
|
|
if ( code ) return code;
|
|
|
|
return overlay_register( &memberof );
|
|
}
|
|
|
|
#if SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC
|
|
int
|
|
init_module( int argc, char *argv[] )
|
|
{
|
|
return memberof_initialize();
|
|
}
|
|
#endif /* SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC */
|
|
|
|
#endif /* SLAPD_OVER_MEMBEROF */
|