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