1 /* $NetBSD: ldapmodify.c,v 1.2 2020/08/11 13:15:34 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-2020 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.2 2020/08/11 13:15:34 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 #define M_SEP 0x7f 82 83 /* strings found in LDIF entries */ 84 static struct berval BV_VERSION = BER_BVC("version"); 85 static struct berval BV_DN = BER_BVC("dn"); 86 static struct berval BV_CONTROL = BER_BVC("control"); 87 static struct berval BV_CHANGETYPE = BER_BVC("changetype"); 88 static struct berval BV_ADDCT = BER_BVC("add"); 89 static struct berval BV_MODIFYCT = BER_BVC("modify"); 90 static struct berval BV_DELETECT = BER_BVC("delete"); 91 static struct berval BV_MODRDNCT = BER_BVC("modrdn"); 92 static struct berval BV_MODDNCT = BER_BVC("moddn"); 93 static struct berval BV_RENAMECT = BER_BVC("rename"); 94 static struct berval BV_MODOPADD = BER_BVC("add"); 95 static struct berval BV_MODOPREPLACE = BER_BVC("replace"); 96 static struct berval BV_MODOPDELETE = BER_BVC("delete"); 97 static struct berval BV_MODOPINCREMENT = BER_BVC("increment"); 98 static struct berval BV_NEWRDN = BER_BVC("newrdn"); 99 static struct berval BV_DELETEOLDRDN = BER_BVC("deleteoldrdn"); 100 static struct berval BV_NEWSUP = BER_BVC("newsuperior"); 101 102 #define BV_CASEMATCH(a, b) \ 103 ((a)->bv_len == (b)->bv_len && 0 == strcasecmp((a)->bv_val, (b)->bv_val)) 104 105 static int process_ldif_rec LDAP_P(( char *rbuf, unsigned long lineno )); 106 static int parse_ldif_control LDAP_P(( struct berval *val, LDAPControl ***pctrls )); 107 static int domodify LDAP_P(( 108 const char *dn, 109 LDAPMod **pmods, 110 LDAPControl **pctrls, 111 int newentry )); 112 static int dodelete LDAP_P(( 113 const char *dn, 114 LDAPControl **pctrls )); 115 static int dorename LDAP_P(( 116 const char *dn, 117 const char *newrdn, 118 const char *newsup, 119 int deleteoldrdn, 120 LDAPControl **pctrls )); 121 static int process_response( 122 LDAP *ld, 123 int msgid, 124 int res, 125 const char *dn ); 126 127 #ifdef LDAP_X_TXN 128 static int txn = 0; 129 static int txnabort = 0; 130 struct berval *txn_id = NULL; 131 #endif 132 133 void 134 usage( void ) 135 { 136 fprintf( stderr, _("Add or modify entries from an LDAP server\n\n")); 137 fprintf( stderr, _("usage: %s [options]\n"), prog); 138 fprintf( stderr, _(" The list of desired operations are read from stdin" 139 " or from the file\n")); 140 fprintf( stderr, _(" specified by \"-f file\".\n")); 141 fprintf( stderr, _("Add or modify options:\n")); 142 fprintf( stderr, _(" -a add values (%s)\n"), 143 (ldapadd ? _("default") : _("default is to replace"))); 144 fprintf( stderr, _(" -c continuous operation mode (do not stop on errors)\n")); 145 fprintf( stderr, _(" -E [!]ext=extparam modify extensions" 146 " (! indicate s criticality)\n")); 147 fprintf( stderr, _(" -f file read operations from `file'\n")); 148 fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n")); 149 fprintf( stderr, _(" -P version protocol version (default: 3)\n")); 150 #ifdef LDAP_X_TXN 151 fprintf( stderr, 152 _(" [!]txn=<commit|abort> (transaction)\n")); 153 #endif 154 fprintf( stderr, _(" -S file write skipped modifications to `file'\n")); 155 156 tool_common_usage(); 157 exit( EXIT_FAILURE ); 158 } 159 160 161 const char options[] = "aE:rS:" 162 "cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z"; 163 164 int 165 handle_private_option( int i ) 166 { 167 char *control, *cvalue; 168 int crit; 169 170 switch ( i ) { 171 case 'E': /* modify extensions */ 172 if( protocol == LDAP_VERSION2 ) { 173 fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"), 174 prog, protocol ); 175 exit( EXIT_FAILURE ); 176 } 177 178 /* should be extended to support comma separated list of 179 * [!]key[=value] parameters, e.g. -E !foo,bar=567 180 */ 181 182 crit = 0; 183 cvalue = NULL; 184 if( optarg[0] == '!' ) { 185 crit = 1; 186 optarg++; 187 } 188 189 control = ber_strdup( optarg ); 190 if ( (cvalue = strchr( control, '=' )) != NULL ) { 191 *cvalue++ = '\0'; 192 } 193 194 #ifdef LDAP_X_TXN 195 if( strcasecmp( control, "txn" ) == 0 ) { 196 /* Transaction */ 197 if( txn ) { 198 fprintf( stderr, 199 _("txn control previously specified\n")); 200 exit( EXIT_FAILURE ); 201 } 202 if( cvalue != NULL ) { 203 if( strcasecmp( cvalue, "abort" ) == 0 ) { 204 txnabort=1; 205 } else if( strcasecmp( cvalue, "commit" ) != 0 ) { 206 fprintf( stderr, _("Invalid value for txn control, %s\n"), 207 cvalue ); 208 exit( EXIT_FAILURE ); 209 } 210 } 211 212 txn = 1 + crit; 213 } else 214 #endif 215 { 216 fprintf( stderr, _("Invalid modify extension name: %s\n"), 217 control ); 218 usage(); 219 } 220 break; 221 222 case 'a': /* add */ 223 ldapadd = 1; 224 break; 225 226 case 'r': /* replace (obsolete) */ 227 break; 228 229 case 'S': /* skipped modifications to file */ 230 if( rejfile != NULL ) { 231 fprintf( stderr, _("%s: -S previously specified\n"), prog ); 232 exit( EXIT_FAILURE ); 233 } 234 rejfile = ber_strdup( optarg ); 235 break; 236 237 default: 238 return 0; 239 } 240 return 1; 241 } 242 243 244 int 245 main( int argc, char **argv ) 246 { 247 char *rbuf = NULL, *rejbuf = NULL; 248 FILE *rejfp; 249 struct LDIFFP *ldiffp = NULL, ldifdummy = {0}; 250 char *matched_msg, *error_msg; 251 int rc, retval, ldifrc; 252 int len; 253 int i = 0, lmax = 0; 254 unsigned long lineno, nextline = 0; 255 LDAPControl c[1]; 256 257 prog = lutil_progname( "ldapmodify", argc, argv ); 258 259 /* strncmp instead of strcmp since NT binaries carry .exe extension */ 260 ldapadd = ( strncasecmp( prog, "ldapadd", sizeof("ldapadd")-1 ) == 0 ); 261 262 tool_init( ldapadd ? TOOL_ADD : TOOL_MODIFY ); 263 264 tool_args( argc, argv ); 265 266 if ( argc != optind ) usage(); 267 268 if ( rejfile != NULL ) { 269 if (( rejfp = fopen( rejfile, "w" )) == NULL ) { 270 perror( rejfile ); 271 retval = EXIT_FAILURE; 272 goto fail; 273 } 274 } else { 275 rejfp = NULL; 276 } 277 278 if ( infile != NULL ) { 279 if (( ldiffp = ldif_open( infile, "r" )) == NULL ) { 280 perror( infile ); 281 retval = EXIT_FAILURE; 282 goto fail; 283 } 284 } else { 285 ldifdummy.fp = stdin; 286 ldiffp = &ldifdummy; 287 } 288 289 if ( debug ) ldif_debug = debug; 290 291 ld = tool_conn_setup( dont, 0 ); 292 293 if ( !dont ) { 294 tool_bind( ld ); 295 } 296 297 #ifdef LDAP_X_TXN 298 if( txn ) { 299 /* start transaction */ 300 rc = ldap_txn_start_s( ld, NULL, NULL, &txn_id ); 301 if( rc != LDAP_SUCCESS ) { 302 tool_perror( "ldap_txn_start_s", rc, NULL, NULL, NULL, NULL ); 303 if( txn > 1 ) { 304 retval = EXIT_FAILURE; 305 goto fail; 306 } 307 txn = 0; 308 } 309 } 310 #endif 311 312 if ( 0 313 #ifdef LDAP_X_TXN 314 || txn 315 #endif 316 ) 317 { 318 #ifdef LDAP_X_TXN 319 if( txn ) { 320 c[i].ldctl_oid = LDAP_CONTROL_X_TXN_SPEC; 321 c[i].ldctl_value = *txn_id; 322 c[i].ldctl_iscritical = 1; 323 i++; 324 } 325 #endif 326 } 327 328 tool_server_controls( ld, c, i ); 329 330 rc = 0; 331 retval = 0; 332 lineno = 1; 333 while (( rc == 0 || contoper ) && ( ldifrc = ldif_read_record( ldiffp, &nextline, 334 &rbuf, &lmax )) > 0 ) 335 { 336 if ( rejfp ) { 337 len = strlen( rbuf ); 338 if (( rejbuf = (char *)ber_memalloc( len+1 )) == NULL ) { 339 perror( "malloc" ); 340 retval = EXIT_FAILURE; 341 goto fail; 342 } 343 memcpy( rejbuf, rbuf, len+1 ); 344 } 345 346 rc = process_ldif_rec( rbuf, lineno ); 347 lineno = nextline+1; 348 349 if ( rc ) retval = rc; 350 if ( rc && rejfp ) { 351 fprintf(rejfp, _("# Error: %s (%d)"), ldap_err2string(rc), rc); 352 353 matched_msg = NULL; 354 ldap_get_option(ld, LDAP_OPT_MATCHED_DN, &matched_msg); 355 if ( matched_msg != NULL ) { 356 if ( *matched_msg != '\0' ) { 357 fprintf( rejfp, _(", matched DN: %s"), matched_msg ); 358 } 359 ldap_memfree( matched_msg ); 360 } 361 362 error_msg = NULL; 363 ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &error_msg); 364 if ( error_msg != NULL ) { 365 if ( *error_msg != '\0' ) { 366 fprintf( rejfp, _(", additional info: %s"), error_msg ); 367 } 368 ldap_memfree( error_msg ); 369 } 370 fprintf( rejfp, "\n%s\n", rejbuf ); 371 } 372 373 if (rejfp) ber_memfree( rejbuf ); 374 } 375 ber_memfree( rbuf ); 376 377 if ( ldifrc < 0 ) 378 retval = LDAP_OTHER; 379 380 #ifdef LDAP_X_TXN 381 if( retval == 0 && txn ) { 382 rc = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL ); 383 if ( rc != LDAP_OPT_SUCCESS ) { 384 fprintf( stderr, "Could not unset controls for ldap_txn_end\n"); 385 } 386 387 /* create transaction */ 388 rc = ldap_txn_end_s( ld, !txnabort, txn_id, NULL, NULL, NULL ); 389 if( rc != LDAP_SUCCESS ) { 390 tool_perror( "ldap_txn_end_s", rc, NULL, NULL, NULL, NULL ); 391 retval = rc; 392 } 393 } 394 #endif 395 396 fail:; 397 if ( rejfp != NULL ) { 398 fclose( rejfp ); 399 } 400 401 if ( ldiffp != NULL && ldiffp != &ldifdummy ) { 402 ldif_close( ldiffp ); 403 } 404 405 tool_exit( ld, retval ); 406 } 407 408 409 static int 410 process_ldif_rec( char *rbuf, unsigned long linenum ) 411 { 412 char *line, *dn, *newrdn, *newsup; 413 int rc, modop; 414 int expect_modop, expect_sep; 415 int deleteoldrdn; 416 int new_entry, delete_entry, got_all; 417 LDAPMod **pmods, *lm = NULL; 418 int version; 419 LDAPControl **pctrls; 420 int i, j, k, lines, idn, nmods; 421 struct berval *btype, *vals, **bvl, bv; 422 char *freeval; 423 unsigned char *mops = NULL; 424 425 new_entry = ldapadd; 426 427 rc = got_all = delete_entry = modop = expect_modop = 0; 428 expect_sep = 0; 429 version = 0; 430 deleteoldrdn = 1; 431 pmods = NULL; 432 pctrls = NULL; 433 dn = newrdn = newsup = NULL; 434 435 lines = ldif_countlines( rbuf ); 436 btype = ber_memcalloc( 1, (lines+1)*2*sizeof(struct berval)+lines ); 437 if ( !btype ) 438 return LDAP_NO_MEMORY; 439 440 vals = btype+lines+1; 441 freeval = (char *)(vals+lines+1); 442 i = -1; 443 444 while ( rc == 0 && ( line = ldif_getline( &rbuf )) != NULL ) { 445 int freev; 446 447 if ( *line == '\n' || *line == '\0' ) { 448 break; 449 } 450 451 ++i; 452 453 if ( line[0] == '-' && !line[1] ) { 454 BER_BVZERO( btype+i ); 455 freeval[i] = 0; 456 continue; 457 } 458 459 if ( ( rc = ldif_parse_line2( line, btype+i, vals+i, &freev ) ) < 0 ) { 460 fprintf( stderr, _("%s: invalid format (line %lu) entry: \"%s\"\n"), 461 prog, linenum+i, dn == NULL ? "" : dn ); 462 rc = LDAP_PARAM_ERROR; 463 goto leave; 464 } 465 freeval[i] = freev; 466 467 if ( dn == NULL ) { 468 if ( linenum+i == 1 && BV_CASEMATCH( btype+i, &BV_VERSION )) { 469 int v; 470 if( vals[i].bv_len == 0 || lutil_atoi( &v, vals[i].bv_val) != 0 || v != 1 ) { 471 fprintf( stderr, 472 _("%s: invalid version %s, line %lu (ignored)\n"), 473 prog, vals[i].bv_val, linenum ); 474 } 475 version++; 476 477 } else if ( BV_CASEMATCH( btype+i, &BV_DN )) { 478 dn = vals[i].bv_val; 479 idn = i; 480 } 481 /* skip all lines until we see "dn:" */ 482 } 483 } 484 485 /* check to make sure there was a dn: line */ 486 if ( !dn ) { 487 rc = 0; 488 goto leave; 489 } 490 491 lines = i+1; 492 493 if( lines == 0 ) { 494 rc = 0; 495 goto leave; 496 } 497 498 if( version && lines == 1 ) { 499 rc = 0; 500 goto leave; 501 } 502 503 i = idn+1; 504 /* Check for "control" tag after dn and before changetype. */ 505 if ( BV_CASEMATCH( btype+i, &BV_CONTROL )) { 506 /* Parse and add it to the list of controls */ 507 rc = parse_ldif_control( vals+i, &pctrls ); 508 if (rc != 0) { 509 fprintf( stderr, 510 _("%s: Error processing %s line, line %lu: %s\n"), 511 prog, BV_CONTROL.bv_val, linenum+i, ldap_err2string(rc) ); 512 } 513 i++; 514 if ( i>= lines ) { 515 short_input: 516 fprintf( stderr, 517 _("%s: Expecting more input after %s line, line %lu\n"), 518 prog, btype[i-1].bv_val, linenum+i ); 519 520 rc = LDAP_PARAM_ERROR; 521 goto leave; 522 } 523 } 524 525 /* Check for changetype */ 526 if ( BV_CASEMATCH( btype+i, &BV_CHANGETYPE )) { 527 #ifdef LIBERAL_CHANGETYPE_MODOP 528 /* trim trailing spaces (and log warning ...) */ 529 int icnt; 530 for ( icnt = vals[i].bv_len; --icnt > 0; ) { 531 if ( !isspace( (unsigned char) vals[i].bv_val[icnt] ) ) { 532 break; 533 } 534 } 535 536 if ( ++icnt != vals[i].bv_len ) { 537 fprintf( stderr, _("%s: illegal trailing space after" 538 " \"%s: %s\" trimmed (line %lu, entry \"%s\")\n"), 539 prog, BV_CHANGETYPE.bv_val, vals[i].bv_val, linenum+i, dn ); 540 vals[i].bv_val[icnt] = '\0'; 541 } 542 #endif /* LIBERAL_CHANGETYPE_MODOP */ 543 544 if ( BV_CASEMATCH( vals+i, &BV_MODIFYCT )) { 545 new_entry = 0; 546 expect_modop = 1; 547 } else if ( BV_CASEMATCH( vals+i, &BV_ADDCT )) { 548 new_entry = 1; 549 modop = LDAP_MOD_ADD; 550 } else if ( BV_CASEMATCH( vals+i, &BV_MODRDNCT ) 551 || BV_CASEMATCH( vals+i, &BV_MODDNCT ) 552 || BV_CASEMATCH( vals+i, &BV_RENAMECT )) 553 { 554 i++; 555 if ( i >= lines ) 556 goto short_input; 557 if ( !BV_CASEMATCH( btype+i, &BV_NEWRDN )) { 558 fprintf( stderr, _("%s: expecting \"%s:\" but saw" 559 " \"%s:\" (line %lu, entry \"%s\")\n"), 560 prog, BV_NEWRDN.bv_val, btype[i].bv_val, linenum+i, dn ); 561 rc = LDAP_PARAM_ERROR; 562 goto leave; 563 } 564 newrdn = vals[i].bv_val; 565 i++; 566 if ( i >= lines ) 567 goto short_input; 568 if ( !BV_CASEMATCH( btype+i, &BV_DELETEOLDRDN )) { 569 fprintf( stderr, _("%s: expecting \"%s:\" but saw" 570 " \"%s:\" (line %lu, entry \"%s\")\n"), 571 prog, BV_DELETEOLDRDN.bv_val, btype[i].bv_val, linenum+i, dn ); 572 rc = LDAP_PARAM_ERROR; 573 goto leave; 574 } 575 deleteoldrdn = ( vals[i].bv_val[0] == '0' ) ? 0 : 1; 576 i++; 577 if ( i < lines ) { 578 if ( !BV_CASEMATCH( btype+i, &BV_NEWSUP )) { 579 fprintf( stderr, _("%s: expecting \"%s:\" but saw" 580 " \"%s:\" (line %lu, entry \"%s\")\n"), 581 prog, BV_NEWSUP.bv_val, btype[i].bv_val, linenum+i, dn ); 582 rc = LDAP_PARAM_ERROR; 583 goto leave; 584 } 585 newsup = vals[i].bv_val; 586 i++; 587 } 588 got_all = 1; 589 } else if ( BV_CASEMATCH( vals+i, &BV_DELETECT )) { 590 got_all = delete_entry = 1; 591 } else { 592 fprintf( stderr, 593 _("%s: unknown %s \"%s\" (line %lu, entry \"%s\")\n"), 594 prog, BV_CHANGETYPE.bv_val, vals[i].bv_val, linenum+i, dn ); 595 rc = LDAP_PARAM_ERROR; 596 goto leave; 597 } 598 i++; 599 } else if ( ldapadd ) { /* missing changetype => add */ 600 new_entry = 1; 601 modop = LDAP_MOD_ADD; 602 } else { 603 expect_modop = 1; /* missing changetype => modify */ 604 } 605 606 if ( got_all ) { 607 if ( i < lines ) { 608 fprintf( stderr, 609 _("%s: extra lines at end (line %lu, entry \"%s\")\n"), 610 prog, linenum+i, dn ); 611 rc = LDAP_PARAM_ERROR; 612 goto leave; 613 } 614 goto doit; 615 } 616 617 nmods = lines - i; 618 idn = i; 619 620 if ( new_entry ) { 621 int fv; 622 623 /* Make sure all attributes with multiple values are contiguous */ 624 for (; i<lines; i++) { 625 for (j=i+1; j<lines; j++) { 626 if ( !btype[j].bv_val ) { 627 fprintf( stderr, 628 _("%s: missing attributeDescription (line %lu, entry \"%s\")\n"), 629 prog, linenum+j, dn ); 630 rc = LDAP_PARAM_ERROR; 631 goto leave; 632 } 633 if ( BV_CASEMATCH( btype+i, btype+j )) { 634 nmods--; 635 /* out of order, move intervening attributes down */ 636 if ( j != i+1 ) { 637 bv = vals[j]; 638 fv = freeval[j]; 639 for (k=j; k>i; k--) { 640 btype[k] = btype[k-1]; 641 vals[k] = vals[k-1]; 642 freeval[k] = freeval[k-1]; 643 } 644 k++; 645 btype[k] = btype[i]; 646 vals[k] = bv; 647 freeval[k] = fv; 648 } 649 i++; 650 } 651 } 652 } 653 /* Allocate space for array of mods, array of pointers to mods, 654 * and array of pointers to values, allowing for NULL terminators 655 * for the pointer arrays... 656 */ 657 lm = ber_memalloc( nmods * sizeof(LDAPMod) + 658 (nmods+1) * sizeof(LDAPMod*) + 659 (lines + nmods - idn) * sizeof(struct berval *)); 660 pmods = (LDAPMod **)(lm+nmods); 661 bvl = (struct berval **)(pmods+nmods+1); 662 663 j = 0; 664 k = -1; 665 BER_BVZERO(&bv); 666 for (i=idn; i<lines; i++) { 667 if ( BV_CASEMATCH( btype+i, &BV_DN )) { 668 fprintf( stderr, _("%s: attributeDescription \"%s\":" 669 " (possible missing newline" 670 " after line %lu, entry \"%s\"?)\n"), 671 prog, btype[i].bv_val, linenum+i - 1, dn ); 672 } 673 if ( !BV_CASEMATCH( btype+i, &bv )) { 674 bvl[k++] = NULL; 675 bv = btype[i]; 676 lm[j].mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; 677 lm[j].mod_type = bv.bv_val; 678 lm[j].mod_bvalues = bvl+k; 679 pmods[j] = lm+j; 680 j++; 681 } 682 bvl[k++] = vals+i; 683 } 684 bvl[k] = NULL; 685 pmods[j] = NULL; 686 goto doit; 687 } 688 689 mops = ber_memalloc( lines+1 ); 690 mops[lines] = M_SEP; 691 mops[i-1] = M_SEP; 692 693 for ( ; i<lines; i++ ) { 694 if ( expect_modop ) { 695 #ifdef LIBERAL_CHANGETYPE_MODOP 696 /* trim trailing spaces (and log warning ...) */ 697 int icnt; 698 for ( icnt = vals[i].bv_len; --icnt > 0; ) { 699 if ( !isspace( (unsigned char) vals[i].bv_val[icnt] ) ) break; 700 } 701 702 if ( ++icnt != vals[i].bv_len ) { 703 fprintf( stderr, _("%s: illegal trailing space after" 704 " \"%s: %s\" trimmed (line %lu, entry \"%s\")\n"), 705 prog, type, vals[i].bv_val, linenum+i, dn ); 706 vals[i].bv_val[icnt] = '\0'; 707 } 708 #endif /* LIBERAL_CHANGETYPE_MODOP */ 709 710 expect_modop = 0; 711 expect_sep = 1; 712 if ( BV_CASEMATCH( btype+i, &BV_MODOPADD )) { 713 modop = LDAP_MOD_ADD; 714 mops[i] = M_SEP; 715 nmods--; 716 } else if ( BV_CASEMATCH( btype+i, &BV_MODOPREPLACE )) { 717 /* defer handling these since they might have no values. 718 * Use the BVALUES flag to signal that these were 719 * deferred. If values are provided later, this 720 * flag will be switched off. 721 */ 722 modop = LDAP_MOD_REPLACE; 723 mops[i] = modop | LDAP_MOD_BVALUES; 724 btype[i] = vals[i]; 725 } else if ( BV_CASEMATCH( btype+i, &BV_MODOPDELETE )) { 726 modop = LDAP_MOD_DELETE; 727 mops[i] = modop | LDAP_MOD_BVALUES; 728 btype[i] = vals[i]; 729 } else if ( BV_CASEMATCH( btype+i, &BV_MODOPINCREMENT )) { 730 modop = LDAP_MOD_INCREMENT; 731 mops[i] = M_SEP; 732 nmods--; 733 } else { /* no modify op: invalid LDIF */ 734 fprintf( stderr, _("%s: modify operation type is missing at" 735 " line %lu, entry \"%s\"\n"), 736 prog, linenum+i, dn ); 737 rc = LDAP_PARAM_ERROR; 738 goto leave; 739 } 740 bv = vals[i]; 741 } else if ( expect_sep && BER_BVISEMPTY( btype+i )) { 742 mops[i] = M_SEP; 743 expect_sep = 0; 744 expect_modop = 1; 745 nmods--; 746 } else { 747 if ( !BV_CASEMATCH( btype+i, &bv )) { 748 fprintf( stderr, _("%s: wrong attributeType at" 749 " line %lu, entry \"%s\"\n"), 750 prog, linenum+i, dn ); 751 rc = LDAP_PARAM_ERROR; 752 goto leave; 753 } 754 mops[i] = modop; 755 /* If prev op was deferred and matches this type, 756 * clear the flag 757 */ 758 if ( (mops[i-1] & LDAP_MOD_BVALUES) 759 && BV_CASEMATCH( btype+i, btype+i-1 )) 760 { 761 mops[i-1] = M_SEP; 762 nmods--; 763 } 764 } 765 } 766 767 #if 0 /* we should faithfully encode the LDIF, not combine */ 768 /* Make sure all modops with multiple values are contiguous */ 769 for (i=idn; i<lines; i++) { 770 if ( mops[i] == M_SEP ) 771 continue; 772 for (j=i+1; j<lines; j++) { 773 if ( mops[j] == M_SEP || mops[i] != mops[j] ) 774 continue; 775 if ( BV_CASEMATCH( btype+i, btype+j )) { 776 nmods--; 777 /* out of order, move intervening attributes down */ 778 if ( j != i+1 ) { 779 int c; 780 struct berval bv; 781 char fv; 782 783 c = mops[j]; 784 bv = vals[j]; 785 fv = freeval[j]; 786 for (k=j; k>i; k--) { 787 btype[k] = btype[k-1]; 788 vals[k] = vals[k-1]; 789 freeval[k] = freeval[k-1]; 790 mops[k] = mops[k-1]; 791 } 792 k++; 793 btype[k] = btype[i]; 794 vals[k] = bv; 795 freeval[k] = fv; 796 mops[k] = c; 797 } 798 i++; 799 } 800 } 801 } 802 #endif 803 804 /* Allocate space for array of mods, array of pointers to mods, 805 * and array of pointers to values, allowing for NULL terminators 806 * for the pointer arrays... 807 */ 808 lm = ber_memalloc( nmods * sizeof(LDAPMod) + 809 (nmods+1) * sizeof(LDAPMod*) + 810 (lines + nmods - idn) * sizeof(struct berval *)); 811 pmods = (LDAPMod **)(lm+nmods); 812 bvl = (struct berval **)(pmods+nmods+1); 813 814 j = 0; 815 k = -1; 816 BER_BVZERO(&bv); 817 mops[idn-1] = M_SEP; 818 for (i=idn; i<lines; i++) { 819 if ( mops[i] == M_SEP ) 820 continue; 821 if ( mops[i] != mops[i-1] || !BV_CASEMATCH( btype+i, &bv )) { 822 bvl[k++] = NULL; 823 bv = btype[i]; 824 lm[j].mod_op = mops[i] | LDAP_MOD_BVALUES; 825 lm[j].mod_type = bv.bv_val; 826 if ( mops[i] & LDAP_MOD_BVALUES ) { 827 lm[j].mod_bvalues = NULL; 828 } else { 829 lm[j].mod_bvalues = bvl+k; 830 } 831 pmods[j] = lm+j; 832 j++; 833 } 834 bvl[k++] = vals+i; 835 } 836 bvl[k] = NULL; 837 pmods[j] = NULL; 838 839 doit: 840 /* If default controls are set (as with -M option) and controls are 841 specified in the LDIF file, we must add the default controls to 842 the list of controls sent with the ldap operation. 843 */ 844 if ( rc == 0 ) { 845 if (pctrls) { 846 LDAPControl **defctrls = NULL; /* Default server controls */ 847 LDAPControl **newctrls = NULL; 848 ldap_get_option(ld, LDAP_OPT_SERVER_CONTROLS, &defctrls); 849 if (defctrls) { 850 int npc=0; /* Num of LDIF controls */ 851 int ndefc=0; /* Num of default controls */ 852 while (pctrls[npc]) npc++; /* Count LDIF controls */ 853 while (defctrls[ndefc]) ndefc++; /* Count default controls */ 854 newctrls = ber_memrealloc(pctrls, 855 (npc+ndefc+1)*sizeof(LDAPControl*)); 856 857 if (newctrls == NULL) { 858 rc = LDAP_NO_MEMORY; 859 } else { 860 int i; 861 pctrls = newctrls; 862 for (i=npc; i<npc+ndefc; i++) { 863 pctrls[i] = ldap_control_dup(defctrls[i-npc]); 864 if (pctrls[i] == NULL) { 865 rc = LDAP_NO_MEMORY; 866 break; 867 } 868 } 869 pctrls[npc+ndefc] = NULL; 870 } 871 ldap_controls_free(defctrls); /* Must be freed by library */ 872 } 873 } 874 } 875 876 if ( rc == 0 ) { 877 if ( delete_entry ) { 878 rc = dodelete( dn, pctrls ); 879 } else if ( newrdn != NULL ) { 880 rc = dorename( dn, newrdn, newsup, deleteoldrdn, pctrls ); 881 } else { 882 rc = domodify( dn, pmods, pctrls, new_entry ); 883 } 884 885 if ( rc == LDAP_SUCCESS ) { 886 rc = 0; 887 } 888 } 889 890 leave: 891 if (pctrls != NULL) { 892 ldap_controls_free( pctrls ); 893 } 894 if ( lm != NULL ) { 895 ber_memfree( lm ); 896 } 897 if ( mops != NULL ) { 898 ber_memfree( mops ); 899 } 900 for (i=lines-1; i>=0; i--) 901 if ( freeval[i] ) ber_memfree( vals[i].bv_val ); 902 ber_memfree( btype ); 903 904 return( rc ); 905 } 906 907 /* Parse an LDIF control line of the form 908 control: oid [true/false] [: value] or 909 control: oid [true/false] [:: base64-value] or 910 control: oid [true/false] [:< url] 911 The control is added to the list of controls in *ppctrls. 912 */ 913 static int 914 parse_ldif_control( 915 struct berval *bval, 916 LDAPControl ***ppctrls ) 917 { 918 char *oid = NULL; 919 int criticality = 0; /* Default is false if not present */ 920 int i, rc=0; 921 char *s, *oidStart; 922 LDAPControl *newctrl = NULL; 923 LDAPControl **pctrls = NULL; 924 struct berval type, bv = BER_BVNULL; 925 int freeval = 0; 926 927 if (ppctrls) pctrls = *ppctrls; 928 /* OID should come first. Validate and extract it. */ 929 s = bval->bv_val; 930 if (*s == 0) return ( LDAP_PARAM_ERROR ); 931 oidStart = s; 932 while (isdigit((unsigned char)*s) || *s == '.') { 933 s++; /* OID should be digits or . */ 934 } 935 if (s == oidStart) { 936 return ( LDAP_PARAM_ERROR ); /* OID was not present */ 937 } 938 if (*s) { /* End of OID should be space or NULL */ 939 if (!isspace((unsigned char)*s)) { 940 return ( LDAP_PARAM_ERROR ); /* else OID contained invalid chars */ 941 } 942 *s++ = 0; /* Replace space with null to terminate */ 943 } 944 945 oid = ber_strdup(oidStart); 946 if (oid == NULL) return ( LDAP_NO_MEMORY ); 947 948 /* Optional Criticality field is next. */ 949 while (*s && isspace((unsigned char)*s)) { 950 s++; /* Skip white space before criticality */ 951 } 952 if (strncasecmp(s, "true", 4) == 0) { 953 criticality = 1; 954 s += 4; 955 } 956 else if (strncasecmp(s, "false", 5) == 0) { 957 criticality = 0; 958 s += 5; 959 } 960 961 /* Optional value field is next */ 962 while (*s && isspace((unsigned char)*s)) { 963 s++; /* Skip white space before value */ 964 } 965 if (*s) { 966 if (*s != ':') { /* If value is present, must start with : */ 967 rc = LDAP_PARAM_ERROR; 968 goto cleanup; 969 } 970 971 /* Back up so value is in the form 972 a: value 973 a:: base64-value 974 a:< url 975 Then we can use ldif_parse_line2 to extract and decode the value 976 */ 977 s--; 978 *s = 'a'; 979 980 rc = ldif_parse_line2(s, &type, &bv, &freeval); 981 if (rc < 0) { 982 rc = LDAP_PARAM_ERROR; 983 goto cleanup; 984 } 985 } 986 987 /* Create a new LDAPControl structure. */ 988 newctrl = (LDAPControl *)ber_memalloc(sizeof(LDAPControl)); 989 if ( newctrl == NULL ) { 990 rc = LDAP_NO_MEMORY; 991 goto cleanup; 992 } 993 newctrl->ldctl_oid = oid; 994 oid = NULL; 995 newctrl->ldctl_iscritical = criticality; 996 if ( freeval ) 997 newctrl->ldctl_value = bv; 998 else 999 ber_dupbv( &newctrl->ldctl_value, &bv ); 1000 1001 /* Add the new control to the passed-in list of controls. */ 1002 i = 0; 1003 if (pctrls) { 1004 while ( pctrls[i] ) { /* Count the # of controls passed in */ 1005 i++; 1006 } 1007 } 1008 /* Allocate 1 more slot for the new control and 1 for the NULL. */ 1009 pctrls = (LDAPControl **) ber_memrealloc(pctrls, 1010 (i+2)*(sizeof(LDAPControl *))); 1011 if (pctrls == NULL) { 1012 rc = LDAP_NO_MEMORY; 1013 goto cleanup; 1014 } 1015 pctrls[i] = newctrl; 1016 newctrl = NULL; 1017 pctrls[i+1] = NULL; 1018 *ppctrls = pctrls; 1019 1020 cleanup: 1021 if (newctrl) { 1022 if (newctrl->ldctl_oid) ber_memfree(newctrl->ldctl_oid); 1023 if (newctrl->ldctl_value.bv_val) { 1024 ber_memfree(newctrl->ldctl_value.bv_val); 1025 } 1026 ber_memfree(newctrl); 1027 } 1028 if (oid) ber_memfree(oid); 1029 1030 return( rc ); 1031 } 1032 1033 1034 static int 1035 domodify( 1036 const char *dn, 1037 LDAPMod **pmods, 1038 LDAPControl **pctrls, 1039 int newentry ) 1040 { 1041 int rc, i, j, k, notascii, op; 1042 struct berval *bvp; 1043 1044 if ( dn == NULL ) { 1045 fprintf( stderr, _("%s: no DN specified\n"), prog ); 1046 return( LDAP_PARAM_ERROR ); 1047 } 1048 1049 if ( pmods == NULL ) { 1050 /* implement "touch" (empty sequence) 1051 * modify operation (note that there 1052 * is no symmetry with the UNIX command, 1053 * since \"touch\" on a non-existent entry 1054 * will fail)*/ 1055 printf( "warning: no attributes to %sadd (entry=\"%s\")\n", 1056 newentry ? "" : "change or ", dn ); 1057 1058 } else { 1059 for ( i = 0; pmods[ i ] != NULL; ++i ) { 1060 op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES; 1061 if( op == LDAP_MOD_ADD && ( pmods[i]->mod_bvalues == NULL )) { 1062 fprintf( stderr, 1063 _("%s: attribute \"%s\" has no values (entry=\"%s\")\n"), 1064 prog, pmods[i]->mod_type, dn ); 1065 return LDAP_PARAM_ERROR; 1066 } 1067 } 1068 1069 if ( verbose ) { 1070 for ( i = 0; pmods[ i ] != NULL; ++i ) { 1071 op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES; 1072 printf( "%s %s:\n", 1073 op == LDAP_MOD_REPLACE ? _("replace") : 1074 op == LDAP_MOD_ADD ? _("add") : 1075 op == LDAP_MOD_INCREMENT ? _("increment") : 1076 op == LDAP_MOD_DELETE ? _("delete") : 1077 _("unknown"), 1078 pmods[ i ]->mod_type ); 1079 1080 if ( pmods[ i ]->mod_bvalues != NULL ) { 1081 for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) { 1082 bvp = pmods[ i ]->mod_bvalues[ j ]; 1083 notascii = 0; 1084 for ( k = 0; (unsigned long) k < bvp->bv_len; ++k ) { 1085 if ( !isascii( bvp->bv_val[ k ] )) { 1086 notascii = 1; 1087 break; 1088 } 1089 } 1090 if ( notascii ) { 1091 printf( _("\tNOT ASCII (%ld bytes)\n"), bvp->bv_len ); 1092 } else { 1093 printf( "\t%s\n", bvp->bv_val ); 1094 } 1095 } 1096 } 1097 } 1098 } 1099 } 1100 1101 if ( newentry ) { 1102 printf( "%sadding new entry \"%s\"\n", dont ? "!" : "", dn ); 1103 } else { 1104 printf( "%smodifying entry \"%s\"\n", dont ? "!" : "", dn ); 1105 } 1106 1107 if ( !dont ) { 1108 int msgid; 1109 if ( newentry ) { 1110 rc = ldap_add_ext( ld, dn, pmods, pctrls, NULL, &msgid ); 1111 } else { 1112 rc = ldap_modify_ext( ld, dn, pmods, pctrls, NULL, &msgid ); 1113 } 1114 1115 if ( rc != LDAP_SUCCESS ) { 1116 /* print error message about failed update including DN */ 1117 fprintf( stderr, _("%s: update failed: %s\n"), prog, dn ); 1118 tool_perror( newentry ? "ldap_add" : "ldap_modify", 1119 rc, NULL, NULL, NULL, NULL ); 1120 goto done; 1121 } 1122 rc = process_response( ld, msgid, 1123 newentry ? LDAP_RES_ADD : LDAP_RES_MODIFY, dn ); 1124 1125 if ( verbose && rc == LDAP_SUCCESS ) { 1126 printf( _("modify complete\n") ); 1127 } 1128 1129 } else { 1130 rc = LDAP_SUCCESS; 1131 } 1132 1133 done: 1134 putchar( '\n' ); 1135 return rc; 1136 } 1137 1138 1139 static int 1140 dodelete( 1141 const char *dn, 1142 LDAPControl **pctrls ) 1143 { 1144 int rc; 1145 int msgid; 1146 1147 printf( _("%sdeleting entry \"%s\"\n"), dont ? "!" : "", dn ); 1148 if ( !dont ) { 1149 rc = ldap_delete_ext( ld, dn, pctrls, NULL, &msgid ); 1150 if ( rc != LDAP_SUCCESS ) { 1151 fprintf( stderr, _("%s: delete failed: %s\n"), prog, dn ); 1152 tool_perror( "ldap_delete", rc, NULL, NULL, NULL, NULL ); 1153 goto done; 1154 } 1155 rc = process_response( ld, msgid, LDAP_RES_DELETE, dn ); 1156 1157 if ( verbose && rc == LDAP_SUCCESS ) { 1158 printf( _("delete complete\n") ); 1159 } 1160 } else { 1161 rc = LDAP_SUCCESS; 1162 } 1163 1164 done: 1165 putchar( '\n' ); 1166 return( rc ); 1167 } 1168 1169 1170 static int 1171 dorename( 1172 const char *dn, 1173 const char *newrdn, 1174 const char* newsup, 1175 int deleteoldrdn, 1176 LDAPControl **pctrls ) 1177 { 1178 int rc; 1179 int msgid; 1180 1181 printf( _("%smodifying rdn of entry \"%s\"\n"), dont ? "!" : "", dn ); 1182 if ( verbose ) { 1183 printf( _("\tnew RDN: \"%s\" (%skeep existing values)\n"), 1184 newrdn, deleteoldrdn ? _("do not ") : "" ); 1185 } 1186 if ( !dont ) { 1187 rc = ldap_rename( ld, dn, newrdn, newsup, deleteoldrdn, 1188 pctrls, NULL, &msgid ); 1189 if ( rc != LDAP_SUCCESS ) { 1190 fprintf( stderr, _("%s: rename failed: %s\n"), prog, dn ); 1191 tool_perror( "ldap_rename", rc, NULL, NULL, NULL, NULL ); 1192 goto done; 1193 } 1194 rc = process_response( ld, msgid, LDAP_RES_RENAME, dn ); 1195 1196 if ( verbose && rc == LDAP_SUCCESS ) { 1197 printf( _("rename complete\n") ); 1198 } 1199 } else { 1200 rc = LDAP_SUCCESS; 1201 } 1202 1203 done: 1204 putchar( '\n' ); 1205 return( rc ); 1206 } 1207 1208 static const char * 1209 res2str( int res ) { 1210 switch ( res ) { 1211 case LDAP_RES_ADD: 1212 return "ldap_add"; 1213 case LDAP_RES_DELETE: 1214 return "ldap_delete"; 1215 case LDAP_RES_MODIFY: 1216 return "ldap_modify"; 1217 case LDAP_RES_MODRDN: 1218 return "ldap_rename"; 1219 default: 1220 assert( 0 ); 1221 } 1222 1223 return "ldap_unknown"; 1224 } 1225 1226 static int process_response( 1227 LDAP *ld, 1228 int msgid, 1229 int op, 1230 const char *dn ) 1231 { 1232 LDAPMessage *res; 1233 int rc = LDAP_OTHER, msgtype; 1234 struct timeval tv = { 0, 0 }; 1235 int err; 1236 char *text = NULL, *matched = NULL, **refs = NULL; 1237 LDAPControl **ctrls = NULL; 1238 1239 for ( ; ; ) { 1240 tv.tv_sec = 0; 1241 tv.tv_usec = 100000; 1242 1243 rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ); 1244 if ( tool_check_abandon( ld, msgid ) ) { 1245 return LDAP_CANCELLED; 1246 } 1247 1248 if ( rc == -1 ) { 1249 ldap_get_option( ld, LDAP_OPT_RESULT_CODE, &rc ); 1250 tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL ); 1251 return rc; 1252 } 1253 1254 if ( rc != 0 ) { 1255 break; 1256 } 1257 } 1258 1259 msgtype = ldap_msgtype( res ); 1260 1261 rc = ldap_parse_result( ld, res, &err, &matched, &text, &refs, &ctrls, 1 ); 1262 if ( rc == LDAP_SUCCESS ) rc = err; 1263 1264 #ifdef LDAP_X_TXN 1265 if ( rc == LDAP_X_TXN_SPECIFY_OKAY ) { 1266 rc = LDAP_SUCCESS; 1267 } else 1268 #endif 1269 if ( rc != LDAP_SUCCESS ) { 1270 tool_perror( res2str( op ), rc, NULL, matched, text, refs ); 1271 } else if ( msgtype != op ) { 1272 fprintf( stderr, "%s: msgtype: expected %d got %d\n", 1273 res2str( op ), op, msgtype ); 1274 rc = LDAP_OTHER; 1275 } 1276 1277 if ( text ) ldap_memfree( text ); 1278 if ( matched ) ldap_memfree( matched ); 1279 if ( refs ) ber_memvfree( (void **)refs ); 1280 1281 if ( ctrls ) { 1282 tool_print_ctrls( ld, ctrls ); 1283 ldap_controls_free( ctrls ); 1284 } 1285 1286 return rc; 1287 } 1288