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 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.tools;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.io.OutputStream;
033    import java.io.PrintStream;
034    import java.util.ArrayList;
035    import java.util.concurrent.atomic.AtomicInteger;
036    
037    import org.opends.server.controls.PasswordPolicyErrorType;
038    import org.opends.server.controls.PasswordPolicyResponseControl;
039    import org.opends.server.controls.PasswordPolicyWarningType;
040    import org.opends.server.protocols.asn1.ASN1Element;
041    import org.opends.server.protocols.asn1.ASN1OctetString;
042    import org.opends.server.protocols.asn1.ASN1Sequence;
043    import org.opends.server.protocols.ldap.ExtendedRequestProtocolOp;
044    import org.opends.server.protocols.ldap.ExtendedResponseProtocolOp;
045    import org.opends.server.protocols.ldap.LDAPControl;
046    import org.opends.server.protocols.ldap.LDAPMessage;
047    import org.opends.server.protocols.ldap.LDAPResultCode;
048    import org.opends.server.protocols.ldap.UnbindRequestProtocolOp;
049    import org.opends.server.types.DN;
050    import org.opends.server.types.NullOutputStream;
051    import org.opends.server.util.EmbeddedUtils;
052    import org.opends.server.util.args.ArgumentException;
053    import org.opends.server.util.args.ArgumentParser;
054    import org.opends.server.util.args.BooleanArgument;
055    import org.opends.server.util.args.FileBasedArgument;
056    import org.opends.server.util.args.IntegerArgument;
057    import org.opends.server.util.args.StringArgument;
058    
059    import static org.opends.server.extensions.ExtensionsConstants.*;
060    import static org.opends.messages.ToolMessages.*;
061    import static org.opends.server.util.ServerConstants.*;
062    import static org.opends.server.util.StaticUtils.*;
063    import static org.opends.server.tools.ToolConstants.*;
064    
065    
066    
067    /**
068     * This program provides a utility that uses the LDAP password modify extended
069     * operation to change the password for a user.  It exposes the three primary
070     * options available for this operation, which are:
071     *
072     * <UL>
073     *   <LI>The user identity whose password should be changed.</LI>
074     *   <LI>The current password for the user.</LI>
075     *   <LI>The new password for the user.
076     * </UL>
077     *
078     * All of these are optional components that may be included or omitted from the
079     * request.
080     */
081    public class LDAPPasswordModify
082    {
083      /**
084       * The fully-qualified name of this class.
085       */
086      private static final String CLASS_NAME =
087           "org.opends.server.tools.LDAPPasswordModify";
088    
089    
090    
091    
092      /**
093       * Parses the command-line arguments, establishes a connection to the
094       * Directory Server, sends the password modify request, and reads the
095       * response.
096       *
097       * @param  args  The command-line arguments provided to this program.
098       */
099      public static void main(String[] args)
100      {
101        int returnCode = mainPasswordModify(args, true, System.out, System.err);
102        if (returnCode != 0)
103        {
104          System.exit(filterExitCode(returnCode));
105        }
106      }
107    
108    
109    
110      /**
111       * Parses the command-line arguments, establishes a connection to the
112       * Directory Server, sends the password modify request, and reads the
113       * response.
114       *
115       * @param  args  The command-line arguments provided to this program.
116       *
117       * @return  An integer value of zero if everything completed successfully, or
118       *          a nonzero value if an error occurred.
119       */
120      public static int mainPasswordModify(String[] args)
121      {
122        return mainPasswordModify(args, true, System.out, System.err);
123      }
124    
125    
126    
127      /**
128       * Parses the command-line arguments, establishes a connection to the
129       * Directory Server, sends the password modify request, and reads the
130       * response.
131       *
132       * @param  args              The command-line arguments provided to this
133       *                           program.
134       * @param  initializeServer  Indicates whether to initialize the server.
135       * @param  outStream         The output stream to use for standard output.
136       * @param  errStream         The output stream to use for standard error.
137       *
138       * @return  An integer value of zero if everything completed successfully, or
139       *          a nonzero value if an error occurred.
140       */
141      public static int mainPasswordModify(String[] args, boolean initializeServer,
142                                           OutputStream outStream,
143                                           OutputStream errStream)
144      {
145        PrintStream out;
146        if (outStream == null)
147        {
148          out = NullOutputStream.printStream();
149        }
150        else
151        {
152          out = new PrintStream(outStream);
153        }
154    
155        PrintStream err;
156        if (errStream == null)
157        {
158          err = NullOutputStream.printStream();
159        }
160        else
161        {
162          err = new PrintStream(errStream);
163        }
164    
165    
166        // Create the arguments that will be used by this program.
167        BooleanArgument   provideDNForAuthzID;
168        BooleanArgument   showUsage;
169        BooleanArgument   sslBlindTrust;
170        BooleanArgument   useSSL;
171        BooleanArgument   useStartTLS;
172        FileBasedArgument bindPWFile;
173        StringArgument    certNickname           = null;
174        FileBasedArgument currentPWFile;
175        FileBasedArgument newPWFile;
176        FileBasedArgument sslKeyStorePINFile;
177        FileBasedArgument sslTrustStorePINFile;
178        IntegerArgument   ldapPort;
179        StringArgument    authzID;
180        StringArgument    bindDN;
181        StringArgument    bindPW;
182        StringArgument    controlStr;
183        StringArgument    currentPW;
184        StringArgument    ldapHost;
185        StringArgument    newPW;
186        StringArgument    sslKeyStore;
187        StringArgument    sslKeyStorePIN;
188        StringArgument    sslTrustStore;
189        StringArgument    sslTrustStorePIN;
190        StringArgument    propertiesFileArgument;
191        BooleanArgument   noPropertiesFileArgument;
192    
193    
194        // Initialize the argument parser.
195        Message toolDescription = INFO_LDAPPWMOD_TOOL_DESCRIPTION.get();
196        ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription,
197                                                      false);
198    
199        try
200        {
201          propertiesFileArgument = new StringArgument("propertiesFilePath",
202              null, OPTION_LONG_PROP_FILE_PATH,
203              false, false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null,
204              INFO_DESCRIPTION_PROP_FILE_PATH.get());
205          argParser.addArgument(propertiesFileArgument);
206          argParser.setFilePropertiesArgument(propertiesFileArgument);
207    
208          noPropertiesFileArgument = new BooleanArgument(
209              "noPropertiesFileArgument", null, OPTION_LONG_NO_PROP_FILE,
210              INFO_DESCRIPTION_NO_PROP_FILE.get());
211          argParser.addArgument(noPropertiesFileArgument);
212          argParser.setNoPropertiesFileArgument(noPropertiesFileArgument);
213    
214          ldapHost = new StringArgument("ldaphost", OPTION_SHORT_HOST,
215                                        OPTION_LONG_HOST, false, false,
216                                        true, INFO_HOST_PLACEHOLDER.get(),
217                                        "127.0.0.1", null,
218                                        INFO_LDAPPWMOD_DESCRIPTION_HOST.get());
219          ldapHost.setPropertyName(OPTION_LONG_HOST);
220          argParser.addArgument(ldapHost);
221    
222    
223          ldapPort = new IntegerArgument(
224                  "ldapport", OPTION_SHORT_PORT,
225                  OPTION_LONG_PORT, false, false,
226                  true, INFO_PORT_PLACEHOLDER.get(), 389,
227                  null, true, 1, true,
228                  65535, INFO_LDAPPWMOD_DESCRIPTION_PORT.get());
229          ldapPort.setPropertyName(OPTION_LONG_PORT);
230          argParser.addArgument(ldapPort);
231    
232    
233          useSSL = new BooleanArgument("usessl", OPTION_SHORT_USE_SSL,
234                                       OPTION_LONG_USE_SSL,
235                                       INFO_LDAPPWMOD_DESCRIPTION_USE_SSL.get());
236          useSSL.setPropertyName(OPTION_LONG_USE_SSL);
237          argParser.addArgument(useSSL);
238    
239    
240          useStartTLS = new BooleanArgument("usestarttls", OPTION_SHORT_START_TLS,
241                                 OPTION_LONG_START_TLS,
242                                 INFO_LDAPPWMOD_DESCRIPTION_USE_STARTTLS.get());
243          useStartTLS.setPropertyName(OPTION_LONG_START_TLS);
244          argParser.addArgument(useStartTLS);
245    
246    
247          bindDN = new StringArgument("binddn", OPTION_SHORT_BINDDN,
248                                      OPTION_LONG_BINDDN, false, false, true,
249                                      INFO_BINDDN_PLACEHOLDER.get(), null, null,
250                                      INFO_LDAPPWMOD_DESCRIPTION_BIND_DN.get());
251          bindDN.setPropertyName(OPTION_LONG_BINDDN);
252          argParser.addArgument(bindDN);
253    
254    
255          bindPW = new StringArgument("bindpw", OPTION_SHORT_BINDPWD,
256                                      OPTION_LONG_BINDPWD, false, false,
257                                      true, INFO_BINDPWD_PLACEHOLDER.get(), null,
258                                      null,
259                                      INFO_LDAPPWMOD_DESCRIPTION_BIND_PW.get());
260          bindPW.setPropertyName(OPTION_LONG_BINDPWD);
261          argParser.addArgument(bindPW);
262    
263    
264          bindPWFile =
265               new FileBasedArgument("bindpwfile", OPTION_SHORT_BINDPWD_FILE,
266                                     OPTION_LONG_BINDPWD_FILE, false,
267                                     false, INFO_BINDPWD_FILE_PLACEHOLDER.get(),
268                                     null, null,
269                                     INFO_LDAPPWMOD_DESCRIPTION_BIND_PW_FILE.get());
270          bindPWFile.setPropertyName(OPTION_LONG_BINDPWD_FILE);
271          argParser.addArgument(bindPWFile);
272    
273    
274          authzID = new StringArgument("authzid", 'a', "authzID", false, false,
275                                       true, INFO_PROXYAUTHID_PLACEHOLDER.get(),
276                                       null, null,
277                                       INFO_LDAPPWMOD_DESCRIPTION_AUTHZID.get());
278          authzID.setPropertyName("authzID");
279          argParser.addArgument(authzID);
280    
281    
282          provideDNForAuthzID =
283               new BooleanArgument("providednforauthzid", 'A',"provideDNForAuthzID",
284                        INFO_LDAPPWMOD_DESCRIPTION_PROVIDE_DN_FOR_AUTHZID.get());
285          provideDNForAuthzID.setPropertyName("provideDNForAuthzID");
286          argParser.addArgument(provideDNForAuthzID);
287    
288    
289          newPW = new StringArgument("newpw", 'n', "newPassword", false, false,
290                                     true, INFO_NEW_PASSWORD_PLACEHOLDER.get(),
291                                     null, null,
292                                     INFO_LDAPPWMOD_DESCRIPTION_NEWPW.get());
293          newPW.setPropertyName("newPassword");
294          argParser.addArgument(newPW);
295    
296    
297          newPWFile = new FileBasedArgument(
298                  "newpwfile", 'N', "newPasswordFile",
299                  false, false, INFO_FILE_PLACEHOLDER.get(), null, null,
300                  INFO_LDAPPWMOD_DESCRIPTION_NEWPWFILE.get());
301          newPWFile.setPropertyName("newPasswordFile");
302          argParser.addArgument(newPWFile);
303    
304    
305          currentPW =
306               new StringArgument("currentpw", 'c', "currentPassword", false, false,
307                                  true, INFO_CURRENT_PASSWORD_PLACEHOLDER.get(),
308                                  null,  null,
309                                  INFO_LDAPPWMOD_DESCRIPTION_CURRENTPW.get());
310          currentPW.setPropertyName("currentPassword");
311          argParser.addArgument(currentPW);
312    
313    
314          currentPWFile =
315               new FileBasedArgument(
316                       "currentpwfile", 'C', "currentPasswordFile",
317                       false, false, INFO_FILE_PLACEHOLDER.get(), null, null,
318                       INFO_LDAPPWMOD_DESCRIPTION_CURRENTPWFILE.get());
319          currentPWFile.setPropertyName("currentPasswordFile");
320          argParser.addArgument(currentPWFile);
321    
322    
323          sslBlindTrust =
324               new BooleanArgument("blindtrust", 'X', "trustAll",
325                                   INFO_LDAPPWMOD_DESCRIPTION_BLIND_TRUST.get());
326          sslBlindTrust.setPropertyName("trustAll");
327          argParser.addArgument(sslBlindTrust);
328    
329    
330          sslKeyStore =
331               new StringArgument("keystorepath", OPTION_SHORT_KEYSTOREPATH,
332                                  OPTION_LONG_KEYSTOREPATH, false, false,
333                                  true, INFO_KEYSTOREPATH_PLACEHOLDER.get(), null,
334                                  null,
335                                  INFO_LDAPPWMOD_DESCRIPTION_KEYSTORE.get());
336          sslKeyStore.setPropertyName(OPTION_LONG_KEYSTOREPATH);
337          argParser.addArgument(sslKeyStore);
338    
339    
340          sslKeyStorePIN =
341               new StringArgument("keystorepassword",
342                                  OPTION_SHORT_KEYSTORE_PWD,
343                                  OPTION_LONG_KEYSTORE_PWD ,
344                                  false, false, true,
345                                  INFO_KEYSTORE_PWD_PLACEHOLDER.get(),
346                                  null, null,
347                                  INFO_LDAPPWMOD_DESCRIPTION_KEYSTORE_PIN.get());
348          sslKeyStorePIN.setPropertyName(OPTION_LONG_KEYSTORE_PWD);
349          argParser.addArgument(sslKeyStorePIN);
350    
351    
352          sslKeyStorePINFile =
353               new FileBasedArgument(
354                       "keystorepasswordfile",
355                       OPTION_SHORT_KEYSTORE_PWD_FILE,
356                       OPTION_LONG_KEYSTORE_PWD_FILE,
357                       false, false, INFO_KEYSTORE_PWD_FILE_PLACEHOLDER.get(),
358                       null, null,
359                       INFO_LDAPPWMOD_DESCRIPTION_KEYSTORE_PINFILE.get());
360          sslKeyStorePINFile.setPropertyName(OPTION_LONG_KEYSTORE_PWD_FILE);
361          argParser.addArgument(sslKeyStorePINFile);
362    
363          certNickname = new StringArgument("certnickname", null, "certNickname",
364              false, false, true, INFO_NICKNAME_PLACEHOLDER.get(), null, null,
365              INFO_DESCRIPTION_CERT_NICKNAME.get());
366          certNickname.setPropertyName("certNickname");
367          argParser.addArgument(certNickname);
368    
369    
370    
371          sslTrustStore =
372               new StringArgument("truststorepath",
373                                  OPTION_SHORT_TRUSTSTOREPATH,
374                                  OPTION_LONG_TRUSTSTOREPATH, false,
375                                  false, true,
376                                  INFO_TRUSTSTOREPATH_PLACEHOLDER.get(), null, null,
377                                  INFO_LDAPPWMOD_DESCRIPTION_TRUSTSTORE.get());
378          sslTrustStore.setPropertyName(OPTION_LONG_TRUSTSTOREPATH);
379          argParser.addArgument(sslTrustStore);
380    
381    
382          sslTrustStorePIN =
383               new StringArgument("truststorepassword", null,
384                                  OPTION_LONG_TRUSTSTORE_PWD,
385                                  false, false, true,
386                                  INFO_TRUSTSTORE_PWD_PLACEHOLDER.get(), null, null,
387                                  INFO_LDAPPWMOD_DESCRIPTION_TRUSTSTORE_PIN.get());
388          sslTrustStorePIN.setPropertyName(OPTION_LONG_TRUSTSTORE_PWD);
389          argParser.addArgument(sslTrustStorePIN);
390    
391    
392          sslTrustStorePINFile =
393               new FileBasedArgument("truststorepasswordfile",
394                        OPTION_SHORT_TRUSTSTORE_PWD_FILE,
395                        OPTION_LONG_TRUSTSTORE_PWD_FILE, false, false,
396                        INFO_TRUSTSTORE_PWD_FILE_PLACEHOLDER.get(), null,
397                        null, INFO_LDAPPWMOD_DESCRIPTION_TRUSTSTORE_PINFILE.get());
398          sslTrustStorePINFile.setPropertyName(OPTION_LONG_TRUSTSTORE_PWD_FILE);
399          argParser.addArgument(sslTrustStorePINFile);
400    
401    
402          controlStr =
403               new StringArgument("control", 'J', "control", false, true, true,
404                        INFO_LDAP_CONTROL_PLACEHOLDER.get(),
405                        null, null, INFO_DESCRIPTION_CONTROLS.get());
406          controlStr.setPropertyName("control");
407          argParser.addArgument(controlStr);
408    
409    
410          showUsage = new BooleanArgument("help", OPTION_SHORT_HELP,
411                                          OPTION_LONG_HELP,
412                                          INFO_DESCRIPTION_USAGE.get());
413          argParser.addArgument(showUsage);
414          argParser.setUsageArgument(showUsage, out);
415        }
416        catch (ArgumentException ae)
417        {
418          Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
419    
420          err.println(wrapText(message, MAX_LINE_WIDTH));
421          return 1;
422        }
423    
424    
425        // Parse the command-line arguments provided to this program.
426        try
427        {
428          argParser.parseArguments(args);
429        }
430        catch (ArgumentException ae)
431        {
432          Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
433    
434          err.println(wrapText(message, MAX_LINE_WIDTH));
435          err.println(argParser.getUsage());
436          return 1;
437        }
438    
439    
440        // If the usage or version argument was provided,
441        // then we don't need to do anything else.
442        if (argParser.usageOrVersionDisplayed())
443        {
444          return 0;
445        }
446    
447    
448        // Make sure that the user didn't specify any conflicting arguments.
449        if (bindPW.isPresent() && bindPWFile.isPresent())
450        {
451          Message message = ERR_LDAPPWMOD_CONFLICTING_ARGS.get(
452                  bindPW.getLongIdentifier(),
453                  bindPWFile.getLongIdentifier());
454          err.println(wrapText(message, MAX_LINE_WIDTH));
455          return 1;
456        }
457    
458        if (newPW.isPresent() && newPWFile.isPresent())
459        {
460          Message message = ERR_LDAPPWMOD_CONFLICTING_ARGS.get(
461                  newPW.getLongIdentifier(),
462                  newPWFile.getLongIdentifier());
463          err.println(wrapText(message, MAX_LINE_WIDTH));
464          return 1;
465        }
466    
467        if (currentPW.isPresent() && currentPWFile.isPresent())
468        {
469          Message message = ERR_LDAPPWMOD_CONFLICTING_ARGS.get(
470                  currentPW.getLongIdentifier(),
471                  currentPWFile.getLongIdentifier());
472          err.println(wrapText(message, MAX_LINE_WIDTH));
473          return 1;
474        }
475    
476        if (useSSL.isPresent() && useStartTLS.isPresent())
477        {
478          Message message = ERR_LDAPPWMOD_CONFLICTING_ARGS.get(
479                  useSSL.getLongIdentifier(),
480                  useStartTLS.getLongIdentifier());
481          err.println(wrapText(message, MAX_LINE_WIDTH));
482          return 1;
483        }
484    
485        if (sslKeyStorePIN.isPresent() && sslKeyStorePINFile.isPresent())
486        {
487          Message message = ERR_TOOL_CONFLICTING_ARGS.get(
488                  sslKeyStorePIN.getLongIdentifier(),
489                  sslKeyStorePINFile.getLongIdentifier());
490          err.println(wrapText(message, MAX_LINE_WIDTH));
491          return 1;
492        }
493    
494        if (sslTrustStorePIN.isPresent() && sslTrustStorePINFile.isPresent())
495        {
496          Message message = ERR_TOOL_CONFLICTING_ARGS.get(
497                  sslTrustStorePIN.getLongIdentifier(),
498                  sslTrustStorePINFile.getLongIdentifier());
499          err.println(wrapText(message, MAX_LINE_WIDTH));
500          return 1;
501        }
502    
503    
504        // If a bind DN was provided, make sure that a password was given.  If a
505        // password was given, make sure a bind DN was provided.  If neither were
506        // given, then make sure that an authorization ID and the current password
507        // were provided.
508        if (bindDN.isPresent())
509        {
510          if (! (bindPW.isPresent() || bindPWFile.isPresent()))
511          {
512            Message message = ERR_LDAPPWMOD_BIND_DN_AND_PW_MUST_BE_TOGETHER.get();
513    
514            err.println(wrapText(message, MAX_LINE_WIDTH));
515            err.println(argParser.getUsage());
516            return 1;
517          }
518        }
519        else if (bindPW.isPresent() || bindPWFile.isPresent())
520        {
521          Message message = ERR_LDAPPWMOD_BIND_DN_AND_PW_MUST_BE_TOGETHER.get();
522    
523          err.println(wrapText(message, MAX_LINE_WIDTH));
524          err.println(argParser.getUsage());
525          return 1;
526        }
527        else
528        {
529          if (provideDNForAuthzID.isPresent())
530          {
531            Message message =
532                    ERR_LDAPPWMOD_DEPENDENT_ARGS.get(
533                            provideDNForAuthzID.getLongIdentifier(),
534                            bindDN.getLongIdentifier());
535            err.println(wrapText(message, MAX_LINE_WIDTH));
536            err.println(argParser.getUsage());
537            return 1;
538          }
539    
540          if (! (authzID.isPresent() &&
541                 (currentPW.isPresent() || currentPWFile.isPresent())))
542          {
543            Message message =
544                    ERR_LDAPPWMOD_ANON_REQUIRES_AUTHZID_AND_CURRENTPW.get();
545            err.println(wrapText(message, MAX_LINE_WIDTH));
546            err.println(argParser.getUsage());
547            return 1;
548          }
549        }
550    
551    
552        // Get the host and port.
553        String host = ldapHost.getValue();
554        int    port;
555        try
556        {
557          port = ldapPort.getIntValue();
558        }
559        catch (Exception e)
560        {
561          // This should never happen.
562          err.println(e);
563          return 1;
564        }
565    
566    
567        // If a control string was provided, then decode the requested controls.
568        ArrayList<LDAPControl> controls = new ArrayList<LDAPControl>();
569        if(controlStr.isPresent())
570        {
571          for (String ctrlString : controlStr.getValues())
572          {
573            LDAPControl ctrl = LDAPToolUtils.getControl(ctrlString, err);
574            if(ctrl == null)
575            {
576              Message message = ERR_TOOL_INVALID_CONTROL_STRING.get(ctrlString);
577              err.println(wrapText(message, MAX_LINE_WIDTH));
578              err.println(argParser.getUsage());
579              return 1;
580            }
581            controls.add(ctrl);
582          }
583        }
584    
585    
586        // Perform a basic Directory Server bootstrap if appropriate.
587        if (initializeServer)
588        {
589          EmbeddedUtils.initializeForClientUse();
590        }
591    
592    
593        // Establish a connection to the Directory Server.
594        AtomicInteger nextMessageID = new AtomicInteger(1);
595        LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
596        connectionOptions.setUseSSL(useSSL.isPresent());
597        connectionOptions.setStartTLS(useStartTLS.isPresent());
598        connectionOptions.setVersionNumber(3);
599        if(connectionOptions.useSSL() || connectionOptions.useStartTLS())
600        {
601          String keyPIN = null;
602          if (sslKeyStorePIN.isPresent())
603          {
604            keyPIN = sslKeyStorePIN.getValue();
605          }
606          else if (sslKeyStorePINFile.isPresent())
607          {
608            keyPIN = sslKeyStorePINFile.getValue();
609          }
610    
611          String trustPIN = null;
612          if (sslTrustStorePIN.isPresent())
613          {
614            trustPIN = sslTrustStorePIN.getValue();
615          }
616          else if (sslTrustStorePINFile.isPresent())
617          {
618            trustPIN = sslTrustStorePINFile.getValue();
619          }
620    
621          try
622          {
623            String clientAlias;
624            if (certNickname.isPresent())
625            {
626              clientAlias = certNickname.getValue();
627            }
628            else
629            {
630              clientAlias = null;
631            }
632            SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
633            sslConnectionFactory.init(sslBlindTrust.isPresent(),
634                                      sslKeyStore.getValue(), keyPIN, clientAlias,
635                                      sslTrustStore.getValue(), trustPIN);
636            connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
637          }
638          catch (Exception e)
639          {
640            Message message =
641                    ERR_LDAPPWMOD_ERROR_INITIALIZING_SSL.get(String.valueOf(e));
642            err.println(wrapText(message, MAX_LINE_WIDTH));
643            return 1;
644          }
645        }
646    
647        LDAPConnection connection = new LDAPConnection(host, port,
648                                                       connectionOptions, out, err);
649        String dn;
650        String pw;
651        if (bindPW.isPresent())
652        {
653          dn = bindDN.getValue();
654          pw = bindPW.getValue();
655        }
656        else if (bindPWFile.isPresent())
657        {
658          dn = bindDN.getValue();
659          pw = bindPWFile.getValue();
660        }
661        else
662        {
663          dn = null;
664          pw = null;
665        }
666    
667        try
668        {
669          connection.connectToHost(dn, pw, nextMessageID);
670        }
671        catch (LDAPConnectionException lce)
672        {
673          Message message = ERR_LDAPPWMOD_CANNOT_CONNECT.get(lce.getMessage());
674          err.println(wrapText(message, MAX_LINE_WIDTH));
675          return lce.getResultCode();
676        }
677    
678        LDAPReader reader = connection.getLDAPReader();
679        LDAPWriter writer = connection.getLDAPWriter();
680    
681    
682        // Construct the password modify request.
683        ArrayList<ASN1Element> requestElements = new ArrayList<ASN1Element>(3);
684        if (authzID.isPresent())
685        {
686          requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_USER_ID,
687                                                  authzID.getValue()));
688        }
689        else if (provideDNForAuthzID.isPresent())
690        {
691          requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_USER_ID,
692                                                  "dn:" + dn));
693        }
694    
695        if (currentPW.isPresent())
696        {
697          requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_OLD_PASSWORD,
698                                                  currentPW.getValue()));
699        }
700        else if (currentPWFile.isPresent())
701        {
702          requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_OLD_PASSWORD,
703                                                  currentPWFile.getValue()));
704        }
705        else if (provideDNForAuthzID.isPresent())
706        {
707          requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_OLD_PASSWORD,
708                                                  pw));
709        }
710    
711        if (newPW.isPresent())
712        {
713          requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_NEW_PASSWORD,
714                                                  newPW.getValue()));
715        }
716        else if (newPWFile.isPresent())
717        {
718          requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_NEW_PASSWORD,
719                                                  newPWFile.getValue()));
720        }
721    
722        ASN1OctetString requestValue =
723             new ASN1OctetString(new ASN1Sequence(requestElements).encode());
724    
725        ExtendedRequestProtocolOp extendedRequest =
726             new ExtendedRequestProtocolOp(OID_PASSWORD_MODIFY_REQUEST,
727                                           requestValue);
728        LDAPMessage requestMessage =
729             new LDAPMessage(nextMessageID.getAndIncrement(), extendedRequest,
730                             controls);
731    
732    
733        // Send the request to the server and read the response.
734        try
735        {
736          writer.writeMessage(requestMessage);
737        }
738        catch (Exception e)
739        {
740          Message message = ERR_LDAPPWMOD_CANNOT_SEND_PWMOD_REQUEST.get(
741                  String.valueOf(e));
742          err.println(wrapText(message, MAX_LINE_WIDTH));
743    
744          try
745          {
746            requestMessage = new LDAPMessage(nextMessageID.getAndIncrement(),
747                                             new UnbindRequestProtocolOp());
748            writer.writeMessage(requestMessage);
749          }
750          catch (Exception e2) {}
751    
752          try
753          {
754            reader.close();
755            writer.close();
756          } catch (Exception e2) {}
757    
758          return 1;
759        }
760    
761    
762        // Read the response from the server.
763        LDAPMessage responseMessage = null;
764        try
765        {
766          responseMessage = reader.readMessage();
767        }
768        catch (Exception e)
769        {
770          Message message = ERR_LDAPPWMOD_CANNOT_READ_PWMOD_RESPONSE.get(
771                  String.valueOf(e));
772          err.println(wrapText(message, MAX_LINE_WIDTH));
773    
774          try
775          {
776            requestMessage = new LDAPMessage(nextMessageID.getAndIncrement(),
777                                             new UnbindRequestProtocolOp());
778            writer.writeMessage(requestMessage);
779          }
780          catch (Exception e2) {}
781    
782          try
783          {
784            reader.close();
785            writer.close();
786          } catch (Exception e2) {}
787    
788          return 1;
789        }
790    
791    
792        // Make sure that the response was acceptable.
793        ExtendedResponseProtocolOp extendedResponse =
794             responseMessage.getExtendedResponseProtocolOp();
795        int resultCode = extendedResponse.getResultCode();
796        if (resultCode != LDAPResultCode.SUCCESS)
797        {
798          Message message = ERR_LDAPPWMOD_FAILED.get(resultCode);
799          err.println(wrapText(message, MAX_LINE_WIDTH));
800    
801          Message errorMessage = extendedResponse.getErrorMessage();
802          if ((errorMessage != null) && (errorMessage.length() > 0))
803          {
804    
805            message = ERR_LDAPPWMOD_FAILURE_ERROR_MESSAGE.get(errorMessage);
806            err.println(wrapText(message, MAX_LINE_WIDTH));
807          }
808    
809          DN matchedDN = extendedResponse.getMatchedDN();
810          if (matchedDN != null)
811          {
812    
813            message = ERR_LDAPPWMOD_FAILURE_MATCHED_DN.get(matchedDN.toString());
814            err.println(wrapText(message, MAX_LINE_WIDTH));
815          }
816    
817          try
818          {
819            requestMessage = new LDAPMessage(nextMessageID.getAndIncrement(),
820                                             new UnbindRequestProtocolOp());
821            writer.writeMessage(requestMessage);
822          }
823          catch (Exception e) {}
824    
825          try
826          {
827            reader.close();
828            writer.close();
829          } catch (Exception e) {}
830    
831          return resultCode;
832        }
833        else
834        {
835          Message message = INFO_LDAPPWMOD_SUCCESSFUL.get();
836          out.println(wrapText(message, MAX_LINE_WIDTH));
837    
838          Message additionalInfo = extendedResponse.getErrorMessage();
839          if ((additionalInfo != null) && (additionalInfo.length() > 0))
840          {
841    
842            message = INFO_LDAPPWMOD_ADDITIONAL_INFO.get(additionalInfo);
843            out.println(wrapText(message, MAX_LINE_WIDTH));
844          }
845        }
846    
847    
848        // See if the response included any controls that we recognize, and if so
849        // then handle them.
850        ArrayList<LDAPControl> responseControls = responseMessage.getControls();
851        if (responseControls != null)
852        {
853          for (LDAPControl c : responseControls)
854          {
855            if (c.getOID().equals(OID_PASSWORD_POLICY_CONTROL))
856            {
857              try
858              {
859                PasswordPolicyResponseControl pwPolicyControl =
860                     PasswordPolicyResponseControl.decodeControl(c.getControl());
861    
862                PasswordPolicyWarningType pwPolicyWarningType =
863                     pwPolicyControl.getWarningType();
864                if (pwPolicyWarningType != null)
865                {
866                  Message message = INFO_LDAPPWMOD_PWPOLICY_WARNING.get(
867                          pwPolicyWarningType.toString(),
868                          pwPolicyControl.getWarningValue());
869                  out.println(wrapText(message, MAX_LINE_WIDTH));
870                }
871    
872                PasswordPolicyErrorType pwPolicyErrorType =
873                     pwPolicyControl.getErrorType();
874                if (pwPolicyErrorType != null)
875                {
876                  Message message = INFO_LDAPPWMOD_PWPOLICY_ERROR.get(
877                          pwPolicyErrorType.toString());
878                  out.println(wrapText(message, MAX_LINE_WIDTH));
879                }
880              }
881              catch (Exception e)
882              {
883                Message message = ERR_LDAPPWMOD_CANNOT_DECODE_PWPOLICY_CONTROL.get(
884                        String.valueOf(e));
885                err.println(wrapText(message, MAX_LINE_WIDTH));
886              }
887            }
888          }
889        }
890    
891    
892        // See if the response included a generated password.
893        ASN1OctetString responseValue = extendedResponse.getValue();
894        if (responseValue != null)
895        {
896          try
897          {
898            ASN1Sequence responseSequence =
899                 ASN1Sequence.decodeAsSequence(responseValue.value());
900            for (ASN1Element e : responseSequence.elements())
901            {
902              if (e.getType() == TYPE_PASSWORD_MODIFY_GENERATED_PASSWORD)
903              {
904                Message message = INFO_LDAPPWMOD_GENERATED_PASSWORD.get(
905                        e.decodeAsOctetString().stringValue());
906                out.println(wrapText(message, MAX_LINE_WIDTH));
907              }
908              else
909              {
910                Message message = ERR_LDAPPWMOD_UNRECOGNIZED_VALUE_TYPE.get(
911                        byteToHex(e.getType()));
912                err.println(wrapText(message, MAX_LINE_WIDTH));
913              }
914            }
915          }
916          catch (Exception e)
917          {
918            Message message = ERR_LDAPPWMOD_COULD_NOT_DECODE_RESPONSE_VALUE.get(
919                    String.valueOf(e));
920            err.println(wrapText(message, MAX_LINE_WIDTH));
921    
922            try
923            {
924              requestMessage = new LDAPMessage(nextMessageID.getAndIncrement(),
925                                               new UnbindRequestProtocolOp());
926              writer.writeMessage(requestMessage);
927            }
928            catch (Exception e2) {}
929    
930            try
931            {
932              reader.close();
933              writer.close();
934            } catch (Exception e2) {}
935    
936            return 1;
937          }
938        }
939    
940    
941        // Unbind from the server and close the connection.
942        try
943        {
944          requestMessage = new LDAPMessage(nextMessageID.getAndIncrement(),
945                                           new UnbindRequestProtocolOp());
946          writer.writeMessage(requestMessage);
947        }
948        catch (Exception e) {}
949    
950        try
951        {
952          reader.close();
953          writer.close();
954        } catch (Exception e) {}
955    
956        return 0;
957      }
958    }
959