001 /*
002 * Copyright 2008-2011 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2011 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 */
021 package com.unboundid.util;
022
023
024
025 import java.io.OutputStream;
026 import java.util.List;
027 import java.util.concurrent.atomic.AtomicReference;
028 import javax.net.SocketFactory;
029 import javax.net.ssl.KeyManager;
030 import javax.net.ssl.SSLContext;
031 import javax.net.ssl.TrustManager;
032
033 import com.unboundid.ldap.sdk.BindRequest;
034 import com.unboundid.ldap.sdk.ExtendedResult;
035 import com.unboundid.ldap.sdk.LDAPConnection;
036 import com.unboundid.ldap.sdk.LDAPConnectionOptions;
037 import com.unboundid.ldap.sdk.LDAPConnectionPool;
038 import com.unboundid.ldap.sdk.LDAPException;
039 import com.unboundid.ldap.sdk.PostConnectProcessor;
040 import com.unboundid.ldap.sdk.ResultCode;
041 import com.unboundid.ldap.sdk.RoundRobinServerSet;
042 import com.unboundid.ldap.sdk.ServerSet;
043 import com.unboundid.ldap.sdk.SimpleBindRequest;
044 import com.unboundid.ldap.sdk.SingleServerSet;
045 import com.unboundid.ldap.sdk.StartTLSPostConnectProcessor;
046 import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
047 import com.unboundid.util.args.ArgumentException;
048 import com.unboundid.util.args.ArgumentParser;
049 import com.unboundid.util.args.BooleanArgument;
050 import com.unboundid.util.args.DNArgument;
051 import com.unboundid.util.args.FileArgument;
052 import com.unboundid.util.args.IntegerArgument;
053 import com.unboundid.util.args.StringArgument;
054 import com.unboundid.util.ssl.KeyStoreKeyManager;
055 import com.unboundid.util.ssl.PromptTrustManager;
056 import com.unboundid.util.ssl.SSLUtil;
057 import com.unboundid.util.ssl.TrustAllTrustManager;
058 import com.unboundid.util.ssl.TrustStoreTrustManager;
059
060 import static com.unboundid.util.Debug.*;
061 import static com.unboundid.util.StaticUtils.*;
062 import static com.unboundid.util.UtilityMessages.*;
063
064
065
066 /**
067 * This class provides a basis for developing command-line tools that
068 * communicate with an LDAP directory server. It provides a common set of
069 * options for connecting and authenticating to a directory server, and then
070 * provides a mechanism for obtaining connections and connection pools to use
071 * when communicating with that server.
072 * <BR><BR>
073 * The arguments that this class supports include:
074 * <UL>
075 * <LI>"-h {address}" or "--hostname {address}" -- Specifies the address of
076 * the directory server. If this isn't specified, then a default of
077 * "localhost" will be used.</LI>
078 * <LI>"-p {port}" or "--port {port}" -- Specifies the port number of the
079 * directory server. If this isn't specified, then a default port of 389
080 * will be used.</LI>
081 * <LI>"-D {bindDN}" or "--bindDN {bindDN}" -- Specifies the DN to use to bind
082 * to the directory server using simple authentication. If this isn't
083 * specified, then simple authentication will not be performed.</LI>
084 * <LI>"-w {password}" or "--bindPassword {password}" -- Specifies the
085 * password to use when binding with simple authentication or a
086 * password-based SASL mechanism.</LI>
087 * <LI>"-j {path}" or "--bindPasswordFile {path}" -- Specifies the path to the
088 * file containing the password to use when binding with simple
089 * authentication or a password-based SASL mechanism.</LI>
090 * <LI>"-Z" or "--useSSL" -- Indicates that the communication with the server
091 * should be secured using SSL.</LI>
092 * <LI>"-q" or "--useStartTLS" -- Indicates that the communication with the
093 * server should be secured using StartTLS.</LI>
094 * <LI>"-X" or "--trustAll" -- Indicates that the client should trust any
095 * certificate that the server presents to it.</LI>
096 * <LI>"-K {path}" or "--keyStorePath {path}" -- Specifies the path to the
097 * key store to use to obtain client certificates.</LI>
098 * <LI>"-W {password}" or "--keyStorePassword {password}" -- Specifies the
099 * password to use to access the contents of the key store.</LI>
100 * <LI>"-u {path}" or "--keyStorePasswordFile {path}" -- Specifies the path to
101 * the file containing the password to use to access the contents of the
102 * key store.</LI>
103 * <LI>"--keyStoreFormat {format}" -- Specifies the format to use for the key
104 * store file.</LI>
105 * <LI>"-P {path}" or "--trustStorePath {path}" -- Specifies the path to the
106 * trust store to use when determining whether to trust server
107 * certificates.</LI>
108 * <LI>"-T {password}" or "--trustStorePassword {password}" -- Specifies the
109 * password to use to access the contents of the trust store.</LI>
110 * <LI>"-U {path}" or "--trustStorePasswordFile {path}" -- Specifies the path
111 * to the file containing the password to use to access the contents of
112 * the trust store.</LI>
113 * <LI>"--trustStoreFormat {format}" -- Specifies the format to use for the
114 * trust store file.</LI>
115 * <LI>"-N {nickname}" or "--certNickname {nickname}" -- Specifies the
116 * nickname of the client certificate to use when performing SSL client
117 * authentication.</LI>
118 * <LI>"-o {name=value}" or "--saslOption {name=value}" -- Specifies a SASL
119 * option to use when performing SASL authentication.</LI>
120 * </UL>
121 * If SASL authentication is to be used, then a "mech" SASL option must be
122 * provided to specify the name of the SASL mechanism to use (e.g.,
123 * "--saslOption mech=EXTERNAL" indicates that the EXTERNAL mechanism should be
124 * used). Depending on the SASL mechanism, additional SASL options may be
125 * required or optional. They include:
126 * <UL>
127 * <LI>
128 * mech=ANONYMOUS
129 * <UL>
130 * <LI>Required SASL options: </LI>
131 * <LI>Optional SASL options: trace</LI>
132 * </UL>
133 * </LI>
134 * <LI>
135 * mech=CRAM-MD5
136 * <UL>
137 * <LI>Required SASL options: authID</LI>
138 * <LI>Optional SASL options: </LI>
139 * </UL>
140 * </LI>
141 * <LI>
142 * mech=DIGEST-MD5
143 * <UL>
144 * <LI>Required SASL options: authID</LI>
145 * <LI>Optional SASL options: authzID, realm</LI>
146 * </UL>
147 * </LI>
148 * <LI>
149 * mech=EXTERNAL
150 * <UL>
151 * <LI>Required SASL options: </LI>
152 * <LI>Optional SASL options: </LI>
153 * </UL>
154 * </LI>
155 * <LI>
156 * mech=GSSAPI
157 * <UL>
158 * <LI>Required SASL options: authID</LI>
159 * <LI>Optional SASL options: authzID, configFile, debug, protocol,
160 * realm, kdcAddress, useTicketCache, requireCache,
161 * renewTGT, ticketCachePath</LI>
162 * </UL>
163 * </LI>
164 * <LI>
165 * mech=PLAIN
166 * <UL>
167 * <LI>Required SASL options: authID</LI>
168 * <LI>Optional SASL options: authzID</LI>
169 * </UL>
170 * </LI>
171 * </UL>
172 * <BR><BR>
173 * Note that in general, methods in this class are not threadsafe. However, the
174 * {@link #getConnection()} and {@link #getConnectionPool(int,int)} methods may
175 * be invoked concurrently by multiple threads accessing the same instance only
176 * while that instance is in the process of invoking the
177 * {@link #doToolProcessing()} method.
178 */
179 @Extensible()
180 @ThreadSafety(level=ThreadSafetyLevel.INTERFACE_NOT_THREADSAFE)
181 public abstract class LDAPCommandLineTool
182 extends CommandLineTool
183 {
184
185
186
187 // Arguments used to communicate with an LDAP directory server.
188 private BooleanArgument trustAll = null;
189 private BooleanArgument useSSL = null;
190 private BooleanArgument useStartTLS = null;
191 private DNArgument bindDN = null;
192 private FileArgument bindPasswordFile = null;
193 private FileArgument keyStorePasswordFile = null;
194 private FileArgument trustStorePasswordFile = null;
195 private IntegerArgument port = null;
196 private StringArgument bindPassword = null;
197 private StringArgument certificateNickname = null;
198 private StringArgument host = null;
199 private StringArgument keyStoreFormat = null;
200 private StringArgument keyStorePath = null;
201 private StringArgument keyStorePassword = null;
202 private StringArgument saslOption = null;
203 private StringArgument trustStoreFormat = null;
204 private StringArgument trustStorePath = null;
205 private StringArgument trustStorePassword = null;
206
207 // Variables used when creating and authenticating connections.
208 private BindRequest bindRequest = null;
209 private ServerSet serverSet = null;
210 private SSLContext startTLSContext = null;
211
212 // The prompt trust manager that will be shared by all connections created
213 // for which it is appropriate. This will allow them to benefit from the
214 // common cache.
215 private final AtomicReference<PromptTrustManager> promptTrustManager;
216
217
218
219 /**
220 * Creates a new instance of this LDAP-enabled command-line tool with the
221 * provided information.
222 *
223 * @param outStream The output stream to use for standard output. It may be
224 * {@code System.out} for the JVM's default standard output
225 * stream, {@code null} if no output should be generated,
226 * or a custom output stream if the output should be sent
227 * to an alternate location.
228 * @param errStream The output stream to use for standard error. It may be
229 * {@code System.err} for the JVM's default standard error
230 * stream, {@code null} if no output should be generated,
231 * or a custom output stream if the output should be sent
232 * to an alternate location.
233 */
234 public LDAPCommandLineTool(final OutputStream outStream,
235 final OutputStream errStream)
236 {
237 super(outStream, errStream);
238
239 promptTrustManager = new AtomicReference<PromptTrustManager>();
240 }
241
242
243
244 /**
245 * {@inheritDoc}
246 */
247 @Override()
248 public final void addToolArguments(final ArgumentParser parser)
249 throws ArgumentException
250 {
251 host = new StringArgument('h', "hostname", true,
252 (supportsMultipleServers() ? 0 : 1),
253 INFO_LDAP_TOOL_PLACEHOLDER_HOST.get(),
254 INFO_LDAP_TOOL_DESCRIPTION_HOST.get(), "localhost");
255 parser.addArgument(host);
256
257 port = new IntegerArgument('p', "port", true,
258 (supportsMultipleServers() ? 0 : 1),
259 INFO_LDAP_TOOL_PLACEHOLDER_PORT.get(),
260 INFO_LDAP_TOOL_DESCRIPTION_PORT.get(), 1, 65535, 389);
261 parser.addArgument(port);
262
263 bindDN = new DNArgument('D', "bindDN", false, 1,
264 INFO_LDAP_TOOL_PLACEHOLDER_DN.get(),
265 INFO_LDAP_TOOL_DESCRIPTION_BIND_DN.get());
266 parser.addArgument(bindDN);
267
268 bindPassword = new StringArgument('w', "bindPassword", false, 1,
269 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(),
270 INFO_LDAP_TOOL_DESCRIPTION_BIND_PW.get());
271 parser.addArgument(bindPassword);
272
273 bindPasswordFile = new FileArgument('j', "bindPasswordFile", false, 1,
274 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(),
275 INFO_LDAP_TOOL_DESCRIPTION_BIND_PW_FILE.get(), true, true, true,
276 false);
277 parser.addArgument(bindPasswordFile);
278
279 useSSL = new BooleanArgument('Z', "useSSL", 1,
280 INFO_LDAP_TOOL_DESCRIPTION_USE_SSL.get());
281 parser.addArgument(useSSL);
282
283 useStartTLS = new BooleanArgument('q', "useStartTLS", 1,
284 INFO_LDAP_TOOL_DESCRIPTION_USE_START_TLS.get());
285 parser.addArgument(useStartTLS);
286
287 trustAll = new BooleanArgument('X', "trustAll", 1,
288 INFO_LDAP_TOOL_DESCRIPTION_TRUST_ALL.get());
289 parser.addArgument(trustAll);
290
291 keyStorePath = new StringArgument('K', "keyStorePath", false, 1,
292 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(),
293 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PATH.get());
294 parser.addArgument(keyStorePath);
295
296 keyStorePassword = new StringArgument('W', "keyStorePassword", false, 1,
297 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(),
298 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD.get());
299 parser.addArgument(keyStorePassword);
300
301 keyStorePasswordFile = new FileArgument('u', "keyStorePasswordFile", false,
302 1, INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(),
303 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_PASSWORD_FILE.get());
304 parser.addArgument(keyStorePasswordFile);
305
306 keyStoreFormat = new StringArgument(null, "keyStoreFormat", false, 1,
307 INFO_LDAP_TOOL_PLACEHOLDER_FORMAT.get(),
308 INFO_LDAP_TOOL_DESCRIPTION_KEY_STORE_FORMAT.get());
309 parser.addArgument(keyStoreFormat);
310
311 trustStorePath = new StringArgument('P', "trustStorePath", false, 1,
312 INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(),
313 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PATH.get());
314 parser.addArgument(trustStorePath);
315
316 trustStorePassword = new StringArgument('T', "trustStorePassword", false, 1,
317 INFO_LDAP_TOOL_PLACEHOLDER_PASSWORD.get(),
318 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD.get());
319 parser.addArgument(trustStorePassword);
320
321 trustStorePasswordFile = new FileArgument('U', "trustStorePasswordFile",
322 false, 1, INFO_LDAP_TOOL_PLACEHOLDER_PATH.get(),
323 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD_FILE.get());
324 parser.addArgument(trustStorePasswordFile);
325
326 trustStoreFormat = new StringArgument(null, "trustStoreFormat", false, 1,
327 INFO_LDAP_TOOL_PLACEHOLDER_FORMAT.get(),
328 INFO_LDAP_TOOL_DESCRIPTION_TRUST_STORE_FORMAT.get());
329 parser.addArgument(trustStoreFormat);
330
331 certificateNickname = new StringArgument('N', "certNickname", false, 1,
332 INFO_LDAP_TOOL_PLACEHOLDER_CERT_NICKNAME.get(),
333 INFO_LDAP_TOOL_DESCRIPTION_CERT_NICKNAME.get());
334 parser.addArgument(certificateNickname);
335
336 saslOption = new StringArgument('o', "saslOption", false, 0,
337 INFO_LDAP_TOOL_PLACEHOLDER_SASL_OPTION.get(),
338 INFO_LDAP_TOOL_DESCRIPTION_SASL_OPTION.get());
339 parser.addArgument(saslOption);
340
341
342 parser.addDependentArgumentSet(bindDN, bindPassword, bindPasswordFile);
343
344 parser.addExclusiveArgumentSet(useSSL, useStartTLS);
345 parser.addExclusiveArgumentSet(bindPassword, bindPasswordFile);
346 parser.addExclusiveArgumentSet(keyStorePassword, keyStorePasswordFile);
347 parser.addExclusiveArgumentSet(trustStorePassword, trustStorePasswordFile);
348 parser.addExclusiveArgumentSet(trustAll, trustStorePath);
349
350 addNonLDAPArguments(parser);
351 }
352
353
354
355 /**
356 * Adds the arguments needed by this command-line tool to the provided
357 * argument parser which are not related to connecting or authenticating to
358 * the directory server.
359 *
360 * @param parser The argument parser to which the arguments should be added.
361 *
362 * @throws ArgumentException If a problem occurs while adding the arguments.
363 */
364 public abstract void addNonLDAPArguments(final ArgumentParser parser)
365 throws ArgumentException;
366
367
368
369 /**
370 * {@inheritDoc}
371 */
372 @Override()
373 public final void doExtendedArgumentValidation()
374 throws ArgumentException
375 {
376 // If more than one hostname or port number was provided, then make sure
377 // that the same number of values were provided for each.
378 if ((host.getValues().size() > 1) || (port.getValues().size() > 1))
379 {
380 if (host.getValues().size() != port.getValues().size())
381 {
382 throw new ArgumentException(
383 ERR_LDAP_TOOL_HOST_PORT_COUNT_MISMATCH.get(
384 host.getLongIdentifier(), port.getLongIdentifier()));
385 }
386 }
387
388
389 doExtendedNonLDAPArgumentValidation();
390 }
391
392
393
394 /**
395 * Indicates whether this tool supports creating connections to multiple
396 * servers. If it is to support multiple servers, then the "--hostname" and
397 * "--port" arguments will be allowed to be provided multiple times, and
398 * will be required to be provided the same number of times. The same type of
399 * communication security and bind credentials will be used for all servers.
400 *
401 * @return {@code true} if this tool supports creating connections to
402 * multiple servers, or {@code false} if not.
403 */
404 protected boolean supportsMultipleServers()
405 {
406 return false;
407 }
408
409
410
411 /**
412 * Performs any necessary processing that should be done to ensure that the
413 * provided set of command-line arguments were valid. This method will be
414 * called after the basic argument parsing has been performed and after all
415 * LDAP-specific argument validation has been processed, and immediately
416 * before the {@link CommandLineTool#doToolProcessing} method is invoked.
417 *
418 * @throws ArgumentException If there was a problem with the command-line
419 * arguments provided to this program.
420 */
421 public void doExtendedNonLDAPArgumentValidation()
422 throws ArgumentException
423 {
424 // No processing will be performed by default.
425 }
426
427
428
429 /**
430 * Retrieves the connection options that should be used for connections that
431 * are created with this command line tool. Subclasses may override this
432 * method to use a custom set of connection options.
433 *
434 * @return The connection options that should be used for connections that
435 * are created with this command line tool.
436 */
437 public LDAPConnectionOptions getConnectionOptions()
438 {
439 return new LDAPConnectionOptions();
440 }
441
442
443
444 /**
445 * Retrieves a connection that may be used to communicate with the target
446 * directory server.
447 * <BR><BR>
448 * Note that this method is threadsafe and may be invoked by multiple threads
449 * accessing the same instance only while that instance is in the process of
450 * invoking the {@link #doToolProcessing} method.
451 *
452 * @return A connection that may be used to communicate with the target
453 * directory server.
454 *
455 * @throws LDAPException If a problem occurs while creating the connection.
456 */
457 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE)
458 public final LDAPConnection getConnection()
459 throws LDAPException
460 {
461 if (serverSet == null)
462 {
463 serverSet = createServerSet();
464 bindRequest = createBindRequest();
465 }
466
467 final LDAPConnection connection = serverSet.getConnection();
468
469 if (useStartTLS.isPresent())
470 {
471 try
472 {
473 final ExtendedResult extendedResult =
474 connection.processExtendedOperation(
475 new StartTLSExtendedRequest(startTLSContext));
476 if (! extendedResult.getResultCode().equals(ResultCode.SUCCESS))
477 {
478 throw new LDAPException(extendedResult.getResultCode(),
479 ERR_LDAP_TOOL_START_TLS_FAILED.get(
480 extendedResult.getDiagnosticMessage()));
481 }
482 }
483 catch (LDAPException le)
484 {
485 debugException(le);
486 connection.close();
487 throw le;
488 }
489 }
490
491 try
492 {
493 if (bindRequest != null)
494 {
495 connection.bind(bindRequest);
496 }
497 }
498 catch (LDAPException le)
499 {
500 debugException(le);
501 connection.close();
502 throw le;
503 }
504
505 return connection;
506 }
507
508
509
510 /**
511 * Retrieves a connection pool that may be used to communicate with the target
512 * directory server.
513 * <BR><BR>
514 * Note that this method is threadsafe and may be invoked by multiple threads
515 * accessing the same instance only while that instance is in the process of
516 * invoking the {@link #doToolProcessing} method.
517 *
518 * @param initialConnections The number of connections that should be
519 * initially established in the pool.
520 * @param maxConnections The maximum number of connections to maintain
521 * in the pool.
522 *
523 * @return A connection that may be used to communicate with the target
524 * directory server.
525 *
526 * @throws LDAPException If a problem occurs while creating the connection
527 * pool.
528 */
529 @ThreadSafety(level=ThreadSafetyLevel.METHOD_THREADSAFE)
530 public final LDAPConnectionPool getConnectionPool(
531 final int initialConnections,
532 final int maxConnections)
533 throws LDAPException
534 {
535 if (serverSet == null)
536 {
537 serverSet = createServerSet();
538 bindRequest = createBindRequest();
539 }
540
541 PostConnectProcessor postConnectProcessor = null;
542 if (useStartTLS.isPresent())
543 {
544 postConnectProcessor = new StartTLSPostConnectProcessor(startTLSContext);
545 }
546
547 return new LDAPConnectionPool(serverSet, bindRequest, initialConnections,
548 maxConnections, postConnectProcessor);
549 }
550
551
552
553 /**
554 * Creates the server set to use when creating connections or connection
555 * pools.
556 *
557 * @return The server set to use when creating connections or connection
558 * pools.
559 *
560 * @throws LDAPException If a problem occurs while creating the server set.
561 */
562 public ServerSet createServerSet()
563 throws LDAPException
564 {
565 final SSLUtil sslUtil = createSSLUtil();
566
567 SocketFactory socketFactory = null;
568 if (useSSL.isPresent())
569 {
570 try
571 {
572 socketFactory = sslUtil.createSSLSocketFactory();
573 }
574 catch (Exception e)
575 {
576 debugException(e);
577 throw new LDAPException(ResultCode.LOCAL_ERROR,
578 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_SOCKET_FACTORY.get(
579 getExceptionMessage(e)), e);
580 }
581 }
582 else if (useStartTLS.isPresent())
583 {
584 try
585 {
586 startTLSContext = sslUtil.createSSLContext();
587 }
588 catch (Exception e)
589 {
590 debugException(e);
591 throw new LDAPException(ResultCode.LOCAL_ERROR,
592 ERR_LDAP_TOOL_CANNOT_CREATE_SSL_CONTEXT.get(
593 getExceptionMessage(e)), e);
594 }
595 }
596
597 if (host.getValues().size() == 1)
598 {
599 return new SingleServerSet(host.getValue(), port.getValue(),
600 socketFactory, getConnectionOptions());
601 }
602 else
603 {
604 final List<String> hostList = host.getValues();
605 final List<Integer> portList = port.getValues();
606
607 final String[] hosts = new String[hostList.size()];
608 final int[] ports = new int[hosts.length];
609
610 for (int i=0; i < hosts.length; i++)
611 {
612 hosts[i] = hostList.get(i);
613 ports[i] = portList.get(i);
614 }
615
616 return new RoundRobinServerSet(hosts, ports, socketFactory,
617 getConnectionOptions());
618 }
619 }
620
621
622
623 /**
624 * Creates the SSLUtil instance to use for secure communication.
625 *
626 * @return The SSLUtil instance to use for secure communication, or
627 * {@code null} if secure communication is not needed.
628 *
629 * @throws LDAPException If a problem occurs while creating the SSLUtil
630 * instance.
631 */
632 private SSLUtil createSSLUtil()
633 throws LDAPException
634 {
635 if (useSSL.isPresent() || useStartTLS.isPresent())
636 {
637 KeyManager keyManager = null;
638 if (keyStorePath.isPresent())
639 {
640 char[] pw = null;
641 if (keyStorePassword.isPresent())
642 {
643 pw = keyStorePassword.getValue().toCharArray();
644 }
645 else if (keyStorePasswordFile.isPresent())
646 {
647 try
648 {
649 pw = keyStorePasswordFile.getNonBlankFileLines().get(0).
650 toCharArray();
651 }
652 catch (Exception e)
653 {
654 debugException(e);
655 throw new LDAPException(ResultCode.LOCAL_ERROR,
656 ERR_LDAP_TOOL_CANNOT_READ_KEY_STORE_PASSWORD.get(
657 getExceptionMessage(e)), e);
658 }
659 }
660
661 try
662 {
663 keyManager = new KeyStoreKeyManager(keyStorePath.getValue(), pw,
664 keyStoreFormat.getValue(), certificateNickname.getValue());
665 }
666 catch (Exception e)
667 {
668 debugException(e);
669 throw new LDAPException(ResultCode.LOCAL_ERROR,
670 ERR_LDAP_TOOL_CANNOT_CREATE_KEY_MANAGER.get(
671 getExceptionMessage(e)), e);
672 }
673 }
674
675 TrustManager trustManager;
676 if (trustAll.isPresent())
677 {
678 trustManager = new TrustAllTrustManager(false);
679 }
680 else if (trustStorePath.isPresent())
681 {
682 char[] pw = null;
683 if (trustStorePassword.isPresent())
684 {
685 pw = trustStorePassword.getValue().toCharArray();
686 }
687 else if (trustStorePasswordFile.isPresent())
688 {
689 try
690 {
691 pw = trustStorePasswordFile.getNonBlankFileLines().get(0).
692 toCharArray();
693 }
694 catch (Exception e)
695 {
696 debugException(e);
697 throw new LDAPException(ResultCode.LOCAL_ERROR,
698 ERR_LDAP_TOOL_CANNOT_READ_TRUST_STORE_PASSWORD.get(
699 getExceptionMessage(e)), e);
700 }
701 }
702
703 trustManager = new TrustStoreTrustManager(trustStorePath.getValue(), pw,
704 trustStoreFormat.getValue(), true);
705 }
706 else
707 {
708 trustManager = promptTrustManager.get();
709 if (trustManager == null)
710 {
711 final PromptTrustManager m = new PromptTrustManager();
712 promptTrustManager.compareAndSet(null, m);
713 trustManager = promptTrustManager.get();
714 }
715 }
716
717 return new SSLUtil(keyManager, trustManager);
718 }
719 else
720 {
721 return null;
722 }
723 }
724
725
726
727 /**
728 * Creates the bind request to use to authenticate to the server.
729 *
730 * @return The bind request to use to authenticate to the server, or
731 * {@code null} if no bind should be performed.
732 *
733 * @throws LDAPException If a problem occurs while creating the bind
734 * request.
735 */
736 private BindRequest createBindRequest()
737 throws LDAPException
738 {
739 final String pw;
740 if (bindPassword.isPresent())
741 {
742 pw = bindPassword.getValue();
743 }
744 else if (bindPasswordFile.isPresent())
745 {
746 try
747 {
748 pw = bindPasswordFile.getNonBlankFileLines().get(0);
749 }
750 catch (Exception e)
751 {
752 debugException(e);
753 throw new LDAPException(ResultCode.LOCAL_ERROR,
754 ERR_LDAP_TOOL_CANNOT_READ_BIND_PASSWORD.get(
755 getExceptionMessage(e)), e);
756 }
757 }
758 else
759 {
760 pw = null;
761 }
762
763 if (saslOption.isPresent())
764 {
765 final String dnStr;
766 if (bindDN.isPresent())
767 {
768 dnStr = bindDN.getValue().toString();
769 }
770 else
771 {
772 dnStr = null;
773 }
774
775 return SASLUtils.createBindRequest(dnStr, pw, null,
776 saslOption.getValues());
777 }
778 else if (bindDN.isPresent())
779 {
780 return new SimpleBindRequest(bindDN.getValue(), pw);
781 }
782 else
783 {
784 return null;
785 }
786 }
787 }