]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/memberof.c
ITS#8663 Improve memberof cn=config handling
[openldap] / servers / slapd / overlays / memberof.c
1 /* memberof.c - back-reference for group membership */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2005-2007 Pierangelo Masarati <ando@sys-net.it>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGMENTS:
17  * This work was initially developed by Pierangelo Masarati for inclusion
18  * in OpenLDAP Software, sponsored by SysNet s.r.l.
19  */
20
21 #include "portable.h"
22
23 #ifdef SLAPD_OVER_MEMBEROF
24
25 #include <stdio.h>
26
27 #include "ac/string.h"
28 #include "ac/socket.h"
29
30 #include "slap.h"
31 #include "config.h"
32 #include "lutil.h"
33
34 /*
35  *      Glossary:
36  *
37  *              GROUP           a group object (an entry with GROUP_OC
38  *                              objectClass)
39  *              MEMBER          a member object (an entry whose DN is
40  *                              listed as MEMBER_AT value of a GROUP)
41  *              GROUP_OC        the objectClass of the group object
42  *                              (default: groupOfNames)
43  *              MEMBER_AT       the membership attribute, DN-valued;
44  *                              note: nameAndOptionalUID is tolerated
45  *                              as soon as the optionalUID is absent
46  *                              (default: member)
47  *              MEMBER_OF       reverse membership attribute
48  *                              (default: memberOf)
49  *
50  *      - add:
51  *              - if the entry that is being added is a GROUP,
52  *                the MEMBER_AT defined as values of the add operation
53  *                get the MEMBER_OF value directly from the request.
54  *
55  *                if configured to do so, the MEMBER objects do not exist,
56  *                and no relax control is issued, either:
57  *                      - fail
58  *                      - drop non-existing members
59  *                (by default: don't muck with values)
60  *
61  *              - if (configured to do so,) the referenced GROUP exists,
62  *                the relax control is set and the user has
63  *                "manage" privileges, allow to add MEMBER_OF values to
64  *                generic entries.
65  *
66  *      - modify:
67  *              - if the entry being modified is a GROUP_OC and the 
68  *                MEMBER_AT attribute is modified, the MEMBER_OF value
69  *                of the (existing) MEMBER_AT entries that are affected
70  *                is modified according to the request:
71  *                      - if a MEMBER is removed from the group,
72  *                        delete the corresponding MEMBER_OF
73  *                      - if a MEMBER is added to a group,
74  *                        add the corresponding MEMBER_OF
75  *
76  *                We need to determine, from the database, if it is
77  *                a GROUP_OC, and we need to check, from the
78  *                modification list, if the MEMBER_AT attribute is being
79  *                affected, and what MEMBER_AT values are affected.
80  *
81  *                if configured to do so, the entries corresponding to
82  *                the MEMBER_AT values do not exist, and no relax control
83  *                is issued, either:
84  *                      - fail
85  *                      - drop non-existing members
86  *                (by default: don't muck with values)
87  *
88  *              - if configured to do so, the referenced GROUP exists,
89  *                (the relax control is set) and the user has
90  *                "manage" privileges, allow to add MEMBER_OF values to
91  *                generic entries; the change is NOT automatically reflected
92  *                in the MEMBER attribute of the GROUP referenced
93  *                by the value of MEMBER_OF; a separate modification,
94  *                with or without relax control, needs to be performed.
95  *
96  *      - modrdn:
97  *              - if the entry being renamed is a GROUP, the MEMBER_OF
98  *                value of the (existing) MEMBER objects is modified
99  *                accordingly based on the newDN of the GROUP.
100  *
101  *                We need to determine, from the database, if it is
102  *                a GROUP; the list of MEMBER objects is obtained from
103  *                the database.
104  *
105  *                Non-existing MEMBER objects are ignored, since the
106  *                MEMBER_AT is not being addressed by the operation.
107  *
108  *              - if the entry being renamed has the MEMBER_OF attribute,
109  *                the corresponding MEMBER value must be modified in the
110  *                respective group entries.
111  *              
112  *
113  *      - delete:
114  *              - if the entry being deleted is a GROUP, the (existing)
115  *                MEMBER objects are modified accordingly; a copy of the 
116  *                values of the MEMBER_AT is saved and, if the delete 
117  *                succeeds, the MEMBER_OF value of the (existing) MEMBER
118  *                objects is deleted.
119  *
120  *                We need to determine, from the database, if it is
121  *                a GROUP.
122  *
123  *                Non-existing MEMBER objects are ignored, since the entry
124  *                is being deleted.
125  *
126  *              - if the entry being deleted has the MEMBER_OF attribute,
127  *                the corresponding value of the MEMBER_AT must be deleted
128  *                from the respective GROUP entries.
129  */
130
131 #define SLAPD_MEMBEROF_ATTR     "memberOf"
132
133 static AttributeDescription     *ad_member;
134 static AttributeDescription     *ad_memberOf;
135
136 static ObjectClass                      *oc_group;
137
138 static slap_overinst            memberof;
139
140 typedef struct memberof_t {
141         struct berval           mo_dn;
142         struct berval           mo_ndn;
143
144         ObjectClass             *mo_oc_group;
145         AttributeDescription    *mo_ad_member;
146         AttributeDescription    *mo_ad_memberof;
147         
148         struct berval           mo_groupFilterstr;
149         AttributeAssertion      mo_groupAVA;
150         Filter                  mo_groupFilter;
151
152         struct berval           mo_memberFilterstr;
153         Filter                  mo_memberFilter;
154
155         unsigned                mo_flags;
156 #define MEMBEROF_NONE           0x00U
157 #define MEMBEROF_FDANGLING_DROP 0x01U
158 #define MEMBEROF_FDANGLING_ERROR        0x02U
159 #define MEMBEROF_FDANGLING_MASK (MEMBEROF_FDANGLING_DROP|MEMBEROF_FDANGLING_ERROR)
160 #define MEMBEROF_FREFINT        0x04U
161 #define MEMBEROF_FREVERSE       0x08U
162
163         ber_int_t               mo_dangling_err;
164
165 #define MEMBEROF_CHK(mo,f) \
166         (((mo)->mo_flags & (f)) == (f))
167 #define MEMBEROF_DANGLING_CHECK(mo) \
168         ((mo)->mo_flags & MEMBEROF_FDANGLING_MASK)
169 #define MEMBEROF_DANGLING_DROP(mo) \
170         MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_DROP)
171 #define MEMBEROF_DANGLING_ERROR(mo) \
172         MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_ERROR)
173 #define MEMBEROF_REFINT(mo) \
174         MEMBEROF_CHK((mo),MEMBEROF_FREFINT)
175 #define MEMBEROF_REVERSE(mo) \
176         MEMBEROF_CHK((mo),MEMBEROF_FREVERSE)
177 } memberof_t;
178
179 typedef enum memberof_is_t {
180         MEMBEROF_IS_NONE = 0x00,
181         MEMBEROF_IS_GROUP = 0x01,
182         MEMBEROF_IS_MEMBER = 0x02,
183         MEMBEROF_IS_BOTH = (MEMBEROF_IS_GROUP|MEMBEROF_IS_MEMBER)
184 } memberof_is_t;
185
186 typedef struct memberof_cookie_t {
187         AttributeDescription    *ad;
188         BerVarray               vals;
189         int                     foundit;
190 } memberof_cookie_t;
191
192 typedef struct memberof_cbinfo_t {
193         slap_overinst *on;
194         BerVarray member;
195         BerVarray memberof;
196         memberof_is_t what;
197 } memberof_cbinfo_t;
198
199 static void
200 memberof_set_backend( Operation *op_target, Operation *op, slap_overinst *on )
201 {
202     BackendInfo *bi = op->o_bd->bd_info;
203
204     if ( bi->bi_type == memberof.on_bi.bi_type )
205         op_target->o_bd->bd_info = (BackendInfo *)on->on_info;
206 }
207
208 static int
209 memberof_isGroupOrMember_cb( Operation *op, SlapReply *rs )
210 {
211         if ( rs->sr_type == REP_SEARCH ) {
212                 memberof_cookie_t       *mc;
213
214                 mc = (memberof_cookie_t *)op->o_callback->sc_private;
215                 mc->foundit = 1;
216         }
217
218         return 0;
219 }
220
221 /*
222  * callback for internal search that saves the member attribute values
223  * of groups being deleted.
224  */
225 static int
226 memberof_saveMember_cb( Operation *op, SlapReply *rs )
227 {
228         if ( rs->sr_type == REP_SEARCH ) {
229                 memberof_cookie_t       *mc;
230                 Attribute               *a;
231
232                 mc = (memberof_cookie_t *)op->o_callback->sc_private;
233                 mc->foundit = 1;
234
235                 assert( rs->sr_entry != NULL );
236                 assert( rs->sr_entry->e_attrs != NULL );
237
238                 a = attr_find( rs->sr_entry->e_attrs, mc->ad );
239                 if ( a != NULL ) {
240                         ber_bvarray_dup_x( &mc->vals, a->a_nvals, op->o_tmpmemctx );
241
242                         assert( attr_find( a->a_next, mc->ad ) == NULL );
243                 }
244         }
245
246         return 0;
247 }
248
249 /*
250  * the delete hook performs an internal search that saves the member
251  * attribute values of groups being deleted.
252  */
253 static int
254 memberof_isGroupOrMember( Operation *op, memberof_cbinfo_t *mci )
255 {
256         slap_overinst           *on = mci->on;
257         memberof_t              *mo = (memberof_t *)on->on_bi.bi_private;
258
259         Operation               op2 = *op;
260         slap_callback           cb = { 0 };
261         BackendInfo     *bi = op->o_bd->bd_info;
262         AttributeName           an[ 2 ];
263
264         memberof_is_t           iswhat = MEMBEROF_IS_NONE;
265         memberof_cookie_t       mc;
266
267         assert( mci->what != MEMBEROF_IS_NONE );
268
269         cb.sc_private = &mc;
270         if ( op->o_tag == LDAP_REQ_DELETE ) {
271                 cb.sc_response = memberof_saveMember_cb;
272
273         } else {
274                 cb.sc_response = memberof_isGroupOrMember_cb;
275         }
276
277         op2.o_tag = LDAP_REQ_SEARCH;
278         op2.o_callback = &cb;
279         op2.o_dn = op->o_bd->be_rootdn;
280         op2.o_ndn = op->o_bd->be_rootndn;
281
282         op2.ors_scope = LDAP_SCOPE_BASE;
283         op2.ors_deref = LDAP_DEREF_NEVER;
284         BER_BVZERO( &an[ 1 ].an_name );
285         op2.ors_attrs = an;
286         op2.ors_attrsonly = 0;
287         op2.ors_limit = NULL;
288         op2.ors_slimit = 1;
289         op2.ors_tlimit = SLAP_NO_LIMIT;
290
291         if ( mci->what & MEMBEROF_IS_GROUP ) {
292                 SlapReply       rs2 = { REP_RESULT };
293
294                 mc.ad = mo->mo_ad_member;
295                 mc.foundit = 0;
296                 mc.vals = NULL;
297                 an[ 0 ].an_desc = mo->mo_ad_member;
298                 an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
299                 op2.ors_filterstr = mo->mo_groupFilterstr;
300                 op2.ors_filter = &mo->mo_groupFilter;
301                 op2.o_do_not_cache = 1; /* internal search, don't log */
302
303                 memberof_set_backend( &op2, op, on );
304                 (void)op->o_bd->be_search( &op2, &rs2 );
305                 op2.o_bd->bd_info = bi;
306
307                 if ( mc.foundit ) {
308                         iswhat |= MEMBEROF_IS_GROUP;
309                         if ( mc.vals ) mci->member = mc.vals;
310
311                 }
312         }
313
314         if ( mci->what & MEMBEROF_IS_MEMBER ) {
315                 SlapReply       rs2 = { REP_RESULT };
316
317                 mc.ad = mo->mo_ad_memberof;
318                 mc.foundit = 0;
319                 mc.vals = NULL;
320                 an[ 0 ].an_desc = mo->mo_ad_memberof;
321                 an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
322                 op2.ors_filterstr = mo->mo_memberFilterstr;
323                 op2.ors_filter = &mo->mo_memberFilter;
324                 op2.o_do_not_cache = 1; /* internal search, don't log */
325
326                 memberof_set_backend( &op2, op, on );
327                 (void)op->o_bd->be_search( &op2, &rs2 );
328                 op2.o_bd->bd_info = bi;
329
330                 if ( mc.foundit ) {
331                         iswhat |= MEMBEROF_IS_MEMBER;
332                         if ( mc.vals ) mci->memberof = mc.vals;
333
334                 }
335         }
336
337         mci->what = iswhat;
338
339         return LDAP_SUCCESS;
340 }
341
342 /*
343  * response callback that adds memberof values when a group is modified.
344  */
345 static void
346 memberof_value_modify(
347         Operation               *op,
348         struct berval           *ndn,
349         AttributeDescription    *ad,
350         struct berval           *old_dn,
351         struct berval           *old_ndn,
352         struct berval           *new_dn,
353         struct berval           *new_ndn )
354 {
355         memberof_cbinfo_t *mci = op->o_callback->sc_private;
356         slap_overinst   *on = mci->on;
357         memberof_t      *mo = (memberof_t *)on->on_bi.bi_private;
358
359         Operation       op2 = *op;
360         unsigned long opid = op->o_opid;
361         SlapReply       rs2 = { REP_RESULT };
362         slap_callback   cb = { NULL, slap_null_cb, NULL, NULL };
363         Modifications   mod[ 2 ] = { { { 0 } } }, *ml;
364         struct berval   values[ 4 ], nvalues[ 4 ];
365         int             mcnt = 0;
366
367         op2.o_tag = LDAP_REQ_MODIFY;
368
369         op2.o_req_dn = *ndn;
370         op2.o_req_ndn = *ndn;
371
372         op2.o_callback = &cb;
373         op2.o_dn = op->o_bd->be_rootdn;
374         op2.o_ndn = op->o_bd->be_rootndn;
375         op2.orm_modlist = NULL;
376
377         /* Internal ops, never replicate these */
378         op2.o_opid = 0;         /* shared with op, saved above */
379         op2.orm_no_opattrs = 1;
380         op2.o_dont_replicate = 1;
381
382         if ( !BER_BVISNULL( &mo->mo_ndn ) ) {
383                 ml = &mod[ mcnt ];
384                 ml->sml_numvals = 1;
385                 ml->sml_values = &values[ 0 ];
386                 ml->sml_values[ 0 ] = mo->mo_dn;
387                 BER_BVZERO( &ml->sml_values[ 1 ] );
388                 ml->sml_nvalues = &nvalues[ 0 ];
389                 ml->sml_nvalues[ 0 ] = mo->mo_ndn;
390                 BER_BVZERO( &ml->sml_nvalues[ 1 ] );
391                 ml->sml_desc = slap_schema.si_ad_modifiersName;
392                 ml->sml_type = ml->sml_desc->ad_cname;
393                 ml->sml_op = LDAP_MOD_REPLACE;
394                 ml->sml_flags = SLAP_MOD_INTERNAL;
395                 ml->sml_next = op2.orm_modlist;
396                 op2.orm_modlist = ml;
397
398                 mcnt++;
399         }
400
401         ml = &mod[ mcnt ];
402         ml->sml_numvals = 1;
403         ml->sml_values = &values[ 2 ];
404         BER_BVZERO( &ml->sml_values[ 1 ] );
405         ml->sml_nvalues = &nvalues[ 2 ];
406         BER_BVZERO( &ml->sml_nvalues[ 1 ] );
407         ml->sml_desc = ad;
408         ml->sml_type = ml->sml_desc->ad_cname;
409         ml->sml_flags = SLAP_MOD_INTERNAL;
410         ml->sml_next = op2.orm_modlist;
411         op2.orm_modlist = ml;
412
413         if ( new_ndn != NULL ) {
414                 BackendInfo *bi = op2.o_bd->bd_info;
415                 OpExtra oex;
416
417                 assert( !BER_BVISNULL( new_dn ) );
418                 assert( !BER_BVISNULL( new_ndn ) );
419
420                 ml = &mod[ mcnt ];
421                 ml->sml_op = LDAP_MOD_ADD;
422
423                 ml->sml_values[ 0 ] = *new_dn;
424                 ml->sml_nvalues[ 0 ] = *new_ndn;
425
426                 oex.oe_key = (void *)&memberof;
427                 LDAP_SLIST_INSERT_HEAD(&op2.o_extra, &oex, oe_next);
428                 memberof_set_backend( &op2, op, on );
429                 (void)op->o_bd->be_modify( &op2, &rs2 );
430                 op2.o_bd->bd_info = bi;
431                 LDAP_SLIST_REMOVE(&op2.o_extra, &oex, OpExtra, oe_next);
432                 if ( rs2.sr_err != LDAP_SUCCESS ) {
433                         char buf[ SLAP_TEXT_BUFLEN ];
434                         snprintf( buf, sizeof( buf ),
435                                 "memberof_value_modify DN=\"%s\" add %s=\"%s\" failed err=%d",
436                                 op2.o_req_dn.bv_val, ad->ad_cname.bv_val, new_dn->bv_val, rs2.sr_err );
437                         Debug( LDAP_DEBUG_ANY, "%s: %s\n",
438                                 op->o_log_prefix, buf, 0 );
439                 }
440
441                 assert( op2.orm_modlist == &mod[ mcnt ] );
442                 assert( mcnt == 0 || op2.orm_modlist->sml_next == &mod[ 0 ] );
443                 ml = op2.orm_modlist->sml_next;
444                 if ( mcnt == 1 ) {
445                         assert( ml == &mod[ 0 ] );
446                         ml = ml->sml_next;
447                 }
448                 if ( ml != NULL ) {
449                         slap_mods_free( ml, 1 );
450                 }
451
452                 mod[ 0 ].sml_next = NULL;
453         }
454
455         if ( old_ndn != NULL ) {
456                 BackendInfo *bi = op2.o_bd->bd_info;
457                 OpExtra oex;
458
459                 assert( !BER_BVISNULL( old_dn ) );
460                 assert( !BER_BVISNULL( old_ndn ) );
461
462                 ml = &mod[ mcnt ];
463                 ml->sml_op = LDAP_MOD_DELETE;
464
465                 ml->sml_values[ 0 ] = *old_dn;
466                 ml->sml_nvalues[ 0 ] = *old_ndn;
467
468                 oex.oe_key = (void *)&memberof;
469                 LDAP_SLIST_INSERT_HEAD(&op2.o_extra, &oex, oe_next);
470                 memberof_set_backend( &op2, op, on );
471                 (void)op->o_bd->be_modify( &op2, &rs2 );
472                 op2.o_bd->bd_info = bi;
473                 LDAP_SLIST_REMOVE(&op2.o_extra, &oex, OpExtra, oe_next);
474                 if ( rs2.sr_err != LDAP_SUCCESS ) {
475                         char buf[ SLAP_TEXT_BUFLEN ];
476                         snprintf( buf, sizeof( buf ),
477                                 "memberof_value_modify DN=\"%s\" delete %s=\"%s\" failed err=%d",
478                                 op2.o_req_dn.bv_val, ad->ad_cname.bv_val, old_dn->bv_val, rs2.sr_err );
479                         Debug( LDAP_DEBUG_ANY, "%s: %s\n",
480                                 op->o_log_prefix, buf, 0 );
481                 }
482
483                 assert( op2.orm_modlist == &mod[ mcnt ] );
484                 ml = op2.orm_modlist->sml_next;
485                 if ( mcnt == 1 ) {
486                         assert( ml == &mod[ 0 ] );
487                         ml = ml->sml_next;
488                 }
489                 if ( ml != NULL ) {
490                         slap_mods_free( ml, 1 );
491                 }
492         }
493         /* restore original opid */
494         op->o_opid = opid;
495
496         /* FIXME: if old_group_ndn doesn't exist, both delete __and__
497          * add will fail; better split in two operations, although
498          * not optimal in terms of performance.  At least it would
499          * move towards self-repairing capabilities. */
500 }
501
502 static int
503 memberof_cleanup( Operation *op, SlapReply *rs )
504 {
505         slap_callback *sc = op->o_callback;
506         memberof_cbinfo_t *mci = sc->sc_private;
507
508         op->o_callback = sc->sc_next;
509         if ( mci->memberof )
510                 ber_bvarray_free_x( mci->memberof, op->o_tmpmemctx );
511         if ( mci->member )
512                 ber_bvarray_free_x( mci->member, op->o_tmpmemctx );
513         op->o_tmpfree( sc, op->o_tmpmemctx );
514         return 0;
515 }
516
517 static int memberof_res_add( Operation *op, SlapReply *rs );
518 static int memberof_res_delete( Operation *op, SlapReply *rs );
519 static int memberof_res_modify( Operation *op, SlapReply *rs );
520 static int memberof_res_modrdn( Operation *op, SlapReply *rs );
521
522 static int
523 memberof_op_add( Operation *op, SlapReply *rs )
524 {
525         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
526         memberof_t      *mo = (memberof_t *)on->on_bi.bi_private;
527
528         Attribute       **ap, **map = NULL;
529         int             rc = SLAP_CB_CONTINUE;
530         int             i;
531         struct berval   save_dn, save_ndn;
532         slap_callback *sc;
533         memberof_cbinfo_t *mci;
534         OpExtra         *oex;
535
536         LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
537                 if ( oex->oe_key == (void *)&memberof )
538                         return SLAP_CB_CONTINUE;
539         }
540
541         if ( op->ora_e->e_attrs == NULL ) {
542                 /* FIXME: global overlay; need to deal with */
543                 Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
544                         "consistency checks not implemented when overlay "
545                         "is instantiated as global.\n",
546                         op->o_log_prefix, op->o_req_dn.bv_val, 0 );
547                 return SLAP_CB_CONTINUE;
548         }
549
550         if ( MEMBEROF_REVERSE( mo ) ) {
551                 for ( ap = &op->ora_e->e_attrs; *ap; ap = &(*ap)->a_next ) {
552                         Attribute       *a = *ap;
553
554                         if ( a->a_desc == mo->mo_ad_memberof ) {
555                                 map = ap;
556                                 break;
557                         }
558                 }
559         }
560
561         save_dn = op->o_dn;
562         save_ndn = op->o_ndn;
563
564         if ( MEMBEROF_DANGLING_CHECK( mo )
565                         && !get_relax( op )
566                         && is_entry_objectclass_or_sub( op->ora_e, mo->mo_oc_group ) )
567         {
568                 op->o_dn = op->o_bd->be_rootdn;
569                 op->o_ndn = op->o_bd->be_rootndn;
570                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
571
572                 for ( ap = &op->ora_e->e_attrs; *ap; ) {
573                         Attribute       *a = *ap;
574
575                         if ( !is_ad_subtype( a->a_desc, mo->mo_ad_member ) ) {
576                                 ap = &a->a_next;
577                                 continue;
578                         }
579
580                         assert( a->a_nvals != NULL );
581
582                         for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
583                                 Entry           *e = NULL;
584
585                                 /* ITS#6670 Ignore member pointing to this entry */
586                                 if ( dn_match( &a->a_nvals[i], &save_ndn ))
587                                         continue;
588
589                                 rc = be_entry_get_rw( op, &a->a_nvals[ i ],
590                                                 NULL, NULL, 0, &e );
591                                 if ( rc == LDAP_SUCCESS ) {
592                                         be_entry_release_r( op, e );
593                                         continue;
594                                 }
595
596                                 if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
597                                         rc = rs->sr_err = mo->mo_dangling_err;
598                                         rs->sr_text = "adding non-existing object "
599                                                 "as group member";
600                                         send_ldap_result( op, rs );
601                                         goto done;
602                                 }
603
604                                 if ( MEMBEROF_DANGLING_DROP( mo ) ) {
605                                         int     j;
606         
607                                         Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
608                                                 "member=\"%s\" does not exist (stripping...)\n",
609                                                 op->o_log_prefix, op->ora_e->e_name.bv_val,
610                                                 a->a_vals[ i ].bv_val );
611         
612                                         for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
613                                         ber_memfree( a->a_vals[ i ].bv_val );
614                                         BER_BVZERO( &a->a_vals[ i ] );
615                                         if ( a->a_nvals != a->a_vals ) {
616                                                 ber_memfree( a->a_nvals[ i ].bv_val );
617                                                 BER_BVZERO( &a->a_nvals[ i ] );
618                                         }
619                                         a->a_numvals--;
620                                         if ( j - i == 1 ) {
621                                                 break;
622                                         }
623                 
624                                         AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
625                                                 sizeof( struct berval ) * ( j - i ) );
626                                         if ( a->a_nvals != a->a_vals ) {
627                                                 AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
628                                                         sizeof( struct berval ) * ( j - i ) );
629                                         }
630                                         i--;
631                                 }
632                         }
633
634                         /* If all values have been removed,
635                          * remove the attribute itself. */
636                         if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
637                                 *ap = a->a_next;
638                                 attr_free( a );
639         
640                         } else {
641                                 ap = &a->a_next;
642                         }
643                 }
644                 op->o_dn = save_dn;
645                 op->o_ndn = save_ndn;
646                 op->o_bd->bd_info = (BackendInfo *)on;
647         }
648
649         if ( map != NULL ) {
650                 Attribute               *a = *map;
651                 AccessControlState      acl_state = ACL_STATE_INIT;
652
653                 for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
654                         Entry           *e;
655
656                         op->o_bd->bd_info = (BackendInfo *)on->on_info;
657                         /* access is checked with the original identity */
658                         rc = access_allowed( op, op->ora_e, mo->mo_ad_memberof,
659                                         &a->a_nvals[ i ], ACL_WADD,
660                                         &acl_state );
661                         if ( rc == 0 ) {
662                                 rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
663                                 rs->sr_text = NULL;
664                                 send_ldap_result( op, rs );
665                                 goto done;
666                         }
667                         /* ITS#6670 Ignore member pointing to this entry */
668                         if ( dn_match( &a->a_nvals[i], &save_ndn ))
669                                 continue;
670
671                         rc = be_entry_get_rw( op, &a->a_nvals[ i ],
672                                         NULL, NULL, 0, &e );
673                         op->o_bd->bd_info = (BackendInfo *)on;
674                         if ( rc != LDAP_SUCCESS ) {
675                                 if ( get_relax( op ) ) {
676                                         continue;
677                                 }
678
679                                 if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
680                                         rc = rs->sr_err = mo->mo_dangling_err;
681                                         rs->sr_text = "adding non-existing object "
682                                                 "as memberof";
683                                         send_ldap_result( op, rs );
684                                         goto done;
685                                 }
686
687                                 if ( MEMBEROF_DANGLING_DROP( mo ) ) {
688                                         int     j;
689         
690                                         Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
691                                                 "memberof=\"%s\" does not exist (stripping...)\n",
692                                                 op->o_log_prefix, op->ora_e->e_name.bv_val,
693                                                 a->a_nvals[ i ].bv_val );
694         
695                                         for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
696                                         ber_memfree( a->a_vals[ i ].bv_val );
697                                         BER_BVZERO( &a->a_vals[ i ] );
698                                         if ( a->a_nvals != a->a_vals ) {
699                                                 ber_memfree( a->a_nvals[ i ].bv_val );
700                                                 BER_BVZERO( &a->a_nvals[ i ] );
701                                         }
702                                         if ( j - i == 1 ) {
703                                                 break;
704                                         }
705                 
706                                         AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
707                                                 sizeof( struct berval ) * ( j - i ) );
708                                         if ( a->a_nvals != a->a_vals ) {
709                                                 AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
710                                                         sizeof( struct berval ) * ( j - i ) );
711                                         }
712                                         i--;
713                                 }
714                                 
715                                 continue;
716                         }
717
718                         /* access is checked with the original identity */
719                         op->o_bd->bd_info = (BackendInfo *)on->on_info;
720                         rc = access_allowed( op, e, mo->mo_ad_member,
721                                         &op->o_req_ndn, ACL_WADD, NULL );
722                         be_entry_release_r( op, e );
723                         op->o_bd->bd_info = (BackendInfo *)on;
724
725                         if ( !rc ) {
726                                 rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
727                                 rs->sr_text = "insufficient access to object referenced by memberof";
728                                 send_ldap_result( op, rs );
729                                 goto done;
730                         }
731                 }
732
733                 if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
734                         *map = a->a_next;
735                         attr_free( a );
736                 }
737         }
738
739         rc = SLAP_CB_CONTINUE;
740
741         sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
742         sc->sc_private = sc+1;
743         sc->sc_response = memberof_res_add;
744         sc->sc_cleanup = memberof_cleanup;
745         sc->sc_writewait = 0;
746         mci = sc->sc_private;
747         mci->on = on;
748         mci->member = NULL;
749         mci->memberof = NULL;
750         sc->sc_next = op->o_callback;
751         op->o_callback = sc;
752
753 done:;
754         op->o_dn = save_dn;
755         op->o_ndn = save_ndn;
756         op->o_bd->bd_info = (BackendInfo *)on;
757
758         return rc;
759 }
760
761 static int
762 memberof_op_delete( Operation *op, SlapReply *rs )
763 {
764         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
765         memberof_t      *mo = (memberof_t *)on->on_bi.bi_private;
766
767         slap_callback *sc;
768         memberof_cbinfo_t *mci;
769         OpExtra         *oex;
770
771         LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
772                 if ( oex->oe_key == (void *)&memberof )
773                         return SLAP_CB_CONTINUE;
774         }
775
776         sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
777         sc->sc_private = sc+1;
778         sc->sc_response = memberof_res_delete;
779         sc->sc_cleanup = memberof_cleanup;
780         sc->sc_writewait = 0;
781         mci = sc->sc_private;
782         mci->on = on;
783         mci->member = NULL;
784         mci->memberof = NULL;
785         mci->what = MEMBEROF_IS_GROUP;
786         if ( MEMBEROF_REFINT( mo ) ) {
787                 mci->what = MEMBEROF_IS_BOTH;
788         }
789
790         memberof_isGroupOrMember( op, mci );
791
792         sc->sc_next = op->o_callback;
793         op->o_callback = sc;
794
795         return SLAP_CB_CONTINUE;
796 }
797
798 static int
799 memberof_op_modify( Operation *op, SlapReply *rs )
800 {
801         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
802         memberof_t      *mo = (memberof_t *)on->on_bi.bi_private;
803
804         Modifications   **mlp, **mmlp = NULL;
805         int             rc = SLAP_CB_CONTINUE, save_member = 0;
806         struct berval   save_dn, save_ndn;
807         slap_callback *sc;
808         memberof_cbinfo_t *mci, mcis;
809         OpExtra         *oex;
810
811         LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
812                 if ( oex->oe_key == (void *)&memberof )
813                         return SLAP_CB_CONTINUE;
814         }
815
816         if ( MEMBEROF_REVERSE( mo ) ) {
817                 for ( mlp = &op->orm_modlist; *mlp; mlp = &(*mlp)->sml_next ) {
818                         Modifications   *ml = *mlp;
819
820                         if ( ml->sml_desc == mo->mo_ad_memberof ) {
821                                 mmlp = mlp;
822                                 break;
823                         }
824                 }
825         }
826
827         save_dn = op->o_dn;
828         save_ndn = op->o_ndn;
829         mcis.on = on;
830         mcis.what = MEMBEROF_IS_GROUP;
831
832         if ( memberof_isGroupOrMember( op, &mcis ) == LDAP_SUCCESS
833                 && ( mcis.what & MEMBEROF_IS_GROUP ) )
834         {
835                 Modifications *ml;
836
837                 for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
838                         if ( ml->sml_desc == mo->mo_ad_member ) {
839                                 switch ( ml->sml_op ) {
840                                 case LDAP_MOD_DELETE:
841                                 case LDAP_MOD_REPLACE:
842                                 case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
843                                         save_member = 1;
844                                         break;
845                                 }
846                         }
847                 }
848
849
850                 if ( MEMBEROF_DANGLING_CHECK( mo )
851                                 && !get_relax( op ) )
852                 {
853                         op->o_dn = op->o_bd->be_rootdn;
854                         op->o_ndn = op->o_bd->be_rootndn;
855                         op->o_bd->bd_info = (BackendInfo *)on->on_info;
856                 
857                         assert( op->orm_modlist != NULL );
858                 
859                         for ( mlp = &op->orm_modlist; *mlp; ) {
860                                 Modifications   *ml = *mlp;
861                                 int             i;
862                 
863                                 if ( !is_ad_subtype( ml->sml_desc, mo->mo_ad_member ) ) {
864                                         mlp = &ml->sml_next;
865                                         continue;
866                                 }
867                 
868                                 switch ( ml->sml_op ) {
869                                 case LDAP_MOD_DELETE:
870                                 case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
871                                         /* we don't care about cancellations: if the value
872                                          * exists, fine; if it doesn't, we let the underlying
873                                          * database fail as appropriate; */
874                                         mlp = &ml->sml_next;
875                                         break;
876                 
877                                 case LDAP_MOD_REPLACE:
878                                         /* Handle this just like a delete (see above) */
879                                         if ( !ml->sml_values ) {
880                                                 mlp = &ml->sml_next;
881                                                 break;
882                                         }
883  
884                                 case LDAP_MOD_ADD:
885                                 case SLAP_MOD_SOFTADD: /* ITS#7487 */
886                                 case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
887                                         /* NOTE: right now, the attributeType we use
888                                          * for member must have a normalized value */
889                                         assert( ml->sml_nvalues != NULL );
890                 
891                                         for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
892                                                 Entry           *e;
893                 
894                                                 /* ITS#6670 Ignore member pointing to this entry */
895                                                 if ( dn_match( &ml->sml_nvalues[i], &save_ndn ))
896                                                         continue;
897
898                                                 if ( be_entry_get_rw( op, &ml->sml_nvalues[ i ],
899                                                                 NULL, NULL, 0, &e ) == LDAP_SUCCESS )
900                                                 {
901                                                         be_entry_release_r( op, e );
902                                                         continue;
903                                                 }
904                 
905                                                 if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
906                                                         rc = rs->sr_err = mo->mo_dangling_err;
907                                                         rs->sr_text = "adding non-existing object "
908                                                                 "as group member";
909                                                         send_ldap_result( op, rs );
910                                                         goto done;
911                                                 }
912                 
913                                                 if ( MEMBEROF_DANGLING_DROP( mo ) ) {
914                                                         int     j;
915                 
916                                                         Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
917                                                                 "member=\"%s\" does not exist (stripping...)\n",
918                                                                 op->o_log_prefix, op->o_req_dn.bv_val,
919                                                                 ml->sml_nvalues[ i ].bv_val );
920                 
921                                                         for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
922                                                         ber_memfree( ml->sml_values[ i ].bv_val );
923                                                         BER_BVZERO( &ml->sml_values[ i ] );
924                                                         ber_memfree( ml->sml_nvalues[ i ].bv_val );
925                                                         BER_BVZERO( &ml->sml_nvalues[ i ] );
926                                                         ml->sml_numvals--;
927                                                         if ( j - i == 1 ) {
928                                                                 break;
929                                                         }
930                 
931                                                         AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
932                                                                 sizeof( struct berval ) * ( j - i ) );
933                                                         AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
934                                                                 sizeof( struct berval ) * ( j - i ) );
935                                                         i--;
936                                                 }
937                                         }
938                 
939                                         if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
940                                                 *mlp = ml->sml_next;
941                                                 slap_mod_free( &ml->sml_mod, 0 );
942                                                 free( ml );
943                 
944                                         } else {
945                                                 mlp = &ml->sml_next;
946                                         }
947                 
948                                         break;
949                 
950                                 default:
951                                         assert( 0 );
952                                 }
953                         }
954                 }
955         }
956         
957         if ( mmlp != NULL ) {
958                 Modifications   *ml = *mmlp;
959                 int             i;
960                 Entry           *target;
961
962                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
963                 rc = be_entry_get_rw( op, &op->o_req_ndn,
964                                 NULL, NULL, 0, &target );
965                 op->o_bd->bd_info = (BackendInfo *)on;
966                 if ( rc != LDAP_SUCCESS ) {
967                         rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
968                         send_ldap_result( op, rs );
969                         goto done;
970                 }
971
972                 switch ( ml->sml_op ) {
973                 case LDAP_MOD_DELETE:
974                 case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
975                         if ( ml->sml_nvalues != NULL ) {
976                                 AccessControlState      acl_state = ACL_STATE_INIT;
977
978                                 for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
979                                         Entry           *e;
980
981                                         op->o_bd->bd_info = (BackendInfo *)on->on_info;
982                                         /* access is checked with the original identity */
983                                         rc = access_allowed( op, target,
984                                                         mo->mo_ad_memberof,
985                                                         &ml->sml_nvalues[ i ],
986                                                         ACL_WDEL,
987                                                         &acl_state );
988                                         if ( rc == 0 ) {
989                                                 rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
990                                                 rs->sr_text = NULL;
991                                                 send_ldap_result( op, rs );
992                                                 goto done2;
993                                         }
994
995                                         rc = be_entry_get_rw( op, &ml->sml_nvalues[ i ],
996                                                         NULL, NULL, 0, &e );
997                                         op->o_bd->bd_info = (BackendInfo *)on;
998                                         if ( rc != LDAP_SUCCESS ) {
999                                                 if ( get_relax( op ) ) {
1000                                                         continue;
1001                                                 }
1002
1003                                                 if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
1004                                                         rc = rs->sr_err = mo->mo_dangling_err;
1005                                                         rs->sr_text = "deleting non-existing object "
1006                                                                 "as memberof";
1007                                                         send_ldap_result( op, rs );
1008                                                         goto done2;
1009                                                 }
1010
1011                                                 if ( MEMBEROF_DANGLING_DROP( mo ) ) {
1012                                                         int     j;
1013         
1014                                                         Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
1015                                                                 "memberof=\"%s\" does not exist (stripping...)\n",
1016                                                                 op->o_log_prefix, op->o_req_ndn.bv_val,
1017                                                                 ml->sml_nvalues[ i ].bv_val );
1018         
1019                                                         for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
1020                                                         ber_memfree( ml->sml_values[ i ].bv_val );
1021                                                         BER_BVZERO( &ml->sml_values[ i ] );
1022                                                         if ( ml->sml_nvalues != ml->sml_values ) {
1023                                                                 ber_memfree( ml->sml_nvalues[ i ].bv_val );
1024                                                                 BER_BVZERO( &ml->sml_nvalues[ i ] );
1025                                                         }
1026                                                         ml->sml_numvals--;
1027                                                         if ( j - i == 1 ) {
1028                                                                 break;
1029                                                         }
1030                 
1031                                                         AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
1032                                                                 sizeof( struct berval ) * ( j - i ) );
1033                                                         if ( ml->sml_nvalues != ml->sml_values ) {
1034                                                                 AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
1035                                                                         sizeof( struct berval ) * ( j - i ) );
1036                                                         }
1037                                                         i--;
1038                                                 }
1039
1040                                                 continue;
1041                                         }
1042
1043                                         /* access is checked with the original identity */
1044                                         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1045                                         rc = access_allowed( op, e, mo->mo_ad_member,
1046                                                         &op->o_req_ndn,
1047                                                         ACL_WDEL, NULL );
1048                                         be_entry_release_r( op, e );
1049                                         op->o_bd->bd_info = (BackendInfo *)on;
1050
1051                                         if ( !rc ) {
1052                                                 rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1053                                                 rs->sr_text = "insufficient access to object referenced by memberof";
1054                                                 send_ldap_result( op, rs );
1055                                                 goto done;
1056                                         }
1057                                 }
1058
1059                                 if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
1060                                         *mmlp = ml->sml_next;
1061                                         slap_mod_free( &ml->sml_mod, 0 );
1062                                         free( ml );
1063                                 }
1064
1065                                 break;
1066                         }
1067                         /* fall thru */
1068
1069                 case LDAP_MOD_REPLACE:
1070
1071                         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1072                         /* access is checked with the original identity */
1073                         rc = access_allowed( op, target,
1074                                         mo->mo_ad_memberof,
1075                                         NULL,
1076                                         ACL_WDEL, NULL );
1077                         op->o_bd->bd_info = (BackendInfo *)on;
1078                         if ( rc == 0 ) {
1079                                 rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1080                                 rs->sr_text = NULL;
1081                                 send_ldap_result( op, rs );
1082                                 goto done2;
1083                         }
1084
1085                         if ( ml->sml_op == LDAP_MOD_DELETE || ml->sml_op == SLAP_MOD_SOFTDEL || !ml->sml_values ) {
1086                                 break;
1087                         }
1088                         /* fall thru */
1089
1090                 case LDAP_MOD_ADD:
1091                 case SLAP_MOD_SOFTADD: /* ITS#7487 */
1092                 case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
1093                         {
1094                         AccessControlState      acl_state = ACL_STATE_INIT;
1095
1096                         for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
1097                                 Entry           *e;
1098
1099                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1100                                 /* access is checked with the original identity */
1101                                 rc = access_allowed( op, target,
1102                                                 mo->mo_ad_memberof,
1103                                                 &ml->sml_nvalues[ i ],
1104                                                 ACL_WADD,
1105                                                 &acl_state );
1106                                 if ( rc == 0 ) {
1107                                         rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1108                                         rs->sr_text = NULL;
1109                                         send_ldap_result( op, rs );
1110                                         goto done2;
1111                                 }
1112
1113                                 /* ITS#6670 Ignore member pointing to this entry */
1114                                 if ( dn_match( &ml->sml_nvalues[i], &save_ndn ))
1115                                         continue;
1116
1117                                 rc = be_entry_get_rw( op, &ml->sml_nvalues[ i ],
1118                                                 NULL, NULL, 0, &e );
1119                                 op->o_bd->bd_info = (BackendInfo *)on;
1120                                 if ( rc != LDAP_SUCCESS ) {
1121                                         if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
1122                                                 rc = rs->sr_err = mo->mo_dangling_err;
1123                                                 rs->sr_text = "adding non-existing object "
1124                                                         "as memberof";
1125                                                 send_ldap_result( op, rs );
1126                                                 goto done2;
1127                                         }
1128
1129                                         if ( MEMBEROF_DANGLING_DROP( mo ) ) {
1130                                                 int     j;
1131
1132                                                 Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
1133                                                         "memberof=\"%s\" does not exist (stripping...)\n",
1134                                                         op->o_log_prefix, op->o_req_ndn.bv_val,
1135                                                         ml->sml_nvalues[ i ].bv_val );
1136
1137                                                 for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
1138                                                 ber_memfree( ml->sml_values[ i ].bv_val );
1139                                                 BER_BVZERO( &ml->sml_values[ i ] );
1140                                                 if ( ml->sml_nvalues != ml->sml_values ) {
1141                                                         ber_memfree( ml->sml_nvalues[ i ].bv_val );
1142                                                         BER_BVZERO( &ml->sml_nvalues[ i ] );
1143                                                 }
1144                                                 ml->sml_numvals--;
1145                                                 if ( j - i == 1 ) {
1146                                                         break;
1147                                                 }
1148         
1149                                                 AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
1150                                                         sizeof( struct berval ) * ( j - i ) );
1151                                                 if ( ml->sml_nvalues != ml->sml_values ) {
1152                                                         AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
1153                                                                 sizeof( struct berval ) * ( j - i ) );
1154                                                 }
1155                                                 i--;
1156                                         }
1157
1158                                         continue;
1159                                 }
1160
1161                                 /* access is checked with the original identity */
1162                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1163                                 rc = access_allowed( op, e, mo->mo_ad_member,
1164                                                 &op->o_req_ndn,
1165                                                 ACL_WDEL, NULL );
1166                                 be_entry_release_r( op, e );
1167                                 op->o_bd->bd_info = (BackendInfo *)on;
1168
1169                                 if ( !rc ) {
1170                                         rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1171                                         rs->sr_text = "insufficient access to object referenced by memberof";
1172                                         send_ldap_result( op, rs );
1173                                         goto done;
1174                                 }
1175                         }
1176
1177                         if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
1178                                 *mmlp = ml->sml_next;
1179                                 slap_mod_free( &ml->sml_mod, 0 );
1180                                 free( ml );
1181                         }
1182
1183                         } break;
1184
1185                 default:
1186                         assert( 0 );
1187                 }
1188
1189 done2:;
1190                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1191                 be_entry_release_r( op, target );
1192                 op->o_bd->bd_info = (BackendInfo *)on;
1193         }
1194
1195         sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
1196         sc->sc_private = sc+1;
1197         sc->sc_response = memberof_res_modify;
1198         sc->sc_cleanup = memberof_cleanup;
1199         sc->sc_writewait = 0;
1200         mci = sc->sc_private;
1201         mci->on = on;
1202         mci->member = NULL;
1203         mci->memberof = NULL;
1204         mci->what = mcis.what;
1205
1206         if ( save_member ) {
1207                 op->o_dn = op->o_bd->be_rootdn;
1208                 op->o_ndn = op->o_bd->be_rootndn;
1209                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1210                 rc = backend_attribute( op, NULL, &op->o_req_ndn,
1211                                 mo->mo_ad_member, &mci->member, ACL_READ );
1212                 op->o_bd->bd_info = (BackendInfo *)on;
1213         }
1214
1215         sc->sc_next = op->o_callback;
1216         op->o_callback = sc;
1217
1218         rc = SLAP_CB_CONTINUE;
1219
1220 done:;
1221         op->o_dn = save_dn;
1222         op->o_ndn = save_ndn;
1223         op->o_bd->bd_info = (BackendInfo *)on;
1224
1225         return rc;
1226 }
1227
1228 static int
1229 memberof_op_modrdn( Operation *op, SlapReply *rs )
1230 {
1231         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
1232         slap_callback *sc;
1233         memberof_cbinfo_t *mci;
1234         OpExtra         *oex;
1235
1236         LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
1237                 if ( oex->oe_key == (void *)&memberof )
1238                         return SLAP_CB_CONTINUE;
1239         }
1240
1241         sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
1242         sc->sc_private = sc+1;
1243         sc->sc_response = memberof_res_modrdn;
1244         sc->sc_cleanup = memberof_cleanup;
1245         sc->sc_writewait = 0;
1246         mci = sc->sc_private;
1247         mci->on = on;
1248         mci->member = NULL;
1249         mci->memberof = NULL;
1250
1251         sc->sc_next = op->o_callback;
1252         op->o_callback = sc;
1253
1254         return SLAP_CB_CONTINUE;
1255 }
1256
1257 /*
1258  * response callback that adds memberof values when a group is added.
1259  */
1260 static int
1261 memberof_res_add( Operation *op, SlapReply *rs )
1262 {
1263         memberof_cbinfo_t *mci = op->o_callback->sc_private;
1264         slap_overinst   *on = mci->on;
1265         memberof_t      *mo = (memberof_t *)on->on_bi.bi_private;
1266
1267         int             i;
1268
1269         if ( rs->sr_err != LDAP_SUCCESS ) {
1270                 return SLAP_CB_CONTINUE;
1271         }
1272
1273         if ( MEMBEROF_REVERSE( mo ) ) {
1274                 Attribute       *ma;
1275
1276                 ma = attr_find( op->ora_e->e_attrs, mo->mo_ad_memberof );
1277                 if ( ma != NULL ) {
1278                         /* relax is required to allow to add
1279                          * a non-existing member */
1280                         op->o_relax = SLAP_CONTROL_CRITICAL;
1281
1282                         for ( i = 0; !BER_BVISNULL( &ma->a_nvals[ i ] ); i++ ) {
1283                 
1284                                 /* ITS#6670 Ignore member pointing to this entry */
1285                                 if ( dn_match( &ma->a_nvals[i], &op->o_req_ndn ))
1286                                         continue;
1287
1288                                 /* the modification is attempted
1289                                  * with the original identity */
1290                                 memberof_value_modify( op,
1291                                         &ma->a_nvals[ i ], mo->mo_ad_member,
1292                                         NULL, NULL, &op->o_req_dn, &op->o_req_ndn );
1293                         }
1294                 }
1295         }
1296
1297         if ( is_entry_objectclass_or_sub( op->ora_e, mo->mo_oc_group ) ) {
1298                 Attribute       *a;
1299
1300                 for ( a = attrs_find( op->ora_e->e_attrs, mo->mo_ad_member );
1301                                 a != NULL;
1302                                 a = attrs_find( a->a_next, mo->mo_ad_member ) )
1303                 {
1304                         for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
1305                                 /* ITS#6670 Ignore member pointing to this entry */
1306                                 if ( dn_match( &a->a_nvals[i], &op->o_req_ndn ))
1307                                         continue;
1308
1309                                 memberof_value_modify( op,
1310                                                 &a->a_nvals[ i ],
1311                                                 mo->mo_ad_memberof,
1312                                                 NULL, NULL,
1313                                                 &op->o_req_dn,
1314                                                 &op->o_req_ndn );
1315                         }
1316                 }
1317         }
1318
1319         return SLAP_CB_CONTINUE;
1320 }
1321
1322 /*
1323  * response callback that deletes memberof values when a group is deleted.
1324  */
1325 static int
1326 memberof_res_delete( Operation *op, SlapReply *rs )
1327 {
1328         memberof_cbinfo_t *mci = op->o_callback->sc_private;
1329         slap_overinst   *on = mci->on;
1330         memberof_t      *mo = (memberof_t *)on->on_bi.bi_private;
1331
1332         BerVarray       vals;
1333         int             i;
1334
1335         if ( rs->sr_err != LDAP_SUCCESS ) {
1336                 return SLAP_CB_CONTINUE;
1337         }
1338
1339         vals = mci->member;
1340         if ( vals != NULL ) {
1341                 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1342                         memberof_value_modify( op,
1343                                         &vals[ i ], mo->mo_ad_memberof,
1344                                         &op->o_req_dn, &op->o_req_ndn,
1345                                         NULL, NULL );
1346                 }
1347         }
1348
1349         if ( MEMBEROF_REFINT( mo ) ) {
1350                 vals = mci->memberof;
1351                 if ( vals != NULL ) {
1352                         for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1353                                 memberof_value_modify( op,
1354                                                 &vals[ i ], mo->mo_ad_member,
1355                                                 &op->o_req_dn, &op->o_req_ndn,
1356                                                 NULL, NULL );
1357                         }
1358                 }
1359         }
1360
1361         return SLAP_CB_CONTINUE;
1362 }
1363
1364 /*
1365  * response callback that adds/deletes memberof values when a group
1366  * is modified.
1367  */
1368 static int
1369 memberof_res_modify( Operation *op, SlapReply *rs )
1370 {
1371         memberof_cbinfo_t *mci = op->o_callback->sc_private;
1372         slap_overinst   *on = mci->on;
1373         memberof_t      *mo = (memberof_t *)on->on_bi.bi_private;
1374
1375         int             i, rc;
1376         Modifications   *ml, *mml = NULL;
1377         BerVarray       vals;
1378
1379         if ( rs->sr_err != LDAP_SUCCESS ) {
1380                 return SLAP_CB_CONTINUE;
1381         }
1382
1383         if ( MEMBEROF_REVERSE( mo ) ) {
1384                 for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
1385                         if ( ml->sml_desc == mo->mo_ad_memberof ) {
1386                                 mml = ml;
1387                                 break;
1388                         }
1389                 }
1390         }
1391
1392         if ( mml != NULL ) {
1393                 BerVarray       vals = mml->sml_nvalues;
1394
1395                 switch ( mml->sml_op ) {
1396                 case LDAP_MOD_DELETE:
1397                 case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
1398                         if ( vals != NULL ) {
1399                                 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1400                                         memberof_value_modify( op,
1401                                                         &vals[ i ], mo->mo_ad_member,
1402                                                         &op->o_req_dn, &op->o_req_ndn,
1403                                                         NULL, NULL );
1404                                 }
1405                                 break;
1406                         }
1407                         /* fall thru */
1408
1409                 case LDAP_MOD_REPLACE:
1410                         /* delete all ... */
1411                         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1412                         rc = backend_attribute( op, NULL, &op->o_req_ndn,
1413                                         mo->mo_ad_memberof, &vals, ACL_READ );
1414                         op->o_bd->bd_info = (BackendInfo *)on;
1415                         if ( rc == LDAP_SUCCESS ) {
1416                                 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1417                                         memberof_value_modify( op,
1418                                                         &vals[ i ], mo->mo_ad_member,
1419                                                         &op->o_req_dn, &op->o_req_ndn,
1420                                                         NULL, NULL );
1421                                 }
1422                                 ber_bvarray_free_x( vals, op->o_tmpmemctx );
1423                         }
1424
1425                         if ( ml->sml_op == LDAP_MOD_DELETE || !mml->sml_values ) {
1426                                 break;
1427                         }
1428                         /* fall thru */
1429
1430                 case LDAP_MOD_ADD:
1431                 case SLAP_MOD_SOFTADD: /* ITS#7487 */
1432                 case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
1433                         assert( vals != NULL );
1434
1435                         for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1436                                 memberof_value_modify( op,
1437                                                 &vals[ i ], mo->mo_ad_member,
1438                                                 NULL, NULL,
1439                                                 &op->o_req_dn, &op->o_req_ndn );
1440                         }
1441                         break;
1442
1443                 default:
1444                         assert( 0 );
1445                 }
1446         }
1447
1448         if ( mci->what & MEMBEROF_IS_GROUP )
1449         {
1450                 for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
1451                         if ( ml->sml_desc != mo->mo_ad_member ) {
1452                                 continue;
1453                         }
1454
1455                         switch ( ml->sml_op ) {
1456                         case LDAP_MOD_DELETE:
1457                         case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
1458                                 vals = ml->sml_nvalues;
1459                                 if ( vals != NULL ) {
1460                                         for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1461                                                 memberof_value_modify( op,
1462                                                                 &vals[ i ], mo->mo_ad_memberof,
1463                                                                 &op->o_req_dn, &op->o_req_ndn,
1464                                                                 NULL, NULL );
1465                                         }
1466                                         break;
1467                                 }
1468                                 /* fall thru */
1469         
1470                         case LDAP_MOD_REPLACE:
1471                                 vals = mci->member;
1472
1473                                 /* delete all ... */
1474                                 if ( vals != NULL ) {
1475                                         for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1476                                                 memberof_value_modify( op,
1477                                                                 &vals[ i ], mo->mo_ad_memberof,
1478                                                                 &op->o_req_dn, &op->o_req_ndn,
1479                                                                 NULL, NULL );
1480                                         }
1481                                 }
1482         
1483                                 if ( ml->sml_op == LDAP_MOD_DELETE || ml->sml_op == SLAP_MOD_SOFTDEL || !ml->sml_values ) {
1484                                         break;
1485                                 }
1486                                 /* fall thru */
1487         
1488                         case LDAP_MOD_ADD:
1489                         case SLAP_MOD_SOFTADD: /* ITS#7487 */
1490                         case SLAP_MOD_ADD_IF_NOT_PRESENT : /* ITS#7487 */
1491                                 assert( ml->sml_nvalues != NULL );
1492                                 vals = ml->sml_nvalues;
1493                                 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1494                                         memberof_value_modify( op,
1495                                                         &vals[ i ], mo->mo_ad_memberof,
1496                                                         NULL, NULL,
1497                                                         &op->o_req_dn, &op->o_req_ndn );
1498                                 }
1499                                 break;
1500         
1501                         default:
1502                                 assert( 0 );
1503                         }
1504                 }
1505         }
1506
1507         return SLAP_CB_CONTINUE;
1508 }
1509
1510 /*
1511  * response callback that adds/deletes member values when a group member
1512  * is renamed.
1513  */
1514 static int
1515 memberof_res_modrdn( Operation *op, SlapReply *rs )
1516 {
1517         memberof_cbinfo_t *mci = op->o_callback->sc_private;
1518         slap_overinst   *on = mci->on;
1519         memberof_t      *mo = (memberof_t *)on->on_bi.bi_private;
1520
1521         struct berval   newPDN, newDN = BER_BVNULL, newPNDN, newNDN;
1522         int             i, rc;
1523         BerVarray       vals;
1524
1525         struct berval   save_dn, save_ndn;
1526
1527         if ( rs->sr_err != LDAP_SUCCESS ) {
1528                 return SLAP_CB_CONTINUE;
1529         }
1530
1531         mci->what = MEMBEROF_IS_GROUP;
1532         if ( MEMBEROF_REFINT( mo ) ) {
1533                 mci->what |= MEMBEROF_IS_MEMBER;
1534         }
1535
1536         if ( op->orr_nnewSup ) {
1537                 newPNDN = *op->orr_nnewSup;
1538
1539         } else {
1540                 dnParent( &op->o_req_ndn, &newPNDN );
1541         }
1542
1543         build_new_dn( &newNDN, &newPNDN, &op->orr_nnewrdn, op->o_tmpmemctx ); 
1544
1545         save_dn = op->o_req_dn;
1546         save_ndn = op->o_req_ndn;
1547
1548         op->o_req_dn = newNDN;
1549         op->o_req_ndn = newNDN;
1550         rc = memberof_isGroupOrMember( op, mci );
1551         op->o_req_dn = save_dn;
1552         op->o_req_ndn = save_ndn;
1553
1554         if ( rc != LDAP_SUCCESS || mci->what == MEMBEROF_IS_NONE ) {
1555                 goto done;
1556         }
1557
1558         if ( op->orr_newSup ) {
1559                 newPDN = *op->orr_newSup;
1560
1561         } else {
1562                 dnParent( &op->o_req_dn, &newPDN );
1563         }
1564
1565         build_new_dn( &newDN, &newPDN, &op->orr_newrdn, op->o_tmpmemctx ); 
1566
1567         if ( mci->what & MEMBEROF_IS_GROUP ) {
1568                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1569                 rc = backend_attribute( op, NULL, &newNDN,
1570                                 mo->mo_ad_member, &vals, ACL_READ );
1571                 op->o_bd->bd_info = (BackendInfo *)on;
1572
1573                 if ( rc == LDAP_SUCCESS ) {
1574                         for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1575                                 memberof_value_modify( op,
1576                                                 &vals[ i ], mo->mo_ad_memberof,
1577                                                 &op->o_req_dn, &op->o_req_ndn,
1578                                                 &newDN, &newNDN );
1579                         }
1580                         ber_bvarray_free_x( vals, op->o_tmpmemctx );
1581                 }
1582         }
1583
1584         if ( MEMBEROF_REFINT( mo ) && ( mci->what & MEMBEROF_IS_MEMBER ) ) {
1585                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1586                 rc = backend_attribute( op, NULL, &newNDN,
1587                                 mo->mo_ad_memberof, &vals, ACL_READ );
1588                 op->o_bd->bd_info = (BackendInfo *)on;
1589
1590                 if ( rc == LDAP_SUCCESS ) {
1591                         for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1592                                 memberof_value_modify( op,
1593                                                 &vals[ i ], mo->mo_ad_member,
1594                                                 &op->o_req_dn, &op->o_req_ndn,
1595                                                 &newDN, &newNDN );
1596                         }
1597                         ber_bvarray_free_x( vals, op->o_tmpmemctx );
1598                 }
1599         }
1600
1601 done:;
1602         if ( !BER_BVISNULL( &newDN ) ) {
1603                 op->o_tmpfree( newDN.bv_val, op->o_tmpmemctx );
1604         }
1605         op->o_tmpfree( newNDN.bv_val, op->o_tmpmemctx );
1606
1607         return SLAP_CB_CONTINUE;
1608 }
1609
1610
1611 static int
1612 memberof_db_init(
1613         BackendDB       *be,
1614         ConfigReply     *cr )
1615 {
1616         slap_overinst   *on = (slap_overinst *)be->bd_info;
1617         memberof_t              *mo;
1618         const char              *text = NULL;
1619         int rc;
1620
1621         mo = (memberof_t *)ch_calloc( 1, sizeof( memberof_t ) );
1622
1623         /* safe default */
1624         mo->mo_dangling_err = LDAP_CONSTRAINT_VIOLATION;
1625
1626         if ( !ad_memberOf ) {
1627                 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &ad_memberOf, &text );
1628                 if ( rc != LDAP_SUCCESS ) {
1629                         Debug( LDAP_DEBUG_ANY, "memberof_db_init: "
1630                                         "unable to find attribute=\"%s\": %s (%d)\n",
1631                                         SLAPD_MEMBEROF_ATTR, text, rc );
1632                         return rc;
1633                 }
1634         }
1635
1636         if ( !ad_member ) {
1637                 rc = slap_str2ad( SLAPD_GROUP_ATTR, &ad_member, &text );
1638                 if ( rc != LDAP_SUCCESS ) {
1639                         Debug( LDAP_DEBUG_ANY, "memberof_db_init: "
1640                                         "unable to find attribute=\"%s\": %s (%d)\n",
1641                                         SLAPD_GROUP_ATTR, text, rc );
1642                         return rc;
1643                 }
1644         }
1645
1646         if ( !oc_group ) {
1647                 oc_group = oc_find( SLAPD_GROUP_CLASS );
1648                 if ( oc_group == NULL ) {
1649                         Debug( LDAP_DEBUG_ANY,
1650                                         "memberof_db_init: "
1651                                         "unable to find objectClass=\"%s\"\n",
1652                                         SLAPD_GROUP_CLASS, 0, 0 );
1653                         return 1;
1654                 }
1655         }
1656
1657         on->on_bi.bi_private = (void *)mo;
1658
1659         return 0;
1660 }
1661
1662 enum {
1663         MO_DN = 1,
1664         MO_DANGLING,
1665         MO_REFINT,
1666         MO_GROUP_OC,
1667         MO_MEMBER_AD,
1668         MO_MEMBER_OF_AD,
1669 #if 0
1670         MO_REVERSE,
1671 #endif
1672
1673         MO_DANGLING_ERROR,
1674
1675         MO_LAST
1676 };
1677
1678 static ConfigDriver mo_cf_gen;
1679
1680 #define OID             "1.3.6.1.4.1.7136.2.666.4"
1681 #define OIDAT           OID ".1.1"
1682 #define OIDCFGAT        OID ".1.2"
1683 #define OIDOC           OID ".2.1"
1684 #define OIDCFGOC        OID ".2.2"
1685
1686
1687 static ConfigTable mo_cfg[] = {
1688         { "memberof-dn", "modifiersName",
1689                 2, 2, 0, ARG_MAGIC|ARG_DN|MO_DN, mo_cf_gen,
1690                 "( OLcfgOvAt:18.0 NAME 'olcMemberOfDN' "
1691                         "DESC 'DN to be used as modifiersName' "
1692                         "SYNTAX OMsDN SINGLE-VALUE )",
1693                 NULL, NULL },
1694
1695         { "memberof-dangling", "ignore|drop|error",
1696                 2, 2, 0, ARG_MAGIC|MO_DANGLING, mo_cf_gen,
1697                 "( OLcfgOvAt:18.1 NAME 'olcMemberOfDangling' "
1698                         "DESC 'Behavior with respect to dangling members, "
1699                                 "constrained to ignore, drop, error' "
1700                         "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1701                 NULL, NULL },
1702
1703         { "memberof-refint", "true|FALSE",
1704                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|MO_REFINT, mo_cf_gen,
1705                 "( OLcfgOvAt:18.2 NAME 'olcMemberOfRefInt' "
1706                         "DESC 'Take care of referential integrity' "
1707                         "SYNTAX OMsBoolean SINGLE-VALUE )",
1708                 NULL, NULL },
1709
1710         { "memberof-group-oc", "objectClass",
1711                 2, 2, 0, ARG_MAGIC|MO_GROUP_OC, mo_cf_gen,
1712                 "( OLcfgOvAt:18.3 NAME 'olcMemberOfGroupOC' "
1713                         "DESC 'Group objectClass' "
1714                         "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1715                 NULL, NULL },
1716
1717         { "memberof-member-ad", "member attribute",
1718                 2, 2, 0, ARG_MAGIC|ARG_ATDESC|MO_MEMBER_AD, mo_cf_gen,
1719                 "( OLcfgOvAt:18.4 NAME 'olcMemberOfMemberAD' "
1720                         "DESC 'member attribute' "
1721                         "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1722                 NULL, NULL },
1723
1724         { "memberof-memberof-ad", "memberOf attribute",
1725                 2, 2, 0, ARG_MAGIC|ARG_ATDESC|MO_MEMBER_OF_AD, mo_cf_gen,
1726                 "( OLcfgOvAt:18.5 NAME 'olcMemberOfMemberOfAD' "
1727                         "DESC 'memberOf attribute' "
1728                         "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1729                 NULL, NULL },
1730
1731 #if 0
1732         { "memberof-reverse", "true|FALSE",
1733                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|MO_REVERSE, mo_cf_gen,
1734                 "( OLcfgOvAt:18.6 NAME 'olcMemberOfReverse' "
1735                         "DESC 'Take care of referential integrity "
1736                                 "also when directly modifying memberOf' "
1737                         "SYNTAX OMsBoolean SINGLE-VALUE )",
1738                 NULL, NULL },
1739 #endif
1740
1741         { "memberof-dangling-error", "error code",
1742                 2, 2, 0, ARG_MAGIC|MO_DANGLING_ERROR, mo_cf_gen,
1743                 "( OLcfgOvAt:18.7 NAME 'olcMemberOfDanglingError' "
1744                         "DESC 'Error code returned in case of dangling back reference' "
1745                         "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1746                 NULL, NULL },
1747
1748         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1749 };
1750
1751 static ConfigOCs mo_ocs[] = {
1752         { "( OLcfgOvOc:18.1 "
1753                 "NAME 'olcMemberOf' "
1754                 "DESC 'Member-of configuration' "
1755                 "SUP olcOverlayConfig "
1756                 "MAY ( "
1757                         "olcMemberOfDN "
1758                         "$ olcMemberOfDangling "
1759                         "$ olcMemberOfDanglingError"
1760                         "$ olcMemberOfRefInt "
1761                         "$ olcMemberOfGroupOC "
1762                         "$ olcMemberOfMemberAD "
1763                         "$ olcMemberOfMemberOfAD "
1764 #if 0
1765                         "$ olcMemberOfReverse "
1766 #endif
1767                         ") "
1768                 ")",
1769                 Cft_Overlay, mo_cfg, NULL, NULL },
1770         { NULL, 0, NULL }
1771 };
1772
1773 static slap_verbmasks dangling_mode[] = {
1774         { BER_BVC( "ignore" ),          MEMBEROF_NONE },
1775         { BER_BVC( "drop" ),            MEMBEROF_FDANGLING_DROP },
1776         { BER_BVC( "error" ),           MEMBEROF_FDANGLING_ERROR },
1777         { BER_BVNULL,                   0 }
1778 };
1779
1780 static int
1781 memberof_make_group_filter( memberof_t *mo )
1782 {
1783         char            *ptr;
1784
1785         if ( !BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
1786                 ch_free( mo->mo_groupFilterstr.bv_val );
1787         }
1788
1789         mo->mo_groupFilter.f_choice = LDAP_FILTER_EQUALITY;
1790         mo->mo_groupFilter.f_ava = &mo->mo_groupAVA;
1791         
1792         mo->mo_groupFilter.f_av_desc = slap_schema.si_ad_objectClass;
1793         mo->mo_groupFilter.f_av_value = mo->mo_oc_group->soc_cname;
1794
1795         mo->mo_groupFilterstr.bv_len = STRLENOF( "(=)" )
1796                 + slap_schema.si_ad_objectClass->ad_cname.bv_len
1797                 + mo->mo_oc_group->soc_cname.bv_len;
1798         ptr = mo->mo_groupFilterstr.bv_val = ch_malloc( mo->mo_groupFilterstr.bv_len + 1 );
1799         *ptr++ = '(';
1800         ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1801         *ptr++ = '=';
1802         ptr = lutil_strcopy( ptr, mo->mo_oc_group->soc_cname.bv_val );
1803         *ptr++ = ')';
1804         *ptr = '\0';
1805
1806         return 0;
1807 }
1808
1809 static int
1810 memberof_make_member_filter( memberof_t *mo )
1811 {
1812         char            *ptr;
1813
1814         if ( !BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
1815                 ch_free( mo->mo_memberFilterstr.bv_val );
1816         }
1817
1818         mo->mo_memberFilter.f_choice = LDAP_FILTER_PRESENT;
1819         mo->mo_memberFilter.f_desc = mo->mo_ad_memberof;
1820
1821         mo->mo_memberFilterstr.bv_len = STRLENOF( "(=*)" )
1822                 + mo->mo_ad_memberof->ad_cname.bv_len;
1823         ptr = mo->mo_memberFilterstr.bv_val = ch_malloc( mo->mo_memberFilterstr.bv_len + 1 );
1824         *ptr++ = '(';
1825         ptr = lutil_strcopy( ptr, mo->mo_ad_memberof->ad_cname.bv_val );
1826         ptr = lutil_strcopy( ptr, "=*)" );
1827
1828         return 0;
1829 }
1830
1831 static int
1832 mo_cf_gen( ConfigArgs *c )
1833 {
1834         slap_overinst   *on = (slap_overinst *)c->bi;
1835         memberof_t      *mo = (memberof_t *)on->on_bi.bi_private;
1836
1837         int             i, rc = 0;
1838
1839         if ( c->op == SLAP_CONFIG_EMIT ) {
1840                 struct berval bv = BER_BVNULL;
1841
1842                 switch( c->type ) {
1843                 case MO_DN:
1844                         if ( mo->mo_dn.bv_val != NULL) {
1845                                 value_add_one( &c->rvalue_vals, &mo->mo_dn );
1846                                 value_add_one( &c->rvalue_nvals, &mo->mo_ndn );
1847                         }
1848                         break;
1849
1850                 case MO_DANGLING:
1851                         enum_to_verb( dangling_mode, (mo->mo_flags & MEMBEROF_FDANGLING_MASK), &bv );
1852                         if ( BER_BVISNULL( &bv ) ) {
1853                                 /* there's something wrong... */
1854                                 assert( 0 );
1855                                 rc = 1;
1856
1857                         } else {
1858                                 value_add_one( &c->rvalue_vals, &bv );
1859                         }
1860                         break;
1861
1862                 case MO_DANGLING_ERROR:
1863                         if ( mo->mo_flags & MEMBEROF_FDANGLING_ERROR ) {
1864                                 char buf[ SLAP_TEXT_BUFLEN ];
1865                                 enum_to_verb( slap_ldap_response_code, mo->mo_dangling_err, &bv );
1866                                 if ( BER_BVISNULL( &bv ) ) {
1867                                         bv.bv_len = snprintf( buf, sizeof( buf ), "0x%x", mo->mo_dangling_err );
1868                                         if ( bv.bv_len < sizeof( buf ) ) {
1869                                                 bv.bv_val = buf;
1870                                         } else {
1871                                                 rc = 1;
1872                                                 break;
1873                                         }
1874                                 }
1875                                 value_add_one( &c->rvalue_vals, &bv );
1876                         } else {
1877                                 rc = 1;
1878                         }
1879                         break;
1880
1881                 case MO_REFINT:
1882                         c->value_int = MEMBEROF_REFINT( mo );
1883                         break;
1884
1885 #if 0
1886                 case MO_REVERSE:
1887                         c->value_int = MEMBEROF_REVERSE( mo );
1888                         break;
1889 #endif
1890
1891                 case MO_GROUP_OC:
1892                         if ( mo->mo_oc_group != NULL ){
1893                                 value_add_one( &c->rvalue_vals, &mo->mo_oc_group->soc_cname );
1894                         }
1895                         break;
1896
1897                 case MO_MEMBER_AD:
1898                         if ( mo->mo_ad_member != NULL ){
1899                                 value_add_one( &c->rvalue_vals, &mo->mo_ad_member->ad_cname );
1900                         }
1901                         break;
1902
1903                 case MO_MEMBER_OF_AD:
1904                         if ( mo->mo_ad_memberof != NULL ){
1905                                 value_add_one( &c->rvalue_vals, &mo->mo_ad_memberof->ad_cname );
1906                         }
1907                         break;
1908
1909                 default:
1910                         assert( 0 );
1911                         return 1;
1912                 }
1913
1914                 return rc;
1915
1916         } else if ( c->op == LDAP_MOD_DELETE ) {
1917                 switch( c->type ) {
1918                 case MO_DN:
1919                         if ( !BER_BVISNULL( &mo->mo_dn ) ) {
1920                                 ber_memfree( mo->mo_dn.bv_val );
1921                                 ber_memfree( mo->mo_ndn.bv_val );
1922                                 BER_BVZERO( &mo->mo_dn );
1923                                 BER_BVZERO( &mo->mo_ndn );
1924                         }
1925                         break;
1926
1927                 case MO_DANGLING:
1928                         mo->mo_flags &= ~MEMBEROF_FDANGLING_MASK;
1929                         break;
1930
1931                 case MO_DANGLING_ERROR:
1932                         mo->mo_dangling_err = LDAP_CONSTRAINT_VIOLATION;
1933                         break;
1934
1935                 case MO_REFINT:
1936                         mo->mo_flags &= ~MEMBEROF_FREFINT;
1937                         break;
1938
1939 #if 0
1940                 case MO_REVERSE:
1941                         mo->mo_flags &= ~MEMBEROF_FREVERSE;
1942                         break;
1943 #endif
1944
1945                 case MO_GROUP_OC:
1946                         mo->mo_oc_group = oc_group;
1947                         memberof_make_group_filter( mo );
1948                         break;
1949
1950                 case MO_MEMBER_AD:
1951                         mo->mo_ad_member = ad_member;
1952                         break;
1953
1954                 case MO_MEMBER_OF_AD:
1955                         mo->mo_ad_memberof = ad_memberOf;
1956                         memberof_make_member_filter( mo );
1957                         break;
1958
1959                 default:
1960                         assert( 0 );
1961                         return 1;
1962                 }
1963
1964         } else {
1965                 switch( c->type ) {
1966                 case MO_DN:
1967                         if ( !BER_BVISNULL( &mo->mo_dn ) ) {
1968                                 ber_memfree( mo->mo_dn.bv_val );
1969                                 ber_memfree( mo->mo_ndn.bv_val );
1970                         }
1971                         mo->mo_dn = c->value_dn;
1972                         mo->mo_ndn = c->value_ndn;
1973                         break;
1974
1975                 case MO_DANGLING:
1976                         i = verb_to_mask( c->argv[ 1 ], dangling_mode );
1977                         if ( BER_BVISNULL( &dangling_mode[ i ].word ) ) {
1978                                 return 1;
1979                         }
1980
1981                         mo->mo_flags &= ~MEMBEROF_FDANGLING_MASK;
1982                         mo->mo_flags |= dangling_mode[ i ].mask;
1983                         break;
1984
1985                 case MO_DANGLING_ERROR:
1986                         i = verb_to_mask( c->argv[ 1 ], slap_ldap_response_code );
1987                         if ( !BER_BVISNULL( &slap_ldap_response_code[ i ].word ) ) {
1988                                 mo->mo_dangling_err = slap_ldap_response_code[ i ].mask;
1989                         } else if ( lutil_atoix( &mo->mo_dangling_err, c->argv[ 1 ], 0 ) ) {
1990                                 return 1;
1991                         }
1992                         break;
1993
1994                 case MO_REFINT:
1995                         if ( c->value_int ) {
1996                                 mo->mo_flags |= MEMBEROF_FREFINT;
1997
1998                         } else {
1999                                 mo->mo_flags &= ~MEMBEROF_FREFINT;
2000                         }
2001                         break;
2002
2003 #if 0
2004                 case MO_REVERSE:
2005                         if ( c->value_int ) {
2006                                 mo->mo_flags |= MEMBEROF_FREVERSE;
2007
2008                         } else {
2009                                 mo->mo_flags &= ~MEMBEROF_FREVERSE;
2010                         }
2011                         break;
2012 #endif
2013
2014                 case MO_GROUP_OC: {
2015                         ObjectClass     *oc = NULL;
2016
2017                         oc = oc_find( c->argv[ 1 ] );
2018                         if ( oc == NULL ) {
2019                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
2020                                         "unable to find group objectClass=\"%s\"",
2021                                         c->argv[ 1 ] );
2022                                 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
2023                                         c->log, c->cr_msg, 0 );
2024                                 return 1;
2025                         }
2026
2027                         mo->mo_oc_group = oc;
2028                         memberof_make_group_filter( mo );
2029                         } break;
2030
2031                 case MO_MEMBER_AD: {
2032                         AttributeDescription    *ad = c->value_ad;
2033
2034                         if ( !is_at_syntax( ad->ad_type, SLAPD_DN_SYNTAX )              /* e.g. "member" */
2035                                 && !is_at_syntax( ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
2036                         {
2037                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
2038                                         "member attribute=\"%s\" must either "
2039                                         "have DN (%s) or nameUID (%s) syntax",
2040                                         c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
2041                                 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
2042                                         c->log, c->cr_msg, 0 );
2043                                 return 1;
2044                         }
2045
2046                         mo->mo_ad_member = ad;
2047                         } break;
2048
2049                 case MO_MEMBER_OF_AD: {
2050                         AttributeDescription    *ad = c->value_ad;
2051
2052                         if ( !is_at_syntax( ad->ad_type, SLAPD_DN_SYNTAX )              /* e.g. "member" */
2053                                 && !is_at_syntax( ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
2054                         {
2055                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
2056                                         "memberof attribute=\"%s\" must either "
2057                                         "have DN (%s) or nameUID (%s) syntax",
2058                                         c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
2059                                 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
2060                                         c->log, c->cr_msg, 0 );
2061                                 return 1;
2062                         }
2063
2064                         mo->mo_ad_memberof = ad;
2065                         memberof_make_member_filter( mo );
2066                         } break;
2067
2068                 default:
2069                         assert( 0 );
2070                         return 1;
2071                 }
2072         }
2073
2074         return 0;
2075 }
2076
2077 static int
2078 memberof_db_open(
2079         BackendDB       *be,
2080         ConfigReply     *cr )
2081 {
2082         slap_overinst   *on = (slap_overinst *)be->bd_info;
2083         memberof_t      *mo = (memberof_t *)on->on_bi.bi_private;
2084         
2085         int             rc;
2086
2087         if ( !mo->mo_ad_memberof ) {
2088                 mo->mo_ad_memberof = ad_memberOf;
2089         }
2090
2091         if ( ! mo->mo_ad_member ) {
2092                 mo->mo_ad_member = ad_member;
2093         }
2094
2095         if ( ! mo->mo_oc_group ) {
2096                 mo->mo_oc_group = oc_group;
2097         }
2098
2099         if ( BER_BVISNULL( &mo->mo_dn ) && !BER_BVISNULL( &be->be_rootdn ) ) {
2100                 ber_dupbv( &mo->mo_dn, &be->be_rootdn );
2101                 ber_dupbv( &mo->mo_ndn, &be->be_rootndn );
2102         }
2103
2104         if ( BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
2105                 memberof_make_group_filter( mo );
2106         }
2107
2108         if ( BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
2109                 memberof_make_member_filter( mo );
2110         }
2111
2112         return 0;
2113 }
2114
2115 static int
2116 memberof_db_destroy(
2117         BackendDB       *be,
2118         ConfigReply     *cr )
2119 {
2120         slap_overinst   *on = (slap_overinst *)be->bd_info;
2121         memberof_t      *mo = (memberof_t *)on->on_bi.bi_private;
2122
2123         if ( mo ) {
2124                 if ( !BER_BVISNULL( &mo->mo_dn ) ) {
2125                         ber_memfree( mo->mo_dn.bv_val );
2126                         ber_memfree( mo->mo_ndn.bv_val );
2127                 }
2128
2129                 if ( !BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
2130                         ber_memfree( mo->mo_groupFilterstr.bv_val );
2131                 }
2132
2133                 if ( !BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
2134                         ber_memfree( mo->mo_memberFilterstr.bv_val );
2135                 }
2136
2137                 ber_memfree( mo );
2138         }
2139
2140         return 0;
2141 }
2142
2143 static struct {
2144         char    *desc;
2145         AttributeDescription **adp;
2146 } as[] = {
2147         { "( 1.2.840.113556.1.2.102 "
2148                 "NAME 'memberOf' "
2149                 "DESC 'Group that the entry belongs to' "
2150                 "SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' "
2151                 "EQUALITY distinguishedNameMatch "      /* added */
2152                 "USAGE dSAOperation "                   /* added; questioned */
2153                 /* "NO-USER-MODIFICATION " */           /* add? */
2154                 "X-ORIGIN 'iPlanet Delegated Administrator' )",
2155                 &ad_memberOf },
2156         { NULL }
2157 };
2158
2159 #if SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC
2160 static
2161 #endif /* SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC */
2162 int
2163 memberof_initialize( void )
2164 {
2165         int                     code, i;
2166
2167         for ( i = 0; as[ i ].desc != NULL; i++ ) {
2168                 code = register_at( as[ i ].desc, as[ i ].adp, 0 );
2169                 if ( code ) {
2170                         Debug( LDAP_DEBUG_ANY,
2171                                 "memberof_initialize: register_at #%d failed\n",
2172                                 i, 0, 0 );
2173                         return code;
2174                 }
2175         }
2176
2177         memberof.on_bi.bi_type = "memberof";
2178
2179         memberof.on_bi.bi_db_init = memberof_db_init;
2180         memberof.on_bi.bi_db_open = memberof_db_open;
2181         memberof.on_bi.bi_db_destroy = memberof_db_destroy;
2182
2183         memberof.on_bi.bi_op_add = memberof_op_add;
2184         memberof.on_bi.bi_op_delete = memberof_op_delete;
2185         memberof.on_bi.bi_op_modify = memberof_op_modify;
2186         memberof.on_bi.bi_op_modrdn = memberof_op_modrdn;
2187
2188         memberof.on_bi.bi_cf_ocs = mo_ocs;
2189
2190         code = config_register_schema( mo_cfg, mo_ocs );
2191         if ( code ) return code;
2192
2193         return overlay_register( &memberof );
2194 }
2195
2196 #if SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC
2197 int
2198 init_module( int argc, char *argv[] )
2199 {
2200         return memberof_initialize();
2201 }
2202 #endif /* SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC */
2203
2204 #endif /* SLAPD_OVER_MEMBEROF */