]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/dupent/dupent.c
1801dc2550637c8d9ef0a9b44147a51a18c04457
[openldap] / contrib / slapd-modules / dupent / dupent.c
1 /* dupent.c - LDAP Control for a Duplicate Entry Representation of Search Results */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2006-2017 The OpenLDAP Foundation.
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 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Pierangelo Masarati for inclusion
18  * in OpenLDAP Software.
19  */
20
21 /*
22  * LDAP Control for a Duplicate Entry Representation of Search Results
23  * <draft-ietf-ldapext-ldapv3-dupent-08.txt> (EXPIRED)
24  * <http://tools.ietf.org/id/draft-ietf-ldapext-ldapv3-dupent-08.txt>
25  */
26
27 #include "portable.h"
28
29 /* define SLAPD_OVER_DUPENT=2 to build as run-time loadable module */
30 #ifdef SLAPD_OVER_DUPENT
31
32 /*
33  * The macros
34  *
35  * LDAP_CONTROL_DUPENT_REQUEST          "2.16.840.1.113719.1.27.101.1"
36  * LDAP_CONTROL_DUPENT_RESPONSE         "2.16.840.1.113719.1.27.101.2"
37  * LDAP_CONTROL_DUPENT_ENTRY            "2.16.840.1.113719.1.27.101.3"
38  *
39  * are already defined in <ldap.h>
40  */
41
42 /*
43  * support for no attrs and "*" in AttributeDescriptionList is missing
44  */
45
46 #include "slap.h"
47 #include "ac/string.h"
48
49 #define o_dupent                        o_ctrlflag[dupent_cid]
50 #define o_ctrldupent            o_controls[dupent_cid]
51
52 static int dupent_cid;
53 static slap_overinst dupent;
54
55 typedef struct dupent_t {
56         AttributeName   *ds_an;
57         ber_len_t               ds_nattrs;
58         slap_mask_t             ds_flags;
59         ber_int_t               ds_paa;
60 } dupent_t;
61
62 static int
63 dupent_parseCtrl (
64         Operation *op,
65         SlapReply *rs,
66         LDAPControl *ctrl )
67 {
68         ber_tag_t tag;
69         BerElementBuffer berbuf;
70         BerElement *ber = (BerElement *)&berbuf;
71         ber_len_t len;
72         BerVarray AttributeDescriptionList = NULL;
73         ber_len_t cnt = sizeof(struct berval);
74         ber_len_t off = 0;
75         ber_int_t PartialApplicationAllowed = 1;
76         dupent_t *ds = NULL;
77         int i;
78
79         if ( op->o_dupent != SLAP_CONTROL_NONE ) {
80                 rs->sr_text = "Dupent control specified multiple times";
81                 return LDAP_PROTOCOL_ERROR;
82         }
83
84         if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
85                 rs->sr_text = "Dupent control value is absent";
86                 return LDAP_PROTOCOL_ERROR;
87         }
88
89         if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
90                 rs->sr_text = "Dupent control value is empty";
91                 return LDAP_PROTOCOL_ERROR;
92         }
93
94         ber_init2( ber, &ctrl->ldctl_value, 0 );
95
96         /*
97
98    DuplicateEntryRequest ::= SEQUENCE { 
99         AttributeDescriptionList, -- from [RFC2251] 
100         PartialApplicationAllowed BOOLEAN DEFAULT TRUE } 
101
102         AttributeDescriptionList ::= SEQUENCE OF 
103                 AttributeDescription 
104     
105         AttributeDescription ::= LDAPString 
106     
107         attributeDescription = AttributeType [ ";" <options> ] 
108
109          */
110
111         tag = ber_skip_tag( ber, &len );
112         if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
113         if ( ber_scanf( ber, "{M}", &AttributeDescriptionList, &cnt, off )
114                 == LBER_ERROR )
115         {
116                 rs->sr_text = "Dupent control: dupentSpec decoding error";
117                 rs->sr_err = LDAP_PROTOCOL_ERROR;
118                 goto done;
119         }
120         tag = ber_skip_tag( ber, &len );
121         if ( tag == LBER_BOOLEAN ) {
122                 /* NOTE: PartialApplicationAllowed is ignored, since the control
123                  * can always be honored
124                  */
125                 if ( ber_scanf( ber, "b", &PartialApplicationAllowed ) == LBER_ERROR )
126                 {
127                         rs->sr_text = "Dupent control: dupentSpec decoding error";
128                         rs->sr_err = LDAP_PROTOCOL_ERROR;
129                         goto done;
130                 }
131                 tag = ber_skip_tag( ber, &len );
132         }
133         if ( len || tag != LBER_DEFAULT ) {
134                 rs->sr_text = "Dupent control: dupentSpec decoding error";
135                 rs->sr_err = LDAP_PROTOCOL_ERROR;
136                 goto done;
137         }
138
139         ds = (dupent_t *)op->o_tmpcalloc( 1,
140                 sizeof(dupent_t) + sizeof(AttributeName)*cnt,
141                 op->o_tmpmemctx );
142
143         ds->ds_paa = PartialApplicationAllowed;
144
145         if ( cnt == 0 ) {
146                 ds->ds_flags |= SLAP_USERATTRS_YES;
147
148         } else {
149                 int c;
150
151                 ds->ds_an = (AttributeName *)&ds[ 1 ];
152
153                 for ( i = 0, c = 0; i < cnt; i++ ) {
154                         const char *text;
155                         int j;
156                         int rc;
157                         AttributeDescription *ad = NULL;
158
159                         if ( bvmatch( &AttributeDescriptionList[i],
160                                 slap_bv_all_user_attrs ) )
161                         {
162                                 if ( ds->ds_flags & SLAP_USERATTRS_YES ) {
163                                         rs->sr_text = "Dupent control: AttributeDescription decoding error";
164                                         rs->sr_err = LDAP_PROTOCOL_ERROR;
165                                         goto done;
166                                 }
167
168                                 ds->ds_flags |= SLAP_USERATTRS_YES;
169                                 continue;
170                         }
171
172                         rc = slap_bv2ad( &AttributeDescriptionList[i], &ad, &text );
173                         if ( rc != LDAP_SUCCESS ) {
174                                 continue;
175                         }
176
177                         ds->ds_an[c].an_desc = ad;
178                         ds->ds_an[c].an_name = ad->ad_cname;
179
180                         /* FIXME: not specified; consider this an error, just in case */
181                         for ( j = 0; j < c; j++ ) {
182                                 if ( ds->ds_an[c].an_desc == ds->ds_an[j].an_desc ) {
183                                         rs->sr_text = "Dupent control: AttributeDescription must be unique within AttributeDescriptionList";
184                                         rs->sr_err = LDAP_PROTOCOL_ERROR;
185                                         goto done;
186                                 }
187                         }
188
189                         c++;
190                 }
191
192                 ds->ds_nattrs = c;
193
194                 if ( ds->ds_flags & SLAP_USERATTRS_YES ) {
195                         /* purge user attrs */
196                         for ( i = 0; i < ds->ds_nattrs;  ) {
197                                 if ( is_at_operational( ds->ds_an[i].an_desc->ad_type ) ) {
198                                         i++;
199                                         continue;
200                                 }
201
202                                 ds->ds_nattrs--;
203                                 if ( i < ds->ds_nattrs ) {
204                                         ds->ds_an[i] = ds->ds_an[ds->ds_nattrs];
205                                 }
206                         }
207                 }
208         }
209
210         op->o_ctrldupent = (void *)ds;
211
212         op->o_dupent = ctrl->ldctl_iscritical
213                 ? SLAP_CONTROL_CRITICAL
214                 : SLAP_CONTROL_NONCRITICAL;
215
216         rs->sr_err = LDAP_SUCCESS;
217
218 done:;
219         if ( rs->sr_err != LDAP_SUCCESS ) {
220                 op->o_tmpfree( ds, op->o_tmpmemctx );
221         }
222
223         if ( AttributeDescriptionList != NULL ) {
224                 ber_memfree_x( AttributeDescriptionList, op->o_tmpmemctx );
225         }
226
227         return rs->sr_err;
228 }
229
230 typedef struct dupent_cb_t {
231         slap_overinst   *dc_on;
232         dupent_t                *dc_ds;
233         int             dc_skip;
234 } dupent_cb_t;
235
236 typedef struct valnum_t {
237         Attribute *ap;
238         Attribute a;
239         struct berval vals[2];
240         struct berval nvals[2];
241         int cnt;
242 } valnum_t;
243
244 static int
245 dupent_response_done( Operation *op, SlapReply *rs )
246 {
247         BerElementBuffer        berbuf;
248         BerElement                      *ber = (BerElement *) &berbuf;
249         struct berval           ctrlval;
250         LDAPControl                     *ctrl, *ctrlsp[2];
251
252         ber_init2( ber, NULL, LBER_USE_DER );
253
254         /*
255
256       DuplicateEntryResponseDone ::= SEQUENCE { 
257          resultCode,     -- From [RFC2251] 
258          errorMessage    [0] LDAPString OPTIONAL, 
259          attribute       [1] AttributeDescription OPTIONAL } 
260
261          */
262
263         ber_printf( ber, "{i}", rs->sr_err );
264         if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
265                 ber_free_buf( ber );
266                 if ( op->o_dupent == SLAP_CONTROL_CRITICAL ) {
267                         return LDAP_CONSTRAINT_VIOLATION;
268                 }
269                 return SLAP_CB_CONTINUE;
270         }
271
272         ctrl = op->o_tmpcalloc( 1,
273                 sizeof( LDAPControl ) + ctrlval.bv_len + 1,
274                 op->o_tmpmemctx );
275         ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
276         ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_RESPONSE;
277         ctrl->ldctl_iscritical = 0;
278         ctrl->ldctl_value.bv_len = ctrlval.bv_len;
279         AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
280         ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';
281
282         ber_free_buf( ber );
283
284         ctrlsp[0] = ctrl;
285         ctrlsp[1] = NULL;
286         slap_add_ctrls( op, rs, ctrlsp );
287
288         return SLAP_CB_CONTINUE;
289 }
290
291 static int
292 dupent_response_entry_1level(
293         Operation *op,
294         SlapReply *rs,
295         Entry *e,
296         valnum_t *valnum,
297         int nattrs,
298         int level )
299 {
300         int i, rc = LDAP_SUCCESS;
301
302         for ( i = 0; i < valnum[level].ap->a_numvals; i++ ) {
303                 LDAPControl     *ctrl = NULL, *ctrlsp[2];
304
305                 valnum[level].a.a_vals[0] = valnum[level].ap->a_vals[i];
306                 if ( valnum[level].ap->a_nvals != valnum[level].ap->a_vals ) {
307                         valnum[level].a.a_nvals[0] = valnum[level].ap->a_nvals[i];
308                 }
309
310                 if ( level < nattrs - 1 ) {
311                         rc = dupent_response_entry_1level( op, rs,
312                                 e, valnum, nattrs, level + 1 );
313                         if ( rc != LDAP_SUCCESS ) {
314                                 break;
315                         }
316
317                         continue;
318                 }
319
320                 /* NOTE: add the control all times, under the assumption
321                  * send_search_entry() honors the REP_CTRLS_MUSTBEFREED
322                  * set by slap_add_ctrls(); this is not true (ITS#6629)
323                  */
324                 ctrl = op->o_tmpcalloc( 1, sizeof( LDAPControl ), op->o_tmpmemctx );
325                 ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_ENTRY;
326                 ctrl->ldctl_iscritical = 0;
327
328                 ctrlsp[0] = ctrl;
329                 ctrlsp[1] = NULL;
330                 slap_add_ctrls( op, rs, ctrlsp );
331
332                 /* do the real send */
333                 rs->sr_entry = e;
334                 rc = send_search_entry( op, rs );
335                 if ( rc != LDAP_SUCCESS ) {
336                         break;
337                 }
338         }
339
340         return rc;
341 }
342
343 static void
344 dupent_attr_prepare( dupent_t *ds, Entry *e, valnum_t *valnum, int nattrs, int c, Attribute **app, Attribute **ap_listp )
345 {
346         valnum[c].ap = *app;
347         *app = (*app)->a_next;
348
349         valnum[c].ap->a_next = *ap_listp;
350         *ap_listp = valnum[c].ap;
351
352         valnum[c].a = *valnum[c].ap;
353         if ( c < nattrs - 1 ) {
354                 valnum[c].a.a_next = &valnum[c + 1].a;
355         } else {
356                 valnum[c].a.a_next = NULL;
357         }
358         valnum[c].a.a_numvals = 1;
359         valnum[c].a.a_vals = valnum[c].vals;
360         BER_BVZERO( &valnum[c].vals[1] );
361         if ( valnum[c].ap->a_nvals != valnum[c].ap->a_vals ) {
362                 valnum[c].a.a_nvals = valnum[c].nvals;
363                 BER_BVZERO( &valnum[c].nvals[1] );
364         } else {
365                 valnum[c].a.a_nvals = valnum[c].a.a_vals;
366         }
367 }
368
369 static int
370 dupent_response_entry( Operation *op, SlapReply *rs )
371 {
372         dupent_cb_t     *dc = (dupent_cb_t *)op->o_callback->sc_private;
373         int                     nattrs = 0;
374         valnum_t        *valnum = NULL;
375         Attribute       **app, *ap_list = NULL;
376         int                     i, c;
377         Entry           *e = NULL;
378         int                     rc;
379
380         assert( rs->sr_type == REP_SEARCH );
381
382         for ( i = 0; i < dc->dc_ds->ds_nattrs; i++ ) {
383                 Attribute *ap;
384
385                 ap = attr_find( rs->sr_entry->e_attrs,
386                         dc->dc_ds->ds_an[ i ].an_desc );
387                 if ( ap && ap->a_numvals > 1 ) {
388                         nattrs++;
389                 }
390         }
391
392         if ( dc->dc_ds->ds_flags & SLAP_USERATTRS_YES ) {
393                 Attribute *ap;
394
395                 for ( ap = rs->sr_entry->e_attrs; ap != NULL; ap = ap->a_next ) {
396                         if ( !is_at_operational( ap->a_desc->ad_type ) && ap->a_numvals > 1 ) {
397                                 nattrs++;
398                         }
399                 }
400         }
401
402         if ( !nattrs ) {
403                 return SLAP_CB_CONTINUE;
404         }
405
406         rs_entry2modifiable( op, rs, dc->dc_on );
407         rs->sr_flags &= ~(REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED);
408         e = rs->sr_entry;
409
410         valnum = op->o_tmpcalloc( sizeof(valnum_t), nattrs, op->o_tmpmemctx );
411
412         for ( c = 0, i = 0; i < dc->dc_ds->ds_nattrs; i++ ) {
413                 for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next ) {
414                         if ( (*app)->a_desc == dc->dc_ds->ds_an[ i ].an_desc ) {
415                                 break;
416                         }
417                 }
418
419                 if ( *app != NULL && (*app)->a_numvals > 1 ) {
420                         assert( c < nattrs );
421                         dupent_attr_prepare( dc->dc_ds, e, valnum, nattrs, c, app, &ap_list );
422                         c++;
423                 }
424         }
425
426         if ( dc->dc_ds->ds_flags & SLAP_USERATTRS_YES ) {
427                 for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next ) {
428                         if ( !is_at_operational( (*app)->a_desc->ad_type ) && (*app)->a_numvals > 1 ) {
429                                 assert( c < nattrs );
430                                 dupent_attr_prepare( dc->dc_ds, e, valnum, nattrs, c, app, &ap_list );
431                                 c++;
432                         }
433                 }
434         }
435
436         for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next )
437                 /* goto tail */ ;
438
439         *app = &valnum[0].a;
440
441         /* NOTE: since send_search_entry() does not honor the
442          * REP_CTRLS_MUSTBEFREED flag set by slap_add_ctrls(),
443          * the control could be added here once for all (ITS#6629)
444          */
445
446         dc->dc_skip = 1;
447         rc = dupent_response_entry_1level( op, rs, e, valnum, nattrs, 0 );
448         dc->dc_skip = 0;
449
450         *app = ap_list;
451
452         entry_free( e );
453
454         op->o_tmpfree( valnum, op->o_tmpmemctx );
455
456         return rc;
457 }
458
459 static int
460 dupent_response( Operation *op, SlapReply *rs )
461 {
462         dupent_cb_t     *dc = (dupent_cb_t *)op->o_callback->sc_private;
463
464         if ( dc->dc_skip ) {
465                 return SLAP_CB_CONTINUE;
466         }
467         
468         switch ( rs->sr_type ) {
469         case REP_RESULT:
470                 return dupent_response_done( op, rs );
471
472         case REP_SEARCH:
473                 return dupent_response_entry( op, rs );
474
475         case REP_SEARCHREF:
476                 break;
477
478         default:
479                 assert( 0 );
480         }
481
482         return SLAP_CB_CONTINUE;
483 }
484
485 static int
486 dupent_cleanup( Operation *op, SlapReply *rs )
487 {
488         if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) {
489                 op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
490                 op->o_callback = NULL;
491
492                 op->o_tmpfree( op->o_ctrldupent, op->o_tmpmemctx );
493                 op->o_ctrldupent = NULL;
494         }
495
496         return SLAP_CB_CONTINUE;
497 }
498
499 static int
500 dupent_op_search( Operation *op, SlapReply *rs )
501 {
502         if ( op->o_dupent != SLAP_CONTROL_NONE ) {
503                 slap_callback *sc;
504                 dupent_cb_t *dc;
505
506                 sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( dupent_cb_t ), op->o_tmpmemctx );
507
508                 dc = (dupent_cb_t *)&sc[ 1 ];
509                 dc->dc_on = (slap_overinst *)op->o_bd->bd_info;
510                 dc->dc_ds = (dupent_t *)op->o_ctrldupent;
511                 dc->dc_skip = 0;
512
513                 sc->sc_response = dupent_response;
514                 sc->sc_cleanup = dupent_cleanup;
515                 sc->sc_private = (void *)dc;
516
517                 sc->sc_next = op->o_callback->sc_next;
518                 op->o_callback->sc_next = sc;
519         }
520         
521         return SLAP_CB_CONTINUE;
522 }
523
524 #if SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC
525 static
526 #endif /* SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC */
527 int
528 dupent_initialize( void )
529 {
530         int rc;
531
532         rc = register_supported_control( LDAP_CONTROL_DUPENT,
533                 SLAP_CTRL_SEARCH, NULL,
534                 dupent_parseCtrl, &dupent_cid );
535         if ( rc != LDAP_SUCCESS ) {
536                 Debug( LDAP_DEBUG_ANY,
537                         "dupent_initialize: Failed to register control (%d)\n",
538                         rc, 0, 0 );
539                 return -1;
540         }
541
542         dupent.on_bi.bi_type = "dupent";
543
544         dupent.on_bi.bi_op_search = dupent_op_search;
545
546         return overlay_register( &dupent );
547 }
548
549 #if SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC
550 int
551 init_module( int argc, char *argv[] )
552 {
553         return dupent_initialize();
554 }
555 #endif /* SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC */
556
557 #endif /* SLAPD_OVER_DUPENT */