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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * Copyright 2013-2015 ForgeRock AS. 024 * 025 */ 026 027package org.forgerock.opendj.examples; 028 029import static org.forgerock.opendj.ldap.LDAPConnectionFactory.*; 030 031import org.forgerock.opendj.ldap.Connection; 032import org.forgerock.opendj.ldap.DN; 033import org.forgerock.opendj.ldap.LdapException; 034import org.forgerock.opendj.ldap.LDAPConnectionFactory; 035import org.forgerock.opendj.ldap.ModificationType; 036import org.forgerock.opendj.ldap.ResultCode; 037import org.forgerock.opendj.ldap.SSLContextBuilder; 038import org.forgerock.opendj.ldap.TrustManagers; 039import org.forgerock.opendj.ldap.requests.ModifyRequest; 040import org.forgerock.opendj.ldap.requests.Requests; 041import org.forgerock.util.Options; 042 043import javax.net.ssl.SSLContext; 044import java.nio.charset.Charset; 045import java.security.GeneralSecurityException; 046 047/** 048 * This command-line client demonstrates how to reset a user password in 049 * Microsoft Active Directory. 050 * <br> 051 * The client takes as arguments the host and port of the Active Directory 052 * server, a flag indicating whether this is a self-reset (user changing own 053 * password) or an administrative reset (administrator changing a password), 054 * the DN and password of the user performing the reset, and target user DN 055 * and new user password. 056 */ 057public final class PasswordResetForAD { 058 059 /** 060 * Reset a user password in Microsoft Active Directory. 061 * <br> 062 * The connection should be LDAPS, not LDAP, in order to perform the 063 * modification. 064 * 065 * @param args The command line arguments: host, port, "admin"|"self", 066 * DN, password, targetDN, newPassword 067 */ 068 public static void main(final String[] args) { 069 // --- JCite main --- 070 if (args.length != 7) { 071 System.err.println("Usage: host port \"admin\"|\"self\" DN " 072 + "password targetDN newPassword"); 073 System.err.println("For example: ad.example.com 636 admin " 074 + "cn=administrator,cn=Users,DC=ad,DC=example,DC=com " 075 + "Secret123 cn=testuser,cn=Users,DC=ad,DC=example,DC=com " 076 + "NewP4s5w0rd"); 077 System.exit(1); 078 } 079 final String host = args[0]; 080 final int port = Integer.parseInt(args[1]); 081 final String mode = args[2]; 082 final String bindDN = args[3]; 083 final String bindPassword = args[4]; 084 final String targetDN = args[5]; 085 final String newPassword = args[6]; 086 087 Connection connection = null; 088 try { 089 final LDAPConnectionFactory factory = 090 new LDAPConnectionFactory(host, port, getTrustAllOptions()); 091 connection = factory.getConnection(); 092 connection.bind(bindDN, bindPassword.toCharArray()); 093 094 ModifyRequest request = 095 Requests.newModifyRequest(DN.valueOf(targetDN)); 096 String passwordAttribute = "unicodePwd"; 097 098 if ("admin".equalsIgnoreCase(mode)) { 099 // Request modify, replacing the password with the new. 100 101 request.addModification( 102 ModificationType.REPLACE, 103 passwordAttribute, 104 encodePassword(newPassword) 105 ); 106 } else if ("self".equalsIgnoreCase(mode)) { 107 // Request modify, deleting the old password, adding the new. 108 109 // The default password policy for Active Directory domain 110 // controller systems sets minimum password age to 1 (day). 111 // If you get a constraint violation error when trying this 112 // example, set this minimum password age to 0 by executing 113 // cmd.exe as Administrator and entering the following 114 // command at the prompt: 115 // 116 // net accounts /MINPWAGE:0 117 118 request.addModification( 119 ModificationType.DELETE, 120 passwordAttribute, 121 encodePassword(bindPassword) 122 ); 123 request.addModification( 124 ModificationType.ADD, 125 passwordAttribute, 126 encodePassword(newPassword) 127 ); 128 } else { 129 System.err.println("Mode must be admin or self, not " + mode); 130 System.exit(1); 131 } 132 133 connection.modify(request); 134 135 System.out.println("Successfully changed password for " 136 + targetDN + " to " + newPassword + "."); 137 } catch (final LdapException e) { 138 System.err.println(e.getMessage()); 139 System.exit(e.getResult().getResultCode().intValue()); 140 } catch (final GeneralSecurityException e) { 141 System.err.println(e.getMessage()); 142 System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue()); 143 } finally { 144 if (connection != null) { 145 connection.close(); 146 } 147 } 148 // --- JCite main --- 149 } 150 151 // --- JCite encodePassword --- 152 /** 153 * Encode new password in UTF-16LE format for use with Active Directory. 154 * 155 * @param password String representation of the password 156 * @return Byte array containing encoded password 157 */ 158 public static byte[] encodePassword(final String password) { 159 return ("\"" + password + "\"").getBytes(Charset.forName("UTF-16LE")); 160 } 161 // --- JCite encodePassword --- 162 163 /** 164 * For SSL the connection factory needs SSL context options. This 165 * implementation simply trusts all server certificates. 166 */ 167 private static Options getTrustAllOptions() throws GeneralSecurityException { 168 Options options = Options.defaultOptions(); 169 SSLContext sslContext = new SSLContextBuilder() 170 .setTrustManager(TrustManagers.trustAll()).getSSLContext(); 171 options.set(SSL_CONTEXT, sslContext); 172 return options; 173 } 174 175 /** 176 * Constructor not used. 177 */ 178 private PasswordResetForAD() { 179 // Not used. 180 } 181}