xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/dupent/dupent.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
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