1 1.3 christos /* $NetBSD: rdnval.c,v 1.4 2025/09/05 21:16:18 christos Exp $ */ 2 1.1 adam 3 1.1 adam /* rdnval.c - RDN value overlay */ 4 1.2 christos /* $OpenLDAP$ */ 5 1.1 adam /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 1.1 adam * 7 1.4 christos * Copyright 1998-2024 The OpenLDAP Foundation. 8 1.1 adam * Portions Copyright 2008 Pierangelo Masarati. 9 1.1 adam * All rights reserved. 10 1.1 adam * 11 1.1 adam * Redistribution and use in source and binary forms, with or without 12 1.1 adam * modification, are permitted only as authorized by the OpenLDAP 13 1.1 adam * Public License. 14 1.1 adam * 15 1.1 adam * A copy of this license is available in the file LICENSE in the 16 1.1 adam * top-level directory of the distribution or, alternatively, at 17 1.1 adam * <http://www.OpenLDAP.org/license.html>. 18 1.1 adam */ 19 1.1 adam /* ACKNOWLEDGEMENTS: 20 1.1 adam * This work was initially developed by Pierangelo Masarati 21 1.1 adam * for inclusion in OpenLDAP Software. 22 1.1 adam */ 23 1.1 adam 24 1.2 christos #include <sys/cdefs.h> 25 1.3 christos __RCSID("$NetBSD: rdnval.c,v 1.4 2025/09/05 21:16:18 christos Exp $"); 26 1.2 christos 27 1.1 adam #include "portable.h" 28 1.1 adam 29 1.1 adam #ifdef SLAPD_OVER_RDNVAL 30 1.1 adam 31 1.1 adam #include <stdio.h> 32 1.1 adam 33 1.1 adam #include "ac/string.h" 34 1.1 adam #include "ac/socket.h" 35 1.1 adam 36 1.1 adam #include "slap.h" 37 1.3 christos #include "slap-config.h" 38 1.1 adam 39 1.1 adam #include "lutil.h" 40 1.1 adam 41 1.1 adam /* 42 1.1 adam * Maintain an attribute (rdnValue) that contains the values of each AVA 43 1.1 adam * that builds up the RDN of an entry. This is required for interoperation 44 1.1 adam * with Samba4. It mimics the "name" attribute provided by Active Directory. 45 1.1 adam * The naming attributes must be directoryString-valued, or compatible. 46 1.1 adam * For example, IA5String values are cast into directoryString unless 47 1.1 adam * consisting of the empty string (""). 48 1.1 adam */ 49 1.1 adam 50 1.1 adam static AttributeDescription *ad_rdnValue; 51 1.1 adam static Syntax *syn_IA5String; 52 1.1 adam 53 1.1 adam static slap_overinst rdnval; 54 1.1 adam 55 1.1 adam static int 56 1.1 adam rdnval_is_valid( AttributeDescription *desc, struct berval *value ) 57 1.1 adam { 58 1.1 adam if ( desc->ad_type->sat_syntax == slap_schema.si_syn_directoryString ) { 59 1.1 adam return 1; 60 1.1 adam } 61 1.1 adam 62 1.1 adam if ( desc->ad_type->sat_syntax == syn_IA5String 63 1.1 adam && !BER_BVISEMPTY( value ) ) 64 1.1 adam { 65 1.1 adam return 1; 66 1.1 adam } 67 1.1 adam 68 1.1 adam return 0; 69 1.1 adam } 70 1.1 adam 71 1.1 adam static int 72 1.1 adam rdnval_unique_check_cb( Operation *op, SlapReply *rs ) 73 1.1 adam { 74 1.1 adam if ( rs->sr_type == REP_SEARCH ) { 75 1.1 adam int *p = (int *)op->o_callback->sc_private; 76 1.1 adam (*p)++; 77 1.1 adam } 78 1.1 adam 79 1.1 adam return 0; 80 1.1 adam } 81 1.1 adam 82 1.1 adam static int 83 1.1 adam rdnval_unique_check( Operation *op, BerVarray vals ) 84 1.1 adam { 85 1.1 adam slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 86 1.1 adam 87 1.1 adam BackendDB db = *op->o_bd; 88 1.1 adam Operation op2 = *op; 89 1.1 adam SlapReply rs2 = { 0 }; 90 1.1 adam int i; 91 1.1 adam BerVarray fvals; 92 1.1 adam char *ptr; 93 1.1 adam int gotit = 0; 94 1.1 adam slap_callback cb = { 0 }; 95 1.1 adam 96 1.1 adam /* short-circuit attempts to add suffix entry */ 97 1.1 adam if ( op->o_tag == LDAP_REQ_ADD 98 1.1 adam && be_issuffix( op->o_bd, &op->o_req_ndn ) ) 99 1.1 adam { 100 1.1 adam return LDAP_SUCCESS; 101 1.1 adam } 102 1.1 adam 103 1.1 adam op2.o_bd = &db; 104 1.1 adam op2.o_bd->bd_info = (BackendInfo *)on->on_info; 105 1.1 adam op2.o_tag = LDAP_REQ_SEARCH; 106 1.1 adam op2.o_dn = op->o_bd->be_rootdn; 107 1.1 adam op2.o_ndn = op->o_bd->be_rootndn; 108 1.1 adam op2.o_callback = &cb; 109 1.1 adam cb.sc_response = rdnval_unique_check_cb; 110 1.1 adam cb.sc_private = (void *)&gotit; 111 1.1 adam 112 1.1 adam dnParent( &op->o_req_ndn, &op2.o_req_dn ); 113 1.1 adam op2.o_req_ndn = op2.o_req_dn; 114 1.1 adam 115 1.1 adam op2.ors_limit = NULL; 116 1.1 adam op2.ors_slimit = 1; 117 1.1 adam op2.ors_tlimit = SLAP_NO_LIMIT; 118 1.1 adam op2.ors_attrs = slap_anlist_no_attrs; 119 1.1 adam op2.ors_attrsonly = 1; 120 1.1 adam op2.ors_deref = LDAP_DEREF_NEVER; 121 1.1 adam op2.ors_scope = LDAP_SCOPE_ONELEVEL; 122 1.1 adam 123 1.1 adam for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) 124 1.1 adam /* just count */ ; 125 1.1 adam 126 1.1 adam fvals = op->o_tmpcalloc( sizeof( struct berval ), i + 1, 127 1.1 adam op->o_tmpmemctx ); 128 1.1 adam 129 1.1 adam op2.ors_filterstr.bv_len = 0; 130 1.1 adam if ( i > 1 ) { 131 1.1 adam op2.ors_filterstr.bv_len = STRLENOF( "(&)" ); 132 1.1 adam } 133 1.1 adam 134 1.1 adam for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) { 135 1.1 adam ldap_bv2escaped_filter_value_x( &vals[ i ], &fvals[ i ], 136 1.1 adam 1, op->o_tmpmemctx ); 137 1.1 adam op2.ors_filterstr.bv_len += ad_rdnValue->ad_cname.bv_len 138 1.1 adam + fvals[ i ].bv_len + STRLENOF( "(=)" ); 139 1.1 adam } 140 1.1 adam 141 1.1 adam op2.ors_filterstr.bv_val = op->o_tmpalloc( op2.ors_filterstr.bv_len + 1, op->o_tmpmemctx ); 142 1.1 adam 143 1.1 adam ptr = op2.ors_filterstr.bv_val; 144 1.1 adam if ( i > 1 ) { 145 1.1 adam ptr = lutil_strcopy( ptr, "(&" ); 146 1.1 adam } 147 1.1 adam for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) { 148 1.1 adam *ptr++ = '('; 149 1.1 adam ptr = lutil_strncopy( ptr, ad_rdnValue->ad_cname.bv_val, ad_rdnValue->ad_cname.bv_len ); 150 1.1 adam *ptr++ = '='; 151 1.1 adam ptr = lutil_strncopy( ptr, fvals[ i ].bv_val, fvals[ i ].bv_len ); 152 1.1 adam *ptr++ = ')'; 153 1.1 adam } 154 1.1 adam 155 1.1 adam if ( i > 1 ) { 156 1.1 adam *ptr++ = ')'; 157 1.1 adam } 158 1.1 adam *ptr = '\0'; 159 1.1 adam 160 1.1 adam assert( ptr == op2.ors_filterstr.bv_val + op2.ors_filterstr.bv_len ); 161 1.1 adam op2.ors_filter = str2filter_x( op, op2.ors_filterstr.bv_val ); 162 1.1 adam assert( op2.ors_filter != NULL ); 163 1.1 adam 164 1.1 adam (void)op2.o_bd->be_search( &op2, &rs2 ); 165 1.1 adam 166 1.1 adam filter_free_x( op, op2.ors_filter, 1 ); 167 1.1 adam op->o_tmpfree( op2.ors_filterstr.bv_val, op->o_tmpmemctx ); 168 1.1 adam for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) { 169 1.1 adam if ( vals[ i ].bv_val != fvals[ i ].bv_val ) { 170 1.1 adam op->o_tmpfree( fvals[ i ].bv_val, op->o_tmpmemctx ); 171 1.1 adam } 172 1.1 adam } 173 1.1 adam op->o_tmpfree( fvals, op->o_tmpmemctx ); 174 1.1 adam 175 1.1 adam if ( rs2.sr_err != LDAP_SUCCESS || gotit > 0 ) { 176 1.1 adam return LDAP_CONSTRAINT_VIOLATION; 177 1.1 adam } 178 1.1 adam 179 1.1 adam return LDAP_SUCCESS; 180 1.1 adam } 181 1.1 adam 182 1.1 adam static int 183 1.1 adam rdnval_rdn2vals( 184 1.1 adam Operation *op, 185 1.1 adam SlapReply *rs, 186 1.1 adam struct berval *dn, 187 1.1 adam struct berval *ndn, 188 1.1 adam BerVarray *valsp, 189 1.1 adam BerVarray *nvalsp, 190 1.1 adam int *numvalsp ) 191 1.1 adam { 192 1.1 adam LDAPRDN rdn = NULL, nrdn = NULL; 193 1.1 adam int nAVA, i; 194 1.1 adam 195 1.1 adam assert( *valsp == NULL ); 196 1.1 adam assert( *nvalsp == NULL ); 197 1.1 adam 198 1.1 adam *numvalsp = 0; 199 1.1 adam 200 1.1 adam if ( ldap_bv2rdn_x( dn, &rdn, (char **)&rs->sr_text, 201 1.1 adam LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) ) 202 1.1 adam { 203 1.1 adam Debug( LDAP_DEBUG_TRACE, 204 1.1 adam "%s rdnval: can't figure out " 205 1.1 adam "type(s)/value(s) of rdn DN=\"%s\"\n", 206 1.3 christos op->o_log_prefix, dn->bv_val ); 207 1.1 adam rs->sr_err = LDAP_INVALID_DN_SYNTAX; 208 1.1 adam rs->sr_text = "unknown type(s) used in RDN"; 209 1.1 adam 210 1.1 adam goto done; 211 1.1 adam } 212 1.1 adam 213 1.1 adam if ( ldap_bv2rdn_x( ndn, &nrdn, 214 1.1 adam (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) ) 215 1.1 adam { 216 1.1 adam Debug( LDAP_DEBUG_TRACE, 217 1.1 adam "%s rdnval: can't figure out " 218 1.1 adam "type(s)/value(s) of normalized rdn DN=\"%s\"\n", 219 1.3 christos op->o_log_prefix, ndn->bv_val ); 220 1.1 adam rs->sr_err = LDAP_INVALID_DN_SYNTAX; 221 1.1 adam rs->sr_text = "unknown type(s) used in RDN"; 222 1.1 adam 223 1.1 adam goto done; 224 1.1 adam } 225 1.1 adam 226 1.1 adam for ( nAVA = 0; rdn[ nAVA ]; nAVA++ ) 227 1.1 adam /* count'em */ ; 228 1.1 adam 229 1.1 adam /* NOTE: we assume rdn and nrdn contain the same AVAs! */ 230 1.1 adam 231 1.3 christos *valsp = ch_calloc( sizeof( struct berval ), nAVA + 1 ); 232 1.3 christos *nvalsp = ch_calloc( sizeof( struct berval ), nAVA + 1 ); 233 1.1 adam 234 1.1 adam /* Add new attribute values to the entry */ 235 1.1 adam for ( i = 0; rdn[ i ]; i++ ) { 236 1.1 adam AttributeDescription *desc = NULL; 237 1.1 adam 238 1.1 adam rs->sr_err = slap_bv2ad( &rdn[ i ]->la_attr, 239 1.1 adam &desc, &rs->sr_text ); 240 1.1 adam 241 1.1 adam if ( rs->sr_err != LDAP_SUCCESS ) { 242 1.1 adam Debug( LDAP_DEBUG_TRACE, 243 1.1 adam "%s rdnval: %s: %s\n", 244 1.1 adam op->o_log_prefix, 245 1.1 adam rs->sr_text, 246 1.1 adam rdn[ i ]->la_attr.bv_val ); 247 1.1 adam goto done; 248 1.1 adam } 249 1.1 adam 250 1.1 adam if ( !rdnval_is_valid( desc, &rdn[ i ]->la_value ) ) { 251 1.1 adam Debug( LDAP_DEBUG_TRACE, 252 1.1 adam "%s rdnval: syntax of naming attribute '%s' " 253 1.1 adam "not compatible with directoryString", 254 1.3 christos op->o_log_prefix, rdn[ i ]->la_attr.bv_val ); 255 1.1 adam continue; 256 1.1 adam } 257 1.1 adam 258 1.1 adam if ( value_find_ex( desc, 259 1.1 adam SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH | 260 1.1 adam SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, 261 1.1 adam *nvalsp, 262 1.1 adam &nrdn[ i ]->la_value, 263 1.1 adam op->o_tmpmemctx ) 264 1.1 adam == LDAP_NO_SUCH_ATTRIBUTE ) 265 1.1 adam { 266 1.1 adam ber_dupbv( &(*valsp)[ *numvalsp ], &rdn[ i ]->la_value ); 267 1.1 adam ber_dupbv( &(*nvalsp)[ *numvalsp ], &nrdn[ i ]->la_value ); 268 1.1 adam 269 1.1 adam (*numvalsp)++; 270 1.1 adam } 271 1.1 adam } 272 1.1 adam 273 1.1 adam if ( rdnval_unique_check( op, *valsp ) != LDAP_SUCCESS ) { 274 1.1 adam rs->sr_err = LDAP_CONSTRAINT_VIOLATION; 275 1.1 adam rs->sr_text = "rdnValue not unique within siblings"; 276 1.1 adam goto done; 277 1.1 adam } 278 1.1 adam 279 1.1 adam done:; 280 1.1 adam if ( rdn != NULL ) { 281 1.1 adam ldap_rdnfree_x( rdn, op->o_tmpmemctx ); 282 1.1 adam } 283 1.1 adam 284 1.1 adam if ( nrdn != NULL ) { 285 1.1 adam ldap_rdnfree_x( nrdn, op->o_tmpmemctx ); 286 1.1 adam } 287 1.1 adam 288 1.1 adam if ( rs->sr_err != LDAP_SUCCESS ) { 289 1.1 adam if ( *valsp != NULL ) { 290 1.1 adam ber_bvarray_free( *valsp ); 291 1.1 adam ber_bvarray_free( *nvalsp ); 292 1.1 adam *valsp = NULL; 293 1.1 adam *nvalsp = NULL; 294 1.1 adam *numvalsp = 0; 295 1.1 adam } 296 1.1 adam } 297 1.1 adam 298 1.1 adam return rs->sr_err; 299 1.1 adam } 300 1.1 adam 301 1.1 adam static int 302 1.1 adam rdnval_op_add( Operation *op, SlapReply *rs ) 303 1.1 adam { 304 1.1 adam Attribute *a, **ap; 305 1.1 adam int numvals = 0; 306 1.1 adam BerVarray vals = NULL, nvals = NULL; 307 1.1 adam int rc; 308 1.1 adam 309 1.1 adam /* NOTE: should we accept an entry still in mods format? */ 310 1.1 adam assert( op->ora_e != NULL ); 311 1.1 adam 312 1.1 adam if ( BER_BVISEMPTY( &op->ora_e->e_nname ) ) { 313 1.1 adam return SLAP_CB_CONTINUE; 314 1.1 adam } 315 1.1 adam 316 1.1 adam a = attr_find( op->ora_e->e_attrs, ad_rdnValue ); 317 1.1 adam if ( a != NULL ) { 318 1.1 adam /* TODO: check consistency? */ 319 1.1 adam return SLAP_CB_CONTINUE; 320 1.1 adam } 321 1.1 adam 322 1.1 adam rc = rdnval_rdn2vals( op, rs, &op->ora_e->e_name, &op->ora_e->e_nname, 323 1.1 adam &vals, &nvals, &numvals ); 324 1.1 adam if ( rc != LDAP_SUCCESS ) { 325 1.1 adam send_ldap_result( op, rs ); 326 1.1 adam } 327 1.1 adam 328 1.1 adam a = attr_alloc( ad_rdnValue ); 329 1.1 adam 330 1.1 adam a->a_vals = vals; 331 1.1 adam a->a_nvals = nvals; 332 1.1 adam a->a_numvals = numvals; 333 1.1 adam 334 1.1 adam for ( ap = &op->ora_e->e_attrs; *ap != NULL; ap = &(*ap)->a_next ) 335 1.1 adam /* goto tail */ ; 336 1.1 adam 337 1.1 adam *ap = a; 338 1.1 adam 339 1.1 adam return SLAP_CB_CONTINUE; 340 1.1 adam } 341 1.1 adam 342 1.1 adam static int 343 1.1 adam rdnval_op_rename( Operation *op, SlapReply *rs ) 344 1.1 adam { 345 1.1 adam Modifications *ml, **mlp; 346 1.1 adam int numvals = 0; 347 1.1 adam BerVarray vals = NULL, nvals = NULL; 348 1.1 adam struct berval old; 349 1.1 adam int rc; 350 1.1 adam 351 1.2 christos dnRdn( &op->o_req_ndn, &old ); 352 1.2 christos if ( dn_match( &old, &op->orr_nnewrdn ) ) { 353 1.2 christos return SLAP_CB_CONTINUE; 354 1.1 adam } 355 1.1 adam 356 1.1 adam rc = rdnval_rdn2vals( op, rs, &op->orr_newrdn, &op->orr_nnewrdn, 357 1.1 adam &vals, &nvals, &numvals ); 358 1.1 adam if ( rc != LDAP_SUCCESS ) { 359 1.1 adam send_ldap_result( op, rs ); 360 1.1 adam } 361 1.1 adam 362 1.3 christos ml = ch_calloc( sizeof( Modifications ), 1 ); 363 1.1 adam ml->sml_values = vals; 364 1.1 adam ml->sml_nvalues = nvals; 365 1.1 adam 366 1.1 adam ml->sml_numvals = numvals; 367 1.1 adam 368 1.1 adam ml->sml_op = LDAP_MOD_REPLACE; 369 1.1 adam ml->sml_flags = SLAP_MOD_INTERNAL; 370 1.1 adam ml->sml_desc = ad_rdnValue; 371 1.1 adam ml->sml_type = ad_rdnValue->ad_cname; 372 1.1 adam 373 1.1 adam for ( mlp = &op->orr_modlist; *mlp != NULL; mlp = &(*mlp)->sml_next ) 374 1.1 adam /* goto tail */ ; 375 1.1 adam 376 1.1 adam *mlp = ml; 377 1.1 adam 378 1.1 adam return SLAP_CB_CONTINUE; 379 1.1 adam } 380 1.1 adam 381 1.1 adam static int 382 1.1 adam rdnval_db_init( 383 1.1 adam BackendDB *be, 384 1.1 adam ConfigReply *cr) 385 1.1 adam { 386 1.1 adam if ( SLAP_ISGLOBALOVERLAY( be ) ) { 387 1.3 christos Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 388 1.1 adam "rdnval_db_init: rdnval cannot be used as global overlay.\n" ); 389 1.1 adam return 1; 390 1.1 adam } 391 1.1 adam 392 1.1 adam if ( be->be_nsuffix == NULL ) { 393 1.3 christos Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 394 1.1 adam "rdnval_db_init: database must have suffix\n" ); 395 1.1 adam return 1; 396 1.1 adam } 397 1.1 adam 398 1.1 adam if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) { 399 1.3 christos Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 400 1.1 adam "rdnval_db_init: missing rootdn for database DN=\"%s\", YMMV\n", 401 1.1 adam be->be_suffix[ 0 ].bv_val ); 402 1.1 adam } 403 1.1 adam 404 1.1 adam return 0; 405 1.1 adam } 406 1.1 adam 407 1.1 adam typedef struct rdnval_mod_t { 408 1.1 adam struct berval ndn; 409 1.1 adam BerVarray vals; 410 1.1 adam BerVarray nvals; 411 1.1 adam int numvals; 412 1.1 adam struct rdnval_mod_t *next; 413 1.1 adam } rdnval_mod_t; 414 1.1 adam 415 1.1 adam typedef struct { 416 1.1 adam BackendDB *bd; 417 1.1 adam rdnval_mod_t *mods; 418 1.1 adam } rdnval_repair_cb_t; 419 1.1 adam 420 1.1 adam static int 421 1.1 adam rdnval_repair_cb( Operation *op, SlapReply *rs ) 422 1.1 adam { 423 1.1 adam int rc; 424 1.1 adam rdnval_repair_cb_t *rcb = op->o_callback->sc_private; 425 1.1 adam rdnval_mod_t *mod; 426 1.1 adam BerVarray vals = NULL, nvals = NULL; 427 1.1 adam int numvals = 0; 428 1.1 adam ber_len_t len; 429 1.1 adam BackendDB *save_bd = op->o_bd; 430 1.1 adam 431 1.1 adam switch ( rs->sr_type ) { 432 1.1 adam case REP_SEARCH: 433 1.1 adam break; 434 1.1 adam 435 1.1 adam case REP_SEARCHREF: 436 1.1 adam case REP_RESULT: 437 1.1 adam return rs->sr_err; 438 1.1 adam 439 1.1 adam default: 440 1.1 adam assert( 0 ); 441 1.1 adam } 442 1.1 adam 443 1.1 adam assert( rs->sr_entry != NULL ); 444 1.1 adam 445 1.1 adam op->o_bd = rcb->bd; 446 1.1 adam rc = rdnval_rdn2vals( op, rs, &rs->sr_entry->e_name, &rs->sr_entry->e_nname, 447 1.1 adam &vals, &nvals, &numvals ); 448 1.1 adam op->o_bd = save_bd; 449 1.1 adam if ( rc != LDAP_SUCCESS ) { 450 1.1 adam return 0; 451 1.1 adam } 452 1.1 adam 453 1.1 adam len = sizeof( rdnval_mod_t ) + rs->sr_entry->e_nname.bv_len + 1; 454 1.1 adam mod = op->o_tmpalloc( len, op->o_tmpmemctx ); 455 1.1 adam mod->ndn.bv_len = rs->sr_entry->e_nname.bv_len; 456 1.1 adam mod->ndn.bv_val = (char *)&mod[1]; 457 1.1 adam lutil_strncopy( mod->ndn.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len ); 458 1.1 adam mod->vals = vals; 459 1.1 adam mod->nvals = nvals; 460 1.1 adam mod->numvals = numvals; 461 1.1 adam 462 1.1 adam mod->next = rcb->mods; 463 1.1 adam rcb->mods = mod; 464 1.1 adam 465 1.1 adam Debug( LDAP_DEBUG_TRACE, "%s: rdnval_repair_cb: scheduling entry DN=\"%s\" for repair\n", 466 1.3 christos op->o_log_prefix, rs->sr_entry->e_name.bv_val ); 467 1.1 adam 468 1.1 adam return 0; 469 1.1 adam } 470 1.1 adam 471 1.1 adam static int 472 1.1 adam rdnval_repair( BackendDB *be ) 473 1.1 adam { 474 1.1 adam slap_overinst *on = (slap_overinst *)be->bd_info; 475 1.1 adam void *ctx = ldap_pvt_thread_pool_context(); 476 1.1 adam Connection conn = { 0 }; 477 1.1 adam OperationBuffer opbuf; 478 1.1 adam Operation *op; 479 1.1 adam BackendDB db; 480 1.1 adam slap_callback sc = { 0 }; 481 1.1 adam rdnval_repair_cb_t rcb = { 0 }; 482 1.1 adam SlapReply rs = { REP_RESULT }; 483 1.1 adam rdnval_mod_t *rmod; 484 1.1 adam int nrepaired = 0; 485 1.1 adam 486 1.1 adam connection_fake_init2( &conn, &opbuf, ctx, 0 ); 487 1.1 adam op = &opbuf.ob_op; 488 1.1 adam 489 1.1 adam op->o_tag = LDAP_REQ_SEARCH; 490 1.1 adam memset( &op->oq_search, 0, sizeof( op->oq_search ) ); 491 1.1 adam 492 1.1 adam assert( !BER_BVISNULL( &be->be_nsuffix[ 0 ] ) ); 493 1.1 adam 494 1.1 adam op->o_bd = select_backend( &be->be_nsuffix[ 0 ], 0 ); 495 1.1 adam assert( op->o_bd != NULL ); 496 1.1 adam assert( op->o_bd->be_nsuffix != NULL ); 497 1.1 adam 498 1.1 adam op->o_req_dn = op->o_bd->be_suffix[ 0 ]; 499 1.1 adam op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ]; 500 1.1 adam 501 1.1 adam op->o_dn = op->o_bd->be_rootdn; 502 1.1 adam op->o_ndn = op->o_bd->be_rootndn; 503 1.1 adam 504 1.1 adam op->ors_scope = LDAP_SCOPE_SUBTREE; 505 1.1 adam op->ors_tlimit = SLAP_NO_LIMIT; 506 1.1 adam op->ors_slimit = SLAP_NO_LIMIT; 507 1.1 adam op->ors_attrs = slap_anlist_no_attrs; 508 1.1 adam 509 1.1 adam op->ors_filterstr.bv_len = STRLENOF( "(!(=*))" ) + ad_rdnValue->ad_cname.bv_len; 510 1.1 adam op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx ); 511 1.1 adam snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1, 512 1.1 adam "(!(%s=*))", ad_rdnValue->ad_cname.bv_val ); 513 1.1 adam 514 1.1 adam op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val ); 515 1.1 adam if ( op->ors_filter == NULL ) { 516 1.1 adam rs.sr_err = LDAP_OTHER; 517 1.1 adam goto done_search; 518 1.1 adam } 519 1.1 adam 520 1.1 adam op->o_callback = ≻ 521 1.1 adam sc.sc_response = rdnval_repair_cb; 522 1.1 adam sc.sc_private = &rcb; 523 1.1 adam rcb.bd = &db; 524 1.1 adam db = *be; 525 1.1 adam db.bd_info = (BackendInfo *)on; 526 1.1 adam 527 1.1 adam (void)op->o_bd->bd_info->bi_op_search( op, &rs ); 528 1.1 adam 529 1.1 adam op->o_tag = LDAP_REQ_MODIFY; 530 1.1 adam sc.sc_response = slap_null_cb; 531 1.1 adam sc.sc_private = NULL; 532 1.1 adam memset( &op->oq_modify, 0, sizeof( req_modify_s ) ); 533 1.1 adam 534 1.1 adam for ( rmod = rcb.mods; rmod != NULL; ) { 535 1.1 adam rdnval_mod_t *rnext; 536 1.1 adam 537 1.1 adam Modifications *mod; 538 1.1 adam SlapReply rs2 = { REP_RESULT }; 539 1.1 adam 540 1.1 adam mod = (Modifications *) ch_malloc( sizeof( Modifications ) ); 541 1.1 adam mod->sml_flags = SLAP_MOD_INTERNAL; 542 1.1 adam mod->sml_op = LDAP_MOD_REPLACE; 543 1.1 adam mod->sml_desc = ad_rdnValue; 544 1.1 adam mod->sml_type = ad_rdnValue->ad_cname; 545 1.1 adam mod->sml_values = rmod->vals; 546 1.1 adam mod->sml_nvalues = rmod->nvals; 547 1.1 adam mod->sml_numvals = rmod->numvals; 548 1.1 adam mod->sml_next = NULL; 549 1.1 adam 550 1.1 adam op->o_req_dn = rmod->ndn; 551 1.1 adam op->o_req_ndn = rmod->ndn; 552 1.1 adam 553 1.1 adam op->orm_modlist = mod; 554 1.1 adam 555 1.1 adam op->o_bd->be_modify( op, &rs2 ); 556 1.1 adam 557 1.1 adam slap_mods_free( op->orm_modlist, 1 ); 558 1.1 adam if ( rs2.sr_err == LDAP_SUCCESS ) { 559 1.1 adam Debug( LDAP_DEBUG_TRACE, "%s: rdnval_repair: entry DN=\"%s\" repaired\n", 560 1.3 christos op->o_log_prefix, rmod->ndn.bv_val ); 561 1.1 adam nrepaired++; 562 1.1 adam 563 1.1 adam } else { 564 1.1 adam Debug( LDAP_DEBUG_ANY, "%s: rdnval_repair: entry DN=\"%s\" repair failed (%d)\n", 565 1.1 adam op->o_log_prefix, rmod->ndn.bv_val, rs2.sr_err ); 566 1.1 adam } 567 1.1 adam 568 1.1 adam rnext = rmod->next; 569 1.1 adam op->o_tmpfree( rmod, op->o_tmpmemctx ); 570 1.1 adam rmod = rnext; 571 1.1 adam } 572 1.1 adam 573 1.1 adam done_search:; 574 1.1 adam op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx ); 575 1.1 adam filter_free_x( op, op->ors_filter, 1 ); 576 1.1 adam 577 1.3 christos Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO, 578 1.1 adam "rdnval: repaired=%d\n", nrepaired ); 579 1.1 adam 580 1.1 adam return 0; 581 1.1 adam } 582 1.1 adam 583 1.1 adam /* search all entries without parentUUID; "repair" them */ 584 1.1 adam static int 585 1.1 adam rdnval_db_open( 586 1.1 adam BackendDB *be, 587 1.1 adam ConfigReply *cr ) 588 1.1 adam { 589 1.1 adam if ( SLAP_SINGLE_SHADOW( be ) ) { 590 1.3 christos Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, 591 1.1 adam "rdnval incompatible with shadow database \"%s\".\n", 592 1.1 adam be->be_suffix[ 0 ].bv_val ); 593 1.1 adam return 1; 594 1.1 adam } 595 1.1 adam 596 1.1 adam return rdnval_repair( be ); 597 1.1 adam } 598 1.1 adam 599 1.1 adam static struct { 600 1.1 adam char *desc; 601 1.1 adam AttributeDescription **adp; 602 1.1 adam } as[] = { 603 1.1 adam { "( 1.3.6.1.4.1.4203.666.1.58 " 604 1.1 adam "NAME 'rdnValue' " 605 1.1 adam "DESC 'the value of the naming attributes' " 606 1.1 adam "SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' " 607 1.1 adam "EQUALITY caseIgnoreMatch " 608 1.1 adam "USAGE dSAOperation " 609 1.1 adam "NO-USER-MODIFICATION " 610 1.1 adam ")", 611 1.1 adam &ad_rdnValue }, 612 1.1 adam { NULL } 613 1.1 adam }; 614 1.1 adam 615 1.1 adam int 616 1.1 adam rdnval_initialize(void) 617 1.1 adam { 618 1.1 adam int code, i; 619 1.1 adam 620 1.1 adam for ( i = 0; as[ i ].desc != NULL; i++ ) { 621 1.1 adam code = register_at( as[ i ].desc, as[ i ].adp, 0 ); 622 1.1 adam if ( code ) { 623 1.1 adam Debug( LDAP_DEBUG_ANY, 624 1.1 adam "rdnval_initialize: register_at #%d failed\n", 625 1.3 christos i ); 626 1.1 adam return code; 627 1.1 adam } 628 1.1 adam 629 1.1 adam /* Allow Manager to set these as needed */ 630 1.1 adam if ( is_at_no_user_mod( (*as[ i ].adp)->ad_type ) ) { 631 1.1 adam (*as[ i ].adp)->ad_type->sat_flags |= 632 1.1 adam SLAP_AT_MANAGEABLE; 633 1.1 adam } 634 1.1 adam } 635 1.1 adam 636 1.1 adam syn_IA5String = syn_find( "1.3.6.1.4.1.1466.115.121.1.26" ); 637 1.1 adam if ( syn_IA5String == NULL ) { 638 1.1 adam Debug( LDAP_DEBUG_ANY, 639 1.3 christos "rdnval_initialize: unable to find syntax '1.3.6.1.4.1.1466.115.121.1.26' (IA5String)\n" ); 640 1.1 adam return LDAP_OTHER; 641 1.1 adam } 642 1.1 adam 643 1.1 adam rdnval.on_bi.bi_type = "rdnval"; 644 1.1 adam 645 1.1 adam rdnval.on_bi.bi_op_add = rdnval_op_add; 646 1.1 adam rdnval.on_bi.bi_op_modrdn = rdnval_op_rename; 647 1.1 adam 648 1.1 adam rdnval.on_bi.bi_db_init = rdnval_db_init; 649 1.1 adam rdnval.on_bi.bi_db_open = rdnval_db_open; 650 1.1 adam 651 1.1 adam return overlay_register( &rdnval ); 652 1.1 adam } 653 1.1 adam 654 1.1 adam #if SLAPD_OVER_RDNVAL == SLAPD_MOD_DYNAMIC 655 1.1 adam int 656 1.1 adam init_module( int argc, char *argv[] ) 657 1.1 adam { 658 1.1 adam return rdnval_initialize(); 659 1.1 adam } 660 1.1 adam #endif /* SLAPD_OVER_RDNVAL == SLAPD_MOD_DYNAMIC */ 661 1.1 adam 662 1.1 adam #endif /* SLAPD_OVER_RDNVAL */ 663