1 /* $NetBSD: ldapmodify.c,v 1.3 2021/08/14 16:14:49 christos Exp $ */ 2 3 /* ldapmodify.c - generic program to modify or add entries using LDAP */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 1998-2021 The OpenLDAP Foundation. 8 * Portions Copyright 2006 Howard Chu. 9 * Portions Copyright 1998-2003 Kurt D. Zeilenga. 10 * Portions Copyright 1998-2001 Net Boolean Incorporated. 11 * Portions Copyright 2001-2003 IBM Corporation. 12 * All rights reserved. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted only as authorized by the OpenLDAP 16 * Public License. 17 * 18 * A copy of this license is available in the file LICENSE in the 19 * top-level directory of the distribution or, alternatively, at 20 * <http://www.OpenLDAP.org/license.html>. 21 */ 22 /* Portions Copyright (c) 1992-1996 Regents of the University of Michigan. 23 * All rights reserved. 24 * 25 * Redistribution and use in source and binary forms are permitted 26 * provided that this notice is preserved and that due credit is given 27 * to the University of Michigan at Ann Arbor. The name of the 28 * University may not be used to endorse or promote products derived 29 * from this software without specific prior written permission. This 30 * software is provided ``as is'' without express or implied warranty. 31 */ 32 /* ACKNOWLEDGEMENTS: 33 * This work was originally developed by the University of Michigan 34 * (as part of U-MICH LDAP). Additional significant contributors 35 * include: 36 * Kurt D. Zeilenga 37 * Norbert Klasen 38 * Howard Chu 39 */ 40 41 #include <sys/cdefs.h> 42 __RCSID("$NetBSD: ldapmodify.c,v 1.3 2021/08/14 16:14:49 christos Exp $"); 43 44 #include "portable.h" 45 46 #include <stdio.h> 47 48 #include <ac/stdlib.h> 49 #include <ac/ctype.h> 50 #include <ac/string.h> 51 #include <ac/unistd.h> 52 #include <ac/socket.h> 53 #include <ac/time.h> 54 55 #ifdef HAVE_SYS_STAT_H 56 #include <sys/stat.h> 57 #endif 58 59 #ifdef HAVE_SYS_FILE_H 60 #include <sys/file.h> 61 #endif 62 #ifdef HAVE_FCNTL_H 63 #include <fcntl.h> 64 #endif 65 66 #include <ldap.h> 67 68 #include "lutil.h" 69 #include "lutil_ldap.h" 70 #include "ldif.h" 71 #include "ldap_defaults.h" 72 #include "ldap_pvt.h" 73 #include "lber_pvt.h" 74 75 #include "common.h" 76 77 static int ldapadd; 78 static char *rejfile = NULL; 79 static LDAP *ld = NULL; 80 81 static int process_ldif_rec LDAP_P(( char *rbuf, unsigned long lineno )); 82 static int domodify LDAP_P(( 83 const struct berval *dn, 84 LDAPMod **pmods, 85 LDAPControl **pctrls, 86 int newentry )); 87 static int dodelete LDAP_P(( 88 const struct berval *dn, 89 LDAPControl **pctrls )); 90 static int dorename LDAP_P(( 91 const struct berval *dn, 92 const struct berval *newrdn, 93 const struct berval *newsup, 94 int deleteoldrdn, 95 LDAPControl **pctrls )); 96 static int process_response( 97 LDAP *ld, 98 int msgid, 99 int res, 100 const struct berval *dn ); 101 102 static int txn = 0; 103 static int txnabort = 0; 104 struct berval *txn_id = NULL; 105 106 void 107 usage( void ) 108 { 109 fprintf( stderr, _("Add or modify entries from an LDAP server\n\n")); 110 fprintf( stderr, _("usage: %s [options]\n"), prog); 111 fprintf( stderr, _(" The list of desired operations are read from stdin" 112 " or from the file\n")); 113 fprintf( stderr, _(" specified by \"-f file\".\n")); 114 fprintf( stderr, _("Add or modify options:\n")); 115 fprintf( stderr, _(" -a add values (%s)\n"), 116 (ldapadd ? _("default") : _("default is to replace"))); 117 fprintf( stderr, _(" -c continuous operation mode (do not stop on errors)\n")); 118 fprintf( stderr, _(" -E [!]ext=extparam modify extensions" 119 " (! indicate s criticality)\n")); 120 fprintf( stderr, _(" -f file read operations from `file'\n")); 121 fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n")); 122 fprintf( stderr, _(" -P version protocol version (default: 3)\n")); 123 fprintf( stderr, 124 _(" [!]txn=<commit|abort> (transaction)\n")); 125 fprintf( stderr, _(" -S file write skipped modifications to `file'\n")); 126 127 tool_common_usage(); 128 exit( EXIT_FAILURE ); 129 } 130 131 132 const char options[] = "aE:rS:" 133 "cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z"; 134 135 int 136 handle_private_option( int i ) 137 { 138 char *control, *cvalue; 139 int crit; 140 141 switch ( i ) { 142 case 'E': /* modify extensions */ 143 if( protocol == LDAP_VERSION2 ) { 144 fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"), 145 prog, protocol ); 146 exit( EXIT_FAILURE ); 147 } 148 149 /* should be extended to support comma separated list of 150 * [!]key[=value] parameters, e.g. -E !foo,bar=567 151 */ 152 153 crit = 0; 154 cvalue = NULL; 155 if( optarg[0] == '!' ) { 156 crit = 1; 157 optarg++; 158 } 159 160 control = optarg; 161 if ( (cvalue = strchr( control, '=' )) != NULL ) { 162 *cvalue++ = '\0'; 163 } 164 165 if( strcasecmp( control, "txn" ) == 0 ) { 166 /* Transaction */ 167 if( txn ) { 168 fprintf( stderr, 169 _("txn control previously specified\n")); 170 exit( EXIT_FAILURE ); 171 } 172 if( cvalue != NULL ) { 173 if( strcasecmp( cvalue, "abort" ) == 0 ) { 174 txnabort=1; 175 } else if( strcasecmp( cvalue, "commit" ) != 0 ) { 176 fprintf( stderr, _("Invalid value for txn control, %s\n"), 177 cvalue ); 178 exit( EXIT_FAILURE ); 179 } 180 } 181 182 txn = 1 + crit; 183 } else 184 { 185 fprintf( stderr, _("Invalid modify extension name: %s\n"), 186 control ); 187 usage(); 188 } 189 break; 190 191 case 'a': /* add */ 192 ldapadd = 1; 193 break; 194 195 case 'r': /* replace (obsolete) */ 196 break; 197 198 case 'S': /* skipped modifications to file */ 199 if( rejfile != NULL ) { 200 fprintf( stderr, _("%s: -S previously specified\n"), prog ); 201 exit( EXIT_FAILURE ); 202 } 203 rejfile = optarg; 204 break; 205 206 default: 207 return 0; 208 } 209 return 1; 210 } 211 212 213 int 214 main( int argc, char **argv ) 215 { 216 char *rbuf = NULL, *rejbuf = NULL; 217 FILE *rejfp; 218 struct LDIFFP *ldiffp = NULL, ldifdummy = {0}; 219 char *matched_msg, *error_msg; 220 int rc, retval, ldifrc; 221 int len; 222 int i = 0, lmax = 0; 223 unsigned long lineno, nextline = 0; 224 LDAPControl c[1]; 225 226 prog = lutil_progname( "ldapmodify", argc, argv ); 227 228 /* strncmp instead of strcmp since NT binaries carry .exe extension */ 229 ldapadd = ( strncasecmp( prog, "ldapadd", sizeof("ldapadd")-1 ) == 0 ); 230 231 tool_init( ldapadd ? TOOL_ADD : TOOL_MODIFY ); 232 233 tool_args( argc, argv ); 234 235 if ( argc != optind ) usage(); 236 237 if ( rejfile != NULL ) { 238 if (( rejfp = fopen( rejfile, "w" )) == NULL ) { 239 perror( rejfile ); 240 retval = EXIT_FAILURE; 241 goto fail; 242 } 243 } else { 244 rejfp = NULL; 245 } 246 247 if ( infile != NULL ) { 248 if (( ldiffp = ldif_open( infile, "r" )) == NULL ) { 249 perror( infile ); 250 retval = EXIT_FAILURE; 251 goto fail; 252 } 253 } else { 254 ldifdummy.fp = stdin; 255 ldiffp = &ldifdummy; 256 } 257 258 if ( debug ) ldif_debug = debug; 259 260 ld = tool_conn_setup( dont, 0 ); 261 262 if ( !dont ) { 263 tool_bind( ld ); 264 } 265 266 if( txn ) { 267 /* start transaction */ 268 rc = ldap_txn_start_s( ld, NULL, NULL, &txn_id ); 269 if( rc != LDAP_SUCCESS || !txn_id ) { 270 tool_perror( "ldap_txn_start_s", rc, NULL, NULL, NULL, NULL ); 271 if( txn > 1 ) { 272 retval = EXIT_FAILURE; 273 goto fail; 274 } 275 txn = 0; 276 } 277 } 278 279 if( txn ) { 280 c[i].ldctl_oid = LDAP_CONTROL_TXN_SPEC; 281 c[i].ldctl_value = *txn_id; 282 c[i].ldctl_iscritical = 1; 283 i++; 284 } 285 286 tool_server_controls( ld, c, i ); 287 288 rc = 0; 289 retval = 0; 290 lineno = 1; 291 while (( rc == 0 || contoper ) && ( ldifrc = ldif_read_record( ldiffp, &nextline, 292 &rbuf, &lmax )) > 0 ) 293 { 294 if ( rejfp ) { 295 len = strlen( rbuf ); 296 if (( rejbuf = (char *)ber_memalloc( len+1 )) == NULL ) { 297 perror( "malloc" ); 298 retval = EXIT_FAILURE; 299 goto fail; 300 } 301 memcpy( rejbuf, rbuf, len+1 ); 302 } 303 304 rc = process_ldif_rec( rbuf, lineno ); 305 lineno = nextline+1; 306 307 if ( rc ) retval = rc; 308 if ( rc && rejfp ) { 309 fprintf(rejfp, _("# Error: %s (%d)"), ldap_err2string(rc), rc); 310 311 matched_msg = NULL; 312 ldap_get_option(ld, LDAP_OPT_MATCHED_DN, &matched_msg); 313 if ( matched_msg != NULL ) { 314 if ( *matched_msg != '\0' ) { 315 fprintf( rejfp, _(", matched DN: %s"), matched_msg ); 316 } 317 ldap_memfree( matched_msg ); 318 } 319 320 error_msg = NULL; 321 ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &error_msg); 322 if ( error_msg != NULL ) { 323 if ( *error_msg != '\0' ) { 324 fprintf( rejfp, _(", additional info: %s"), error_msg ); 325 } 326 ldap_memfree( error_msg ); 327 } 328 fprintf( rejfp, "\n%s\n", rejbuf ); 329 } 330 331 if (rejfp) ber_memfree( rejbuf ); 332 } 333 ber_memfree( rbuf ); 334 335 if ( ldifrc < 0 ) 336 retval = LDAP_OTHER; 337 338 if( retval == 0 && txn ) { 339 rc = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL ); 340 if ( rc != LDAP_OPT_SUCCESS ) { 341 fprintf( stderr, "Could not unset controls for ldap_txn_end\n"); 342 } 343 344 /* create transaction */ 345 rc = ldap_txn_end_s( ld, !txnabort, txn_id, NULL, NULL, NULL ); 346 if( rc != LDAP_SUCCESS ) { 347 tool_perror( "ldap_txn_end_s", rc, NULL, NULL, NULL, NULL ); 348 retval = rc; 349 } 350 } 351 352 fail:; 353 if ( rejfp != NULL ) { 354 fclose( rejfp ); 355 } 356 357 if ( ldiffp != NULL && ldiffp != &ldifdummy ) { 358 ldif_close( ldiffp ); 359 } 360 361 tool_exit( ld, retval ); 362 } 363 364 365 static int 366 process_ldif_rec( char *rbuf, unsigned long linenum ) 367 { 368 LDIFRecord lr; 369 int lrflags = ldapadd ? LDIF_DEFAULT_ADD : 0; 370 int rc; 371 struct berval rbuf_bv; 372 373 #ifdef TEST_LDIF_API 374 if ( getenv( "LDIF_ENTRIES_ONLY" ) ) { 375 lrflags |= LDIF_ENTRIES_ONLY; 376 } 377 if ( getenv( "LDIF_NO_CONTROLS" ) ) { 378 lrflags |= LDIF_NO_CONTROLS; 379 } 380 #endif /* TEST_LDIF_API */ 381 382 rbuf_bv.bv_val = rbuf; 383 rbuf_bv.bv_len = 0; /* not used */ 384 rc = ldap_parse_ldif_record( &rbuf_bv, linenum, &lr, prog, lrflags ); 385 386 /* If default controls are set (as with -M option) and controls are 387 specified in the LDIF file, we must add the default controls to 388 the list of controls sent with the ldap operation. 389 */ 390 if ( rc == 0 ) { 391 if (lr.lr_ctrls) { 392 LDAPControl **defctrls = NULL; /* Default server controls */ 393 LDAPControl **newctrls = NULL; 394 ldap_get_option(ld, LDAP_OPT_SERVER_CONTROLS, &defctrls); 395 if (defctrls) { 396 int npc=0; /* Num of LDIF controls */ 397 int ndefc=0; /* Num of default controls */ 398 while (lr.lr_ctrls[npc]) npc++; /* Count LDIF controls */ 399 while (defctrls[ndefc]) ndefc++; /* Count default controls */ 400 newctrls = ber_memrealloc(lr.lr_ctrls, 401 (npc+ndefc+1)*sizeof(LDAPControl*)); 402 403 if (newctrls == NULL) { 404 rc = LDAP_NO_MEMORY; 405 } else { 406 int i; 407 lr.lr_ctrls = newctrls; 408 for (i=npc; i<npc+ndefc; i++) { 409 lr.lr_ctrls[i] = ldap_control_dup(defctrls[i-npc]); 410 if (lr.lr_ctrls[i] == NULL) { 411 rc = LDAP_NO_MEMORY; 412 break; 413 } 414 } 415 lr.lr_ctrls[npc+ndefc] = NULL; 416 } 417 ldap_controls_free(defctrls); /* Must be freed by library */ 418 } 419 } 420 } 421 422 if ( rc == 0 ) { 423 if ( LDAP_REQ_DELETE == lr.lr_op ) { 424 rc = dodelete( &lr.lr_dn, lr.lr_ctrls ); 425 } else if ( LDAP_REQ_RENAME == lr.lr_op ) { 426 rc = dorename( &lr.lr_dn, &lr.lrop_newrdn, &lr.lrop_newsup, lr.lrop_delold, lr.lr_ctrls ); 427 } else if ( ( LDAP_REQ_ADD == lr.lr_op ) || ( LDAP_REQ_MODIFY == lr.lr_op ) ) { 428 rc = domodify( &lr.lr_dn, lr.lrop_mods, lr.lr_ctrls, LDAP_REQ_ADD == lr.lr_op ); 429 } else { 430 /* record skipped e.g. version: or comment or something we don't handle yet */ 431 } 432 433 if ( rc == LDAP_SUCCESS ) { 434 rc = 0; 435 } 436 } 437 438 ldap_ldif_record_done( &lr ); 439 440 return( rc ); 441 } 442 443 444 static int 445 domodify( 446 const struct berval *dn, 447 LDAPMod **pmods, 448 LDAPControl **pctrls, 449 int newentry ) 450 { 451 int rc, i, j, k, notascii, op; 452 struct berval *bvp; 453 454 if ( ( dn == NULL ) || ( dn->bv_val == NULL ) ) { 455 fprintf( stderr, _("%s: no DN specified\n"), prog ); 456 return( LDAP_PARAM_ERROR ); 457 } 458 459 if ( pmods == NULL ) { 460 /* implement "touch" (empty sequence) 461 * modify operation (note that there 462 * is no symmetry with the UNIX command, 463 * since \"touch\" on a non-existent entry 464 * will fail)*/ 465 printf( "warning: no attributes to %sadd (entry=\"%s\")\n", 466 newentry ? "" : "change or ", dn->bv_val ); 467 468 } else { 469 for ( i = 0; pmods[ i ] != NULL; ++i ) { 470 op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES; 471 if( op == LDAP_MOD_ADD && ( pmods[i]->mod_bvalues == NULL )) { 472 fprintf( stderr, 473 _("%s: attribute \"%s\" has no values (entry=\"%s\")\n"), 474 prog, pmods[i]->mod_type, dn->bv_val ); 475 return LDAP_PARAM_ERROR; 476 } 477 } 478 479 if ( verbose ) { 480 for ( i = 0; pmods[ i ] != NULL; ++i ) { 481 op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES; 482 printf( "%s %s:\n", 483 op == LDAP_MOD_REPLACE ? _("replace") : 484 op == LDAP_MOD_ADD ? _("add") : 485 op == LDAP_MOD_INCREMENT ? _("increment") : 486 op == LDAP_MOD_DELETE ? _("delete") : 487 _("unknown"), 488 pmods[ i ]->mod_type ); 489 490 if ( pmods[ i ]->mod_bvalues != NULL ) { 491 for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) { 492 bvp = pmods[ i ]->mod_bvalues[ j ]; 493 notascii = 0; 494 for ( k = 0; (unsigned long) k < bvp->bv_len; ++k ) { 495 if ( !isascii( bvp->bv_val[ k ] )) { 496 notascii = 1; 497 break; 498 } 499 } 500 if ( notascii ) { 501 printf( _("\tNOT ASCII (%ld bytes)\n"), bvp->bv_len ); 502 } else { 503 printf( "\t%s\n", bvp->bv_val ); 504 } 505 } 506 } 507 } 508 } 509 } 510 511 if ( newentry ) { 512 printf( "%sadding new entry \"%s\"\n", dont ? "!" : "", dn->bv_val ); 513 } else { 514 printf( "%smodifying entry \"%s\"\n", dont ? "!" : "", dn->bv_val ); 515 } 516 517 if ( !dont ) { 518 int msgid; 519 if ( newentry ) { 520 rc = ldap_add_ext( ld, dn->bv_val, pmods, pctrls, NULL, &msgid ); 521 } else { 522 rc = ldap_modify_ext( ld, dn->bv_val, pmods, pctrls, NULL, &msgid ); 523 } 524 525 if ( rc != LDAP_SUCCESS ) { 526 /* print error message about failed update including DN */ 527 fprintf( stderr, _("%s: update failed: %s\n"), prog, dn->bv_val ); 528 tool_perror( newentry ? "ldap_add" : "ldap_modify", 529 rc, NULL, NULL, NULL, NULL ); 530 goto done; 531 } 532 rc = process_response( ld, msgid, 533 newentry ? LDAP_RES_ADD : LDAP_RES_MODIFY, dn ); 534 535 if ( verbose && rc == LDAP_SUCCESS ) { 536 printf( _("modify complete\n") ); 537 } 538 539 } else { 540 rc = LDAP_SUCCESS; 541 } 542 543 done: 544 putchar( '\n' ); 545 return rc; 546 } 547 548 549 static int 550 dodelete( 551 const struct berval *dn, 552 LDAPControl **pctrls ) 553 { 554 int rc; 555 int msgid; 556 557 assert( dn != NULL ); 558 assert( dn->bv_val != NULL ); 559 printf( _("%sdeleting entry \"%s\"\n"), dont ? "!" : "", dn->bv_val ); 560 if ( !dont ) { 561 rc = ldap_delete_ext( ld, dn->bv_val, pctrls, NULL, &msgid ); 562 if ( rc != LDAP_SUCCESS ) { 563 fprintf( stderr, _("%s: delete failed: %s\n"), prog, dn->bv_val ); 564 tool_perror( "ldap_delete", rc, NULL, NULL, NULL, NULL ); 565 goto done; 566 } 567 rc = process_response( ld, msgid, LDAP_RES_DELETE, dn ); 568 569 if ( verbose && rc == LDAP_SUCCESS ) { 570 printf( _("delete complete\n") ); 571 } 572 } else { 573 rc = LDAP_SUCCESS; 574 } 575 576 done: 577 putchar( '\n' ); 578 return( rc ); 579 } 580 581 582 static int 583 dorename( 584 const struct berval *dn, 585 const struct berval *newrdn, 586 const struct berval *newsup, 587 int deleteoldrdn, 588 LDAPControl **pctrls ) 589 { 590 int rc; 591 int msgid; 592 593 assert( dn != NULL ); 594 assert( dn->bv_val != NULL ); 595 assert( newrdn != NULL ); 596 assert( newrdn->bv_val != NULL ); 597 printf( _("%smodifying rdn of entry \"%s\"\n"), dont ? "!" : "", dn->bv_val ); 598 if ( verbose ) { 599 printf( _("\tnew RDN: \"%s\" (%skeep existing values)\n"), 600 newrdn->bv_val, deleteoldrdn ? _("do not ") : "" ); 601 } 602 if ( !dont ) { 603 rc = ldap_rename( ld, dn->bv_val, newrdn->bv_val, 604 ( newsup && newsup->bv_val ) ? newsup->bv_val : NULL, 605 deleteoldrdn, pctrls, NULL, &msgid ); 606 if ( rc != LDAP_SUCCESS ) { 607 fprintf( stderr, _("%s: rename failed: %s\n"), prog, dn->bv_val ); 608 tool_perror( "ldap_rename", rc, NULL, NULL, NULL, NULL ); 609 goto done; 610 } 611 rc = process_response( ld, msgid, LDAP_RES_RENAME, dn ); 612 613 if ( verbose && rc == LDAP_SUCCESS ) { 614 printf( _("rename complete\n") ); 615 } 616 } else { 617 rc = LDAP_SUCCESS; 618 } 619 620 done: 621 putchar( '\n' ); 622 return( rc ); 623 } 624 625 static const char * 626 res2str( int res ) { 627 switch ( res ) { 628 case LDAP_RES_ADD: 629 return "ldap_add"; 630 case LDAP_RES_DELETE: 631 return "ldap_delete"; 632 case LDAP_RES_MODIFY: 633 return "ldap_modify"; 634 case LDAP_RES_MODRDN: 635 return "ldap_rename"; 636 default: 637 assert( 0 ); 638 } 639 640 return "ldap_unknown"; 641 } 642 643 static int process_response( 644 LDAP *ld, 645 int msgid, 646 int op, 647 const struct berval *dn ) 648 { 649 LDAPMessage *res; 650 int rc = LDAP_OTHER, msgtype; 651 struct timeval tv = { 0, 0 }; 652 int err; 653 char *text = NULL, *matched = NULL, **refs = NULL; 654 LDAPControl **ctrls = NULL; 655 656 assert( dn != NULL ); 657 for ( ; ; ) { 658 tv.tv_sec = 0; 659 tv.tv_usec = 100000; 660 661 rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ); 662 if ( tool_check_abandon( ld, msgid ) ) { 663 return LDAP_CANCELLED; 664 } 665 666 if ( rc == -1 ) { 667 ldap_get_option( ld, LDAP_OPT_RESULT_CODE, &rc ); 668 tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL ); 669 return rc; 670 } 671 672 if ( rc != 0 ) { 673 break; 674 } 675 } 676 677 msgtype = ldap_msgtype( res ); 678 679 rc = ldap_parse_result( ld, res, &err, &matched, &text, &refs, &ctrls, 1 ); 680 if ( rc == LDAP_SUCCESS ) rc = err; 681 682 if ( rc == LDAP_TXN_SPECIFY_OKAY ) { 683 rc = LDAP_SUCCESS; 684 } else if ( rc != LDAP_SUCCESS ) { 685 tool_perror( res2str( op ), rc, NULL, matched, text, refs ); 686 } else if ( msgtype != op ) { 687 fprintf( stderr, "%s: msgtype: expected %d got %d\n", 688 res2str( op ), op, msgtype ); 689 rc = LDAP_OTHER; 690 } 691 692 if ( text ) ldap_memfree( text ); 693 if ( matched ) ldap_memfree( matched ); 694 if ( refs ) ber_memvfree( (void **)refs ); 695 696 if ( ctrls ) { 697 tool_print_ctrls( ld, ctrls ); 698 ldap_controls_free( ctrls ); 699 } 700 701 return rc; 702 } 703