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