001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    
028    package org.opends.server.util.cli;
029    
030    import org.opends.messages.Message;
031    import static org.opends.messages.UtilityMessages.*;
032    import static org.opends.messages.QuickSetupMessages.*;
033    import static org.opends.messages.ToolMessages.*;
034    
035    import org.opends.quicksetup.Step;
036    import org.opends.quicksetup.UserDataCertificateException;
037    import org.opends.quicksetup.util.Utils;
038    import org.opends.server.tools.dsconfig.ArgumentExceptionFactory;
039    import org.opends.server.tools.LDAPConnectionOptions;
040    import org.opends.server.tools.SSLConnectionFactory;
041    import org.opends.server.tools.SSLConnectionException;
042    import org.opends.server.admin.client.cli.SecureConnectionCliArgs;
043    import org.opends.server.util.args.ArgumentException;
044    import org.opends.server.util.SelectableCertificateKeyManager;
045    import org.opends.admin.ads.ADSContext;
046    import org.opends.admin.ads.util.ApplicationTrustManager;
047    import org.opends.admin.ads.util.ApplicationKeyManager;
048    
049    import javax.net.ssl.KeyManager;
050    import javax.net.ssl.TrustManager;
051    import java.net.InetAddress;
052    import java.net.URI;
053    import java.net.UnknownHostException;
054    import java.io.File;
055    import java.io.FileInputStream;
056    import java.io.FileNotFoundException;
057    import java.io.FileOutputStream;
058    import java.security.KeyStore;
059    import java.security.KeyStoreException;
060    import java.security.cert.X509Certificate;
061    import java.util.Enumeration;
062    import java.util.logging.Level;
063    import java.util.logging.Logger;
064    
065    /**
066     * Supports interacting with a user through the command line to
067     * prompt for information necessary to create an LDAP connection.
068     */
069    public class LDAPConnectionConsoleInteraction {
070    
071      private boolean useSSL;
072      private boolean useStartTLS;
073      private String hostName;
074      private int portNumber;
075      private String bindDN;
076      private String providedBindDN;
077      private String adminUID;
078      private String providedAdminUID;
079      private String bindPassword;
080      private KeyManager keyManager;
081      private ApplicationTrustManager trustManager;
082      // Boolean that tells if we ask for bind DN or admin UID in the same prompt.
083      private boolean useAdminOrBindDn = false;
084      // Boolean that tells if we must propose LDAP if it is available even if the
085      // user provided certificate parameters.
086      private boolean displayLdapIfSecureParameters = false;
087    
088      // The SecureConnectionCliArgsList object.
089      private SecureConnectionCliArgs secureArgsList = null;
090    
091      // Indicate if we need to display the heading
092      private boolean isHeadingDisplayed = false;
093    
094      // the Console application
095      private ConsoleApplication app;
096    
097      // Indicate if the truststore in in memory
098      private boolean trustStoreInMemory = false;
099    
100      // Indicate that the trust manager was created with the parameters provided
101      private boolean trustManagerInitialized;
102    
103      // The truststore to use for the SSL or STARTTLS connection
104      private KeyStore truststore;
105    
106      private String keystorePath;
107    
108      private String keystorePassword;
109    
110      private String certifNickname;
111    
112      private String truststorePath;
113    
114      private String truststorePassword;
115    
116      private Message heading = INFO_LDAP_CONN_HEADING_CONNECTION_PARAMETERS.get();
117    
118      // A copy of the secureArgList for convenience.
119      private SecureConnectionCliArgs copySecureArgsList = null;
120    
121      // The command builder that we can return with the connection information.
122      private CommandBuilder commandBuilder;
123    
124      /**
125       * Enumeration description protocols for interactive CLI choices.
126       */
127      private enum Protocols
128      {
129        LDAP(1, INFO_LDAP_CONN_PROMPT_SECURITY_LDAP.get()), SSL(2,
130            INFO_LDAP_CONN_PROMPT_SECURITY_USE_SSL.get()), START_TLS(3,
131            INFO_LDAP_CONN_PROMPT_SECURITY_USE_START_TLS.get());
132    
133        private Integer choice;
134    
135        private Message msg;
136    
137        /**
138         * Private constructor.
139         *
140         * @param i
141         *          the menu return value.
142         * @param msg
143         *          the message message.
144         */
145        private Protocols(int i, Message msg)
146        {
147          choice = i;
148          this.msg = msg;
149        }
150    
151        /**
152         * Returns the choice number.
153         *
154         * @return the attribute name.
155         */
156        public Integer getChoice()
157        {
158          return choice;
159        }
160    
161        /**
162         * Return the menu message.
163         *
164         * @return the menu message.
165         */
166        public Message getMenuMessage()
167        {
168          return msg;
169        }
170      }
171    
172      /**
173       * Enumeration description protocols for interactive CLI choices.
174       */
175      private enum TrustMethod
176      {
177        TRUSTALL(1, INFO_LDAP_CONN_PROMPT_SECURITY_USE_TRUST_ALL.get()),
178    
179        TRUSTSTORE(2,INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE.get()),
180    
181        DISPLAY_CERTIFICATE(3,INFO_LDAP_CONN_PROMPT_SECURITY_MANUAL_CHECK.get());
182    
183        private Integer choice;
184    
185        private Message msg;
186    
187        /**
188         * Private constructor.
189         *
190         * @param i
191         *          the menu return value.
192         * @param msg
193         *          the message message.
194         */
195        private TrustMethod(int i, Message msg)
196        {
197          choice = new Integer(i);
198          this.msg = msg;
199        }
200    
201        /**
202         * Returns the choice number.
203         *
204         * @return the attribute name.
205         */
206        public Integer getChoice()
207        {
208          return choice;
209        }
210    
211        /**
212         * Return the menu message.
213         *
214         * @return the menu message.
215         */
216        public Message getMenuMessage()
217        {
218          return msg;
219        }
220      }
221    
222      /**
223       * Enumeration description server certificate trust option.
224       */
225      private enum TrustOption
226      {
227        UNTRUSTED(1, INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_NO.get()),
228        SESSION(2,INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_SESSION.get()),
229        PERMAMENT(3,INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION_ALWAYS.get()),
230        CERTIFICATE_DETAILS(4,
231            INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_DETAILS.get());
232    
233        private Integer choice;
234    
235        private Message msg;
236    
237        /**
238         * Private constructor.
239         *
240         * @param i
241         *          the menu return value.
242         * @param msg
243         *          the message message.
244         */
245        private TrustOption(int i, Message msg)
246        {
247          choice = new Integer(i);
248          this.msg = msg;
249        }
250    
251        /**
252         * Returns the choice number.
253         *
254         * @return the attribute name.
255         */
256        public Integer getChoice()
257        {
258          return choice;
259        }
260    
261        /**
262         * Return the menu message.
263         *
264         * @return the menu message.
265         */
266        public Message getMenuMessage()
267        {
268          return msg;
269        }
270      }
271      /**
272       * Constructs a parameterized instance.
273       *
274       * @param app console application
275       * @param secureArgs existing set of arguments that have already
276       *        been parsed and contain some potential command line specified
277       *        LDAP arguments
278       */
279      public LDAPConnectionConsoleInteraction(ConsoleApplication app,
280                                              SecureConnectionCliArgs secureArgs) {
281        this.app = app;
282        this.secureArgsList = secureArgs;
283        this.commandBuilder = new CommandBuilder(null);
284        copySecureArgsList = new SecureConnectionCliArgs();
285        try
286        {
287          copySecureArgsList.createGlobalArguments();
288        }
289        catch (Throwable t)
290        {
291          // This is  a bug: we should always be able to create the global arguments
292          // no need to localize this one.
293          throw new RuntimeException("Unexpected error: "+t, t);
294        }
295      }
296    
297      /**
298       * Interact with the user though the console to get information
299       * necessary to establish an LDAP connection.
300       *
301       * @throws ArgumentException if there is a problem with the arguments
302       */
303      public void run()
304              throws ArgumentException
305      {
306        run(true, true);
307      }
308    
309    
310      /**
311       * Interact with the user though the console to get information
312       * necessary to establish an LDAP connection.
313       * @param canUseSSL whether we can propose to connect using SSL or not.
314       * @param canUseStartTLS whether we can propose to connect using Start TLS or
315       * not.
316       *
317       * @throws ArgumentException if there is a problem with the arguments
318       */
319      public void run(boolean canUseSSL, boolean canUseStartTLS)
320              throws ArgumentException
321      {
322        // Reset everything
323        commandBuilder.clearArguments();
324        copySecureArgsList.createGlobalArguments();
325        boolean secureConnection = (canUseSSL || canUseStartTLS) &&
326          (
327              secureArgsList.useSSLArg.isPresent()
328              ||
329              secureArgsList.useStartTLSArg.isPresent()
330              ||
331              secureArgsList.trustAllArg.isPresent()
332              ||
333              secureArgsList.trustStorePathArg.isPresent()
334              ||
335              secureArgsList.trustStorePasswordArg.isPresent()
336              ||
337              secureArgsList.trustStorePasswordFileArg.isPresent()
338              ||
339              secureArgsList.keyStorePathArg.isPresent()
340              ||
341              secureArgsList.keyStorePasswordArg.isPresent()
342              ||
343              secureArgsList.keyStorePasswordFileArg.isPresent()
344          );
345    
346        // Get the LDAP host.
347        hostName = secureArgsList.hostNameArg.getValue();
348        final String tmpHostName = hostName;
349        if (app.isInteractive() && !secureArgsList.hostNameArg.isPresent())
350        {
351          checkHeadingDisplayed();
352    
353          ValidationCallback<String> callback = new ValidationCallback<String>()
354          {
355    
356            public String validate(ConsoleApplication app, String input)
357                throws CLIException
358            {
359              String ninput = input.trim();
360              if (ninput.length() == 0)
361              {
362                return tmpHostName;
363              }
364              else
365              {
366                try
367                {
368                  InetAddress.getByName(ninput);
369                  return ninput;
370                }
371                catch (UnknownHostException e)
372                {
373                  // Try again...
374                  app.println();
375                  app.println(ERR_LDAP_CONN_BAD_HOST_NAME.get(ninput));
376                  app.println();
377                  return null;
378                }
379              }
380            }
381    
382          };
383    
384          try
385          {
386            app.println();
387            hostName = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_HOST_NAME
388                .get(hostName), callback);
389          }
390          catch (CLIException e)
391          {
392            throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
393          }
394        }
395    
396        copySecureArgsList.hostNameArg.clearValues();
397        copySecureArgsList.hostNameArg.addValue(hostName);
398        commandBuilder.addArgument(copySecureArgsList.hostNameArg);
399    
400        useSSL = secureArgsList.useSSL();
401        useStartTLS = secureArgsList.useStartTLS();
402        boolean connectionTypeIsSet =
403          (
404            secureArgsList.useSSLArg.isPresent()
405            ||
406            secureArgsList.useStartTLSArg.isPresent()
407            ||
408            (
409              secureArgsList.useSSLArg.isValueSetByProperty()
410              &&
411              secureArgsList.useStartTLSArg.isValueSetByProperty()
412            )
413          );
414        if (app.isInteractive() && !connectionTypeIsSet)
415        {
416          checkHeadingDisplayed();
417    
418          MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
419          builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_USE_SECURE_CTX.get());
420    
421          Protocols defaultProtocol ;
422          if (secureConnection)
423          {
424            defaultProtocol = Protocols.SSL;
425          }
426          else
427          {
428            defaultProtocol = Protocols.LDAP;
429          }
430          for (Protocols p : Protocols.values())
431          {
432            if (secureConnection && p.equals(Protocols.LDAP) &&
433                !displayLdapIfSecureParameters)
434            {
435              continue ;
436            }
437            if (!canUseSSL && p.equals(Protocols.SSL))
438            {
439              continue;
440            }
441            if (!canUseStartTLS && p.equals(Protocols.START_TLS))
442            {
443              continue;
444            }
445            int i = builder.addNumberedOption(p.getMenuMessage(), MenuResult
446                .success(p.getChoice()));
447            if (p.equals(defaultProtocol))
448            {
449              builder.setDefault(
450                  INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE
451                      .get(i), MenuResult.success(p.getChoice()));
452            }
453          }
454    
455          Menu<Integer> menu = builder.toMenu();
456          try
457          {
458            MenuResult<Integer> result = menu.run();
459            if (result.isSuccess())
460            {
461              if (result.getValue().equals(Protocols.SSL.getChoice()))
462              {
463                useSSL = true;
464              }
465              else if (result.getValue()
466                  .equals(Protocols.START_TLS.getChoice()))
467              {
468                useStartTLS = true;
469              }
470            }
471            else
472            {
473              // Should never happen.
474              throw new RuntimeException();
475            }
476          }
477          catch (CLIException e)
478          {
479            throw new RuntimeException(e);
480          }
481        }
482    
483        if (useSSL)
484        {
485          commandBuilder.addArgument(copySecureArgsList.useSSLArg);
486        }
487        else if (useStartTLS)
488        {
489          commandBuilder.addArgument(copySecureArgsList.useStartTLSArg);
490        }
491    
492        if ((useSSL || useStartTLS) && (trustManager == null))
493        {
494          initializeTrustManager();
495        }
496    
497        // Get the LDAP port.
498        if (!useSSL)
499        {
500          portNumber = secureArgsList.portArg.getIntValue();
501        }
502        else
503        {
504          if (secureArgsList.portArg.isPresent())
505          {
506            portNumber = secureArgsList.portArg.getIntValue();
507          }
508          else
509          {
510            portNumber = 636;
511          }
512        }
513        final int tmpPortNumber = portNumber;
514        if (app.isInteractive() && !secureArgsList.portArg.isPresent())
515        {
516          checkHeadingDisplayed();
517    
518          ValidationCallback<Integer> callback = new ValidationCallback<Integer>()
519          {
520    
521            public Integer validate(ConsoleApplication app, String input)
522                throws CLIException
523            {
524              String ninput = input.trim();
525              if (ninput.length() == 0)
526              {
527                return tmpPortNumber;
528              }
529              else
530              {
531                try
532                {
533                  int i = Integer.parseInt(ninput);
534                  if (i < 1 || i > 65535)
535                  {
536                    throw new NumberFormatException();
537                  }
538                  return i;
539                }
540                catch (NumberFormatException e)
541                {
542                  // Try again...
543                  app.println();
544                  app.println(ERR_LDAP_CONN_BAD_PORT_NUMBER.get(ninput));
545                  app.println();
546                  return null;
547                }
548              }
549            }
550    
551          };
552    
553          try
554          {
555            app.println();
556            portNumber = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_PORT_NUMBER
557                .get(portNumber), callback);
558          }
559          catch (CLIException e)
560          {
561            throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
562          }
563        }
564    
565        copySecureArgsList.portArg.clearValues();
566        copySecureArgsList.portArg.addValue(String.valueOf(portNumber));
567        commandBuilder.addArgument(copySecureArgsList.portArg);
568    
569        // Get the LDAP bind credentials.
570        bindDN = secureArgsList.bindDnArg.getValue();
571        adminUID = secureArgsList.adminUidArg.getValue();
572        final boolean useAdmin = secureArgsList.useAdminUID();
573        if (useAdmin && secureArgsList.adminUidArg.isPresent())
574        {
575          providedAdminUID = adminUID;
576        }
577        else
578        {
579          providedAdminUID = null;
580        }
581        if ((!useAdmin || useAdminOrBindDn) &&
582            secureArgsList.bindDnArg.isPresent())
583        {
584          providedBindDN = bindDN;
585        }
586        else
587        {
588          providedBindDN = null;
589        }
590        boolean argIsPresent = (providedAdminUID != null) ||
591        (providedBindDN != null);
592        final String tmpBindDN = bindDN;
593        final String tmpAdminUID = adminUID;
594        if (keyManager == null)
595        {
596          if (app.isInteractive() && !argIsPresent)
597          {
598            checkHeadingDisplayed();
599    
600            ValidationCallback<String> callback = new ValidationCallback<String>()
601            {
602    
603              public String validate(ConsoleApplication app, String input)
604                  throws CLIException
605              {
606                String ninput = input.trim();
607                if (ninput.length() == 0)
608                {
609                  if (useAdmin)
610                  {
611                    return tmpAdminUID;
612                  }
613                  else
614                  {
615                    return tmpBindDN;
616                  }
617                }
618                else
619                {
620                  return ninput;
621                }
622              }
623    
624            };
625    
626            try
627            {
628              app.println();
629              if (useAdminOrBindDn)
630              {
631                String def = (adminUID != null) ? adminUID : bindDN;
632                String v = app.readValidatedInput(
633                    INFO_LDAP_CONN_GLOBAL_ADMINISTRATOR_OR_BINDDN_PROMPT.get(def),
634                    callback);
635                if (Utils.isDn(v))
636                {
637                  bindDN = v;
638                  providedBindDN = v;
639                  adminUID = null;
640                  providedAdminUID = null;
641                }
642                else
643                {
644                  bindDN = null;
645                  providedBindDN = null;
646                  adminUID = v;
647                  providedAdminUID = v;
648                }
649              }
650              else if (useAdmin)
651              {
652                adminUID = app.readValidatedInput(
653                    INFO_LDAP_CONN_PROMPT_ADMINISTRATOR_UID.get(adminUID),
654                    callback);
655                providedAdminUID = adminUID;
656              }
657              else
658              {
659                bindDN = app.readValidatedInput(INFO_LDAP_CONN_PROMPT_BIND_DN
660                  .get(bindDN), callback);
661                providedBindDN = bindDN;
662              }
663            }
664            catch (CLIException e)
665            {
666              throw ArgumentExceptionFactory
667                  .unableToReadConnectionParameters(e);
668            }
669          }
670          if (useAdmin)
671          {
672            copySecureArgsList.adminUidArg.clearValues();
673            copySecureArgsList.adminUidArg.addValue(getAdministratorUID());
674            commandBuilder.addArgument(copySecureArgsList.adminUidArg);
675          }
676          else
677          {
678            copySecureArgsList.bindDnArg.clearValues();
679            copySecureArgsList.bindDnArg.addValue(getBindDN());
680            commandBuilder.addArgument(copySecureArgsList.bindDnArg);
681          }
682        }
683        else
684        {
685          bindDN = null;
686          adminUID = null;
687        }
688    
689        bindPassword = secureArgsList.bindPasswordArg.getValue();
690        if (keyManager == null)
691        {
692          if (secureArgsList.bindPasswordFileArg.isPresent())
693          {
694            // Read from file if it exists.
695            bindPassword = secureArgsList.bindPasswordFileArg.getValue();
696    
697            if (bindPassword == null)
698            {
699              if (useAdmin)
700              {
701                throw ArgumentExceptionFactory.missingBindPassword(adminUID);
702              }
703              else
704              {
705                throw ArgumentExceptionFactory.missingBindPassword(bindDN);
706              }
707            }
708            copySecureArgsList.bindPasswordFileArg.clearValues();
709            copySecureArgsList.bindPasswordFileArg.getNameToValueMap().putAll(
710                secureArgsList.bindPasswordFileArg.getNameToValueMap());
711            commandBuilder.addArgument(secureArgsList.bindPasswordFileArg);
712          }
713          else if (bindPassword == null || bindPassword.equals("-"))
714          {
715            // Read the password from the stdin.
716            if (!app.isInteractive())
717            {
718              throw ArgumentExceptionFactory
719                  .unableToReadBindPasswordInteractively();
720            }
721    
722            checkHeadingDisplayed();
723    
724            try
725            {
726              app.println();
727              Message prompt;
728              if (providedAdminUID != null)
729              {
730                prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(providedAdminUID);
731              }
732              else if (providedBindDN != null)
733              {
734                prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(providedBindDN);
735              }
736              else if (bindDN != null)
737              {
738                prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(bindDN);
739              }
740              else
741              {
742                prompt = INFO_LDAPAUTH_PASSWORD_PROMPT.get(adminUID);
743              }
744              bindPassword = app.readPassword(prompt);
745            }
746            catch (Exception e)
747            {
748              throw ArgumentExceptionFactory
749                  .unableToReadConnectionParameters(e);
750            }
751          }
752          copySecureArgsList.bindPasswordArg.clearValues();
753          copySecureArgsList.bindPasswordArg.addValue(bindPassword);
754          commandBuilder.addObfuscatedArgument(
755              copySecureArgsList.bindPasswordArg);
756        }
757      }
758    
759      /**
760       * Get the trust manager.
761       *
762       * @return The trust manager based on CLI args on interactive prompt.
763       * @throws ArgumentException If an error occurs when getting args values.
764       */
765      private ApplicationTrustManager getTrustManagerInternal()
766      throws ArgumentException
767      {
768        // Remove these arguments since this method might be called several times.
769        commandBuilder.removeArgument(copySecureArgsList.trustAllArg);
770        commandBuilder.removeArgument(copySecureArgsList.trustStorePathArg);
771        commandBuilder.removeArgument(copySecureArgsList.trustStorePasswordArg);
772        commandBuilder.removeArgument(copySecureArgsList.trustStorePasswordFileArg);
773    
774        // If we have the trustALL flag, don't do anything
775        // just return null
776        if (secureArgsList.trustAllArg.isPresent())
777        {
778          commandBuilder.addArgument(copySecureArgsList.trustAllArg);
779          return null;
780        }
781    
782          // Check if some trust manager info are set
783        boolean weDontKnowTheTrustMethod =
784          !(  secureArgsList.trustAllArg.isPresent()
785              ||
786              secureArgsList.trustStorePathArg.isPresent()
787              ||
788              secureArgsList.trustStorePasswordArg.isPresent()
789              ||
790              secureArgsList.trustStorePasswordFileArg.isPresent()
791            );
792        boolean askForTrustStore = false;
793        if (app.isInteractive() && weDontKnowTheTrustMethod)
794        {
795          checkHeadingDisplayed();
796    
797          app.println();
798          MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
799          builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_METHOD.get());
800    
801          TrustMethod defaultTrustMethod = TrustMethod.DISPLAY_CERTIFICATE;
802          for (TrustMethod t : TrustMethod.values())
803          {
804            int i = builder.addNumberedOption(t.getMenuMessage(), MenuResult
805                .success(t.getChoice()));
806            if (t.equals(defaultTrustMethod))
807            {
808              builder.setDefault(
809                  INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE
810                      .get(new Integer(i)), MenuResult.success(t.getChoice()));
811            }
812          }
813    
814          Menu<Integer> menu = builder.toMenu();
815          trustStoreInMemory = false;
816          try
817          {
818            MenuResult<Integer> result = menu.run();
819            if (result.isSuccess())
820            {
821              if (result.getValue().equals(TrustMethod.TRUSTALL.getChoice()))
822              {
823                commandBuilder.addArgument(copySecureArgsList.trustAllArg);
824                // If we have the trustALL flag, don't do anything
825                // just return null
826                return null;
827              }
828              else if (result.getValue().equals(
829                  TrustMethod.TRUSTSTORE.getChoice()))
830              {
831                // We have to ask for truststore info
832                askForTrustStore = true;
833              }
834              else if (result.getValue().equals(
835                  TrustMethod.DISPLAY_CERTIFICATE.getChoice()))
836              {
837                // The certificate will be displayed to the user
838                askForTrustStore = false;
839                trustStoreInMemory = true;
840    
841                // There is no direct equivalent for this option, so propose the
842                // trust all option as command-line argument.
843                commandBuilder.addArgument(copySecureArgsList.trustAllArg);
844              }
845              else
846              {
847                // Should never happen.
848                throw new RuntimeException();
849              }
850            }
851            else
852            {
853              // Should never happen.
854              throw new RuntimeException();
855            }
856          }
857          catch (CLIException e)
858          {
859            throw new RuntimeException(e);
860    
861          }
862        }
863    
864        // If we do not trust all server certificates, we have to get info
865        // about truststore. First get the truststore path.
866        truststorePath = secureArgsList.trustStorePathArg.getValue();
867    
868        if (app.isInteractive() && !secureArgsList.trustStorePathArg.isPresent()
869            && askForTrustStore)
870        {
871          checkHeadingDisplayed();
872    
873          ValidationCallback<String> callback = new ValidationCallback<String>()
874          {
875            public String validate(ConsoleApplication app, String input)
876                throws CLIException
877            {
878              String ninput = input.trim();
879              if (ninput.length() == 0)
880              {
881                app.println();
882                app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
883                    .get());
884                app.println();
885                return null;
886              }
887              File f = new File(ninput);
888              if (f.exists() && f.canRead() && !f.isDirectory())
889              {
890                return ninput;
891              }
892              else
893              {
894                app.println();
895                app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
896                    .get());
897                app.println();
898                return null;
899              }
900            }
901          };
902    
903          try
904          {
905            app.println();
906            truststorePath = app.readValidatedInput(
907                INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH.get(), callback);
908          }
909          catch (CLIException e)
910          {
911            throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
912          }
913        }
914    
915        if (truststorePath != null)
916        {
917          copySecureArgsList.trustStorePathArg.clearValues();
918          copySecureArgsList.trustStorePathArg.addValue(truststorePath);
919          commandBuilder.addArgument(copySecureArgsList.trustStorePathArg);
920        }
921    
922        // Then the truststore password.
923        //  As the most common case is to have no password for truststore,
924        // we don't ask it in the interactive mode.
925        truststorePassword = secureArgsList.trustStorePasswordArg
926            .getValue();
927    
928        if (secureArgsList.trustStorePasswordFileArg.isPresent())
929        {
930          // Read from file if it exists.
931          truststorePassword = secureArgsList.trustStorePasswordFileArg
932              .getValue();
933        }
934        if ((truststorePassword !=  null) && (truststorePassword.equals("-")))
935        {
936          // Read the password from the stdin.
937          if (!app.isInteractive())
938          {
939            truststorePassword = null;
940          }
941          else
942          {
943            checkHeadingDisplayed();
944    
945            try
946            {
947              app.println();
948              Message prompt = INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PASSWORD
949                  .get(truststorePath);
950              truststorePassword = app.readPassword(prompt);
951            }
952            catch (Exception e)
953            {
954              throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
955            }
956          }
957        }
958    
959        // We've got all the information to get the truststore manager
960        try
961        {
962          truststore = KeyStore.getInstance(KeyStore.getDefaultType());
963          if (truststorePath != null)
964          {
965            FileInputStream fos = new FileInputStream(truststorePath);
966            if (truststorePassword != null)
967            {
968              truststore.load(fos, truststorePassword.toCharArray());
969            }
970            else
971            {
972              truststore.load(fos, null);
973            }
974            fos.close();
975          }
976          else
977          {
978            truststore.load(null, null);
979          }
980    
981          if (secureArgsList.trustStorePasswordFileArg.isPresent())
982          {
983            copySecureArgsList.trustStorePasswordFileArg.clearValues();
984            copySecureArgsList.trustStorePasswordFileArg.getNameToValueMap().putAll(
985                secureArgsList.trustStorePasswordFileArg.getNameToValueMap());
986            commandBuilder.addArgument(
987                copySecureArgsList.trustStorePasswordFileArg);
988          }
989          else
990          {
991            copySecureArgsList.trustStorePasswordArg.clearValues();
992            copySecureArgsList.trustStorePasswordArg.addValue(truststorePassword);
993            commandBuilder.addObfuscatedArgument(
994                copySecureArgsList.trustStorePasswordArg);
995          }
996    
997          return new ApplicationTrustManager(truststore);
998        }
999        catch (Exception e)
1000        {
1001          throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
1002        }
1003      }
1004    
1005      /**
1006       * Get the key manager.
1007       *
1008       * @return The key manager based on CLI args on interactive prompt.
1009       * @throws ArgumentException If an error occurs when getting args values.
1010       */
1011      private KeyManager getKeyManagerInternal()
1012      throws ArgumentException
1013      {
1014    //  Remove these arguments since this method might be called several times.
1015        commandBuilder.removeArgument(copySecureArgsList.certNicknameArg);
1016        commandBuilder.removeArgument(copySecureArgsList.keyStorePathArg);
1017        commandBuilder.removeArgument(copySecureArgsList.keyStorePasswordArg);
1018        commandBuilder.removeArgument(copySecureArgsList.keyStorePasswordFileArg);
1019    
1020        // Do we need client side authentication ?
1021        // If one of the client side authentication args is set, we assume
1022        // that we
1023        // need client side authentication.
1024        boolean weDontKnowIfWeNeedKeystore = !(secureArgsList.keyStorePathArg
1025            .isPresent()
1026            || secureArgsList.keyStorePasswordArg.isPresent()
1027            || secureArgsList.keyStorePasswordFileArg.isPresent()
1028            || secureArgsList.certNicknameArg
1029            .isPresent());
1030    
1031        // We don't have specific key manager parameter.
1032        // We assume that no client side authentication is required
1033        // Client side authentication is not the common use case. As a
1034        // consequence, interactive mode doesn't add an extra question
1035        // which will be in most cases useless.
1036        if (weDontKnowIfWeNeedKeystore)
1037        {
1038          return null;
1039        }
1040    
1041        // Get info about keystore. First get the keystore path.
1042        keystorePath = secureArgsList.keyStorePathArg.getValue();
1043        if (app.isInteractive() && !secureArgsList.keyStorePathArg.isPresent())
1044        {
1045          checkHeadingDisplayed();
1046    
1047          ValidationCallback<String> callback = new ValidationCallback<String>()
1048          {
1049            public String validate(ConsoleApplication app, String input)
1050                throws CLIException
1051            {
1052              String ninput = input.trim();
1053              if (ninput.length() == 0)
1054              {
1055                return ninput;
1056              }
1057              File f = new File(ninput);
1058              if (f.exists() && f.canRead() && !f.isDirectory())
1059              {
1060                return ninput;
1061              }
1062              else
1063              {
1064                app.println();
1065                app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
1066                    .get());
1067                app.println();
1068                return null;
1069              }
1070            }
1071          };
1072    
1073          try
1074          {
1075            app.println();
1076            keystorePath = app.readValidatedInput(
1077                INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PATH.get(), callback);
1078          }
1079          catch (CLIException e)
1080          {
1081            throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
1082          }
1083        }
1084    
1085        if (keystorePath != null)
1086        {
1087          copySecureArgsList.keyStorePathArg.clearValues();
1088          copySecureArgsList.keyStorePathArg.addValue(keystorePath);
1089          commandBuilder.addArgument(copySecureArgsList.keyStorePathArg);
1090        }
1091    
1092    
1093        // Then the keystore password.
1094        keystorePassword = secureArgsList.keyStorePasswordArg.getValue();
1095    
1096        if (secureArgsList.keyStorePasswordFileArg.isPresent())
1097        {
1098          // Read from file if it exists.
1099          keystorePassword = secureArgsList.keyStorePasswordFileArg.getValue();
1100    
1101          if (keystorePassword == null)
1102          {
1103            throw ArgumentExceptionFactory.missingBindPassword(keystorePassword);
1104          }
1105        }
1106        else if (keystorePassword == null || keystorePassword.equals("-"))
1107        {
1108          // Read the password from the stdin.
1109          if (!app.isInteractive())
1110          {
1111            throw ArgumentExceptionFactory
1112                .unableToReadBindPasswordInteractively();
1113          }
1114    
1115          checkHeadingDisplayed();
1116    
1117          try
1118          {
1119            app.println();
1120            Message prompt = INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD
1121                .get(keystorePath);
1122            keystorePassword = app.readPassword(prompt);
1123          }
1124          catch (Exception e)
1125          {
1126            throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
1127          }
1128        }
1129    
1130        // finally the certificate name, if needed.
1131        KeyStore keystore = null;
1132        Enumeration<String> aliasesEnum = null;
1133        try
1134        {
1135          FileInputStream fos = new FileInputStream(keystorePath);
1136          keystore = KeyStore.getInstance(KeyStore.getDefaultType());
1137          keystore.load(fos, keystorePassword.toCharArray());
1138          fos.close();
1139          aliasesEnum = keystore.aliases();
1140        }
1141        catch (Exception e)
1142        {
1143          throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
1144        }
1145    
1146        certifNickname = secureArgsList.certNicknameArg.getValue();
1147        if (app.isInteractive() && !secureArgsList.certNicknameArg.isPresent()
1148            && aliasesEnum.hasMoreElements())
1149        {
1150          checkHeadingDisplayed();
1151    
1152          try
1153          {
1154            MenuBuilder<String> builder = new MenuBuilder<String>(app);
1155            builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_ALIASES
1156                .get());
1157            int certificateNumber = 0;
1158            for (; aliasesEnum.hasMoreElements();)
1159            {
1160              String alias = aliasesEnum.nextElement();
1161              if (keystore.isKeyEntry(alias))
1162              {
1163                X509Certificate certif = (X509Certificate) keystore
1164                    .getCertificate(alias);
1165                certificateNumber++;
1166                builder.addNumberedOption(
1167                    INFO_LDAP_CONN_PROMPT_SECURITY_CERTIFICATE_ALIAS.get(alias,
1168                        certif.getSubjectDN().getName()), MenuResult
1169                        .success(alias));
1170              }
1171            }
1172    
1173            if (certificateNumber > 1)
1174            {
1175              app.println();
1176              Menu<String> menu = builder.toMenu();
1177              MenuResult<String> result = menu.run();
1178              if (result.isSuccess())
1179              {
1180                certifNickname = result.getValue();
1181              }
1182              else
1183              {
1184                // Should never happen.
1185                throw new RuntimeException();
1186              }
1187            }
1188            else
1189            {
1190              certifNickname = null;
1191            }
1192          }
1193          catch (KeyStoreException e)
1194          {
1195            throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
1196          }
1197          catch (CLIException e)
1198          {
1199            throw ArgumentExceptionFactory.unableToReadConnectionParameters(e);
1200          }
1201        }
1202    
1203        // We'we got all the information to get the keys manager
1204        ApplicationKeyManager akm = new ApplicationKeyManager(keystore,
1205            keystorePassword.toCharArray());
1206    
1207    
1208        if (secureArgsList.keyStorePasswordFileArg.isPresent())
1209        {
1210          copySecureArgsList.keyStorePasswordFileArg.clearValues();
1211          copySecureArgsList.keyStorePasswordFileArg.getNameToValueMap().putAll(
1212              secureArgsList.keyStorePasswordFileArg.getNameToValueMap());
1213          commandBuilder.addArgument(
1214              copySecureArgsList.keyStorePasswordFileArg);
1215        }
1216        else
1217        {
1218          copySecureArgsList.keyStorePasswordArg.clearValues();
1219          copySecureArgsList.keyStorePasswordArg.addValue(keystorePassword);
1220          commandBuilder.addObfuscatedArgument(
1221              copySecureArgsList.keyStorePasswordArg);
1222        }
1223    
1224        if (certifNickname != null)
1225        {
1226          copySecureArgsList.certNicknameArg.clearValues();
1227          copySecureArgsList.certNicknameArg.addValue(certifNickname);
1228        }
1229    
1230        if (certifNickname != null)
1231        {
1232          return new SelectableCertificateKeyManager(akm, certifNickname);
1233        }
1234        else
1235        {
1236          return akm;
1237        }
1238      }
1239    
1240      /**
1241       * Indicates whether or not a connection should use SSL based on
1242       * this interaction.
1243       *
1244       * @return boolean where true means use SSL
1245       */
1246      public boolean useSSL() {
1247        return useSSL;
1248      }
1249    
1250      /**
1251       * Indicates whether or not a connection should use StartTLS based on
1252       * this interaction.
1253       *
1254       * @return boolean where true means use StartTLS
1255       */
1256      public boolean useStartTLS() {
1257        return useStartTLS;
1258      }
1259    
1260      /**
1261       * Gets the host name that should be used for connections based on
1262       * this interaction.
1263       *
1264       * @return host name for connections
1265       */
1266      public String getHostName() {
1267        return hostName;
1268      }
1269    
1270      /**
1271       * Gets the port number name that should be used for connections based on
1272       * this interaction.
1273       *
1274       * @return port number for connections
1275       */
1276      public int getPortNumber() {
1277        return portNumber;
1278      }
1279    
1280      /**
1281       * Sets the port number name that should be used for connections based on
1282       * this interaction.
1283       *
1284       * @param portNumber port number for connections
1285       */
1286      public void setPortNumber(int portNumber) {
1287        this.portNumber = portNumber;
1288      }
1289    
1290      /**
1291       * Gets the bind DN name that should be used for connections based on
1292       * this interaction.
1293       *
1294       * @return bind DN for connections
1295       */
1296      public String getBindDN() {
1297        String dn;
1298        if (useAdminOrBindDn)
1299        {
1300          if (providedBindDN != null)
1301          {
1302            dn = providedBindDN;
1303          }
1304          else if (providedAdminUID != null)
1305          {
1306            dn = ADSContext.getAdministratorDN(providedAdminUID);
1307          }
1308          else if (this.bindDN != null)
1309          {
1310            dn = this.bindDN;
1311          }
1312          else if (this.adminUID != null)
1313          {
1314            dn = ADSContext.getAdministratorDN(this.adminUID);
1315          }
1316          else
1317          {
1318            dn = null;
1319          }
1320        }
1321        else if (secureArgsList.useAdminUID())
1322        {
1323          dn = ADSContext.getAdministratorDN(this.adminUID);
1324        }
1325        else
1326        {
1327          dn = this.bindDN;
1328        }
1329        return dn;
1330      }
1331    
1332      /**
1333       * Gets the administrator UID name that should be used for connections based
1334       * on this interaction.
1335       *
1336       * @return administrator UID for connections
1337       */
1338      public String getAdministratorUID() {
1339        return this.adminUID;
1340      }
1341    
1342      /**
1343       * Gets the bind password that should be used for connections based on
1344       * this interaction.
1345       *
1346       * @return bind password for connections
1347       */
1348      public String getBindPassword() {
1349        return this.bindPassword;
1350      }
1351    
1352      /**
1353       * Gets the trust manager that should be used for connections based on
1354       * this interaction.
1355       *
1356       * @return trust manager for connections
1357       */
1358      public TrustManager getTrustManager() {
1359        return this.trustManager;
1360      }
1361    
1362      /**
1363       * Gets the key store that should be used for connections based on
1364       * this interaction.
1365       *
1366       * @return key store for connections
1367       */
1368      public KeyStore getKeyStore() {
1369        return this.truststore;
1370      }
1371    
1372      /**
1373       * Gets the key manager that should be used for connections based on
1374       * this interaction.
1375       *
1376       * @return key manager for connections
1377       */
1378      public KeyManager getKeyManager() {
1379        return this.keyManager;
1380      }
1381    
1382      /**
1383       * Indicate if the truststore is in memory.
1384       *
1385       * @return true if the truststore is in memory.
1386       */
1387      public boolean isTrustStoreInMemory() {
1388        return this.trustStoreInMemory;
1389      }
1390    
1391      /**
1392       * Indicate if the certificate chain can be trusted.
1393       *
1394       * @param chain The certificate chain to validate
1395       * @return true if the server certificate is trusted.
1396       */
1397      public boolean checkServerCertificate(X509Certificate[] chain)
1398      {
1399        return checkServerCertificate(chain, null, null);
1400      }
1401    
1402      /**
1403       * Indicate if the certificate chain can be trusted.
1404       *
1405       * @param chain The certificate chain to validate
1406       * @param authType the authentication type.
1407       * @param host the host we tried to connect and that presented the
1408       * certificate.
1409       * @return true if the server certificate is trusted.
1410       */
1411      public boolean checkServerCertificate(X509Certificate[] chain,
1412          String authType, String host)
1413        {
1414        if (trustManager == null)
1415        {
1416          try
1417          {
1418            initializeTrustManager();
1419          }
1420          catch (ArgumentException ae)
1421          {
1422            // Should not occur
1423            throw new RuntimeException(ae);
1424          }
1425        }
1426        app.println();
1427        app.println(INFO_LDAP_CONN_PROMPT_SECURITY_SERVER_CERTIFICATE.get());
1428        app.println();
1429        for (int i = 0; i < chain.length; i++)
1430        {
1431          // Certificate DN
1432          app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_USER_DN.get(
1433              chain[i].getSubjectDN().toString()));
1434    
1435          // certificate validity
1436          app.println(
1437              INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_VALIDITY.get(
1438                  chain[i].getNotBefore().toString(),
1439                  chain[i].getNotAfter().toString()));
1440    
1441          // certificate Issuer
1442          app.println(
1443              INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE_ISSUER.get(
1444                  chain[i].getIssuerDN().toString()));
1445    
1446          if (i+1 <chain.length)
1447          {
1448            app.println();
1449            app.println();
1450          }
1451        }
1452        MenuBuilder<Integer> builder = new MenuBuilder<Integer>(app);
1453        builder.setPrompt(INFO_LDAP_CONN_PROMPT_SECURITY_TRUST_OPTION.get());
1454    
1455        TrustOption defaultTrustMethod = TrustOption.SESSION ;
1456        for (TrustOption t : TrustOption.values())
1457        {
1458          int i = builder.addNumberedOption(t.getMenuMessage(), MenuResult
1459              .success(t.getChoice()));
1460          if (t.equals(defaultTrustMethod))
1461          {
1462            builder.setDefault(
1463                INFO_LDAP_CONN_PROMPT_SECURITY_PROTOCOL_DEFAULT_CHOICE
1464                    .get(new Integer(i)), MenuResult.success(t.getChoice()));
1465          }
1466        }
1467    
1468        app.println();
1469        app.println();
1470    
1471        Menu<Integer> menu = builder.toMenu();
1472        while (true)
1473        {
1474          try
1475          {
1476            MenuResult<Integer> result = menu.run();
1477            if (result.isSuccess())
1478            {
1479              if (result.getValue().equals(TrustOption.UNTRUSTED.getChoice()))
1480              {
1481                return false;
1482              }
1483    
1484              if ((result.getValue().equals(TrustOption.CERTIFICATE_DETAILS
1485                  .getChoice())))
1486              {
1487                for (int i = 0; i < chain.length; i++)
1488                {
1489                  app.println();
1490                  app.println(INFO_LDAP_CONN_SECURITY_SERVER_CERTIFICATE
1491                      .get(chain[i].toString()));
1492                }
1493                continue;
1494              }
1495    
1496              // We should add it in the memory truststore
1497              for (int i = 0; i < chain.length; i++)
1498              {
1499                String alias = chain[i].getSubjectDN().getName();
1500                try
1501                {
1502                  truststore.setCertificateEntry(alias, chain[i]);
1503                }
1504                catch (KeyStoreException e1)
1505                {
1506                  // What else should we do?
1507                  return false;
1508                }
1509              }
1510    
1511              // Update the trust manager
1512              if (trustManager == null)
1513              {
1514                trustManager = new ApplicationTrustManager(truststore);
1515              }
1516              if ((authType != null) && (host != null))
1517              {
1518                // Update the trust manager with the new certificate
1519                trustManager.acceptCertificate(chain, authType, host);
1520              }
1521              else
1522              {
1523                // Do a full reset of the contents of the keystore.
1524                trustManager = new ApplicationTrustManager(truststore);
1525              }
1526              if (result.getValue().equals(TrustOption.PERMAMENT.getChoice()))
1527              {
1528                ValidationCallback<String> callback =
1529                  new ValidationCallback<String>()
1530                {
1531                  public String validate(ConsoleApplication app, String input)
1532                      throws CLIException
1533                  {
1534                    String ninput = input.trim();
1535                    if (ninput.length() == 0)
1536                    {
1537                      app.println();
1538                      app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
1539                          .get());
1540                      app.println();
1541                      return null;
1542                    }
1543                    File f = new File(ninput);
1544                    if (!f.isDirectory())
1545                    {
1546                      return ninput;
1547                    }
1548                    else
1549                    {
1550                      app.println();
1551                      app.println(ERR_LDAP_CONN_PROMPT_SECURITY_INVALID_FILE_PATH
1552                          .get());
1553                      app.println();
1554                      return null;
1555                    }
1556                  }
1557                };
1558    
1559                String truststorePath;
1560                try
1561                {
1562                  app.println();
1563                  truststorePath = app.readValidatedInput(
1564                      INFO_LDAP_CONN_PROMPT_SECURITY_TRUSTSTORE_PATH.get(),
1565                      callback);
1566                }
1567                catch (CLIException e)
1568                {
1569                  return true;
1570                }
1571    
1572                // Read the password from the stdin.
1573                String truststorePassword;
1574                try
1575                {
1576                  app.println();
1577                  Message prompt = INFO_LDAP_CONN_PROMPT_SECURITY_KEYSTORE_PASSWORD
1578                      .get(truststorePath);
1579                  truststorePassword = app.readPassword(prompt);
1580                }
1581                catch (Exception e)
1582                {
1583                  return true;
1584                }
1585                try
1586                {
1587                  KeyStore ts = KeyStore.getInstance("JKS");
1588                  FileInputStream fis;
1589                  try
1590                  {
1591                    fis = new FileInputStream(truststorePath);
1592                  }
1593                  catch (FileNotFoundException e)
1594                  {
1595                    fis = null;
1596                  }
1597                  ts.load(fis, truststorePassword.toCharArray());
1598                  if (fis != null)
1599                  {
1600                    fis.close();
1601                  }
1602                  for (int i = 0; i < chain.length; i++)
1603                  {
1604                    String alias = chain[i].getSubjectDN().getName();
1605                    ts.setCertificateEntry(alias, chain[i]);
1606                  }
1607                  FileOutputStream fos = new FileOutputStream(truststorePath);
1608                  ts.store(fos, truststorePassword.toCharArray());
1609                  if (fos != null)
1610                  {
1611                    fos.close();
1612                  }
1613                }
1614                catch (Exception e)
1615                {
1616                  return true;
1617                }
1618              }
1619              return true;
1620            }
1621            else
1622            {
1623              // Should never happen.
1624              throw new RuntimeException();
1625            }
1626          }
1627          catch (CLIException cliE)
1628          {
1629            throw new RuntimeException(cliE);
1630          }
1631        }
1632      }
1633    
1634     /**
1635      * Populates a set of LDAP options with state from this interaction.
1636      *
1637      * @param  options existing set of options; may be null in which case this
1638      *         method will create a new set of <code>LDAPConnectionOptions</code>
1639      *         to be returned
1640      * @return used during this interaction
1641      * @throws SSLConnectionException if this interaction has specified the use
1642      *         of SSL and there is a problem initializing the SSL connection
1643      *         factory
1644      */
1645     public LDAPConnectionOptions populateLDAPOptions(
1646             LDAPConnectionOptions options)
1647             throws SSLConnectionException
1648     {
1649       if (options == null) {
1650         options = new LDAPConnectionOptions();
1651       }
1652       if (this.useSSL) {
1653         options.setUseSSL(true);
1654         SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
1655         sslConnectionFactory.init(getTrustManager() == null, keystorePath,
1656                                   keystorePassword, certifNickname,
1657                                   truststorePath, truststorePassword);
1658         options.setSSLConnectionFactory(sslConnectionFactory);
1659       } else {
1660         options.setUseSSL(false);
1661       }
1662       options.setStartTLS(this.useStartTLS);
1663       return options;
1664     }
1665    
1666     /**
1667      * Prompts the user to accept the certificate.
1668      * @param t the throwable that was generated because the certificate was
1669      * not trusted.
1670      * @param usedTrustManager the trustManager used when trying to establish the
1671      * connection.
1672      * @param usedUrl the LDAP URL used to connect to the server.
1673      * @param displayErrorMessage whether to display an error message before
1674      * asking to accept the certificate or not.
1675      * @param logger the Logger used to log messages.
1676      * @return <CODE>true</CODE> if the user accepted the certificate and
1677      * <CODE>false</CODE> otherwise.
1678      */
1679     public boolean promptForCertificateConfirmation(Throwable t,
1680         ApplicationTrustManager usedTrustManager, String usedUrl,
1681         boolean displayErrorMessage, Logger logger)
1682     {
1683       boolean returnValue = false;
1684       ApplicationTrustManager.Cause cause;
1685       if (usedTrustManager != null)
1686       {
1687         cause = usedTrustManager.getLastRefusedCause();
1688       }
1689       else
1690       {
1691         cause = null;
1692       }
1693       if (logger != null)
1694       {
1695         logger.log(Level.INFO, "Certificate exception cause: "+cause);
1696       }
1697       UserDataCertificateException.Type excType = null;
1698       if (cause == ApplicationTrustManager.Cause.NOT_TRUSTED)
1699       {
1700         excType = UserDataCertificateException.Type.NOT_TRUSTED;
1701       }
1702       else if (cause ==
1703         ApplicationTrustManager.Cause.HOST_NAME_MISMATCH)
1704       {
1705         excType = UserDataCertificateException.Type.HOST_NAME_MISMATCH;
1706       }
1707       else
1708       {
1709         Message msg = Utils.getThrowableMsg(INFO_ERROR_CONNECTING_TO_LOCAL.get(),
1710             t);
1711         app.println(msg);
1712       }
1713    
1714       if (excType != null)
1715       {
1716         String h;
1717         int p;
1718         try
1719         {
1720           URI uri = new URI(usedUrl);
1721           h = uri.getHost();
1722           p = uri.getPort();
1723         }
1724         catch (Throwable t1)
1725         {
1726           if (logger != null)
1727           {
1728             logger.log(Level.WARNING, "Error parsing ldap url of ldap url.", t1);
1729           }
1730           h = INFO_NOT_AVAILABLE_LABEL.get().toString();
1731           p = -1;
1732         }
1733    
1734    
1735    
1736         UserDataCertificateException udce =
1737           new UserDataCertificateException(Step.REPLICATION_OPTIONS,
1738               INFO_CERTIFICATE_EXCEPTION.get(h, String.valueOf(p)), t, h, p,
1739                   usedTrustManager.getLastRefusedChain(),
1740                   usedTrustManager.getLastRefusedAuthType(), excType);
1741    
1742         Message msg;
1743         if (udce.getType() == UserDataCertificateException.Type.NOT_TRUSTED)
1744         {
1745           msg = INFO_CERTIFICATE_NOT_TRUSTED_TEXT_CLI.get(
1746               udce.getHost(), String.valueOf(udce.getPort()));
1747         }
1748         else
1749         {
1750           msg = INFO_CERTIFICATE_NAME_MISMATCH_TEXT_CLI.get(
1751               udce.getHost(), String.valueOf(udce.getPort()),
1752               udce.getHost(),
1753               udce.getHost(), String.valueOf(udce.getPort()));
1754         }
1755         if (displayErrorMessage)
1756         {
1757           app.println(msg);
1758         }
1759         X509Certificate[] chain = udce.getChain();
1760         String authType = udce.getAuthType();
1761         String host = udce.getHost();
1762         if (logger != null)
1763         {
1764           if (chain == null)
1765           {
1766             logger.log(Level.WARNING,
1767             "The chain is null for the UserDataCertificateException");
1768           }
1769           if (authType == null)
1770           {
1771             logger.log(Level.WARNING,
1772             "The auth type is null for the UserDataCertificateException");
1773           }
1774           if (host == null)
1775           {
1776             logger.log(Level.WARNING,
1777             "The host is null for the UserDataCertificateException");
1778           }
1779         }
1780         if (chain != null)
1781         {
1782           returnValue = checkServerCertificate(chain, authType, host);
1783         }
1784       }
1785       return returnValue;
1786     }
1787    
1788     /**
1789      * Sets the heading that is displayed in interactive mode.
1790      * @param heading the heading that is displayed in interactive mode.
1791      */
1792     public void setHeadingMessage(Message heading)
1793     {
1794       this.heading = heading;
1795     }
1796    
1797     /**
1798      * Returns the command builder with the equivalent arguments on the
1799      * non-interactive mode.
1800      * @return the command builder with the equivalent arguments on the
1801      * non-interactive mode.
1802      */
1803     public CommandBuilder getCommandBuilder()
1804     {
1805       return commandBuilder;
1806     }
1807    
1808     /**
1809      * Displays the heading if it was not displayed before.
1810      *
1811      */
1812     private void checkHeadingDisplayed()
1813     {
1814       if (!isHeadingDisplayed)
1815       {
1816         app.println();
1817         app.println();
1818         app.println(heading);
1819         isHeadingDisplayed = true;
1820       }
1821     }
1822    
1823     /**
1824      * Tells whether during interaction we can ask for both the DN or the admin
1825      * UID.
1826      * @return <CODE>true</CODE> if during interaction we can ask for both the DN
1827      * and the admin UID and <CODE>false</CODE> otherwise.
1828      */
1829     public boolean isUseAdminOrBindDn()
1830     {
1831       return useAdminOrBindDn;
1832     }
1833    
1834     /**
1835      * Tells whether we can ask during interaction for both the DN and the admin
1836      * UID or not.
1837      * @param useAdminOrBindDn whether we can ask for both the DN and the admin UID
1838      * during interaction or not.
1839      */
1840     public void setUseAdminOrBindDn(boolean useAdminOrBindDn)
1841     {
1842       this.useAdminOrBindDn = useAdminOrBindDn;
1843     }
1844    
1845     /**
1846      * Tells whether we propose LDAP as protocol even if the user provided security
1847      * parameters.  This is required in command-lines that access multiple servers
1848      * (like dsreplication).
1849      * @param displayLdapIfSecureParameters whether propose LDAP as protocol even
1850      * if the user provided security parameters or not.
1851      */
1852     public void setDisplayLdapIfSecureParameters(
1853         boolean displayLdapIfSecureParameters)
1854     {
1855       this.displayLdapIfSecureParameters = displayLdapIfSecureParameters;
1856     }
1857    
1858     /**
1859      * Resets the heading displayed flag, so that next time we call run the heading
1860      * is displayed.
1861      */
1862     public void resetHeadingDisplayed()
1863     {
1864       isHeadingDisplayed = false;
1865     }
1866    
1867     /**
1868      * Forces the initialization of the trust manager with the arguments provided
1869      * by the user.
1870      * @throws ArgumentException if there is an error with the arguments provided
1871      * by the user.
1872      */
1873     public void initializeTrustManagerIfRequired() throws ArgumentException
1874     {
1875       if (!trustManagerInitialized)
1876       {
1877         initializeTrustManager();
1878       }
1879     }
1880    
1881     private void initializeTrustManager() throws ArgumentException
1882     {
1883       // Get truststore info
1884       trustManager = getTrustManagerInternal();
1885    
1886       // Check if we need client side authentication
1887       keyManager = getKeyManagerInternal();
1888    
1889       trustManagerInitialized = true;
1890     }
1891     /**
1892      * Returns the explicitly provided Admin UID from the user (interactively
1893      * or through the argument).
1894      * @return the explicitly provided Admin UID from the user (interactively
1895      * or through the argument).
1896      */
1897     public String getProvidedAdminUID()
1898     {
1899       return providedAdminUID;
1900     }
1901    
1902     /**
1903      * Returns the explicitly provided bind DN from the user (interactively
1904      * or through the argument).
1905      * @return the explicitly provided bind DN from the user (interactively
1906      * or through the argument).
1907      */
1908     public String getProvidedBindDN()
1909     {
1910       return providedBindDN;
1911     }
1912    }