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