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