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