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-2021 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
dupent_parseCtrl(Operation * op,SlapReply * rs,LDAPControl * ctrl)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
dupent_response_done(Operation * op,SlapReply * rs)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
dupent_response_entry_1level(Operation * op,SlapReply * rs,Entry * e,valnum_t * valnum,int nattrs,int level)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
dupent_attr_prepare(dupent_t * ds,Entry * e,valnum_t * valnum,int nattrs,int c,Attribute ** app,Attribute ** ap_listp)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
dupent_response_entry(Operation * op,SlapReply * rs)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
dupent_response(Operation * op,SlapReply * rs)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
dupent_cleanup(Operation * op,SlapReply * rs)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
dupent_op_search(Operation * op,SlapReply * rs)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
dupent_initialize(void)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 );
539 return -1;
540 }
541
542 dupent.on_bi.bi_type = "dupent";
543
544 dupent.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
545 dupent.on_bi.bi_op_search = dupent_op_search;
546
547 return overlay_register( &dupent );
548 }
549
550 #if SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC
551 int
init_module(int argc,char * argv[])552 init_module( int argc, char *argv[] )
553 {
554 return dupent_initialize();
555 }
556 #endif /* SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC */
557
558 #endif /* SLAPD_OVER_DUPENT */
559