001/* 002 * Copyright 2008-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.util; 022 023 024 025import java.io.OutputStream; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.LinkedHashSet; 029import java.util.List; 030import java.util.Set; 031import java.util.concurrent.atomic.AtomicReference; 032import javax.net.SocketFactory; 033import javax.net.ssl.KeyManager; 034import javax.net.ssl.SSLSocketFactory; 035import javax.net.ssl.TrustManager; 036 037import com.unboundid.ldap.sdk.AggregatePostConnectProcessor; 038import com.unboundid.ldap.sdk.BindRequest; 039import com.unboundid.ldap.sdk.Control; 040import com.unboundid.ldap.sdk.EXTERNALBindRequest; 041import com.unboundid.ldap.sdk.ExtendedResult; 042import com.unboundid.ldap.sdk.LDAPConnection; 043import com.unboundid.ldap.sdk.LDAPConnectionOptions; 044import com.unboundid.ldap.sdk.LDAPConnectionPool; 045import com.unboundid.ldap.sdk.LDAPConnectionPoolHealthCheck; 046import com.unboundid.ldap.sdk.LDAPException; 047import com.unboundid.ldap.sdk.PostConnectProcessor; 048import com.unboundid.ldap.sdk.ResultCode; 049import com.unboundid.ldap.sdk.RoundRobinServerSet; 050import com.unboundid.ldap.sdk.ServerSet; 051import com.unboundid.ldap.sdk.SimpleBindRequest; 052import com.unboundid.ldap.sdk.SingleServerSet; 053import com.unboundid.ldap.sdk.StartTLSPostConnectProcessor; 054import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest; 055import com.unboundid.util.args.ArgumentException; 056import com.unboundid.util.args.ArgumentParser; 057import com.unboundid.util.args.BooleanArgument; 058import com.unboundid.util.args.DNArgument; 059import com.unboundid.util.args.FileArgument; 060import com.unboundid.util.args.IntegerArgument; 061import com.unboundid.util.args.StringArgument; 062import com.unboundid.util.ssl.KeyStoreKeyManager; 063import com.unboundid.util.ssl.PromptTrustManager; 064import com.unboundid.util.ssl.SSLUtil; 065import com.unboundid.util.ssl.TrustAllTrustManager; 066import com.unboundid.util.ssl.TrustStoreTrustManager; 067 068import static com.unboundid.util.Debug.*; 069import static com.unboundid.util.StaticUtils.*; 070import static com.unboundid.util.UtilityMessages.*; 071 072 073 074/** 075 * This class provides a basis for developing command-line tools that 076 * communicate with an LDAP directory server. It provides a common set of 077 * options for connecting and authenticating to a directory server, and then 078 * provides a mechanism for obtaining connections and connection pools to use 079 * when communicating with that server. 080 * <BR><BR> 081 * The arguments that this class supports include: 082 * <UL> 083 * <LI>"-h {address}" or "--hostname {address}" -- Specifies the address of 084 * the directory server. If this isn't specified, then a default of 085 * "localhost" will be used.</LI> 086 * <LI>"-p {port}" or "--port {port}" -- Specifies the port number of the 087 * directory server. If this isn't specified, then a default port of 389 088 * will be used.</LI> 089 * <LI>"-D {bindDN}" or "--bindDN {bindDN}" -- Specifies the DN to use to bind 090 * to the directory server using simple authentication. If this isn't 091 * specified, then simple authentication will not be performed.</LI> 092 * <LI>"-w {password}" or "--bindPassword {password}" -- Specifies the 093 * password to use when binding with simple authentication or a 094 * password-based SASL mechanism.</LI> 095 * <LI>"-j {path}" or "--bindPasswordFile {path}" -- Specifies the path to the 096 * file containing the password to use when binding with simple 097 * authentication or a password-based SASL mechanism.</LI> 098 * <LI>"--promptForBindPassword" -- Indicates that the tool should 099 * interactively prompt the user for the bind password.</LI> 100 * <LI>"-Z" or "--useSSL" -- Indicates that the communication with the server 101 * should be secured using SSL.</LI> 102 * <LI>"-q" or "--useStartTLS" -- Indicates that the communication with the 103 * server should be secured using StartTLS.</LI> 104 * <LI>"-X" or "--trustAll" -- Indicates that the client should trust any 105 * certificate that the server presents to it.</LI> 106 * <LI>"-K {path}" or "--keyStorePath {path}" -- Specifies the path to the 107 * key store to use to obtain client certificates.</LI> 108 * <LI>"-W {password}" or "--keyStorePassword {password}" -- Specifies the 109 * password to use to access the contents of the key store.</LI> 110 * <LI>"-u {path}" or "--keyStorePasswordFile {path}" -- Specifies the path to 111 * the file containing the password to use to access the contents of the 112 * key store.</LI> 113 * <LI>"--promptForKeyStorePassword" -- Indicates that the tool should 114 * interactively prompt the user for the key store password.</LI> 115 * <LI>"--keyStoreFormat {format}" -- Specifies the format to use for the key 116 * store file.</LI> 117 * <LI>"-P {path}" or "--trustStorePath {path}" -- Specifies the path to the 118 * trust store to use when determining whether to trust server 119 * certificates.</LI> 120 * <LI>"-T {password}" or "--trustStorePassword {password}" -- Specifies the 121 * password to use to access the contents of the trust store.</LI> 122 * <LI>"-U {path}" or "--trustStorePasswordFile {path}" -- Specifies the path 123 * to the file containing the password to use to access the contents of 124 * the trust store.</LI> 125 * <LI>"--promptForTrustStorePassword" -- Indicates that the tool should 126 * interactively prompt the user for the trust store password.</LI> 127 * <LI>"--trustStoreFormat {format}" -- Specifies the format to use for the 128 * trust store file.</LI> 129 * <LI>"-N {nickname}" or "--certNickname {nickname}" -- Specifies the 130 * nickname of the client certificate to use when performing SSL client 131 * authentication.</LI> 132 * <LI>"-o {name=value}" or "--saslOption {name=value}" -- Specifies a SASL 133 * option to use when performing SASL authentication.</LI> 134 * </UL> 135 * If SASL authentication is to be used, then a "mech" SASL option must be 136 * provided to specify the name of the SASL mechanism to use (e.g., 137 * "--saslOption mech=EXTERNAL" indicates that the EXTERNAL mechanism should be 138 * used). Depending on the SASL mechanism, additional SASL options may be 139 * required or optional. They include: 140 * <UL> 141 * <LI> 142 * mech=ANONYMOUS 143 * <UL> 144 * <LI>Required SASL options: </LI> 145 * <LI>Optional SASL options: trace</LI> 146 * </UL> 147 * </LI> 148 * <LI> 149 * mech=CRAM-MD5 150 * <UL> 151 * <LI>Required SASL options: authID</LI> 152 * <LI>Optional SASL options: </LI> 153 * </UL> 154 * </LI> 155 * <LI> 156 * mech=DIGEST-MD5 157 * <UL> 158 * <LI>Required SASL options: authID</LI> 159 * <LI>Optional SASL options: authzID, realm</LI> 160 * </UL> 161 * </LI> 162 * <LI> 163 * mech=EXTERNAL 164 * <UL> 165 * <LI>Required SASL options: </LI> 166 * <LI>Optional SASL options: </LI> 167 * </UL> 168 * </LI> 169 * <LI> 170 * mech=GSSAPI 171 * <UL> 172 * <LI>Required SASL options: authID</LI> 173 * <LI>Optional SASL options: authzID, configFile, debug, protocol, 174 * realm, kdcAddress, useTicketCache, requireCache, 175 * renewTGT, ticketCachePath</LI> 176 * </UL> 177 * </LI> 178 * <LI> 179 * mech=PLAIN 180 * <UL> 181 * <LI>Required SASL options: authID</LI> 182 * <LI>Optional SASL options: authzID</LI> 183 * </UL> 184 * </LI> 185 * </UL> 186 * <BR><BR> 187 * Note that in general, methods in this class are not threadsafe. However, the 188 * {@link #getConnection()} and {@link #getConnectionPool(int,int)} methods may 189 * be invoked concurrently by multiple threads accessing the same instance only 190 * while that instance is in the process of invoking the 191 * {@link #doToolProcessing()} method. 192 */ 193@Extensible() 194@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_NOT_THREADSAFE) 195public abstract class LDAPCommandLineTool 196 extends CommandLineTool 197{ 198 // Arguments used to communicate with an LDAP directory server. 199 private BooleanArgument helpSASL = null; 200 private BooleanArgument promptForBindPassword = null; 201 private BooleanArgument promptForKeyStorePassword = null; 202 private BooleanArgument promptForTrustStorePassword = null; 203 private BooleanArgument trustAll = null; 204 private BooleanArgument useSASLExternal = null; 205 private BooleanArgument useSSL = null; 206 private BooleanArgument useStartTLS = null; 207 private DNArgument bindDN = null; 208 private FileArgument bindPasswordFile = null; 209 private FileArgument keyStorePasswordFile = null; 210 private FileArgument trustStorePasswordFile = null; 211 private IntegerArgument port = null; 212 private StringArgument bindPassword = null; 213 private StringArgument certificateNickname = null; 214 private StringArgument host = null; 215 private StringArgument keyStoreFormat = null; 216 private StringArgument keyStorePath = null; 217 private StringArgument keyStorePassword = null; 218 private StringArgument saslOption = null; 219 private StringArgument trustStoreFormat = null; 220 private StringArgument trustStorePath = null; 221 private StringArgument trustStorePassword = null; 222 223 // Variables used when creating and authenticating connections. 224 private BindRequest bindRequest = null; 225 private ServerSet serverSet = null; 226 private SSLSocketFactory startTLSSocketFactory = null; 227 228 // The prompt trust manager that will be shared by all connections created 229 // for which it is appropriate. This will allow them to benefit from the 230 // common cache. 231 private final AtomicReference<PromptTrustManager> promptTrustManager; 232 233 234 235 /** 236 * Creates a new instance of this LDAP-enabled command-line tool with the 237 * provided information. 238 * 239 * @param outStream The output stream to use for standard output. It may be 240 * {@code System.out} for the JVM's default standard output 241 * stream, {@code null} if no output should be generated, 242 * or a custom output stream if the output should be sent 243 * to an alternate location. 244 * @param errStream The output stream to use for standard error. It may be 245 * {@code System.err} for the JVM's default standard error 246 * stream, {@code null} if no output should be generated, 247 * or a custom output stream if the output should be sent 248 * to an alternate location. 249 */ 250 public LDAPCommandLineTool(final OutputStream outStream, 251 final OutputStream errStream) 252 { 253 super(outStream, errStream); 254 255 promptTrustManager = new AtomicReference<PromptTrustManager>(); 256 } 257 258 259 260 /** 261 * Retrieves a set containing the long identifiers used for LDAP-related 262 * arguments injected by this class. 263 * 264 * @param tool The tool to use to help make the determination. 265 * 266 * @return A set containing the long identifiers used for LDAP-related 267 * arguments injected by this class. 268 */ 269 static Set<String> getLongLDAPArgumentIdentifiers( 270 final LDAPCommandLineTool tool) 271 { 272 final LinkedHashSet<String> ids = new LinkedHashSet<String>(21); 273 274 ids.add("hostname"); 275 ids.add("port"); 276 277 if (tool.supportsAuthentication()) 278 { 279 ids.add("bindDN"); 280 ids.add("bindPassword"); 281 ids.add("bindPasswordFile"); 282 ids.add("promptForBindPassword"); 283 } 284 285 ids.add("useSSL"); 286 ids.add("useStartTLS"); 287 ids.add("trustAll"); 288 ids.add("keyStorePath"); 289 ids.add("keyStorePassword"); 290 ids.add("keyStorePasswordFile"); 291 ids.add("promptForKeyStorePassword"); 292 ids.add("keyStoreFormat"); 293 ids.add("trustStorePath"); 294 ids.add("trustStorePassword"); 295 ids.add("trustStorePasswordFile"); 296 ids.add("promptForTrustStorePassword"); 297 ids.add("trustStoreFormat"); 298 ids.add("certNickname"); 299 300 if (tool.supportsAuthentication()) 301 { 302 ids.add("saslOption"); 303 ids.add("useSASLExternal"); 304 ids.add("helpSASL"); 305 } 306 307 return Collections.unmodifiableSet(ids); 308 } 309 310 311 312 /** 313 * {@inheritDoc} 314 */ 315 @Override() 316 public final void addToolArguments(final ArgumentParser parser) 317 throws ArgumentException 318 { 319 final String argumentGroup; 320 final boolean supportsAuthentication = supportsAuthentication(); 321 if (supportsAuthentication) 322 { 323 argumentGroup = INFO_LDAP_TOOL_ARG_GROUP_CONNECT_AND_AUTH.get(); 324 } 325 else 326 { 327 argumentGroup = INFO_LDAP_TOOL_ARG_GROUP_CONNECT.get(); 328 } 329 330 331 host = new StringArgument('h', "hostname", true, 332 (supportsMultipleServers() ? 0 : 1), 333 INFO_LDAP_TOOL_PLACEHOLDER_HOST.get(), 334 INFO_LDAP_TOOL_DESCRIPTION_HOST.get(), "localhost"); 335 host.setArgumentGroupName(argumentGroup); 336 parser.addArgument(host); 337 338 port = new IntegerArgument('p', "port", true, 339 (supportsMultipleServers() ? 0 : 1), 340 INFO_LDAP_TOOL_PLACEHOLDER_PORT.get(), 341 INFO_LDAP_TOOL_DESCRIPTION_PORT.get(), 1, 65535, 389); 342 port.setArgumentGroupName(argumentGroup); 343 parser.addArgument(port); 344 345 if (supportsAuthentication) 346 { 347 bindDN = new DNArgument('D', "bindDN", false, 1, 348 INFO_LDAP_TOOL_PLACEHOLDER_DN.get(), 349 INFO_LDAP_TOOL_DESCRIPTION_BIND_DN.get()); 350 bindDN.setArgumentGroupName(argumentGroup); 351 if (includeAlternateLongIdentifiers()) 352 { 353 bindDN.addLongIdentifier("bind-dn"); 354 } 355 parser.addArgument(bindDN); 356 357 bindPassword = new StringArgument('w', "bindPassword", false, 1, 358 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 359 INFO_LDAP_TOOL_DESCRIPTION_BIND_PW.get()); 360 bindPassword.setSensitive(true); 361 bindPassword.setArgumentGroupName(argumentGroup); 362 if (includeAlternateLongIdentifiers()) 363 { 364 bindPassword.addLongIdentifier("bind-password"); 365 } 366 parser.addArgument(bindPassword); 367 368 bindPasswordFile = new FileArgument('j', "bindPasswordFile", false, 1, 369 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 370 INFO_LDAP_TOOL_DESCRIPTION_BIND_PW_FILE.get(), true, true, true, 371 false); 372 bindPasswordFile.setArgumentGroupName(argumentGroup); 373 if (includeAlternateLongIdentifiers()) 374 { 375 bindPasswordFile.addLongIdentifier("bind-password-file"); 376 } 377 parser.addArgument(bindPasswordFile); 378 379 promptForBindPassword = new BooleanArgument(null, "promptForBindPassword", 380 1, INFO_LDAP_TOOL_DESCRIPTION_BIND_PW_PROMPT.get()); 381 promptForBindPassword.setArgumentGroupName(argumentGroup); 382 if (includeAlternateLongIdentifiers()) 383 { 384 promptForBindPassword.addLongIdentifier("prompt-for-bind-password"); 385 } 386 parser.addArgument(promptForBindPassword); 387 } 388 389 useSSL = new BooleanArgument('Z', "useSSL", 1, 390 INFO_LDAP_TOOL_DESCRIPTION_USE_SSL.get()); 391 useSSL.setArgumentGroupName(argumentGroup); 392 if (includeAlternateLongIdentifiers()) 393 { 394 useSSL.addLongIdentifier("use-ssl"); 395 } 396 parser.addArgument(useSSL); 397 398 useStartTLS = new BooleanArgument('q', "useStartTLS", 1, 399 INFO_LDAP_TOOL_DESCRIPTION_USE_START_TLS.get()); 400 useStartTLS.setArgumentGroupName(argumentGroup); 401 if (includeAlternateLongIdentifiers()) 402 { 403 useStartTLS.addLongIdentifier("use-starttls"); 404 useStartTLS.addLongIdentifier("use-start-tls"); 405 } 406 parser.addArgument(useStartTLS); 407 408 trustAll = new BooleanArgument('X', "trustAll", 1, 409 INFO_LDAP_TOOL_DESCRIPTION_TRUST_ALL.get()); 410 trustAll.setArgumentGroupName(argumentGroup); 411 if (includeAlternateLongIdentifiers()) 412 { 413 trustAll.addLongIdentifier("trustAllCertificates"); 414 trustAll.addLongIdentifier("trust-all"); 415 trustAll.addLongIdentifier("trust-all-certificates"); 416 } 417 parser.addArgument(trustAll); 418 419 keyStorePath = new StringArgument('K', "keyStorePath", false, 1, 420 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 421 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PATH.get()); 422 keyStorePath.setArgumentGroupName(argumentGroup); 423 if (includeAlternateLongIdentifiers()) 424 { 425 keyStorePath.addLongIdentifier("key-store-path"); 426 } 427 parser.addArgument(keyStorePath); 428 429 keyStorePassword = new StringArgument('W', "keyStorePassword", false, 1, 430 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 431 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD.get()); 432 keyStorePassword.setSensitive(true); 433 keyStorePassword.setArgumentGroupName(argumentGroup); 434 if (includeAlternateLongIdentifiers()) 435 { 436 keyStorePassword.addLongIdentifier("keyStorePIN"); 437 keyStorePassword.addLongIdentifier("key-store-password"); 438 keyStorePassword.addLongIdentifier("key-store-pin"); 439 } 440 parser.addArgument(keyStorePassword); 441 442 keyStorePasswordFile = new FileArgument('u', "keyStorePasswordFile", false, 443 1, INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 444 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD_FILE.get()); 445 keyStorePasswordFile.setArgumentGroupName(argumentGroup); 446 if (includeAlternateLongIdentifiers()) 447 { 448 keyStorePasswordFile.addLongIdentifier("keyStorePINFile"); 449 keyStorePasswordFile.addLongIdentifier("key-store-password-file"); 450 keyStorePasswordFile.addLongIdentifier("key-store-pin-file"); 451 } 452 parser.addArgument(keyStorePasswordFile); 453 454 promptForKeyStorePassword = new BooleanArgument(null, 455 "promptForKeyStorePassword", 1, 456 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD_PROMPT.get()); 457 promptForKeyStorePassword.setArgumentGroupName(argumentGroup); 458 if (includeAlternateLongIdentifiers()) 459 { 460 promptForKeyStorePassword.addLongIdentifier("promptForKeyStorePIN"); 461 promptForKeyStorePassword.addLongIdentifier( 462 "prompt-for-key-store-password"); 463 promptForKeyStorePassword.addLongIdentifier("prompt-for-key-store-pin"); 464 } 465 parser.addArgument(promptForKeyStorePassword); 466 467 keyStoreFormat = new StringArgument(null, "keyStoreFormat", false, 1, 468 INFO_LDAP_TOOL_PLACEHOLDER_FORMAT.get(), 469 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_FORMAT.get()); 470 keyStoreFormat.setArgumentGroupName(argumentGroup); 471 if (includeAlternateLongIdentifiers()) 472 { 473 keyStoreFormat.addLongIdentifier("keyStoreType"); 474 keyStoreFormat.addLongIdentifier("key-store-format"); 475 keyStoreFormat.addLongIdentifier("key-store-type"); 476 } 477 parser.addArgument(keyStoreFormat); 478 479 trustStorePath = new StringArgument('P', "trustStorePath", false, 1, 480 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 481 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PATH.get()); 482 trustStorePath.setArgumentGroupName(argumentGroup); 483 if (includeAlternateLongIdentifiers()) 484 { 485 trustStorePath.addLongIdentifier("trust-store-path"); 486 } 487 parser.addArgument(trustStorePath); 488 489 trustStorePassword = new StringArgument('T', "trustStorePassword", false, 1, 490 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(), 491 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD.get()); 492 trustStorePassword.setSensitive(true); 493 trustStorePassword.setArgumentGroupName(argumentGroup); 494 if (includeAlternateLongIdentifiers()) 495 { 496 trustStorePassword.addLongIdentifier("trustStorePIN"); 497 trustStorePassword.addLongIdentifier("trust-store-password"); 498 trustStorePassword.addLongIdentifier("trust-store-pin"); 499 } 500 parser.addArgument(trustStorePassword); 501 502 trustStorePasswordFile = new FileArgument('U', "trustStorePasswordFile", 503 false, 1, INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(), 504 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD_FILE.get()); 505 trustStorePasswordFile.setArgumentGroupName(argumentGroup); 506 if (includeAlternateLongIdentifiers()) 507 { 508 trustStorePasswordFile.addLongIdentifier("trustStorePINFile"); 509 trustStorePasswordFile.addLongIdentifier("trust-store-password-file"); 510 trustStorePasswordFile.addLongIdentifier("trust-store-pin-file"); 511 } 512 parser.addArgument(trustStorePasswordFile); 513 514 promptForTrustStorePassword = new BooleanArgument(null, 515 "promptForTrustStorePassword", 1, 516 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD_PROMPT.get()); 517 promptForTrustStorePassword.setArgumentGroupName(argumentGroup); 518 if (includeAlternateLongIdentifiers()) 519 { 520 promptForTrustStorePassword.addLongIdentifier("promptForTrustStorePIN"); 521 promptForTrustStorePassword.addLongIdentifier( 522 "prompt-for-trust-store-password"); 523 promptForTrustStorePassword.addLongIdentifier( 524 "prompt-for-trust-store-pin"); 525 } 526 parser.addArgument(promptForTrustStorePassword); 527 528 trustStoreFormat = new StringArgument(null, "trustStoreFormat", false, 1, 529 INFO_LDAP_TOOL_PLACEHOLDER_FORMAT.get(), 530 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_FORMAT.get()); 531 trustStoreFormat.setArgumentGroupName(argumentGroup); 532 if (includeAlternateLongIdentifiers()) 533 { 534 trustStoreFormat.addLongIdentifier("trustStoreType"); 535 trustStoreFormat.addLongIdentifier("trust-store-format"); 536 trustStoreFormat.addLongIdentifier("trust-store-type"); 537 } 538 parser.addArgument(trustStoreFormat); 539 540 certificateNickname = new StringArgument('N', "certNickname", false, 1, 541 INFO_LDAP_TOOL_PLACEHOLDER_CERT_NICKNAME.get(), 542 INFO_LDAP_TOOL_DESCRIPTION_CERT_NICKNAME.get()); 543 certificateNickname.setArgumentGroupName(argumentGroup); 544 if (includeAlternateLongIdentifiers()) 545 { 546 certificateNickname.addLongIdentifier("certificateNickname"); 547 certificateNickname.addLongIdentifier("cert-nickname"); 548 certificateNickname.addLongIdentifier("certificate-nickname"); 549 } 550 parser.addArgument(certificateNickname); 551 552 if (supportsAuthentication) 553 { 554 saslOption = new StringArgument('o', "saslOption", false, 0, 555 INFO_LDAP_TOOL_PLACEHOLDER_SASL_OPTION.get(), 556 INFO_LDAP_TOOL_DESCRIPTION_SASL_OPTION.get()); 557 saslOption.setArgumentGroupName(argumentGroup); 558 if (includeAlternateLongIdentifiers()) 559 { 560 saslOption.addLongIdentifier("sasl-option"); 561 } 562 parser.addArgument(saslOption); 563 564 useSASLExternal = new BooleanArgument(null, "useSASLExternal", 1, 565 INFO_LDAP_TOOL_DESCRIPTION_USE_SASL_EXTERNAL.get()); 566 useSASLExternal.setArgumentGroupName(argumentGroup); 567 if (includeAlternateLongIdentifiers()) 568 { 569 useSASLExternal.addLongIdentifier("use-sasl-external"); 570 } 571 parser.addArgument(useSASLExternal); 572 573 if (supportsSASLHelp()) 574 { 575 helpSASL = new BooleanArgument(null, "helpSASL", 576 INFO_LDAP_TOOL_DESCRIPTION_HELP_SASL.get()); 577 helpSASL.setArgumentGroupName(argumentGroup); 578 if (includeAlternateLongIdentifiers()) 579 { 580 helpSASL.addLongIdentifier("help-sasl"); 581 } 582 helpSASL.setUsageArgument(true); 583 parser.addArgument(helpSASL); 584 setHelpSASLArgument(helpSASL); 585 } 586 } 587 588 589 // Both useSSL and useStartTLS cannot be used together. 590 parser.addExclusiveArgumentSet(useSSL, useStartTLS); 591 592 // Only one option may be used for specifying the key store password. 593 parser.addExclusiveArgumentSet(keyStorePassword, keyStorePasswordFile, 594 promptForKeyStorePassword); 595 596 // Only one option may be used for specifying the trust store password. 597 parser.addExclusiveArgumentSet(trustStorePassword, trustStorePasswordFile, 598 promptForTrustStorePassword); 599 600 // It doesn't make sense to provide a trust store path if any server 601 // certificate should be trusted. 602 parser.addExclusiveArgumentSet(trustAll, trustStorePath); 603 604 // If a key store password is provided, then a key store path must have also 605 // been provided. 606 parser.addDependentArgumentSet(keyStorePassword, keyStorePath); 607 parser.addDependentArgumentSet(keyStorePasswordFile, keyStorePath); 608 parser.addDependentArgumentSet(promptForKeyStorePassword, keyStorePath); 609 610 // If a trust store password is provided, then a trust store path must have 611 // also been provided. 612 parser.addDependentArgumentSet(trustStorePassword, trustStorePath); 613 parser.addDependentArgumentSet(trustStorePasswordFile, trustStorePath); 614 parser.addDependentArgumentSet(promptForTrustStorePassword, trustStorePath); 615 616 // If a key or trust store path is provided, then the tool must either use 617 // SSL or StartTLS. 618 parser.addDependentArgumentSet(keyStorePath, useSSL, useStartTLS); 619 parser.addDependentArgumentSet(trustStorePath, useSSL, useStartTLS); 620 621 // If the tool should trust all server certificates, then the tool must 622 // either use SSL or StartTLS. 623 parser.addDependentArgumentSet(trustAll, useSSL, useStartTLS); 624 625 if (supportsAuthentication) 626 { 627 // If a bind DN was provided, then a bind password must have also been 628 // provided unless defaultToPromptForBindPassword returns true. 629 if (! defaultToPromptForBindPassword()) 630 { 631 parser.addDependentArgumentSet(bindDN, bindPassword, bindPasswordFile, 632 promptForBindPassword); 633 } 634 635 // The bindDN, saslOption, and useSASLExternal arguments are all mutually 636 // exclusive. 637 parser.addExclusiveArgumentSet(bindDN, saslOption, useSASLExternal); 638 639 // Only one option may be used for specifying the bind password. 640 parser.addExclusiveArgumentSet(bindPassword, bindPasswordFile, 641 promptForBindPassword); 642 643 // If a bind password was provided, then the a bind DN or SASL option 644 // must have also been provided. 645 parser.addDependentArgumentSet(bindPassword, bindDN, saslOption); 646 parser.addDependentArgumentSet(bindPasswordFile, bindDN, saslOption); 647 parser.addDependentArgumentSet(promptForBindPassword, bindDN, saslOption); 648 } 649 650 addNonLDAPArguments(parser); 651 } 652 653 654 655 /** 656 * Adds the arguments needed by this command-line tool to the provided 657 * argument parser which are not related to connecting or authenticating to 658 * the directory server. 659 * 660 * @param parser The argument parser to which the arguments should be added. 661 * 662 * @throws ArgumentException If a problem occurs while adding the arguments. 663 */ 664 public abstract void addNonLDAPArguments(final ArgumentParser parser) 665 throws ArgumentException; 666 667 668 669 /** 670 * {@inheritDoc} 671 */ 672 @Override() 673 public final void doExtendedArgumentValidation() 674 throws ArgumentException 675 { 676 // If more than one hostname or port number was provided, then make sure 677 // that the same number of values were provided for each. 678 if ((host.getValues().size() > 1) || (port.getValues().size() > 1)) 679 { 680 if (host.getValues().size() != port.getValues().size()) 681 { 682 throw new ArgumentException( 683 ERR_LDAP_TOOL_HOST_PORT_COUNT_MISMATCH.get( 684 host.getLongIdentifier(), port.getLongIdentifier())); 685 } 686 } 687 688 689 doExtendedNonLDAPArgumentValidation(); 690 } 691 692 693 694 /** 695 * Indicates whether this tool should provide the arguments that allow it to 696 * bind via simple or SASL authentication. 697 * 698 * @return {@code true} if this tool should provide the arguments that allow 699 * it to bind via simple or SASL authentication, or {@code false} if 700 * not. 701 */ 702 protected boolean supportsAuthentication() 703 { 704 return true; 705 } 706 707 708 709 /** 710 * Indicates whether this tool should default to interactively prompting for 711 * the bind password if a password is required but no argument was provided 712 * to indicate how to get the password. 713 * 714 * @return {@code true} if this tool should default to interactively 715 * prompting for the bind password, or {@code false} if not. 716 */ 717 protected boolean defaultToPromptForBindPassword() 718 { 719 return false; 720 } 721 722 723 724 /** 725 * Indicates whether this tool should provide a "--help-sasl" argument that 726 * provides information about the supported SASL mechanisms and their 727 * associated properties. 728 * 729 * @return {@code true} if this tool should provide a "--help-sasl" argument, 730 * or {@code false} if not. 731 */ 732 protected boolean supportsSASLHelp() 733 { 734 return true; 735 } 736 737 738 739 /** 740 * Indicates whether the LDAP-specific arguments should include alternate 741 * versions of all long identifiers that consist of multiple words so that 742 * they are available in both camelCase and dash-separated versions. 743 * 744 * @return {@code true} if this tool should provide multiple versions of 745 * long identifiers for LDAP-specific arguments, or {@code false} if 746 * not. 747 */ 748 protected boolean includeAlternateLongIdentifiers() 749 { 750 return false; 751 } 752 753 754 755 /** 756 * Retrieves a set of controls that should be included in any bind request 757 * generated by this tool. 758 * 759 * @return A set of controls that should be included in any bind request 760 * generated by this tool. It may be {@code null} or empty if no 761 * controls should be included in the bind request. 762 */ 763 protected List<Control> getBindControls() 764 { 765 return null; 766 } 767 768 769 770 /** 771 * Indicates whether this tool supports creating connections to multiple 772 * servers. If it is to support multiple servers, then the "--hostname" and 773 * "--port" arguments will be allowed to be provided multiple times, and 774 * will be required to be provided the same number of times. The same type of 775 * communication security and bind credentials will be used for all servers. 776 * 777 * @return {@code true} if this tool supports creating connections to 778 * multiple servers, or {@code false} if not. 779 */ 780 protected boolean supportsMultipleServers() 781 { 782 return false; 783 } 784 785 786 787 /** 788 * Performs any necessary processing that should be done to ensure that the 789 * provided set of command-line arguments were valid. This method will be 790 * called after the basic argument parsing has been performed and after all 791 * LDAP-specific argument validation has been processed, and immediately 792 * before the {@link CommandLineTool#doToolProcessing} method is invoked. 793 * 794 * @throws ArgumentException If there was a problem with the command-line 795 * arguments provided to this program. 796 */ 797 public void doExtendedNonLDAPArgumentValidation() 798 throws ArgumentException 799 { 800 // No processing will be performed by default. 801 } 802 803 804 805 /** 806 * Retrieves the connection options that should be used for connections that 807 * are created with this command line tool. Subclasses may override this 808 * method to use a custom set of connection options. 809 * 810 * @return The connection options that should be used for connections that 811 * are created with this command line tool. 812 */ 813 public LDAPConnectionOptions getConnectionOptions() 814 { 815 return new LDAPConnectionOptions(); 816 } 817 818 819 820 /** 821 * Retrieves a connection that may be used to communicate with the target 822 * directory server. 823 * <BR><BR> 824 * Note that this method is threadsafe and may be invoked by multiple threads 825 * accessing the same instance only while that instance is in the process of 826 * invoking the {@link #doToolProcessing} method. 827 * 828 * @return A connection that may be used to communicate with the target 829 * directory server. 830 * 831 * @throws LDAPException If a problem occurs while creating the connection. 832 */ 833 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 834 public final LDAPConnection getConnection() 835 throws LDAPException 836 { 837 final LDAPConnection connection = getUnauthenticatedConnection(); 838 839 try 840 { 841 if (bindRequest != null) 842 { 843 connection.bind(bindRequest); 844 } 845 } 846 catch (LDAPException le) 847 { 848 debugException(le); 849 connection.close(); 850 throw le; 851 } 852 853 return connection; 854 } 855 856 857 858 /** 859 * Retrieves an unauthenticated connection that may be used to communicate 860 * with the target directory server. 861 * <BR><BR> 862 * Note that this method is threadsafe and may be invoked by multiple threads 863 * accessing the same instance only while that instance is in the process of 864 * invoking the {@link #doToolProcessing} method. 865 * 866 * @return An unauthenticated connection that may be used to communicate with 867 * the target directory server. 868 * 869 * @throws LDAPException If a problem occurs while creating the connection. 870 */ 871 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 872 public final LDAPConnection getUnauthenticatedConnection() 873 throws LDAPException 874 { 875 if (serverSet == null) 876 { 877 serverSet = createServerSet(); 878 bindRequest = createBindRequest(); 879 } 880 881 final LDAPConnection connection = serverSet.getConnection(); 882 883 if (useStartTLS.isPresent()) 884 { 885 try 886 { 887 final ExtendedResult extendedResult = 888 connection.processExtendedOperation( 889 new StartTLSExtendedRequest(startTLSSocketFactory)); 890 if (! extendedResult.getResultCode().equals(ResultCode.SUCCESS)) 891 { 892 throw new LDAPException(extendedResult.getResultCode(), 893 ERR_LDAP_TOOL_START_TLS_FAILED.get( 894 extendedResult.getDiagnosticMessage())); 895 } 896 } 897 catch (LDAPException le) 898 { 899 debugException(le); 900 connection.close(); 901 throw le; 902 } 903 } 904 905 return connection; 906 } 907 908 909 910 /** 911 * Retrieves a connection pool that may be used to communicate with the target 912 * directory server. 913 * <BR><BR> 914 * Note that this method is threadsafe and may be invoked by multiple threads 915 * accessing the same instance only while that instance is in the process of 916 * invoking the {@link #doToolProcessing} method. 917 * 918 * @param initialConnections The number of connections that should be 919 * initially established in the pool. 920 * @param maxConnections The maximum number of connections to maintain 921 * in the pool. 922 * 923 * @return A connection that may be used to communicate with the target 924 * directory server. 925 * 926 * @throws LDAPException If a problem occurs while creating the connection 927 * pool. 928 */ 929 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 930 public final LDAPConnectionPool getConnectionPool( 931 final int initialConnections, 932 final int maxConnections) 933 throws LDAPException 934 { 935 return getConnectionPool(initialConnections, maxConnections, 1, null, null, 936 true, null); 937 } 938 939 940 941 /** 942 * Retrieves a connection pool that may be used to communicate with the target 943 * directory server. 944 * <BR><BR> 945 * Note that this method is threadsafe and may be invoked by multiple threads 946 * accessing the same instance only while that instance is in the process of 947 * invoking the {@link #doToolProcessing} method. 948 * 949 * @param initialConnections The number of connections that should be 950 * initially established in the pool. 951 * @param maxConnections The maximum number of connections to 952 * maintain in the pool. 953 * @param initialConnectThreads The number of concurrent threads to use to 954 * establish the initial set of connections. 955 * A value greater than one indicates that 956 * the attempt to establish connections 957 * should be parallelized. 958 * @param beforeStartTLSProcessor An optional post-connect processor that 959 * should be used for the connection pool and 960 * should be invoked before any StartTLS 961 * post-connect processor that may be needed 962 * based on the selected arguments. It may 963 * be {@code null} if no such post-connect 964 * processor is needed. 965 * @param afterStartTLSProcessor An optional post-connect processor that 966 * should be used for the connection pool and 967 * should be invoked after any StartTLS 968 * post-connect processor that may be needed 969 * based on the selected arguments. It may 970 * be {@code null} if no such post-connect 971 * processor is needed. 972 * @param throwOnConnectFailure If an exception should be thrown if a 973 * problem is encountered while attempting to 974 * create the specified initial number of 975 * connections. If {@code true}, then the 976 * attempt to create the pool will fail if 977 * any connection cannot be established. If 978 * {@code false}, then the pool will be 979 * created but may have fewer than the 980 * initial number of connections (or possibly 981 * no connections). 982 * @param healthCheck An optional health check that should be 983 * configured for the connection pool. It 984 * may be {@code null} if the default health 985 * checking should be performed. 986 * 987 * @return A connection that may be used to communicate with the target 988 * directory server. 989 * 990 * @throws LDAPException If a problem occurs while creating the connection 991 * pool. 992 */ 993 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE) 994 public final LDAPConnectionPool getConnectionPool( 995 final int initialConnections, final int maxConnections, 996 final int initialConnectThreads, 997 final PostConnectProcessor beforeStartTLSProcessor, 998 final PostConnectProcessor afterStartTLSProcessor, 999 final boolean throwOnConnectFailure, 1000 final LDAPConnectionPoolHealthCheck healthCheck) 1001 throws LDAPException 1002 { 1003 // Create the server set and bind request, if necessary. 1004 if (serverSet == null) 1005 { 1006 serverSet = createServerSet(); 1007 bindRequest = createBindRequest(); 1008 } 1009 1010 1011 // Prepare the post-connect processor for the pool. 1012 final ArrayList<PostConnectProcessor> pcpList = 1013 new ArrayList<PostConnectProcessor>(3); 1014 if (beforeStartTLSProcessor != null) 1015 { 1016 pcpList.add(beforeStartTLSProcessor); 1017 } 1018 1019 if (useStartTLS.isPresent()) 1020 { 1021 pcpList.add(new StartTLSPostConnectProcessor(startTLSSocketFactory)); 1022 } 1023 1024 if (afterStartTLSProcessor != null) 1025 { 1026 pcpList.add(afterStartTLSProcessor); 1027 } 1028 1029 final PostConnectProcessor postConnectProcessor; 1030 switch (pcpList.size()) 1031 { 1032 case 0: 1033 postConnectProcessor = null; 1034 break; 1035 case 1: 1036 postConnectProcessor = pcpList.get(0); 1037 break; 1038 default: 1039 postConnectProcessor = new AggregatePostConnectProcessor(pcpList); 1040 break; 1041 } 1042 1043 return new LDAPConnectionPool(serverSet, bindRequest, initialConnections, 1044 maxConnections, initialConnectThreads, postConnectProcessor, 1045 throwOnConnectFailure, healthCheck); 1046 } 1047 1048 1049 1050 /** 1051 * Creates the server set to use when creating connections or connection 1052 * pools. 1053 * 1054 * @return The server set to use when creating connections or connection 1055 * pools. 1056 * 1057 * @throws LDAPException If a problem occurs while creating the server set. 1058 */ 1059 public ServerSet createServerSet() 1060 throws LDAPException 1061 { 1062 final SSLUtil sslUtil = createSSLUtil(); 1063 1064 SocketFactory socketFactory = null; 1065 if (useSSL.isPresent()) 1066 { 1067 try 1068 { 1069 socketFactory = sslUtil.createSSLSocketFactory(); 1070 } 1071 catch (Exception e) 1072 { 1073 debugException(e); 1074 throw new LDAPException(ResultCode.LOCAL_ERROR, 1075 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_SOCKET_FACTORY.get( 1076 getExceptionMessage(e)), e); 1077 } 1078 } 1079 else if (useStartTLS.isPresent()) 1080 { 1081 try 1082 { 1083 startTLSSocketFactory = sslUtil.createSSLSocketFactory(); 1084 } 1085 catch (Exception e) 1086 { 1087 debugException(e); 1088 throw new LDAPException(ResultCode.LOCAL_ERROR, 1089 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_SOCKET_FACTORY.get( 1090 getExceptionMessage(e)), e); 1091 } 1092 } 1093 1094 if (host.getValues().size() == 1) 1095 { 1096 return new SingleServerSet(host.getValue(), port.getValue(), 1097 socketFactory, getConnectionOptions()); 1098 } 1099 else 1100 { 1101 final List<String> hostList = host.getValues(); 1102 final List<Integer> portList = port.getValues(); 1103 1104 final String[] hosts = new String[hostList.size()]; 1105 final int[] ports = new int[hosts.length]; 1106 1107 for (int i=0; i < hosts.length; i++) 1108 { 1109 hosts[i] = hostList.get(i); 1110 ports[i] = portList.get(i); 1111 } 1112 1113 return new RoundRobinServerSet(hosts, ports, socketFactory, 1114 getConnectionOptions()); 1115 } 1116 } 1117 1118 1119 1120 /** 1121 * Creates the SSLUtil instance to use for secure communication. 1122 * 1123 * @return The SSLUtil instance to use for secure communication, or 1124 * {@code null} if secure communication is not needed. 1125 * 1126 * @throws LDAPException If a problem occurs while creating the SSLUtil 1127 * instance. 1128 */ 1129 public SSLUtil createSSLUtil() 1130 throws LDAPException 1131 { 1132 return createSSLUtil(false); 1133 } 1134 1135 1136 1137 /** 1138 * Creates the SSLUtil instance to use for secure communication. 1139 * 1140 * @param force Indicates whether to create the SSLUtil object even if 1141 * neither the "--useSSL" nor the "--useStartTLS" argument was 1142 * provided. The key store and/or trust store paths must still 1143 * have been provided. This may be useful for tools that 1144 * accept SSL-based communication but do not themselves intend 1145 * to perform SSL-based communication as an LDAP client. 1146 * 1147 * @return The SSLUtil instance to use for secure communication, or 1148 * {@code null} if secure communication is not needed. 1149 * 1150 * @throws LDAPException If a problem occurs while creating the SSLUtil 1151 * instance. 1152 */ 1153 public SSLUtil createSSLUtil(final boolean force) 1154 throws LDAPException 1155 { 1156 if (force || useSSL.isPresent() || useStartTLS.isPresent()) 1157 { 1158 KeyManager keyManager = null; 1159 if (keyStorePath.isPresent()) 1160 { 1161 char[] pw = null; 1162 if (keyStorePassword.isPresent()) 1163 { 1164 pw = keyStorePassword.getValue().toCharArray(); 1165 } 1166 else if (keyStorePasswordFile.isPresent()) 1167 { 1168 try 1169 { 1170 pw = keyStorePasswordFile.getNonBlankFileLines().get(0). 1171 toCharArray(); 1172 } 1173 catch (Exception e) 1174 { 1175 debugException(e); 1176 throw new LDAPException(ResultCode.LOCAL_ERROR, 1177 ERR_LDAP_TOOL_CANNOT_READ_KEY_STORE_PASSWORD.get( 1178 getExceptionMessage(e)), e); 1179 } 1180 } 1181 else if (promptForKeyStorePassword.isPresent()) 1182 { 1183 getOut().print(INFO_LDAP_TOOL_ENTER_KEY_STORE_PASSWORD.get()); 1184 pw = StaticUtils.toUTF8String( 1185 PasswordReader.readPassword()).toCharArray(); 1186 getOut().println(); 1187 } 1188 1189 try 1190 { 1191 keyManager = new KeyStoreKeyManager(keyStorePath.getValue(), pw, 1192 keyStoreFormat.getValue(), certificateNickname.getValue()); 1193 } 1194 catch (Exception e) 1195 { 1196 debugException(e); 1197 throw new LDAPException(ResultCode.LOCAL_ERROR, 1198 ERR_LDAP_TOOL_CANNOT_CREATE_KEY_MANAGER.get( 1199 getExceptionMessage(e)), e); 1200 } 1201 } 1202 1203 TrustManager trustManager; 1204 if (trustAll.isPresent()) 1205 { 1206 trustManager = new TrustAllTrustManager(false); 1207 } 1208 else if (trustStorePath.isPresent()) 1209 { 1210 char[] pw = null; 1211 if (trustStorePassword.isPresent()) 1212 { 1213 pw = trustStorePassword.getValue().toCharArray(); 1214 } 1215 else if (trustStorePasswordFile.isPresent()) 1216 { 1217 try 1218 { 1219 pw = trustStorePasswordFile.getNonBlankFileLines().get(0). 1220 toCharArray(); 1221 } 1222 catch (Exception e) 1223 { 1224 debugException(e); 1225 throw new LDAPException(ResultCode.LOCAL_ERROR, 1226 ERR_LDAP_TOOL_CANNOT_READ_TRUST_STORE_PASSWORD.get( 1227 getExceptionMessage(e)), e); 1228 } 1229 } 1230 else if (promptForTrustStorePassword.isPresent()) 1231 { 1232 getOut().print(INFO_LDAP_TOOL_ENTER_TRUST_STORE_PASSWORD.get()); 1233 pw = StaticUtils.toUTF8String( 1234 PasswordReader.readPassword()).toCharArray(); 1235 getOut().println(); 1236 } 1237 1238 trustManager = new TrustStoreTrustManager(trustStorePath.getValue(), pw, 1239 trustStoreFormat.getValue(), true); 1240 } 1241 else 1242 { 1243 trustManager = promptTrustManager.get(); 1244 if (trustManager == null) 1245 { 1246 final PromptTrustManager m = new PromptTrustManager(); 1247 promptTrustManager.compareAndSet(null, m); 1248 trustManager = promptTrustManager.get(); 1249 } 1250 } 1251 1252 return new SSLUtil(keyManager, trustManager); 1253 } 1254 else 1255 { 1256 return null; 1257 } 1258 } 1259 1260 1261 1262 /** 1263 * Creates the bind request to use to authenticate to the server. 1264 * 1265 * @return The bind request to use to authenticate to the server, or 1266 * {@code null} if no bind should be performed. 1267 * 1268 * @throws LDAPException If a problem occurs while creating the bind 1269 * request. 1270 */ 1271 public BindRequest createBindRequest() 1272 throws LDAPException 1273 { 1274 if (! supportsAuthentication()) 1275 { 1276 return null; 1277 } 1278 1279 final Control[] bindControls; 1280 final List<Control> bindControlList = getBindControls(); 1281 if ((bindControlList == null) || bindControlList.isEmpty()) 1282 { 1283 bindControls = NO_CONTROLS; 1284 } 1285 else 1286 { 1287 bindControls = new Control[bindControlList.size()]; 1288 bindControlList.toArray(bindControls); 1289 } 1290 1291 byte[] pw; 1292 if (bindPassword.isPresent()) 1293 { 1294 pw = StaticUtils.getBytes(bindPassword.getValue()); 1295 } 1296 else if (bindPasswordFile.isPresent()) 1297 { 1298 try 1299 { 1300 pw = StaticUtils.getBytes( 1301 bindPasswordFile.getNonBlankFileLines().get(0)); 1302 } 1303 catch (Exception e) 1304 { 1305 debugException(e); 1306 throw new LDAPException(ResultCode.LOCAL_ERROR, 1307 ERR_LDAP_TOOL_CANNOT_READ_BIND_PASSWORD.get( 1308 getExceptionMessage(e)), e); 1309 } 1310 } 1311 else if (promptForBindPassword.isPresent()) 1312 { 1313 getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 1314 pw = PasswordReader.readPassword(); 1315 getOriginalOut().println(); 1316 } 1317 else 1318 { 1319 pw = null; 1320 } 1321 1322 if (saslOption.isPresent()) 1323 { 1324 final String dnStr; 1325 if (bindDN.isPresent()) 1326 { 1327 dnStr = bindDN.getValue().toString(); 1328 } 1329 else 1330 { 1331 dnStr = null; 1332 } 1333 1334 return SASLUtils.createBindRequest(dnStr, pw, 1335 defaultToPromptForBindPassword(), this, null, 1336 saslOption.getValues(), bindControls); 1337 } 1338 else if (useSASLExternal.isPresent()) 1339 { 1340 return new EXTERNALBindRequest(bindControls); 1341 } 1342 else if (bindDN.isPresent()) 1343 { 1344 if ((pw == null) && (! bindDN.getValue().isNullDN()) && 1345 defaultToPromptForBindPassword()) 1346 { 1347 getOriginalOut().print(INFO_LDAP_TOOL_ENTER_BIND_PASSWORD.get()); 1348 pw = PasswordReader.readPassword(); 1349 getOriginalOut().println(); 1350 } 1351 1352 return new SimpleBindRequest(bindDN.getValue(), pw, bindControls); 1353 } 1354 else 1355 { 1356 return null; 1357 } 1358 } 1359}