1/* $NetBSD: modrdn.c,v 1.2 2021/08/14 16:14:59 christos Exp $ */ 2 3/* modrdn.c - modrdn request handler for back-syncmeta */ 4/* $OpenLDAP$ */ 5/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2016-2021 The OpenLDAP Foundation. 8 * Portions Copyright 2016 Symas Corporation. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted only as authorized by the OpenLDAP 13 * Public License. 14 * 15 * A copy of this license is available in the file LICENSE in the 16 * top-level directory of the distribution or, alternatively, at 17 * <http://www.OpenLDAP.org/license.html>. 18 */ 19 20/* ACKNOWLEDGEMENTS: 21 * This work was developed by Symas Corporation 22 * based on back-meta module for inclusion in OpenLDAP Software. 23 * This work was sponsored by Ericsson. */ 24 25#include <sys/cdefs.h> 26__RCSID("$NetBSD: modrdn.c,v 1.2 2021/08/14 16:14:59 christos Exp $"); 27 28#include "portable.h" 29 30#include <stdio.h> 31 32#include <ac/socket.h> 33#include <ac/string.h> 34#include "slap.h" 35#include "../../../libraries/liblber/lber-int.h" 36#include "../../../libraries/libldap/ldap-int.h" 37#include "../back-ldap/back-ldap.h" 38#include "back-asyncmeta.h" 39 40meta_search_candidate_t 41asyncmeta_back_modrdn_start(Operation *op, 42 SlapReply *rs, 43 a_metaconn_t *mc, 44 bm_context_t *bc, 45 int candidate, 46 int do_lock) 47{ 48 a_dncookie dc; 49 a_metainfo_t *mi = mc->mc_info; 50 a_metatarget_t *mt = mi->mi_targets[ candidate ]; 51 struct berval mdn = BER_BVNULL, 52 mnewSuperior = BER_BVNULL, 53 newrdn = BER_BVNULL; 54 int rc = 0; 55 LDAPControl **ctrls = NULL; 56 meta_search_candidate_t retcode = META_SEARCH_CANDIDATE; 57 BerElement *ber = NULL; 58 a_metasingleconn_t *msc = &mc->mc_conns[ candidate ]; 59 SlapReply *candidates = bc->candidates; 60 ber_int_t msgid; 61 62 dc.op = op; 63 dc.target = mt; 64 dc.memctx = op->o_tmpmemctx; 65 dc.to_from = MASSAGE_REQ; 66 67 if ( op->orr_newSup ) { 68 69 /* 70 * NOTE: the newParent, if defined, must be on the 71 * same target as the entry to be renamed. This check 72 * has been anticipated in meta_back_getconn() 73 */ 74 /* 75 * FIXME: one possibility is to delete the entry 76 * from one target and add it to the other; 77 * unfortunately we'd need write access to both, 78 * which is nearly impossible; for administration 79 * needs, the rootdn of the metadirectory could 80 * be mapped to an administrative account on each 81 * target (the binddn?); we'll see. 82 */ 83 /* 84 * NOTE: we need to port the identity assertion 85 * feature from back-ldap 86 */ 87 88 /* needs LDAPv3 */ 89 switch ( mt->mt_version ) { 90 case LDAP_VERSION3: 91 break; 92 93 case 0: 94 if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) { 95 break; 96 } 97 /* fall thru */ 98 99 default: 100 /* op->o_protocol cannot be anything but LDAPv3, 101 * otherwise wouldn't be here */ 102 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 103 retcode = META_SEARCH_ERR; 104 goto done; 105 } 106 107 /* 108 * Rewrite the new superior, if defined and required 109 */ 110 asyncmeta_dn_massage( &dc, op->orr_newSup, &mnewSuperior ); 111 } 112 113 /* 114 * Rewrite the modrdn dn, if required 115 */ 116 asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn ); 117 118 /* NOTE: we need to copy the newRDN in case it was formed 119 * from a DN by simply changing the length (ITS#5397) */ 120 newrdn = op->orr_newrdn; 121 if ( newrdn.bv_val[ newrdn.bv_len ] != '\0' ) { 122 ber_dupbv_x( &newrdn, &op->orr_newrdn, op->o_tmpmemctx ); 123 } 124 125 asyncmeta_set_msc_time(msc); 126 ctrls = op->o_ctrls; 127 if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls ) != LDAP_SUCCESS ) 128 { 129 candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; 130 retcode = META_SEARCH_ERR; 131 goto done; 132 } 133 /* someone might have reset the connection */ 134 if (!( LDAP_BACK_CONN_ISBOUND( msc ) 135 || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) { 136 Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ ); 137 goto error_unavailable; 138 } 139 ber = ldap_build_moddn_req( msc->msc_ld, mdn.bv_val, newrdn.bv_val, 140 mnewSuperior.bv_val, op->orr_deleteoldrdn, ctrls, NULL, &msgid); 141 142 if (!ber) { 143 Debug( asyncmeta_debug, "%s asyncmeta_back_modrdn_start: Operation encoding failed with errno %d\n", 144 op->o_log_prefix, msc->msc_ld->ld_errno ); 145 rs->sr_err = LDAP_OPERATIONS_ERROR; 146 rs->sr_text = "Failed to encode proxied request"; 147 retcode = META_SEARCH_ERR; 148 goto done; 149 } 150 151 if (ber) { 152 struct timeval tv = {0, mt->mt_network_timeout*1000}; 153 ber_socket_t s; 154 155 if (!( LDAP_BACK_CONN_ISBOUND( msc ) 156 || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) { 157 Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ ); 158 goto error_unavailable; 159 } 160 161 ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s ); 162 if (s < 0) { 163 Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ ); 164 goto error_unavailable; 165 } 166 167 rc = ldap_int_poll( msc->msc_ld, s, &tv, 1); 168 if (rc < 0) { 169 Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ ); 170 if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) { 171 rc = LDAP_SERVER_DOWN; 172 } else { 173 goto error_unavailable; 174 } 175 } else { 176 candidates[ candidate ].sr_msgid = msgid; 177 rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_MODRDN, 178 mdn.bv_val, ber, msgid ); 179 if (rc == msgid) 180 rc = LDAP_SUCCESS; 181 else 182 rc = LDAP_SERVER_DOWN; 183 ber = NULL; 184 } 185 186 switch ( rc ) { 187 case LDAP_SUCCESS: 188 retcode = META_SEARCH_CANDIDATE; 189 asyncmeta_set_msc_time(msc); 190 goto done; 191 192 case LDAP_SERVER_DOWN: 193 /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */ 194 if (do_lock > 0) { 195 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); 196 asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__ ); 197 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); 198 } 199 /* fall though*/ 200 default: 201 Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ ); 202 goto error_unavailable; 203 } 204 } 205 206error_unavailable: 207 if (ber) 208 ber_free(ber, 1); 209 switch (bc->nretries[candidate]) { 210 case -1: /* nretries = forever */ 211 retcode = META_SEARCH_NEED_BIND; 212 ldap_pvt_thread_yield(); 213 break; 214 case 0: /* no retries left */ 215 candidates[ candidate ].sr_msgid = META_MSGID_IGNORE; 216 rs->sr_err = LDAP_UNAVAILABLE; 217 rs->sr_text = "Unable to send modrdn request to target"; 218 retcode = META_SEARCH_ERR; 219 break; 220 default: /* more retries left - try to rebind and go again */ 221 retcode = META_SEARCH_NEED_BIND; 222 bc->nretries[candidate]--; 223 ldap_pvt_thread_yield(); 224 break; 225 } 226done: 227 (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls ); 228 229 if ( mdn.bv_val != op->o_req_dn.bv_val ) { 230 op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx ); 231 } 232 233 if ( !BER_BVISNULL( &mnewSuperior ) 234 && mnewSuperior.bv_val != op->orr_newSup->bv_val ) 235 { 236 op->o_tmpfree( mnewSuperior.bv_val, op->o_tmpmemctx ); 237 } 238 239 if ( newrdn.bv_val != op->orr_newrdn.bv_val ) { 240 op->o_tmpfree( newrdn.bv_val, op->o_tmpmemctx ); 241 } 242 243 Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_modrdn_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid ); 244 return retcode; 245} 246 247int 248asyncmeta_back_modrdn( Operation *op, SlapReply *rs ) 249{ 250 a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private; 251 a_metatarget_t *mt; 252 a_metaconn_t *mc; 253 int rc, candidate = -1; 254 void *thrctx = op->o_threadctx; 255 bm_context_t *bc; 256 SlapReply *candidates; 257 time_t current_time = slap_get_time(); 258 int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops; 259 260 Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_modrdn: %s\n", 261 op->o_req_dn.bv_val ); 262 263 if (current_time > op->o_time) { 264 Debug(asyncmeta_debug, "==> asyncmeta_back_modrdn[%s]: o_time:[%ld], current time: [%ld]\n", 265 op->o_log_prefix, op->o_time, current_time ); 266 } 267 asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi ); 268 if (bc == NULL) { 269 rs->sr_err = LDAP_OTHER; 270 send_ldap_result(op, rs); 271 return rs->sr_err; 272 } 273 274 candidates = bc->candidates; 275 mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0); 276 if ( !mc || rs->sr_err != LDAP_SUCCESS) { 277 send_ldap_result(op, rs); 278 return rs->sr_err; 279 } 280 281 mt = mi->mi_targets[ candidate ]; 282 bc->timeout = mt->mt_timeout[ SLAP_OP_MODRDN ]; 283 bc->retrying = LDAP_BACK_RETRYING; 284 bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying ); 285 bc->stoptime = op->o_time + bc->timeout; 286 bc->bc_active = 1; 287 288 if (mc->pending_ops >= max_pending_ops) { 289 rs->sr_err = LDAP_BUSY; 290 rs->sr_text = "Maximum pending ops limit exceeded"; 291 send_ldap_result(op, rs); 292 return rs->sr_err; 293 } 294 295 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); 296 rc = asyncmeta_add_message_queue(mc, bc); 297 mc->mc_conns[candidate].msc_active++; 298 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); 299 300 if (rc != LDAP_SUCCESS) { 301 rs->sr_err = LDAP_BUSY; 302 rs->sr_text = "Maximum pending ops limit exceeded"; 303 send_ldap_result(op, rs); 304 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); 305 mc->mc_conns[candidate].msc_active--; 306 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); 307 goto finish; 308 } 309 310retry: 311 if (bc->timeout && bc->stoptime < slap_get_time()) { 312 int timeout_err; 313 timeout_err = op->o_protocol >= LDAP_VERSION3 ? 314 LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER; 315 rs->sr_err = timeout_err; 316 rs->sr_text = "Operation timed out before it was sent to target"; 317 asyncmeta_error_cleanup(op, rs, bc, mc, candidate); 318 goto finish; 319 320 } 321 322 rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, candidate); 323 switch (rc) 324 { 325 case META_SEARCH_CANDIDATE: 326 /* target is already bound, just send the request */ 327 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: " 328 "cnd=\"%d\"\n", op->o_log_prefix, candidate ); 329 330 rc = asyncmeta_back_modrdn_start( op, rs, mc, bc, candidate, 1); 331 if (rc == META_SEARCH_ERR) { 332 asyncmeta_error_cleanup(op, rs, bc, mc, candidate); 333 goto finish; 334 335 } else if (rc == META_SEARCH_NEED_BIND) { 336 goto retry; 337 } 338 break; 339 case META_SEARCH_NOT_CANDIDATE: 340 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: NOT_CANDIDATE " 341 "cnd=\"%d\"\n", op->o_log_prefix, candidate ); 342 asyncmeta_error_cleanup(op, rs, bc, mc, candidate); 343 goto finish; 344 345 case META_SEARCH_NEED_BIND: 346 case META_SEARCH_BINDING: 347 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: BINDING " 348 "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]); 349 /* Todo add the context to the message queue but do not send the request 350 the receiver must send this when we are done binding */ 351 /* question - how would do receiver know to which targets??? */ 352 break; 353 354 case META_SEARCH_ERR: 355 Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: ERR " 356 "cnd=\"%d\"\n", op->o_log_prefix, candidate ); 357 asyncmeta_error_cleanup(op, rs, bc, mc, candidate); 358 goto finish; 359 default: 360 assert( 0 ); 361 break; 362 } 363 364 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex); 365 mc->mc_conns[candidate].msc_active--; 366 asyncmeta_start_one_listener(mc, candidates, bc, candidate); 367 bc->bc_active--; 368 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex); 369 rs->sr_err = SLAPD_ASYNCOP; 370finish: 371 return rs->sr_err; 372} 373