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 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 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 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 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 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 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 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 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 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 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