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