Gonzo
Gonzo

Reputation:

Change AD Password with Java

I have a good connection to AD. I can authenticate and check error messages from failed auths.

The issue I'm having comes from trying to change the password. I have an LDAPContext established at this point (yes it is an SSL connection). The issue comes from not knowing what value to use in the "username" parameter. I've tried all variations I can think of and end up getting one of three errors:

A) NO_OBJECT - I'm assuming this means it is connecting to AD properly but can't find what I'm looking for.

B) DIR_ERROR - I'm assuming this means it can get into AD properly but doesn't know wtf I want it to do after that.

C) Some type of ref error that only happens when I don't qualify the DC, so I think that's pretty much a given.

Here is the code I am using:

public void changePassword(String username, String password) {
    ModificationItem[] mods = new ModificationItem[1];
    String newQuotedPassword = "\"" + password + "\"";
    byte[] newUnicodePassword = newQuotedPassword.getBytes();
    try {
        newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));
    try {

        ldapContext.modifyAttributes(username, mods);
    } catch (NamingException e) {
        System.out.println("Error changing password for '" + username + "': " + e.getMessage());
        e.printStackTrace();
    }           
}       

Upvotes: 3

Views: 13747

Answers (4)

Eng.Fouad
Eng.Fouad

Reputation: 117587

Here is a working example:

Main.java:

package io.fouad.ldap;

import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;

public class Main
{
    public static void main(String[] args)
    {
        final String LDAP_SERVERS = "ldap://AD_SERVER:636 ldap://AD_SERVER2:636"; // separated by single spaces
        final String LDAP_CONNECT_TIMEOUT_MS = "10000"; // 10 seconds
        final String LDAP_READ_TIMEOUT_MS = "10000"; // 10 seconds
        final String AUTHENTICATION_DOMAIN = "domain.com";
        final String USERNAME = "username";
        final String OLD_PASSWORD = "123";
        final String NEW_PASSWORD = "456";
        final String TARGET_BASE_DN = "dc=domain,dc=com";

        Hashtable<String, String> ldapEnv = new Hashtable<>();
        ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        ldapEnv.put(Context.PROVIDER_URL, LDAP_SERVERS);
        ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
        ldapEnv.put("java.naming.ldap.version", "3");
        ldapEnv.put(Context.SECURITY_PRINCIPAL, USERNAME + "@" + AUTHENTICATION_DOMAIN);
        ldapEnv.put(Context.SECURITY_CREDENTIALS, OLD_PASSWORD);
        ldapEnv.put(Context.SECURITY_PROTOCOL, "ssl");
        ldapEnv.put("java.naming.ldap.factory.socket", "io.fouad.ldap.MySSLSocketFactory");
        //ldapEnv.put("com.sun.jndi.ldap.connect.timeout", LDAP_CONNECT_TIMEOUT_MS);
        //ldapEnv.put("com.sun.jndi.ldap.read.timeout", LDAP_READ_TIMEOUT_MS);
        DirContext ldapContext = null;

        try
        {
            ldapContext = new InitialDirContext(ldapEnv);
        }
        catch(AuthenticationException e)
        {
            System.out.println("Wrong username/password!");
            e.printStackTrace();
        }
        catch(NamingException e)
        {
            e.printStackTrace();
        }

        if(ldapContext == null) return;


        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        NamingEnumeration objects = null;
        try
        {
            objects = ldapContext.search(TARGET_BASE_DN, String.format("(&(objectClass=user)(sAMAccountName=%s))", USERNAME), searchControls);
        }
        catch(NamingException e)
        {
            e.printStackTrace();
        }

        if(objects == null) return;

        try
        {
            if(objects.hasMore())
            {
                SearchResult entry = (SearchResult) objects.next();
                ModificationItem[] mods = new ModificationItem[2];
                mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, new BasicAttribute("unicodePwd", getPasswordByteArray(OLD_PASSWORD)));
                mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE, new BasicAttribute("unicodePwd", getPasswordByteArray(NEW_PASSWORD)));
                ldapContext.modifyAttributes(entry.getName() + "," + TARGET_BASE_DN, mods);

                System.out.println("Successfully changed the password!");
            }
            else
            {
                System.out.println("User (" + USERNAME + ") was not found!");
            }
        }
        catch(NamingException e)
        {
            e.printStackTrace();
        }

        System.out.println("DONE!");

    }

    private static byte[] getPasswordByteArray(String password)
    {
        String quotedPassword = "\"" + password + "\"";
        try
        {
            return quotedPassword.getBytes("UTF-16LE");
        }
        catch(UnsupportedEncodingException e)
        {
            e.printStackTrace();
            return null;
        }
    }
}

MySSLSocketFactory.java: (Use it at your own risk)

package io.fouad.ldap;

import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;

public class MySSLSocketFactory extends SSLSocketFactory
{
    private SSLSocketFactory socketFactory;

    public MySSLSocketFactory()
    {
        try
        {
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, new TrustManager[] {new X509TrustManager()
            {
                @Override
                public void checkClientTrusted(X509Certificate[] x509Certificates, String s){}

                @Override
                public void checkServerTrusted(X509Certificate[] x509Certificates, String s){}

                @Override
                public X509Certificate[] getAcceptedIssuers()
                {
                    return new X509Certificate[0];
                }
            }}, new SecureRandom());

            socketFactory = ctx.getSocketFactory();
        }
        catch(Exception ex)
        {
            ex.printStackTrace(System.err);
        }
    }

    public static SocketFactory getDefault()
    {
        return new MySSLSocketFactory();
    }

    @Override
    public String[] getDefaultCipherSuites()
    {
        return socketFactory.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites()
    {
        return socketFactory.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException
    {
        return socketFactory.createSocket(socket, string, i, bln);
    }

    @Override
    public Socket createSocket(String string, int i) throws IOException
    {
        return socketFactory.createSocket(string, i);
    }

    @Override
    public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException
    {
        return socketFactory.createSocket(string, i, ia, i1);
    }

    @Override
    public Socket createSocket(InetAddress ia, int i) throws IOException
    {
        return socketFactory.createSocket(ia, i);
    }

    @Override
    public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException
    {
        return socketFactory.createSocket(ia, i, ia1, i1);
    }
}

Upvotes: 4

jwilleke
jwilleke

Reputation: 10986

We have a reference for Java fro JNDI here http://ldapwiki.willeke.com/wiki/Set%20Active%20Directory%20Password%20From%20Java

Upvotes: 1

duffymo
duffymo

Reputation: 308763

Spring has an LDAP module that works very nicely. I'll bet it will do what you need.

Upvotes: 2

Martin v. L&#246;wis
Martin v. L&#246;wis

Reputation: 127457

You cannot change the password of a user by just modifying the property that stores it. Instead, you need to use a special LDAP operation SetPassword. I couldn't find a Java reference, but a C# one, and a Perl one.

Upvotes: 0

Related Questions