1 /*
2 * The contents of this file are subject to the terms of the Common Development and
3 * Distribution License (the License). You may not use this file except in compliance with the
4 * License.
5 *
6 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7 * specific language governing permission and limitations under the License.
8 *
9 * When distributing Covered Software, include this CDDL Header Notice in each file and include
10 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11 * Header, with the fields enclosed by brackets [] replaced by your own identifying
12 * information: "Portions Copyright [year] [name of copyright owner]".
13 *
14 * Copyright 2013-2015 ForgeRock AS.
15 */
16
17 package org.forgerock.opendj.examples;
18
19 import static org.forgerock.opendj.ldap.LDAPConnectionFactory.*;
20
21 import org.forgerock.opendj.ldap.Connection;
22 import org.forgerock.opendj.ldap.DN;
23 import org.forgerock.opendj.ldap.LdapException;
24 import org.forgerock.opendj.ldap.LDAPConnectionFactory;
25 import org.forgerock.opendj.ldap.ModificationType;
26 import org.forgerock.opendj.ldap.ResultCode;
27 import org.forgerock.opendj.ldap.SSLContextBuilder;
28 import org.forgerock.opendj.ldap.TrustManagers;
29 import org.forgerock.opendj.ldap.requests.ModifyRequest;
30 import org.forgerock.opendj.ldap.requests.Requests;
31 import org.forgerock.util.Options;
32
33 import javax.net.ssl.SSLContext;
34 import java.nio.charset.Charset;
35 import java.security.GeneralSecurityException;
36
37 /**
38 * This command-line client demonstrates how to reset a user password in
39 * Microsoft Active Directory.
40 * <br>
41 * The client takes as arguments the host and port of the Active Directory
42 * server, a flag indicating whether this is a self-reset (user changing own
43 * password) or an administrative reset (administrator changing a password),
44 * the DN and password of the user performing the reset, and target user DN
45 * and new user password.
46 */
47 public final class PasswordResetForAD {
48
49 /**
50 * Reset a user password in Microsoft Active Directory.
51 * <br>
52 * The connection should be LDAPS, not LDAP, in order to perform the
53 * modification.
54 *
55 * @param args The command line arguments: host, port, "admin"|"self",
56 * DN, password, targetDN, newPassword
57 */
58 public static void main(final String[] args) {
59 // --- JCite main ---
60 if (args.length != 7) {
61 System.err.println("Usage: host port \"admin\"|\"self\" DN "
62 + "password targetDN newPassword");
63 System.err.println("For example: ad.example.com 636 admin "
64 + "cn=administrator,cn=Users,DC=ad,DC=example,DC=com "
65 + "Secret123 cn=testuser,cn=Users,DC=ad,DC=example,DC=com "
66 + "NewP4s5w0rd");
67 System.exit(1);
68 }
69 final String host = args[0];
70 final int port = Integer.parseInt(args[1]);
71 final String mode = args[2];
72 final String bindDN = args[3];
73 final String bindPassword = args[4];
74 final String targetDN = args[5];
75 final String newPassword = args[6];
76
77 Connection connection = null;
78 try {
79 final LDAPConnectionFactory factory =
80 new LDAPConnectionFactory(host, port, getTrustAllOptions());
81 connection = factory.getConnection();
82 connection.bind(bindDN, bindPassword.toCharArray());
83
84 ModifyRequest request =
85 Requests.newModifyRequest(DN.valueOf(targetDN));
86 String passwordAttribute = "unicodePwd";
87
88 if ("admin".equalsIgnoreCase(mode)) {
89 // Request modify, replacing the password with the new.
90
91 request.addModification(
92 ModificationType.REPLACE,
93 passwordAttribute,
94 encodePassword(newPassword)
95 );
96 } else if ("self".equalsIgnoreCase(mode)) {
97 // Request modify, deleting the old password, adding the new.
98
99 // The default password policy for Active Directory domain
100 // controller systems sets minimum password age to 1 (day).
101 // If you get a constraint violation error when trying this
102 // example, set this minimum password age to 0 by executing
103 // cmd.exe as Administrator and entering the following
104 // command at the prompt:
105 //
106 // net accounts /MINPWAGE:0
107
108 request.addModification(
109 ModificationType.DELETE,
110 passwordAttribute,
111 encodePassword(bindPassword)
112 );
113 request.addModification(
114 ModificationType.ADD,
115 passwordAttribute,
116 encodePassword(newPassword)
117 );
118 } else {
119 System.err.println("Mode must be admin or self, not " + mode);
120 System.exit(1);
121 }
122
123 connection.modify(request);
124
125 System.out.println("Successfully changed password for "
126 + targetDN + " to " + newPassword + ".");
127 } catch (final LdapException e) {
128 System.err.println(e.getMessage());
129 System.exit(e.getResult().getResultCode().intValue());
130 } catch (final GeneralSecurityException e) {
131 System.err.println(e.getMessage());
132 System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue());
133 } finally {
134 if (connection != null) {
135 connection.close();
136 }
137 }
138 // --- JCite main ---
139 }
140
141 // --- JCite encodePassword ---
142 /**
143 * Encode new password in UTF-16LE format for use with Active Directory.
144 *
145 * @param password String representation of the password
146 * @return Byte array containing encoded password
147 */
148 public static byte[] encodePassword(final String password) {
149 return ("\"" + password + "\"").getBytes(Charset.forName("UTF-16LE"));
150 }
151 // --- JCite encodePassword ---
152
153 /**
154 * For SSL the connection factory needs SSL context options. This
155 * implementation simply trusts all server certificates.
156 */
157 private static Options getTrustAllOptions() throws GeneralSecurityException {
158 Options options = Options.defaultOptions();
159 SSLContext sslContext = new SSLContextBuilder()
160 .setTrustManager(TrustManagers.trustAll()).getSSLContext();
161 options.set(SSL_CONTEXT, sslContext);
162 return options;
163 }
164
165 /**
166 * Constructor not used.
167 */
168 private PasswordResetForAD() {
169 // Not used.
170 }
171 }