xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/modrdn.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: modrdn.c,v 1.1.1.4 2014/05/28 09:58:47 tron Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2014 The OpenLDAP Foundation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* Portions Copyright 1999, Juan C. Gomez, All rights reserved.
18  * This software is not subject to any license of Silicon Graphics
19  * Inc. or Purdue University.
20  *
21  * Redistribution and use in source and binary forms are permitted
22  * without restriction or fee of any kind as long as this notice
23  * is preserved.
24  */
25 /* Portions Copyright (c) 1995 Regents of the University of Michigan.
26  * All rights reserved.
27  *
28  * Redistribution and use in source and binary forms are permitted
29  * provided that this notice is preserved and that due credit is given
30  * to the University of Michigan at Ann Arbor. The name of the University
31  * may not be used to endorse or promote products derived from this
32  * software without specific prior written permission. This software
33  * is provided ``as is'' without express or implied warranty.
34  */
35 
36 #include "portable.h"
37 
38 #include <stdio.h>
39 
40 #include <ac/socket.h>
41 #include <ac/string.h>
42 
43 #include "slap.h"
44 
45 int
46 do_modrdn(
47     Operation	*op,
48     SlapReply	*rs
49 )
50 {
51 	struct berval	dn = BER_BVNULL;
52 	struct berval	newrdn = BER_BVNULL;
53 	struct berval	newSuperior = BER_BVNULL;
54 	ber_int_t	deloldrdn;
55 
56 	struct berval pnewSuperior = BER_BVNULL;
57 
58 	struct berval nnewSuperior = BER_BVNULL;
59 
60 	ber_len_t	length;
61 
62 	Debug( LDAP_DEBUG_TRACE, "%s do_modrdn\n",
63 			op->o_log_prefix, 0, 0 );
64 	/*
65 	 * Parse the modrdn request.  It looks like this:
66 	 *
67 	 *	ModifyRDNRequest := SEQUENCE {
68 	 *		entry	DistinguishedName,
69 	 *		newrdn	RelativeDistinguishedName
70 	 *		deleteoldrdn	BOOLEAN,
71 	 *		newSuperior	[0] LDAPDN OPTIONAL (v3 Only!)
72 	 *	}
73 	 */
74 
75 	if ( ber_scanf( op->o_ber, "{mmb", &dn, &newrdn, &deloldrdn )
76 	    == LBER_ERROR )
77 	{
78 		Debug( LDAP_DEBUG_ANY, "%s do_modrdn: ber_scanf failed\n",
79 			op->o_log_prefix, 0, 0 );
80 		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
81 		return SLAPD_DISCONNECT;
82 	}
83 
84 	/* Check for newSuperior parameter, if present scan it */
85 
86 	if ( ber_peek_tag( op->o_ber, &length ) == LDAP_TAG_NEWSUPERIOR ) {
87 		if ( op->o_protocol < LDAP_VERSION3 ) {
88 			/* Connection record indicates v2 but field
89 			 * newSuperior is present: report error.
90 			 */
91 			Debug( LDAP_DEBUG_ANY,
92 				"%s do_modrdn: newSuperior requires LDAPv3\n",
93 				op->o_log_prefix, 0, 0 );
94 
95 			send_ldap_discon( op, rs,
96 				LDAP_PROTOCOL_ERROR, "newSuperior requires LDAPv3" );
97 			rs->sr_err = SLAPD_DISCONNECT;
98 			goto cleanup;
99 		}
100 
101 		if ( ber_scanf( op->o_ber, "m", &newSuperior )
102 		     == LBER_ERROR ) {
103 
104 			Debug( LDAP_DEBUG_ANY, "%s do_modrdn: ber_scanf(\"m\") failed\n",
105 				op->o_log_prefix, 0, 0 );
106 
107 			send_ldap_discon( op, rs,
108 				LDAP_PROTOCOL_ERROR, "decoding error" );
109 			rs->sr_err = SLAPD_DISCONNECT;
110 			goto cleanup;
111 		}
112 		op->orr_newSup = &pnewSuperior;
113 		op->orr_nnewSup = &nnewSuperior;
114 	}
115 
116 	Debug( LDAP_DEBUG_ARGS,
117 	    "do_modrdn: dn (%s) newrdn (%s) newsuperior (%s)\n",
118 		dn.bv_val, newrdn.bv_val,
119 		newSuperior.bv_len ? newSuperior.bv_val : "" );
120 
121 	if ( ber_scanf( op->o_ber, /*{*/ "}") == LBER_ERROR ) {
122 		Debug( LDAP_DEBUG_ANY, "%s do_modrdn: ber_scanf failed\n",
123 			op->o_log_prefix, 0, 0 );
124 		send_ldap_discon( op, rs,
125 			LDAP_PROTOCOL_ERROR, "decoding error" );
126 		rs->sr_err = SLAPD_DISCONNECT;
127 		goto cleanup;
128 	}
129 
130 	if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
131 		Debug( LDAP_DEBUG_ANY, "%s do_modrdn: get_ctrls failed\n",
132 			op->o_log_prefix, 0, 0 );
133 		/* get_ctrls has sent results.	Now clean up. */
134 		goto cleanup;
135 	}
136 
137 	rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn, op->o_tmpmemctx );
138 	if( rs->sr_err != LDAP_SUCCESS ) {
139 		Debug( LDAP_DEBUG_ANY, "%s do_modrdn: invalid dn (%s)\n",
140 			op->o_log_prefix, dn.bv_val, 0 );
141 		send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
142 		goto cleanup;
143 	}
144 
145 	/* FIXME: should have/use rdnPretty / rdnNormalize routines */
146 
147 	rs->sr_err = dnPrettyNormal( NULL, &newrdn, &op->orr_newrdn, &op->orr_nnewrdn, op->o_tmpmemctx );
148 	if( rs->sr_err != LDAP_SUCCESS ) {
149 		Debug( LDAP_DEBUG_ANY, "%s do_modrdn: invalid newrdn (%s)\n",
150 			op->o_log_prefix, newrdn.bv_val, 0 );
151 		send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid new RDN" );
152 		goto cleanup;
153 	}
154 
155 	if( rdn_validate( &op->orr_newrdn ) != LDAP_SUCCESS ) {
156 		Debug( LDAP_DEBUG_ANY, "%s do_modrdn: invalid rdn (%s)\n",
157 			op->o_log_prefix, op->orr_newrdn.bv_val, 0 );
158 		send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid new RDN" );
159 		goto cleanup;
160 	}
161 
162 	if( op->orr_newSup ) {
163 		rs->sr_err = dnPrettyNormal( NULL, &newSuperior, &pnewSuperior,
164 			&nnewSuperior, op->o_tmpmemctx );
165 		if( rs->sr_err != LDAP_SUCCESS ) {
166 			Debug( LDAP_DEBUG_ANY,
167 				"%s do_modrdn: invalid newSuperior (%s)\n",
168 				op->o_log_prefix, newSuperior.bv_val, 0 );
169 			send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid newSuperior" );
170 			goto cleanup;
171 		}
172 	}
173 
174 	Statslog( LDAP_DEBUG_STATS, "%s MODRDN dn=\"%s\"\n",
175 	    op->o_log_prefix, op->o_req_dn.bv_val, 0, 0, 0 );
176 
177 	op->orr_deleteoldrdn = deloldrdn;
178 	op->orr_modlist = NULL;
179 
180 	/* prepare modlist of modifications from old/new RDN */
181 	rs->sr_err = slap_modrdn2mods( op, rs );
182 	if ( rs->sr_err != LDAP_SUCCESS ) {
183 		send_ldap_result( op, rs );
184 		goto cleanup;
185 	}
186 
187 	op->o_bd = frontendDB;
188 	rs->sr_err = frontendDB->be_modrdn( op, rs );
189 
190 #ifdef LDAP_X_TXN
191 	if( rs->sr_err == LDAP_X_TXN_SPECIFY_OKAY ) {
192 		/* skip cleanup */
193 	}
194 #endif
195 
196 cleanup:
197 	op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
198 	op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
199 
200 	op->o_tmpfree( op->orr_newrdn.bv_val, op->o_tmpmemctx );
201 	op->o_tmpfree( op->orr_nnewrdn.bv_val, op->o_tmpmemctx );
202 
203 	if ( op->orr_modlist != NULL )
204 		slap_mods_free( op->orr_modlist, 1 );
205 
206 	if ( !BER_BVISNULL( &pnewSuperior ) ) {
207 		op->o_tmpfree( pnewSuperior.bv_val, op->o_tmpmemctx );
208 	}
209 	if ( !BER_BVISNULL( &nnewSuperior ) ) {
210 		op->o_tmpfree( nnewSuperior.bv_val, op->o_tmpmemctx );
211 	}
212 
213 	return rs->sr_err;
214 }
215 
216 int
217 fe_op_modrdn( Operation *op, SlapReply *rs )
218 {
219 	struct berval	dest_ndn = BER_BVNULL, dest_pndn, pdn = BER_BVNULL;
220 	BackendDB	*op_be, *bd = op->o_bd;
221 	ber_slen_t	diff;
222 
223 	if( op->o_req_ndn.bv_len == 0 ) {
224 		Debug( LDAP_DEBUG_ANY, "%s do_modrdn: root dse!\n",
225 			op->o_log_prefix, 0, 0 );
226 		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
227 			"cannot rename the root DSE" );
228 		goto cleanup;
229 
230 	} else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) {
231 		Debug( LDAP_DEBUG_ANY, "%s do_modrdn: subschema subentry: %s (%ld)\n",
232 			op->o_log_prefix, frontendDB->be_schemandn.bv_val, (long)frontendDB->be_schemandn.bv_len );
233 
234 		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
235 			"cannot rename subschema subentry" );
236 		goto cleanup;
237 	}
238 
239 	if( op->orr_nnewSup ) {
240 		dest_pndn = *op->orr_nnewSup;
241 	} else {
242 		dnParent( &op->o_req_ndn, &dest_pndn );
243 	}
244 	build_new_dn( &dest_ndn, &dest_pndn, &op->orr_nnewrdn, op->o_tmpmemctx );
245 
246 	diff = (ber_slen_t) dest_ndn.bv_len - (ber_slen_t) op->o_req_ndn.bv_len;
247 	if ( diff > 0 ? dnIsSuffix( &dest_ndn, &op->o_req_ndn )
248 		: diff < 0 && dnIsSuffix( &op->o_req_ndn, &dest_ndn ) )
249 	{
250 		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
251 			diff > 0 ? "cannot place an entry below itself"
252 			: "cannot place an entry above itself" );
253 		goto cleanup;
254 	}
255 
256 	/*
257 	 * We could be serving multiple database backends.  Select the
258 	 * appropriate one, or send a referral to our "referral server"
259 	 * if we don't hold it.
260 	 */
261 	op->o_bd = select_backend( &op->o_req_ndn, 1 );
262 	if ( op->o_bd == NULL ) {
263 		op->o_bd = bd;
264 		rs->sr_ref = referral_rewrite( default_referral,
265 			NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
266 		if (!rs->sr_ref) rs->sr_ref = default_referral;
267 
268 		if ( rs->sr_ref != NULL ) {
269 			rs->sr_err = LDAP_REFERRAL;
270 			send_ldap_result( op, rs );
271 
272 			if (rs->sr_ref != default_referral) ber_bvarray_free( rs->sr_ref );
273 		} else {
274 			send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
275 				"no global superior knowledge" );
276 		}
277 		goto cleanup;
278 	}
279 
280 	/* If we've got a glued backend, check the real backend */
281 	op_be = op->o_bd;
282 	if ( SLAP_GLUE_INSTANCE( op->o_bd )) {
283 		op->o_bd = select_backend( &op->o_req_ndn, 0 );
284 	}
285 
286 	/* check restrictions */
287 	if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
288 		send_ldap_result( op, rs );
289 		goto cleanup;
290 	}
291 
292 	/* check for referrals */
293 	if ( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
294 		goto cleanup;
295 	}
296 
297 	/* check that destination DN is in the same backend as source DN */
298 	if ( select_backend( &dest_ndn, 0 ) != op->o_bd ) {
299 			send_ldap_error( op, rs, LDAP_AFFECTS_MULTIPLE_DSAS,
300 				"cannot rename between DSAs" );
301 			goto cleanup;
302 	}
303 
304 	/*
305 	 * do the modrdn if 1 && (2 || 3)
306 	 * 1) there is a modrdn function implemented in this backend;
307 	 * 2) this backend is master for what it holds;
308 	 * 3) it's a replica and the dn supplied is the update_ndn.
309 	 */
310 	if ( op->o_bd->be_modrdn ) {
311 		/* do the update here */
312 		int repl_user = be_isupdate( op );
313 		if ( !SLAP_SINGLE_SHADOW(op->o_bd) || repl_user )
314 		{
315 			op->o_bd = op_be;
316 			op->o_bd->be_modrdn( op, rs );
317 
318 			if ( op->o_bd->be_delete ) {
319 				struct berval	org_req_dn = BER_BVNULL;
320 				struct berval	org_req_ndn = BER_BVNULL;
321 				struct berval	org_dn = BER_BVNULL;
322 				struct berval	org_ndn = BER_BVNULL;
323 				int		org_managedsait;
324 
325 				org_req_dn = op->o_req_dn;
326 				org_req_ndn = op->o_req_ndn;
327 				org_dn = op->o_dn;
328 				org_ndn = op->o_ndn;
329 				org_managedsait = get_manageDSAit( op );
330 				op->o_dn = op->o_bd->be_rootdn;
331 				op->o_ndn = op->o_bd->be_rootndn;
332 				op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
333 
334 				while ( rs->sr_err == LDAP_SUCCESS &&
335 						op->o_delete_glue_parent ) {
336 					op->o_delete_glue_parent = 0;
337 					if ( !be_issuffix( op->o_bd, &op->o_req_ndn )) {
338 						slap_callback cb = { NULL };
339 						cb.sc_response = slap_null_cb;
340 						dnParent( &op->o_req_ndn, &pdn );
341 						op->o_req_dn = pdn;
342 						op->o_req_ndn = pdn;
343 						op->o_callback = &cb;
344 						op->o_bd->be_delete( op, rs );
345 					} else {
346 						break;
347 					}
348 				}
349 				op->o_managedsait = org_managedsait;
350 				op->o_dn = org_dn;
351 				op->o_ndn = org_ndn;
352 				op->o_req_dn = org_req_dn;
353 				op->o_req_ndn = org_req_ndn;
354 				op->o_delete_glue_parent = 0;
355 			}
356 
357 		} else {
358 			BerVarray defref = op->o_bd->be_update_refs
359 				? op->o_bd->be_update_refs : default_referral;
360 
361 			if ( defref != NULL ) {
362 				rs->sr_ref = referral_rewrite( defref,
363 					NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
364 				if (!rs->sr_ref) rs->sr_ref = defref;
365 
366 				rs->sr_err = LDAP_REFERRAL;
367 				send_ldap_result( op, rs );
368 
369 				if (rs->sr_ref != defref) ber_bvarray_free( rs->sr_ref );
370 			} else {
371 				send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
372 					"shadow context; no update referral" );
373 			}
374 		}
375 	} else {
376 		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
377 			"operation not supported within namingContext" );
378 	}
379 
380 cleanup:;
381 	if ( dest_ndn.bv_val != NULL )
382 		ber_memfree_x( dest_ndn.bv_val, op->o_tmpmemctx );
383 	op->o_bd = bd;
384 	return rs->sr_err;
385 }
386 
387 int
388 slap_modrdn2mods(
389 	Operation	*op,
390 	SlapReply	*rs )
391 {
392 	int		a_cnt, d_cnt;
393 	LDAPRDN		old_rdn = NULL;
394 	LDAPRDN		new_rdn = NULL;
395 
396 	assert( !BER_BVISEMPTY( &op->oq_modrdn.rs_newrdn ) );
397 
398 	/* if requestDN is empty, silently reset deleteOldRDN */
399 	if ( BER_BVISEMPTY( &op->o_req_dn ) ) op->orr_deleteoldrdn = 0;
400 
401 	if ( ldap_bv2rdn_x( &op->oq_modrdn.rs_newrdn, &new_rdn,
402 		(char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) ) {
403 		Debug( LDAP_DEBUG_TRACE,
404 			"%s slap_modrdn2mods: can't figure out "
405 			"type(s)/value(s) of newrdn\n",
406 			op->o_log_prefix, 0, 0 );
407 		rs->sr_err = LDAP_INVALID_DN_SYNTAX;
408 		rs->sr_text = "unknown type(s)/value(s) used in RDN";
409 		goto done;
410 	}
411 
412 	if ( op->oq_modrdn.rs_deleteoldrdn ) {
413 		if ( ldap_bv2rdn_x( &op->o_req_dn, &old_rdn,
414 			(char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) ) {
415 			Debug( LDAP_DEBUG_TRACE,
416 				"%s slap_modrdn2mods: can't figure out "
417 				"type(s)/value(s) of oldrdn\n",
418 				op->o_log_prefix, 0, 0 );
419 			rs->sr_err = LDAP_OTHER;
420 			rs->sr_text = "cannot parse RDN from old DN";
421 			goto done;
422 		}
423 	}
424 	rs->sr_text = NULL;
425 
426 	/* Add new attribute values to the entry */
427 	for ( a_cnt = 0; new_rdn[a_cnt]; a_cnt++ ) {
428 		AttributeDescription	*desc = NULL;
429 		Modifications 		*mod_tmp;
430 
431 		rs->sr_err = slap_bv2ad( &new_rdn[a_cnt]->la_attr, &desc, &rs->sr_text );
432 
433 		if ( rs->sr_err != LDAP_SUCCESS ) {
434 			Debug( LDAP_DEBUG_TRACE,
435 				"%s slap_modrdn2mods: %s: %s (new)\n",
436 				op->o_log_prefix,
437 				rs->sr_text,
438 				new_rdn[ a_cnt ]->la_attr.bv_val );
439 			goto done;
440 		}
441 
442 		/* Apply modification */
443 		mod_tmp = ( Modifications * )ch_malloc( sizeof( Modifications ) );
444 		mod_tmp->sml_desc = desc;
445 		BER_BVZERO( &mod_tmp->sml_type );
446 		mod_tmp->sml_numvals = 1;
447 		mod_tmp->sml_values = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) );
448 		ber_dupbv( &mod_tmp->sml_values[0], &new_rdn[a_cnt]->la_value );
449 		mod_tmp->sml_values[1].bv_val = NULL;
450 		if( desc->ad_type->sat_equality->smr_normalize) {
451 			mod_tmp->sml_nvalues = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) );
452 			rs->sr_err = desc->ad_type->sat_equality->smr_normalize(
453 				SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
454 				desc->ad_type->sat_syntax,
455 				desc->ad_type->sat_equality,
456 				&mod_tmp->sml_values[0],
457 				&mod_tmp->sml_nvalues[0], NULL );
458 			if (rs->sr_err != LDAP_SUCCESS) {
459 				ch_free(mod_tmp->sml_nvalues);
460 				ch_free(mod_tmp->sml_values[0].bv_val);
461 				ch_free(mod_tmp->sml_values);
462 				ch_free(mod_tmp);
463 				goto done;
464 			}
465 			mod_tmp->sml_nvalues[1].bv_val = NULL;
466 		} else {
467 			mod_tmp->sml_nvalues = NULL;
468 		}
469 		mod_tmp->sml_op = SLAP_MOD_SOFTADD;
470 		mod_tmp->sml_flags = 0;
471 		mod_tmp->sml_next = op->orr_modlist;
472 		op->orr_modlist = mod_tmp;
473 	}
474 
475 	/* Remove old rdn value if required */
476 	if ( op->orr_deleteoldrdn ) {
477 		for ( d_cnt = 0; old_rdn[d_cnt]; d_cnt++ ) {
478 			AttributeDescription	*desc = NULL;
479 			Modifications 		*mod_tmp;
480 
481 			rs->sr_err = slap_bv2ad( &old_rdn[d_cnt]->la_attr, &desc, &rs->sr_text );
482 			if ( rs->sr_err != LDAP_SUCCESS ) {
483 				Debug( LDAP_DEBUG_TRACE,
484 					"%s slap_modrdn2mods: %s: %s (old)\n",
485 					op->o_log_prefix,
486 					rs->sr_text,
487 					old_rdn[d_cnt]->la_attr.bv_val );
488 				goto done;
489 			}
490 
491 			/* Apply modification */
492 			mod_tmp = ( Modifications * )ch_malloc( sizeof( Modifications ) );
493 			mod_tmp->sml_desc = desc;
494 			BER_BVZERO( &mod_tmp->sml_type );
495 			mod_tmp->sml_numvals = 1;
496 			mod_tmp->sml_values = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) );
497 			ber_dupbv( &mod_tmp->sml_values[0], &old_rdn[d_cnt]->la_value );
498 			mod_tmp->sml_values[1].bv_val = NULL;
499 			if( desc->ad_type->sat_equality->smr_normalize) {
500 				mod_tmp->sml_nvalues = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) );
501 				(void) (*desc->ad_type->sat_equality->smr_normalize)(
502 					SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
503 					desc->ad_type->sat_syntax,
504 					desc->ad_type->sat_equality,
505 					&mod_tmp->sml_values[0],
506 					&mod_tmp->sml_nvalues[0], NULL );
507 				mod_tmp->sml_nvalues[1].bv_val = NULL;
508 			} else {
509 				mod_tmp->sml_nvalues = NULL;
510 			}
511 			mod_tmp->sml_op = LDAP_MOD_DELETE;
512 			mod_tmp->sml_flags = 0;
513 			mod_tmp->sml_next = op->orr_modlist;
514 			op->orr_modlist = mod_tmp;
515 		}
516 	}
517 
518 done:
519 
520 	/* LDAP v2 supporting correct attribute handling. */
521 	if ( rs->sr_err != LDAP_SUCCESS && op->orr_modlist != NULL ) {
522 		Modifications *tmp;
523 
524 		for ( ; op->orr_modlist != NULL; op->orr_modlist = tmp ) {
525 			tmp = op->orr_modlist->sml_next;
526 			ch_free( op->orr_modlist );
527 		}
528 	}
529 
530 	if ( new_rdn != NULL ) {
531 		ldap_rdnfree_x( new_rdn, op->o_tmpmemctx );
532 	}
533 	if ( old_rdn != NULL ) {
534 		ldap_rdnfree_x( old_rdn, op->o_tmpmemctx );
535 	}
536 
537 	return rs->sr_err;
538 }
539 
540